类对象是单例吗?
Are classobjects singletons?
如果我们有x = type(a)
和x == y
,是否一定意味着x is y
?
这是一个反例,但它是作弊:
>>> class BrokenEq(type):
... def __eq__(cls, other):
... return True
...
>>> class A(metaclass=BrokenEq):
... pass
...
>>> a = A()
>>> x = type(a)
>>> x == A, x is A
(True, True)
>>> x == BrokenEq, x is BrokenEq
(True, False)
而且我无法创建这样的反例:
>>> A1 = type('A', (), {})
>>> A2 = type('A', (), {})
>>> a = A1()
>>> x = type(a)
>>> x == A1, x is A1
(True, True)
>>> x == A2, x is A2
(False, False)
为了澄清我的问题 - 在不覆盖相等性 运算符的情况下做一些疯狂的事情,class 是否有可能存在于两个不同的内存位置或导入系统以某种方式阻止了这个?
如果是这样,我们如何证明这种行为 - 例如,用 reload 或 __import__
做奇怪的事情?
如果不是,是否由语言保证或在任何地方记录?
结语:
# thing.py
class A:
pass
最后,这就是为我阐明真实行为的原因(并且它支持 Blckknght 回答中的声明)
>>> import sys
>>> from thing import A
>>> a = A()
>>> isinstance(a, A), type(a) == A, type(a) is A
(True, True, True)
>>> del sys.modules['thing']
>>> from thing import A
>>> isinstance(a, A), type(a) == A, type(a) is A
(False, False, False)
因此,虽然使用 importlib.reload
的代码可能会破坏 class 标识的类型检查,但它也会破坏 isinstance
无论如何。
不,除了使用 metaclass __eq__
方法之外,无法创建两个比较相等但不相同的 class 对象。
虽然这种行为并不是 classes 独有的。对于没有在 class 中定义 __eq__
方法的任何对象,这是默认行为。该行为继承自 object
,它是所有其他(新样式)class 的基础 class。它仅被具有其他语义的内置类型覆盖(例如,比较其内容的容器类型)和自定义 classes 定义了自己的 __eq__
运算符。
至于在不同的内存位置获得对相同 class 的两个不同引用,由于 Python 的对象语义,这实际上是不可能的。对象的内存位置 是 它的标识(至少在 cpython 中)。另一个具有相同内容的 class 可以存在于其他地方,但就像在您的 A1
和 A2
示例中一样,它将被所有 Python 逻辑视为不同的对象。
我不知道有任何关于 ==
如何适用于类型的文档,但它肯定适用于身份。可以看到CPython 2.7 implementation是一个指针比较:
static PyObject*
type_richcompare(PyObject *v, PyObject *w, int op)
{
...
/* Compare addresses */
vv = (Py_uintptr_t)v;
ww = (Py_uintptr_t)w;
switch (op) {
...
case Py_EQ: c = vv == ww; break;
在CPython 3.5中,type
没有实现自己的tp_richcompare
,所以它继承了object
的默认相等比较,这是一个指针比较:
PyTypeObject PyType_Type = {
...
0, /* tp_richcompare */
如果我们有x = type(a)
和x == y
,是否一定意味着x is y
?
这是一个反例,但它是作弊:
>>> class BrokenEq(type):
... def __eq__(cls, other):
... return True
...
>>> class A(metaclass=BrokenEq):
... pass
...
>>> a = A()
>>> x = type(a)
>>> x == A, x is A
(True, True)
>>> x == BrokenEq, x is BrokenEq
(True, False)
而且我无法创建这样的反例:
>>> A1 = type('A', (), {})
>>> A2 = type('A', (), {})
>>> a = A1()
>>> x = type(a)
>>> x == A1, x is A1
(True, True)
>>> x == A2, x is A2
(False, False)
为了澄清我的问题 - 在不覆盖相等性 运算符的情况下做一些疯狂的事情,class 是否有可能存在于两个不同的内存位置或导入系统以某种方式阻止了这个?
如果是这样,我们如何证明这种行为 - 例如,用 reload 或 __import__
做奇怪的事情?
如果不是,是否由语言保证或在任何地方记录?
结语:
# thing.py
class A:
pass
最后,这就是为我阐明真实行为的原因(并且它支持 Blckknght 回答中的声明)
>>> import sys
>>> from thing import A
>>> a = A()
>>> isinstance(a, A), type(a) == A, type(a) is A
(True, True, True)
>>> del sys.modules['thing']
>>> from thing import A
>>> isinstance(a, A), type(a) == A, type(a) is A
(False, False, False)
因此,虽然使用 importlib.reload
的代码可能会破坏 class 标识的类型检查,但它也会破坏 isinstance
无论如何。
不,除了使用 metaclass __eq__
方法之外,无法创建两个比较相等但不相同的 class 对象。
虽然这种行为并不是 classes 独有的。对于没有在 class 中定义 __eq__
方法的任何对象,这是默认行为。该行为继承自 object
,它是所有其他(新样式)class 的基础 class。它仅被具有其他语义的内置类型覆盖(例如,比较其内容的容器类型)和自定义 classes 定义了自己的 __eq__
运算符。
至于在不同的内存位置获得对相同 class 的两个不同引用,由于 Python 的对象语义,这实际上是不可能的。对象的内存位置 是 它的标识(至少在 cpython 中)。另一个具有相同内容的 class 可以存在于其他地方,但就像在您的 A1
和 A2
示例中一样,它将被所有 Python 逻辑视为不同的对象。
我不知道有任何关于 ==
如何适用于类型的文档,但它肯定适用于身份。可以看到CPython 2.7 implementation是一个指针比较:
static PyObject*
type_richcompare(PyObject *v, PyObject *w, int op)
{
...
/* Compare addresses */
vv = (Py_uintptr_t)v;
ww = (Py_uintptr_t)w;
switch (op) {
...
case Py_EQ: c = vv == ww; break;
在CPython 3.5中,type
没有实现自己的tp_richcompare
,所以它继承了object
的默认相等比较,这是一个指针比较:
PyTypeObject PyType_Type = {
...
0, /* tp_richcompare */