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。
  • 一般如果两个对象的所有属性相同时,尽管他们的名字不同,那么他们还是相等的。所以就是需要判断所有属性相同与否的方法。

例子:

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

 

 

 

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 装饰的方法是删除属性值的方法。
上一篇:数据库--外键--用法/缺点


下一篇:C# 整个网页保存成图片