掌握一些判断对象相等时用的关键字,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 装饰的方法是删除属性值的方法。