Home  >  Article  >  Backend Development  >  Deep Digging into Python Classes and Metaclasses II

Deep Digging into Python Classes and Metaclasses II

高洛峰
高洛峰Original
2016-11-22 17:06:191181browse

Let’s go back one level and see how the class object itself is generated.

We know that the type() method can check the type of an object, or determine which class this object is generated from:

print(type(12))
print(type('python'))
<class &#39;int&#39;>
<class &#39;str&#39;>
class A:
    passprint(type(A))
<class &#39;type&#39;>

It can be seen from this code that the class object A is generated by type(), also That is to say, type can also be used to generate new objects, and what is generated is a class object, so it is the class of all class objects:

print(type.__doc__)
type(object_or_name, bases, dict)
type(object) -> the object&#39;s type
type(name, bases, dict) -> a new type

class The syntax for defining a class is actually converted to type(name, bases, dict), where The name parameter is the name of the class, bases is the tuple that inherits the parent class, and dict is the attributes and methods of the class:

class A:
    pass# 实际上等于B = type(&#39;A&#39;, (), {})

print(A.__name__ == B.__name__)
True

Theoretically, this is the meaning of metaclass, but from a practical point of view, it is obviously more convenient to use class syntax. Reasonable, and the actual meaning of a metaclass is to construct a new metaclass by inheriting the type class and perform specific operations to produce class objects with specific behaviors. From this point of view, its essence is no different from ordinary class objects, except that it inherits the type class.

When generating an instance, it is initialized by calling the __init__ method. In fact, the __new__ method is called first to create the instance, and then initialized by __init__. It is like __new__ is responsible for declaring variables, and __init__ is responsible for declaring variables. Same as initialization. There is a rule here that the return value of __new__(cls,) must be an instance of the cls parameter, otherwise __init__ will not be triggered. For example, in the definition of enum.Enum, since the enumeration type is a singleton mode, so in the definition of __new__ When its instance is not returned, it will not be initialized:

class Enum:
    def __new__(cls, value):
        print(cls, value)
        return value
    def __init__(self):
        print("Will not be called!")
e = Enum(1)
<class &#39;__main__.Enum&#39;> 1

Normally, when defining __new__ yourself, you need to create an instance of cls by calling the __new__ method of the parent class. Similarly, when defining a metaclass, you call the above mentioned Usage of type (because the metaclass inherits from type):

class MetaEnum(type):
    def __new__(metaclass, name, base, attrs):
        print("Metaclass: {}\nName: {}\nParents: {}\nAttributes: {}".format(metaclass, name, base, attrs))
        return super().__new__(metaclass, name, base, attrs)
class Enum(metaclass=MetaEnum):
    # Python 2.7 中定义元类的方法是使用 __metaclass__ 变量
    # [PEP 3115](https://www.python.org/dev/peps/pep-3115/)
    # 将 Python 3.0 以后语法改为 class Cls(metaclass=Meta)
    test = 0
Metaclass: <class &#39;__main__.MetaEnum&#39;>
Name: Enum
Parents: ()
Attributes: {&#39;__qualname__&#39;: &#39;Enum&#39;, &#39;__module__&#39;: &#39;__main__&#39;, &#39;test&#39;: 0}

At this time, let’s look at the class of Enum. It is no longer type but its metaclass MetaEnum:

type(Enum)
__main__.MetaEnum

Except In addition to the __new__ method, PEP 3115 also defines the __prepare__ attribute, which is used to set the initialized namespace (that is, the third parameter of type). Taking enum.Enum as an example, we need to restrict the attribute names in the enumeration type from being repeated. If you use it, you can limit the behavior of the class through the metaclass

# 定义新的字典类,在赋值新的 dict[k] = v 时
# 检查 k 是否重复
class _EnumDict(dict):
    def __init__(self):
        super().__init__()
        self.members = []
    def __setitem__(self, k, v):
        if k in self.members:
            raise TypeError("Attempted to reuse key: &#39;{}&#39;".format(k))
        else:
            self.members.append(k)
            super().__setitem__(k, v)

class MetaEnum(type):
    @classmethod
    def __prepare__(metaclass, cls, bases):
        return _EnumDict()
    def __new__(metaclass, name, base, attrs):
        return super().__new__(metaclass, name, base, attrs)
class Enum(metaclass=MetaEnum):
    pass

class Color(Enum):
    try:
        red = 1
        red = 2
    except TypeError:# 这里没有使用 as err: 的原因是?
        print("TypeError catched")
TypeError catched

Everything in Python is an object. All objects are instances of a certain class or an instance of a certain metaclass. type is its own metaclass and its own instance. :

Deep Digging into Python Classes and Metaclasses II

Summary

Metaclasses are relatively deep black magic in Python and may not be commonly used in ordinary daily applications, but understanding the principles behind them is crucial to understanding Python object-oriented programming and the idea that everything is an object. Ideas are helpful; if you need to make deep changes to a class, at least you know where to start.


Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn