最近在学习元类的知识。我了解到
isinstance(object, type)
和issubclass(type, object)
。我想写像object
和type
这样的自定义类行为,但是发生循环依赖时如何声明类?伪代码如下:
declare class MyType
class MyObject(metaclass=MyType):
pass
class MyType(MyObject, type):
pass
如上面非常恰当的评论所述:
“”“ 你不能,不是在 Python 中。您可能能够使用 C 扩展,但 Python 使用引导程序来设置该依赖项,这涉及改变在正常情况下不打算改变的内部数据。 – 西尔维奥马约洛 “”.
好吧,这虽然是正确的,但并不完全正确——Python 动态允许以有限的形式改变类基类和继承,甚至对象类,after 创建一个类或对象。
这意味着可以在纯 Python 代码中执行部分相同的引导。
Python 中有一个 second 硬编码关系,您在问题中没有提到:
type
是自己的元类 - 也就是说 type
是 type
的一个实例:真的无法模拟在纯 Python 代码中。然而,type
和 object
的相互关系可以。同样,这没有任何实际用途,并且 object
和 type
始终作为基类和基元类保持在循环中:没有它们你真的做不了任何事情。
就是说,检查下面的例子。
首先是
type
非常独特,不是因为与 object
的这种关系,而是因为它完成了在引擎盖下实际在 Python 中创建类的所有工作 - 这是很多事情。最终,所有自定义元类都在某个时候调用 type.__new__
来实际创建一个类型 - 所以虽然您的 MyType
可以继承自 MyObject
,但它也必须保留来自 type
的继承(作为其中之一基地) - 这样就可以完成班级创建工作。 (好的 - 最终,有一种方法可以解决它,覆盖正确的方法,并代理对type
的调用 - 但让我们推迟)。
事实是:要获得这种鸡蛋-母鸡关系,您必须至少更改一个对象after它被创建。
类类(它的元类)创建后不能修改:
class A: pass
class M(A, type): pass
A.__class__ = M
会提高
TypeError: __class__ assignment only supported for mutable types or ModuleType subclasses
。但反过来实际上是可能的:
class M(type): pass
class A(metaclass=M): pass
M.__bases__ = (A, type)
正如我在上面所写的,需要将
type
作为 M 的基础之一 - 试图将其从作业中的基础中删除,将提出:TypeError: __bases__ assignment: 'A' object layout differs from 'type'
。但是做 M.__bases__ = (A, type)
工作,并且在这样做之后:
In [18]: isinstance(A, M)
Out[18]: True
In [19]: issubclass(M, A)
Out[19]: True
现在,对于第二部分,如果您根本不想
M
从 type
继承,只需在 __new__
上实现 __call__
和 M
就足以拥有一个“工作元类”。
但是,没有办法将 M
作为调用 type.__new__
的第一个参数传递:Python 在内部检查此调用中传递的元类是 type
的“真实”子类。这意味着当您随后请求 A
的类时,它不会使用普通方法说它是“M 类”——但是,我们可以通过实施 __instancecheck__
特殊方法来“伪造”它“元类的元类”中的方法。
请注意,由于
__bases__
assignmen 中的一些限制,以及 __instancecheck__
必须存在于我们要检查其实例的类的元类上(因此,在我们的元类的元类中),我们需要现在还有 2 个中级班。
__instancecheck__
和 __subclasscheck__
中的代码可以调整以执行一些“真实检查” - 只需返回“True”就足以用于演示目的:
class A: pass
class M(type):
def __instancecheck__(self, instance):
return True
def __subclasscheck__(self, subclass):
return True
class N(A, metaclass=M):
def __new__(mcls, name, bases, ns):
cls = type.__new__(type, name, bases, ns) # <- here, M can't be the first argument, as it does not inherit from "type"
return cls
def __call__(cls, *args, **kw):
instance = cls.__new__(cls, *args, **kw)
if isinstance(instance, cls):
instance.__init__(*args, **kw)
return instance
class B(metaclass=N): pass
N.__bases__ = (N,)
现在,有了不从类型继承的元类,您可以:
In [42]: isinstance(B, N)
Out[42]: True
In [43]: issubclass(N, B)
Out[43]: True
但是,如果通过
type
进行测试,您将不会看到 N
作为元类:
In [44]: type(B)
Out[44]: type