29

我读过这样的帖子:

  1. Python 中的元类是什么?
  2. Python 中元类的(具体)用例是什么?
  3. Python 的 Super 很漂亮,但你不能用

但不知何故,我感到困惑。许多困惑,例如:

我何时以及为什么必须执行以下操作?

# Refer link1
return super(MyType, cls).__new__(cls, name, bases, newattrs)

或者

# Refer link2
return super(MetaSingleton, cls).__call__(*args, **kw)

或者

# Refer link2
return type(self.__name__ + other.__name__, (self, other), {})

super 究竟是如何工作的?

什么是链接 1 中的类注册和取消注册,它是如何工作的?(我认为这与singleton有关。我可能是错的,来自 C 背景。我的编码风格仍然是函数式和 OO 的混合体)。

类实例化(子类、元类、超类、类型)和方法调用(

metaclass->__new__, metaclass->__init__, super->__new__, subclass->__init__ inherited from metaclass

) 带有注释良好的工作代码(虽然第一个链接非常接近,但它没有谈论 cls 关键字和 super(..) 和注册表)。最好是具有多重继承的示例。

PS:我将最后一部分作为代码制作,因为堆栈溢出格式正在将文本转换metaclass->__new__ 为元类->

4

2 回答 2

24

好的,您在这里混合了很多概念!我将提出您提出的一些具体问题。

一般来说,理解 super、MRO 和 metaclasses 变得更加复杂,因为在 Python 的最后几个版本中,这个棘手的领域发生了很多变化。

Python 自己的文档是一个很好的参考,并且完全是最新的。有一篇IBM developerWorks 文章作为介绍很好,它采用了更多基于教程的方法,但请注意,它已有 5 年历史,并且花了很多时间讨论元类的旧式方法。

super是您访问对象的超类的方式。它比(例如)Java 的super关键字更复杂,主要是因为 Python 中的多重继承。正如Super Considered Harmful解释的那样,使用super()可能会导致您隐式使用一系列超类,其顺序由方法解析顺序(MRO) 定义。

mro()您可以通过调用类(而不是实例)轻松查看类的 MRO 。请注意,元类不在对象的超类层次结构中。

Thomas在这里对元类的描述非常好:

元类是类的类。就像类定义类的实例的行为方式一样,元类定义类的行为方式。类是元类的一个实例。

在您提供的示例中,发生了以下情况:

  1. 对 MRO 中的下一件事的呼唤__new__正在冒泡。在这种情况下,super(MyType, cls)将解析为type; 调用type.__new__让 Python 完成它的正常实例创建步骤。

  2. 此示例使用元类来强制执行单例。他在元类中进行了覆盖__call__,因此每当创建类实例时,他都会拦截该实例,并且如果已经有一个实例(存储在 中cls.instance),则可以绕过实例创建。请注意,__new__在元类中覆盖还不够好,因为只有在创建时才会调用它。但是,覆盖 __new__类会起作用。

  3. 这显示了一种动态创建类的方法。这是他将提供的类名称附加到创建的类名称,并将其添加到类层次结构中。

我不确定您正在寻找什么样的代码示例,但这里有一个显示元类、继承和方法解析的简短示例:

print('>>> # Defining classes:')

class MyMeta(type):
    def __new__(cls, name, bases, dct):
        print("meta: creating %s %s" % (name, bases))
        return type.__new__(cls, name, bases, dct)

    def meta_meth(cls):
        print("MyMeta.meta_meth")

    __repr__ = lambda c: c.__name__

class A(metaclass=MyMeta):
    def __init__(self):
        super(A, self).__init__()
        print("A init")

    def meth(self):
        print("A.meth")

class B(metaclass=MyMeta):
    def __init__(self):
        super(B, self).__init__()
        print("B init")

    def meth(self):
        print("B.meth")

class C(A, B, metaclass=MyMeta):
    def __init__(self):
        super(C, self).__init__()
        print("C init")

print('>>> c_obj = C()')
c_obj = C()
print('>>> c_obj.meth()')
c_obj.meth()
print('>>> C.meta_meth()')
C.meta_meth()
print('>>> c_obj.meta_meth()')
c_obj.meta_meth()

示例输出(使用 Python >= 3.6):

>>> # Defining classes:
meta: creating A ()
meta: creating B ()
meta: creating C (A, B)
>>> c_obj = C()
B init
A init
C init
>>> c_obj.meth()
A.meth
>>> C.meta_meth()
MyMeta.meta_meth
>>> c_obj.meta_meth()
Traceback (most recent call last):
File "metatest.py", line 41, in <module>
    c_obj.meta_meth()
AttributeError: 'C' object has no attribute 'meta_meth'
于 2008-12-28T11:36:08.443 回答
9

这是更务实的答案。

很少有关系

  1. 什么是 Python 中的元类”。底线type是所有类的元类。您对此几乎没有实际用途。

    class X(object):
        pass
    type(X) == type
    
  2. Python 中元类的(具体)用例是什么? ”。底线。没有任何。

  3. Python 的 Super 很漂亮,但你不能用它”。有趣的笔记,但实用价值不大。您永远不需要解决复杂的多重继承网络。通过使用显式策略设计而不是多重继承,很容易防止出现此问题。

这是我过去 7 年的 Python 编程经验。

  1. 一个类有 1 个或多个超类,形成从我的类到object.

  2. “类”的概念是由一个名为 的元类定义的type。我可能想扩展“类”的概念,但到目前为止,它从未在实践中出现过。不止一次。 type总是做正确的事。

  3. 在实践中使用super效果非常好。它允许子类遵循它的超类。它恰好出现在这些元类示例中,因为它们扩展了内置的元类,type.

    但是,在所有子类情况下,您都将使用super来扩展超类。

元类

元类问题是这样的:

  • 每个对象都有对其类型定义或“类”的引用。

  • Aclass本身也是一个对象。

  • 因此,类型的对象class具有对其类型或“类”的引用。“类”的“类”是元类。

由于“类”不是 C++ 运行时对象,因此在 C++ 中不会发生这种情况。它确实发生在 Java、Smalltalk 和 Python 中。

元类定义了类对象的行为。

  • 你与一个类的 90% 的交互是要求类创建一个新对象。

  • 10% 的时间,您将使用类方法或类变量(C++ 或 Java 用语中的“静态”。)

我发现了一些类级方法的用例。我几乎没有类变量的用例。我从来没有改变过对象构造的工作方式。

于 2008-12-28T14:11:03.110 回答