Python 内存别名(简单)
Python memory aliasing (simple)
我想知道这是怎么发生的,为什么会发生。有人可以解释一下吗?
联系人是我自己的 class,我在编辑器中制作的。
想让它们指向同一个内存地址怎么办?
通过实现 __eq__
(文档链接):
为对象定义等价性 (==
) 非常简单
class Contact:
def __init__(self, phone, name):
self.phone = phone
self.name = name
def __eq__(self, other):
return (self.phone == other.phone) and (self.name == other.name)
c1 = Contact('647-000-000', 'Andy')
print(c1 == Contact('647-000-000', 'Andy')) # True
在这里,__eq__
是一种接受一个参数(它被比较的东西)的方法并且应该 return True
或 False
(或可强制的东西)一个布尔值)。这是一个非常简单的 "flimsy" 实现,因为像 c1 == "string"
这样的东西会抛出错误,但它演示了这个想法。
但是,您没有可以实施的神奇方法来定义引用相等性 (is
)。通常,您应该想到 is
用于测试它们是否 完全相同的 对象,而 ==
用于测试两个对象是否 相当于.
[1] 是的,通过缓存和使用元类或 __new__
、
还是有可能的
好吧,基本上 Python 有别名。并检查 2 个对象是否是您编写的彼此的别名
obj1 is obj2
如果 returns 为真,则它们是别名。好吧,基本上 Python 检查两个对象 id。上面的语句相当于:
id(obj1) == id(obj2)
id(object)
用于计算对象在内存中的地址。
当你写作时b is Contact('647-000-000', 'Andy')
b 已经在内存中有一个地址,但是当你将它与 Contact('647-000-000', 'Andy')
进行比较时,Contact('647-000-000', 'Andy')
在内存中有一个不同的地址。
我不明白你为什么要比较整个 class 而不是属性。
b.number == '647-000-000'
和 b.name == 'Andy'
将解决您的问题。
每次您调用 Contact()
都会创建一个新实例,即使您向它传递相同的参数也是如此。 Python 如何知道您希望具有相同参数的联系人实际上是同一个对象?一般来说,这是不可取的。如果你想要同一个实例的两个名字,只需做一个简单的赋值,例如
c1 = Contact('647-000-000', 'Andy')
c2 = c1
如果你真的确实想要两次(或更多次)调用Contact()
并使用与return相同的args相同的对象,你可以给__new__
构造一个缓存,例如functools.lru_cache
。这是一个简短的演示。
from functools import lru_cache
class Contact:
@lru_cache(None)
def __new__(cls, *args):
return super().__new__(cls)
def __init__(self, phone, name):
self.phone = phone
self.name = name
def __str__(self):
return f'Contact({self.phone}, {self.name})'
c1 = Contact('647-000-000', 'Andy')
c2 = Contact('647-000-001', 'Amos')
c3 = Contact('647-000-000', 'Andy')
for c in (c1, c2, c3):
print(c, repr(c))
输出
Contact(647-000-000, Andy) <__main__.Contact object at 0xb7285d4c>
Contact(647-000-001, Amos) <__main__.Contact object at 0xb7285dac>
Contact(647-000-000, Andy) <__main__.Contact object at 0xb7285d4c>
当你调用Contact
时,会调用它的__new__
方法来构造新的实例对象。然后将该对象传递给 __init__
进行初始化。在大多数classes中,没有定义__new__
方法,所以调用父class的__new__
方法,通常是默认的__new__
继承来自 object
基地 class。
在上面的代码中我们定义了__new__
并用lru_cache
修饰了它。因此,当我们调用 Contact()
时,Contact
类型和任何其他 args 都由 lru_cache
处理,它维护着我们创建的所有 Contact
实例的不可见字典,由传递的 args 键入为__new__
(包括Contact
类型)。如果该键在字典中,相应的实例将被 __new__
编辑 return。否则,将分配一个新的 Contact
并将其添加到 dict
。在任何一种情况下,实例都会被传递给 __init__
进行初始化。
以上代码是概念验证。我不 推荐在实际代码中这样做。由 lru_cache
维护的不可见 dict
保留对您创建的每个联系人的引用,因此当它们(似乎)超出范围时它们不会被删除,即使您将它们传递给 del
,直到程序终止。要强制删除联系人,您需要将其从缓存中清除,您可以使用:
Contact.__new__.cache_clear()
但当然会清除整个缓存。
我想知道这是怎么发生的,为什么会发生。有人可以解释一下吗?
联系人是我自己的 class,我在编辑器中制作的。
想让它们指向同一个内存地址怎么办?
通过实现 __eq__
(文档链接):
==
) 非常简单
class Contact:
def __init__(self, phone, name):
self.phone = phone
self.name = name
def __eq__(self, other):
return (self.phone == other.phone) and (self.name == other.name)
c1 = Contact('647-000-000', 'Andy')
print(c1 == Contact('647-000-000', 'Andy')) # True
在这里,__eq__
是一种接受一个参数(它被比较的东西)的方法并且应该 return True
或 False
(或可强制的东西)一个布尔值)。这是一个非常简单的 "flimsy" 实现,因为像 c1 == "string"
这样的东西会抛出错误,但它演示了这个想法。
但是,您没有可以实施的神奇方法来定义引用相等性 (is
)。通常,您应该想到 is
用于测试它们是否 完全相同的 对象,而 ==
用于测试两个对象是否 相当于.
[1] 是的,通过缓存和使用元类或 __new__
、
好吧,基本上 Python 有别名。并检查 2 个对象是否是您编写的彼此的别名
obj1 is obj2
如果 returns 为真,则它们是别名。好吧,基本上 Python 检查两个对象 id。上面的语句相当于:
id(obj1) == id(obj2)
id(object)
用于计算对象在内存中的地址。
当你写作时b is Contact('647-000-000', 'Andy')
b 已经在内存中有一个地址,但是当你将它与 Contact('647-000-000', 'Andy')
进行比较时,Contact('647-000-000', 'Andy')
在内存中有一个不同的地址。
我不明白你为什么要比较整个 class 而不是属性。
b.number == '647-000-000'
和 b.name == 'Andy'
将解决您的问题。
每次您调用 Contact()
都会创建一个新实例,即使您向它传递相同的参数也是如此。 Python 如何知道您希望具有相同参数的联系人实际上是同一个对象?一般来说,这是不可取的。如果你想要同一个实例的两个名字,只需做一个简单的赋值,例如
c1 = Contact('647-000-000', 'Andy')
c2 = c1
如果你真的确实想要两次(或更多次)调用Contact()
并使用与return相同的args相同的对象,你可以给__new__
构造一个缓存,例如functools.lru_cache
。这是一个简短的演示。
from functools import lru_cache
class Contact:
@lru_cache(None)
def __new__(cls, *args):
return super().__new__(cls)
def __init__(self, phone, name):
self.phone = phone
self.name = name
def __str__(self):
return f'Contact({self.phone}, {self.name})'
c1 = Contact('647-000-000', 'Andy')
c2 = Contact('647-000-001', 'Amos')
c3 = Contact('647-000-000', 'Andy')
for c in (c1, c2, c3):
print(c, repr(c))
输出
Contact(647-000-000, Andy) <__main__.Contact object at 0xb7285d4c>
Contact(647-000-001, Amos) <__main__.Contact object at 0xb7285dac>
Contact(647-000-000, Andy) <__main__.Contact object at 0xb7285d4c>
当你调用Contact
时,会调用它的__new__
方法来构造新的实例对象。然后将该对象传递给 __init__
进行初始化。在大多数classes中,没有定义__new__
方法,所以调用父class的__new__
方法,通常是默认的__new__
继承来自 object
基地 class。
在上面的代码中我们定义了__new__
并用lru_cache
修饰了它。因此,当我们调用 Contact()
时,Contact
类型和任何其他 args 都由 lru_cache
处理,它维护着我们创建的所有 Contact
实例的不可见字典,由传递的 args 键入为__new__
(包括Contact
类型)。如果该键在字典中,相应的实例将被 __new__
编辑 return。否则,将分配一个新的 Contact
并将其添加到 dict
。在任何一种情况下,实例都会被传递给 __init__
进行初始化。
以上代码是概念验证。我不 推荐在实际代码中这样做。由 lru_cache
维护的不可见 dict
保留对您创建的每个联系人的引用,因此当它们(似乎)超出范围时它们不会被删除,即使您将它们传递给 del
,直到程序终止。要强制删除联系人,您需要将其从缓存中清除,您可以使用:
Contact.__new__.cache_clear()
但当然会清除整个缓存。