• Day 17:Python 对象的相等性比较


    掌握一些判断对象相等时用的关键字,Python 中,对象相等性比较相关关键字包括 is、in,比较运算符有 ==

    is 关键字判断标识号是否相等

    is 比较的是两个对象的标识号是否相等,标识号的获取用id()函数。

    a=[1,2,3,4,5]
    b=[1,2,3,4,5]
    print(id(a))
    print(id(b))
    a is b

    output:
    7696573018440
    7696556316232
    False

    由于这两个list实例化与不同的内存地址,标识号不相等,那么两个空list实例,会不会相等呢?实则不然。

    c,d = [], []
    c is d
    
    output:
    
    False

    对于序列型、字典型、集合型对象,只有当一个对象实例指向另一个对象实例时候,is的比较结果才返回true.

    a,b = [1,2,3],{'a':111,'2':222}
    print(a is b)
    a = b
    print(a,b,a is b)
    
    output:
    False
    {'a': 111, '2': 222} {'a': 111, '2': 222} True

    但是对于数值类型,结果可能会不太一样,看编译器。

     a = 1
     b = 1
     print(a is b)
     c = 65535
     d = 65535
     print(c is d)

    output:
    True

    Python 解释器,对位于区间 [-5,256] 内的小整数,会进行缓存,不在该范围内的不会缓存,所以才出现上面的现象。

    还需要注意的是,python中有一个None对象,具有唯一,不变的标识号(当前环境下)

    print(id(None))
    a = None
    print(a is None) #判断某个对象是否为None
    print(id(None))
    
    output:
    17106540000
    True
    17106540000

    in 关键字用于成员检测

    • 如果元素 i 是 s 的成员,则 i in s 为 True;
    • 若不是 s 的成员,则返回 False,也就是 i not in s 为 True。
    • python内置的序列类型、字典类型和集合类型,都支持 in 操作。
    • 字典使用,in 操作判断 i 是否是字典的键。
    • 如果是字符串s,判断子串i的操作,也就是 s.find(i) 返回大于 - 的值。
    • 对于自定义类型,判断是否位于序列类型中,需要重写序列类型的__contains__。
    # 根据 Student 类的 name 属性(所以要重写__contains__),判断某 Student 是否在 Students 序列对象中。
    
    # 自定义 Student 类,无特殊之处
    class Student():
        def __init__(self,name):
            self._name = name
    
        @property
        def name(self):
            return self._name
    
        @name.setter
        def name(self,val):
            self._name = val
    # Students 类继承 list,并重写 __contains__ 方法
    class Students(list):
            def __contains__(self,_):
                for s in self:
                    if s.name ==_.name:
                        return True
                return False
    
    s1 = Student('xiaoming')
    s2 = Student('xiaohong')
    a = Students()
    a.extend([s1,s2])#list方法
    s3 = Student('xiaoming')
    print(s3 in a) # True
    s4 = Student('xiaoli')
    print(s4 in a) # False
    
    output:
    True
    False

    == 关键字判断值是否相等

    • 对于数值型、字符串、列表、字典、集合,默认只要元素值相等,== 比较结果是 True。
    • 对于自定义类型,当所有属性取值完全相同的两个实例,判断 == 时,返回 False。
    • 一般如果两个对象的所有属性相同时,尽管他们的名字不同,那么他们还是相等的。所以就是需要判断所有属性相同与否的方法。

    例子:

    class Student():
        def __init__(self,name,age):
            self._name = name
            self._age = age
    
        @property
        def name(self):
            return self._name    
        @name.setter
        def name(self,val):
            self._name = val
    
        @property
        def age(self):
            return self._age    
        @age.setter
        def age(self,val):
            self._age = val
       # 重写方法 __eq__,使用 __dict__ 获取实例的所有属性。
        def __eq__(self,val):
            print(self.__dict__)
            return self.__dict__ == val.__dict__
        
    a = []
    xiaoming = Student('xiaoming',29)
    if xiaoming not in a:
        a.append(xiaoming)
        
    xiaohong = Student('xiaohong',30)
    if xiaohong not in a:
        a.append(xiaohong)
        
    xiaoming2 = Student('xiaoming',29)
    if xiaoming2 == xiaoming:
        print('对象完全一致,相等')
        
    if xiaoming2 not in a:
        a.append(xiaoming2)
    
    print(len(a))
    
    output:
    {'_name': 'xiaohong', '_age': 30}
    {'_name': 'xiaoming', '_age': 29}
    对象完全一致,相等
    {'_name': 'xiaoming', '_age': 29}
    2

    Bonus - @property说明

    对于类的变量,有私有和全局,看例子

    class Menber(object):
        def __init__(self, name, sexual):
            self.__name = name
            self.__sexual = sexual
    
        def get_sexual_fun(self):
             return self.__sexual
    
        def set_sexual_fun(self, value):
            if value not in ['male','female']:
                raise ValueError('sexual must be male or female.')
            self.__sexual = value
    
        def print_info(self):
            print('%s: %s' % (self.__name, self.__sexual))
    
    

    p = Menber('linda','female')
    p.__sexual = 'male'
    print(p.__sexual) # male
    print(p.get_sexual_fun()) # female
    p.print_info()
    p.set_sexual_fun('male')
    print(p.get_sexual_fun()) # male
    p.print_info()


    output:
    male
    female
    linda: female
    male
    linda: male
     
     

    使用一个set来设置变量,使用一个get来获取变量,就可以达到检查参数的目的。

    还有重要的一点,表面上在外部看似把sexual设置成了另一个值,但其实并没有改变内部的值,

    这个p.__sexual = 'male'和内部的self.__sexual = 'female'压根就是两个东西。

    内部的_sexual 变量已经被Python解释器自动改成了_Person_sexual,而外部代码给p新增了一个_sexual变量。 所以调用get_sexual_fun输出的是初始值 female.

    而set_sexual_fun通过class内部改变了_sexual变量值,所以最终输出 linda: male

      现在,只是改私有变量为全局变量,看效果:

    class Menber(object):
        def __init__(self, name, sexual):
            self._name = name
            self._sexual = sexual
    
        def get_sexual_fun(self):
             return self._sexual
    
        def set_sexual_fun(self, value):
            if value not in ['male','female']:
                raise ValueError('sexual must be male or female.')
            self._sexual = value
    
        def print_info(self):
            print('%s: %s' % (self._name, self._sexual))
    
    p = Menber('linda','female')
    p._sexual = 'male'
    print(p._sexual) # male
    print(p.get_sexual_fun()) # female
    p.print_info()
    p.set_sexual_fun('male')
    print(p.get_sexual_fun()) # male
    p.print_info()
    
    output:
    male
    male
    linda: male
    male
    linda: male

    因为此时_sexual是全局变量,外部直接影响到类内部的更新值

    那么怎样才能避免这种繁琐的方式呢?

    有没有可以用类似属性这样简单的方式来访问类的变量呢?

    答:装饰器@property

    class Menber(object):
        def __init__(self, name, sexual):
            self.__name = name
            self.__sexual = sexual
    
        @property
        def get_sexual_fun(self):
             return self.__sexual
    
        @get_sexual_fun.setter # get_sexual_fun
        def set_sexual_fun(self, value):
            if value not in ['male','female']:
                raise ValueError('sexual must be male or female.')
            self.__sexual = value
    
        def print_info(self):
            print('%s: %s' % (self.__name, self.__sexual))
    
    p = Menber('linda','female')
    p.__sexual = 'male'
    print(p.__sexual) # male
    print(p.get_sexual_fun) # female   这里不能带()  如果带()要报错
    
    p.set_sexual_fun = 'male'
    print(p.get_sexual_fun) # male
    p.print_info()
    
    output:
    male
    female #尽管没有改变内部的值
    male # 但是这样的设置使得能够检查参数
    linda: male

    这样,既能检查参数,又可以用类似属性这样简单的方式来访问类的变量。

    @property本身又创建了另一个装饰器@get_sexual_fun.setter,负责把一个setter方法变成属性赋值,于是,我们就拥有一个可控的属性操作:

    class Menber(object):
        def __init__(self, name, sexual):
            self.__name = name
            self.__sexual = sexual
    
        @property
        def get_sexual_fun(self):
             return self.__sexual
    
        @get_sexual_fun.setter # get_sexual_fun
        def set_sexual_fun(self, value):
            if value not in ['male','female']:
                raise ValueError('sexual must be male or female.')
            self.__sexual = value
    
        def print_info(self):
            print('%s: %s' % (self.__name, self.__sexual))
            
    q = Menber('david', 'male')
    q.__sexual 
    
    output:
    ---------------------------------------------------------------------------
    AttributeError                            Traceback (most recent call last)
    <ipython-input-64-116170232977> in <module>()
         18 
         19 q = Menber('david', 'male')
    ---> 20 q.__sexual
    
    AttributeError: 'Menber' object has no attribute '__sexual'

    意到这个神奇的@property,我们在对实例属性操作的时候,就知道该属性很可能不是直接暴露的

    总的来说:使用 property 工具,它把方法包装成属性,让方法可以以属性的形式被访问和调用。

    • 被 @property 装饰的方法是获取属性值的方法,被装饰方法的名字会被用做 属性名。
    • 被 @属性名.setter 装饰的方法是设置属性值的方法。
    • 被 @属性名.deleter 装饰的方法是删除属性值的方法。
  • 相关阅读:
    Sqlite框架Delphi10.3(07)
    FastReport 6.8.11在Delphi10.3上的安装
    Delphi 中,InputQuery 函数的妙用
    Javaday25(sax解析xml,json,设计模式)
    并发学习第五篇——volatile关键字
    并发学习第四篇——synchronized关键字
    并发学习第二篇——Thread
    并发学习第一篇——Runnable
    git-仓库迁移并保留commit log
    并发编程理论基础二——画图理解
  • 原文地址:https://www.cnblogs.com/PiaYie/p/14972208.html
Copyright © 2020-2023  润新知