python关于metaclass元类

1.元类的相关说明

By default, classes are constructed using type(). The class body is executed in a new namespace and the class name is bound locally to the result of type(name, bases, namespace).
The class creation process can be customized by passing the metaclass keyword argument in the class definition line, or by inheriting from an existing class that included such an argument. In the following example, both MyClass and MySubclass are instances of Meta:

class Meta(type):
    pass

class MyClass(metaclass=Meta):
    pass

class MySubclass(MyClass):
    pass

默认情况下,类是通过type()函数构建的,类体在新的命名空间中运行并且类名绑定到type运行的结果上.
类的创建还可通过在类的定义行传递metaclass关键字参数来定制, 或者从具有metaclass参数的父类来继承; 在上面例子中,Myclass, Mysubclass都是元类Meta的实例.

Any other keyword arguments that are specified in the class definition are passed through to all metaclass operations described below.
When a class definition is executed, the following steps occur:
•MRO entries are resolved
•the appropriate metaclass is determined
•the class namespace is prepared
•the class body is executed
•the class object is created
在类定义中指定的任何其他关键字参数将传递给下面描述的所有元类操作,类的定义主要经过五步:

1). Resolving MRO entries
If a base that appears in class definition is not an instance of type, then an __ mro_entries__ method is searched on it. If found, it is called with the original bases tuple. This method must return a tuple of classes that will be used instead of this base. The tuple may be empty, in such case the original base is ignored.

2).Determining the appropriate metaclass
The appropriate metaclass for a class definition is determined as follows:
•if no bases and no explicit metaclass are given, then type() is used
•if an explicit metaclass is given and it is not an instance of type(), then it is used directly as the metaclass
•if an instance of type() is given as the explicit metaclass, or bases are defined, then the most derived metaclass is used
The most derived metaclass is selected from the explicitly specified metaclass (if any) and the metaclasses (i.e. type(cls)) of all specified base classes. The most derived metaclass is one which is a subtype of all of these candidate metaclasses. If none of the candidate metaclasses meets that criterion, then the class definition will fail with TypeError.
选择合适的元类规则:
-如果没有基类和显性的元类被给出,则使用type()
-如果显性的元类被给出并且他不是type()的实例,那将直接被用作元类
-如果type()的实例被当作元类给出,或者定义了基类,那么衍生最多的元类被选择
派生最多的元类是从明确指定的元类(如果有)和所有指定基类的元类(即类型(cls))中选择的。 衍生最多的元类是所有这些候选元类的子类型。 如果没有候选元类符合该标准,则类定义将因TypeError而失败。

3)Preparing the class namespace
Once the appropriate metaclass has been identified, then the class namespace is prepared. If the metaclass has a __ prepare__ attribute, it is called as namespace = metaclass.__ prepare__(name, bases, **kwds) (where the additional keyword arguments, if any, come from the class definition).
If the metaclass has no __ prepare__ attribute, then the class namespace is initialised as an empty ordered mapping.

一旦识别出适当的元类,就会准备类命名空间。 如果元类具有__ prepare__属性,则将其称为namespace = metaclass .__ prepare __(name,bases,** kwds)(其中附加关键字参数(如果有)来自类定义).
如果元类没有__prepare__属性,则类命名空间初始化为空的有序映射。

4)The class body is executed (approximately) as exec(body, globals(), namespace). The key difference from a normal call to exec() is that lexical scoping allows the class body (including any methods) to reference names from the current and outer scopes when the class definition occurs inside a function.
However, even when the class definition occurs inside the function, methods defined inside the class still cannot see names defined at the class scope. Class variables must be accessed through the first parameter of instance or class methods, or through the implicit lexically scoped __ class__ reference described in the next section.
类主体(大致上)执行为exec(body,globals(),namespace)。 与正常调用exec()的关键区别在于,当类定义发生在函数内部时,词法作用域允许类主体(包括任何方法)引用当前和外部作用域的名称(变量)。
但是,即使类定义发生在函数内部,类中定义的方法仍然无法访问到在类范围内定义的名称。 必须通过实例或类方法的第一个参数或通过下一节中描述的隐式词法作用域__class__引用来访问类变量

示例1:

a ='shit'
class Fuck:
    b = 'hhh'
    def test(self):
        # print(b)  # b无法引用
        print(a)

t = Fuck()
print(t.test())
#result
shit
None

5)Creating the class object
Once the class namespace has been populated by executing the class body, the class object is created by calling metaclass(name, bases, namespace, **kwds) (the additional keywords passed here are the same as those passed to __ prepare__).
This class object is the one that will be referenced by the zero-argument form of super(). __ class__ is an implicit closure reference created by the compiler if any methods in a class body refer to either __ class__ or super. This allows the zero argument form of super() to correctly identify the class being defined based on lexical scoping, while the class or instance that was used to make the current call is identified based on the first argument passed to the method.

In CPython 3.6 and later, the __ class__ cell is passed to the metaclass as a __ classcell__ entry in the class namespace. If present, this must be propagated up to the type.__ new__ call in order for the class to be initialised correctly. Failing to do so will result in a DeprecationWarning in Python 3.6, and a RuntimeError in Python 3.8.

When using the default metaclass type, or any metaclass that ultimately calls type.__ new__, the following additional customisation steps are invoked after creating the class object:
•first, type.__ new__ collects all of the descriptors in the class namespace that define a __ set_name__() method;
•second, all of these __ set_name__ methods are called with the class being defined and the assigned name of that particular descriptor; and
•finally, the __ init_subclass__() hook is called on the immediate parent of the new class in its method resolution order.

After the class object is created, it is passed to the class decorators included in the class definition (if any) and the resulting object is bound in the local namespace as the defined class.
When a new class is created by type.__ new__, the object provided as the namespace parameter is copied to a new ordered mapping and the original object is discarded. The new copy is wrapped in a read-only proxy, which becomes the __ dict__ attribute of the class object.

2.元类的应用

6)The potential uses for metaclasses are boundless. Some ideas that have been explored include enum, logging, interface checking, automatic delegation, automatic property creation, proxies, frameworks, and automatic resource locking/synchronization.

示例2: 自定制元类

class Meta(type):
    
    def __init__(self, a, b, c):  # a:类名  b:继承的父类  c:属性字典
        print('metaclass', a, b, c)

    def __call__(self, *args, **kwargs):
        print('call is running')
        obj = object.__new__(self)  # 产生一个self类的实例
        self.__init__(obj, *args, **kwargs)
        return obj


class Test(metaclass=Meta):
    def __init__(self, a, b):
        self.a = a
        self.b = b

    def shit(self):
        print('Test.shit')


t = Test('a', 'b')  # 实例化时调用元类的__call__方法
print(t.__dict__)

#result
metaclass Test () {'__module__': '__main__', '__qualname__': 'Test', '__init__': <function Test.__init__ at 0x00000234DE449840>, 'shit': <function Test.shit at 0x00000234DE4498C8>}
call is running
{'a': 'a', 'b': 'b'}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值