好的,显然,__new__在元类中,当元类的实例(即类对象被实例化)时运行,因此__new__在元类中提供了一个钩子来在类定义时拦截事件(/运行的代码)(例如,验证/执行类属性的规则,例如如方法等)。
元类中的许多在线示例__new__返回来自 的类型构造函数的实例__new__,这似乎有点问题,因为这会阻止__init__(文档:“如果__new__()不返回 cls 的实例,则新的实例\xe2\x80\x99s__init__()方法将不会被调用”)。
在修改元类中的返回值时,__new__我遇到了一些我不完全理解的奇怪情况,例如:
class Meta(type):\n \n def __new__(self, name, bases, attrs):\n print("Meta __new__ running!")\n # return type(name, bases, attrs) # 1. \n # return super().__new__(self, name, bases, attrs) # 2.\n # return super().__new__(name, bases, attrs) # 3. \n # return super().__new__(type, name, bases, attrs) # 4. \n # return self(name, bases, attrs) # 5.\n\n def __init__(self, *args, **kwargs):\n print("Meta __init__ running!")\n return super().__init__(*args, **kwargs)\n \nclass Cls(metaclass=Meta):\n pass\nRun Code Online (Sandbox Code Playgroud)\n__init____init__也会触发;但为什么要将 self 传递给 super() 调用呢?self/cls 不应该通过 super() 自动传递吗?type.__new__(X) : X is not a type object (str); X是什么?self 不应该自动传递吗?__init__。这里发生了什么?特别是情况 1. 和 2. 似乎对于从绑定到元类的类继承具有相当深远的影响:
\nclass LowerCaseEnforcer(type):\n """ Allows only lower case names as class attributes! """\n\n def __new__(self, name, bases, attrs): \n for name in attrs:\n if name.lower() != name:\n raise TypeError(f"Inappropriate method name: {name}")\n \n # return type(name, bases, attrs) # 1.\n # return super().__new__(self, name, bases, attrs) # 2.\n\n class Super(metaclass=LowerCaseEnforcer):\n pass\n \n class Sub(Super):\n \n def some_method(self):\n pass\n \n ## this will error in case 2 but not case 1\n def Another_method(self):\n pass\nRun Code Online (Sandbox Code Playgroud)\n如果有人能慢慢地、友善地解释上面的例子到底发生了什么,我将不胜感激!:D
\n它比你得到的也更简单。
正如您所指出的,正确的做法是2上面的内容:
return super().__new__(self, name, bases, attrs) # 2.
Run Code Online (Sandbox Code Playgroud)
是这样的:__new__是一个特殊的方法 - 尽管在某些文档中,甚至是官方文档的一部分,它被描述为 a classmethod,但事实并非如此:它作为一个对象,行为更像是静态方法 - 在某种意义上当调用时,Python 不会自动填充第一个参数MyClass.__new__()- 即,您必须调用MyClass.__new__(MyClass)它才能工作。(我退一步 - 该信息适用于所有类:元类和普通类)。
当您调用MyClass()创建新实例时,Python 将调用MyClass.__new__并插入该cls参数作为第一个参数。
对于元类,创建元类新实例的调用是由class语句及其类主体的执行触发的。同样,Python 将第一个参数填充到Metaclass.__new__,传递元类本身。
当您super().__new__从元类中调用时__new__,您处于与手动调用相同的情况:必须显式填充指定应应用__new__哪个类的参数。__new__
现在,让您感到困惑的是您正在将第一个参数写入__new__as self- 如果它是元类的实例(即普通类),那么这是正确的。实际上,该参数是对元类本身的引用。
文档没有告知元类的第一个参数的官方或推荐名称__new__,但通常它是一致的mcls, mcs, metaclass, metacls- 使其不同于 cls非元类方法的第一个参数的通常名称__new__。在元类中,“类” -cls是通过最终调用type.__new__(硬编码或使用super())创建的,它的返回是新生类(可以在__new__调用超类后在方法中进一步修改) ) - 返回后,对 的调用__init__正常进行。
因此,我将进一步评论尝试调用type(name, bases, namespace)而不是type.__new__(mcls, name, bases, namespace):第一种形式将只创建一个普通类,就好像根本没有使用元类一样 - (元类中__new__修改命名空间或基类的行,当然,有它们的效果。但是生成的类将具有type其元类,并且它的子类根本不会调用元类。(根据记录,它充当“类前装饰器” - 可以作用于类创建之前的参数,它甚至可以是一个普通的函数,而不是具有方法的类-毕竟__new__调用将创建新类)type
检查元类是否“绑定”到您的类的一个简单方法是使用type(MyClass)或检查其类型MyClass.__class__。