Jean's Blog

一个专注软件测试开发技术的个人博客

0%

Python对象间的相等性比较使用总结

Python中,对象相等性比较相关关键字包括isin,比较运算符有==

  • is判断两个对象的标识号是否相等
  • in用于成员检测
  • ==用于判断值或内容是否相等,默认是基于两个对象的标识号比较

也就是说,如果a is b为True且如果按照默认行为,意味着a==b也为True。

is判断标识号是否相等

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

1
2
3
4
5
6
>>> a = [ 1, 2, 3]
>>> id(a)
4355817792
>>> b = [ 1, 2, 3]
>>> id(b)
4355822976

上面创建的两个列表实例位于不同的内存地址,所以它们的标识号不等。

1
2
>>> a is b
False

即便对于两个空列表实例,它们is比较的结果也是False:

1
2
3
>>> a, b = [], []
>>> a is b
False

数据类型不同

序列型、字典型、集合型

对于序列型、字典型、集合型对象,一个对象实例指向另一个对象实例,is比较才返回真值。

1
2
3
4
>>> a, b = { 'a':[1,2,3]},{'id':'book id', 'price':'book price'}
>>> a = b
>>> a is b
True

值类型

对于值类型而言,不同的编译器可能会做不同的优化。从性能角度考虑,它们会缓存一些值类型的对象实例。所以,使用is比较时,返回的结果看起来会有些不太符合预期。以下有两种情况,同样的整数值,使用is得到不同结果

情况一

1
2
3
4
>>> a = 123
>>> b = 123
>>> a is b
True

情况二

1
2
3
4
>>> c = 123456
>>> d = 123456
>>> c is d
False

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

None

Python中None对象是一个单例类的实例,具有唯一的标识号,如下所示:

1
2
>>> id(None)
4359531472

在判断某个对象是否为None时,最便捷的做法:variable is None,如下所示:

1
2
3
4
5
>>> a = None
>>> a is None
True
>>> id(a)
4359531472

in用于成员检测

  • 如果元素i是s的成员,则i in s为True
  • 若不是s的成员,则返回False,也就是i not in s为True

对于字符串类型,i in s为True,意味着i是s的子串,也就是s.find(i)返回大于-1的值。

1
2
3
4
5
6
7
8
>>> 'ab' in 'abc'
True
>>> 'abc'.find('ab')
0
>>> 'ab' in 'acb'
False
>>> 'abc'.find('ac')
-1

内置的序列类型、字典类型和集合类型

内置的序列类型、字典类型和集合类型,都支持in操作。对于字典类型,in操作判断i是否是字典的键值。

1
2
3
4
>>> [1, 2] in [[1, 2], 'str']
True
>>> 'apple' in { 'orange':1.5, 'banana':2.3, 'apple':5.2 }
True

自定义类型

对于自定义类型,判断是否位于序列类型中,需要重写序列类型的魔法方法__contains__

具体操作步骤如下:

  1. 自定义Student类
  2. Students类继承list,并重写__contains__方法

根据Student类的name属性,判断某Student是否在Students序列对象中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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


class Students(list):
def __contains__(self, stu):
for s in self:
if s.name == stu.name:
return True
return False

Student、Students类的示意图:

image-20220418141345247

1
2
3
4
5
6
7
8
9
10
s1 = Student('xiaoming')
s2 = Student('xiaohong')

a = Students()
a.extend([s1, s2])

s3 = Student('xiaoming')
print(s3 in a) # 输出True
s4 = Student('xiali')
print(s4 in a) # 输出False

使用自定义类,s3的名字与列表a中的第一个元素s1重名,所以s3 in a返回True。

s4不在列表a中,所以in返回False。

==判断值是否相等

数值型、字符串、列表、字典、集合

对于数值型、字符串、列表、字典、集合,默认只要元素值相等,==比较结果是True。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
>>> str1 = "alg-channel"
>>> str2 = "alg-channel"
>>> str1 == str2
True
>>> a = [ 1, 2, 3 ]
>>> b = [ 1, 2, 3 ]
>>> a == b
True
>>> c = [ 1, 3, 2 ]
>>> a == c
False
>>> a = { 'a': 1.0, 'b': 2.0 }
>>> b = { 'a': 1.0, 'b': 2.0 }
>>> a == b
True
>>> c = ( 1, 2 )
>>> d = ( 1, 2 )
>>> c == d
True
>>> c = { 1, 2, 3 }
>>> d = { 1, 3, 2 }
>>> c == d
True

自定义类型

对于自定义类型,当所有属性取值完全相同的两个实例,判断==时,返回False。

但是,大部分场景下,我们希望这两个对象是相等的,这样不用重复添加到列表中。如:判断用户是都已经登入时,只要用户所有属性与登入列表中某个用户完全一致时,就认为已经登入。

如下实例,自定义类型重写方法__eq__,使用__dict__获取实例的所有属性。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
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

def __eq__(self, val):
print(self.__dict__)
return self.__dict__ == val.__dict__

Student类的示意图

image-20220419125440050

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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))

执行结果

1
2
3
4
5
6
7
8
/Users/lvjing/PycharmProjects/python_base_project/venv/bin/python /Users/lvjing/PycharmProjects/python_base_project/object_sample_02.py
{'_name': 'xiaoming', '_age': 29}
{'_name': 'xiaoming', '_age': 29}
对象完全一致,相等
{'_name': 'xiaoming', '_age': 29}
2

Process finished with exit code 0

第三个实例 xiaoming2 与已添加到列表 a 中的 xiaoming 属性完全一致,所以 == 比较或 in 时,都会返回 True。

image-20220419130127654