在py中,有两个特别的运算符,is和==分别用来判断两个变量是不是相同的和两个变量的值是不是相同。
1. is运算符:用来比较两个对象的身份,即判断两个变量是否指向内存中的同一个对象。
应用场景:1)单例模式:在需要确保整个程序中某个类只有一个实例的情况下,is运算符可以用来检查是否确实只有一个实例被创建。
2)检查None值:由于None在py中是单例的,因此使用is运算符来检查变量是否为None是最佳实践。
3)小整数和短字符串缓存:py为了提高效率,对一定范围内的小整数和短字符串实现了缓存。这意味着,在这个范围内的整数和字符串在赋值给不同变量时,实际上是指向内存中的同一个对象。此时,使用is运算符可以判断这些变量是否指向相同的对象。
原理:py中的每个对象都有一个唯一的标识符(通常是一个整数),称为id. 当创建新对象时,py会自动分配一个唯一的id. is运算符通过比较两个对象的id值来判断它们是否相同。如果id值相同,则这两个对象在内存中实际上是同一个对象;如果id值不同,则它们是不同的对象,即使它们的内容相同。
举例:
a = 'hello'
b = 'hello'
print(a is b)
print(id(a), id(b))
返回:
True
2715382857968 2715382857968
这里a is b返回True, 代表a,b两个变量是相等的,它们指向同一个内存地址,通过id()函数打印出它们的id, 也发现它们的id相同。
2. ==运算符:用来比较两个对象的值(value)是否相同,而不是它们的身份。它会调用对象的__eq__方法(如果该方法被定义)来执行比较操作。
应用场景:1)比较基本数据类型:如整数、浮点数、字符串等,使用==比较它们的值是否相等。2)比较自定义对象:在自定义类中,可以重写__eq__方法来定义对象之间的等价性判断逻辑。
3)容器类型比较:如列表、元组,字典等,==运算符会比较这些容器的内容是否相等,而不是它们的身份。
原理:==运算符通过对象的__eq__方法(或等价地,对于某些类型,可能直接比较内部状态)来比较这两个对象的值,如果__eq__方法返回True,则认为这两个对象相等;否则,认为它们不相等。需要注意的是,并不是所有的对象都有__eq__方法,对于没有定义__eq__方法的对象,使用==运算符可能会导致TypeError.
举例:
def is_equal_int():
n = 123
m = 123
print(n == m)
print(n is m)
print(id(n),id(m))
输出:
True
True
3052616945712 3052616945712
从上面这个例子,可以看出来整数n, m的比较,通过==运算符可以看出它们的值是相等的。
def is_equal_list():
a = [1, 3, 4]
b = a
c = [1, 3, 4]
print(a == b)
print(a == c)
print(id(a),id(b),id(c))
print(a is b)
print(a is c)
输出:
True
True
2357738778304 2357738778304 2357738764416
True
False
从上面这个例子可以看出,a==b结果是True,a==c结果也是True,因为它们比较的是值,而a is b返回True,因为a,b指向内存的同一个对象,而a is c返回False,虽然它们的值相同,但是它们的id不同,即它们的身份不同。
def is_none():
x = None
y = None
print(x==y)
print(x is y)
输出:
True
True
因为None是单例,所有值为None的变量都指向内存的同一个对象。
3. is和==区别:1)性能差异:is比较通常比==比较更快,因为它只是比较两个对象的id值,这是一个简单的整数比较。==比较可能需要调用对象的__eq__方法,执行更复杂的逻辑,因此可能更慢。
注意事项:1)对于可变类型(如列表、字典等),即使两个对象的内容相同,使用is也可能得到False,因为它们指向的是不同的内存地址。
2)对于不可变类型(如整数、字符串、元组等),在特定情况下(如小整数和短字符串缓存),使用is可能会得到True,即使这些对象是通过不同的字面量创建的。然而,这种情况并不常见,且不应作为编写代码时的依赖。