闽公网安备 35020302035485号
一 Python 中的对象 ID
我们在学习基础的时候没听说 Python 有 C 或 C++ 中的指针啊,Python 中指针是什么?先把指针这个概念放一放,一提到指针可能初学 C 和 C++ 的人都害怕(本人也害怕),先来理解一下 Python 里面对象的本质。比如下面的代码,当声明 a = 100 和 b = 100 的时候,能发现 id(a) == id(b),为什么 a 和 b 的 id 值是一样的呢?
>>> a = 100 >>> b = 100 >>> id(a) 4343720720 >>> id(b) 4343720720 >>>我们来看一下这个图:

>>> a, b = 10, 20 >>> id(a) 4343717840 >>> id(b) 4343718160 >>> a, b = b, a >>> id(a) 4343718160 >>> id(b) 4343717840 >>>当声明 a = 10 和 b = 20 的时候,情况发生了改变,这个过程其实也好理解,就是相当于小 a 和小 b 分别看中了不同的房间(小 a 看中的是门牌号 4343717840 的房间,小 b 看中的是门牌号 4343718160 ),当他们住下来后,这个房间存着不同数据( a=10, b=20 )。

>>> a is b如果这两个对象确实是内存中的同一个对象将返回 True, 否则不一致就会返回 False 。
>>> a = ["Learning", "Python"] >>> id(a) 1769202632064 >>> b = a >>> id(a), id(b) (1769202632064, 1769202632064) >>>我们定义了一个 a 列表,然后通过 b = a 建立指向 a 的新指针,当我们检查他们的 id 时,发现它们的结果是相同的 —— 1769202632064。接下来,我们使用 copy() 函数来看一下会不会有不同的结果:
>>> c = a.copy() >>> id(a), id(c) (1769202632064, 1769171462656) >>>如上所示,a 和 c 有着不同的 id,说明调用 copy() 函数完全生成了一个全新的列表对象。
>>> a is b True >>>然而,这还不是故事的结局。 Python 有另一种更灵活的方式来定义对象相等性。
>>> a == b True >>>就和 is 一样,== 比较符也是返回 True 或 False。但是 Python 决定返回哪个的方式与 is 运算符不同,这意味着 == 和 is 可以为相同的对象提供不同的结果。
>>> a = ["Learning", "Python"] >>> b = a >>> c = a.copy() >>> a == b True >>> a is b True >>> a == c True >>> a is c False >>>将看到 a ==c 为 True, 反而 a is c 为 False。为什么?原因是:
class MyEqualClass:
def __eq__(self, other):
return self is other
然后进行测试:>>> class MyEqualClass:
def __eq__(self, other):
return self is other
>>> a = MyEqualClass()
>>> a == 'Hello World'
False
>>>
然后这会调用 MyEqualClass 的 __eq__ 方法,其中 a 作为 self 参数,字符串 'Hello World' 作为另一个参数。在上面的例子中,MyClass.__eq__ 使用了 is 比较器,相当于比较每个对象的 id。这实际上是 Python 中任何用户定义类的默认行为。如果我们想自定义一个类,所以对比的对象都相等,就可以这样写:class MyAlwaysTrueClass:
def __init__(self, name):
self.name = name
def __eq__(self, other):
return True
因为我们重写了 __eq__ 方法以始终返回 True,这意味着在 == 比较器下,此类的所有实例都将被视为相等,即使它们的名称具有不同的值!>>> jane = MyAlwaysTrueClass("Jane")
>>> bob = MyAlwaysTrueClass("Bob")
>>> jane.name == bob.name
False
>>> jane == bob
True
此外,与不同类型的对象相比,我们还可以看到 MyAlwaysTrueClass 的实例表现得很奇怪:>>> jane = MyAlwaysTrueClass("Jane")
>>> jane == "some string"
True
>>> jane == None
True
>>> jane == True
True
当将 MyAlwaysTrueClass 的实例与任何东西(不仅仅是同一个类的实例)进行比较时,它将返回 True。这是因为,正如我们之前所说,__eq__ 方法默认情况下不检查被比较对象的类型。那如果我们写一个功能相反的 __eq__ 的 MyAlwaysFalseClass 呢?class MyAlwaysFalseClass:
def __init__(self, name):
self.name = name
def __eq__(self, other):
return False
你可能觉得这很合理,但是:>>> class MyAlwaysFalseClass:
def __init__(self, name):
self.name = name
def __eq__(self, other):
return False
>>> a = MyAlwaysFalseClass("name")
>>> a == a
False
上述的列子,a == a 的结果是 False,那是因为我们在任何类型作对比中都会返回 Flase。>>> jane = MyAlwaysTrueClass("Jane")
>>> bob = MyAlwaysFalseClass("Bob")
>>> jane == bob
True
>>> bob == jane
False
>>>
这是因为我们比较 jane 和 bob 的顺序很重要。当我们执行 jane == bob 时,我们使用 MyAlwaysTrueClass 中定义的 __eq__ 方法:jane 是 self 而 bob 是 other,并且该方法总是返回 True。当我们写 bob == jane 时,情况正好相反,所以我们得到该表达式的 False 值。总而言之:魔术方法是一种有趣的方式,它可以让 Python 做一些看起来很奇怪的事情,并且应该自行承担修改的风险!