Heim  >  Artikel  >  Backend-Entwicklung  >  Tiefer Einblick in Python-Klassen und Metaklassen II

Tiefer Einblick in Python-Klassen und Metaklassen II

高洛峰
高洛峰Original
2016-11-22 17:06:191227Durchsuche

Gehen wir eine Ebene zurück und sehen uns an, wie das Klassenobjekt selbst generiert wird.

Wir wissen, dass die Methode type() den Typ eines Objekts überprüfen oder bestimmen kann, aus welcher Klasse das Objekt generiert wird:

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

Pass Wie aus diesem Code ersichtlich ist, wird Klassenobjekt A von type() generiert, was bedeutet, dass dieser Typ auch zum Generieren neuer Objekte verwendet werden kann und was generiert wird, ein Klassenobjekt, also die Klasse aller Klassenobjekte ist :

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

class Die Syntax zum Definieren einer Klasse wird tatsächlich in type(name, bases, dict) konvertiert, wobei der Name-Parameter der Name der Klasse und bases ein erbendes Tupel ist die übergeordnete Klasse, und dict ist das Attribut und die Methode:

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

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

Theoretisch ist dies die Bedeutung der Metaklasse, aber aus praktischer Sicht ist die Verwendung offensichtlich bequemer und sinnvoller Klassensyntax, und die eigentliche Bedeutung von Metaklasse besteht darin, den Klassentyp zu erben, um eine neue Metaklasse zu erstellen und bestimmte Operationen auszuführen, um Klassenobjekte mit bestimmtem Verhalten zu erzeugen. Unter diesem Gesichtspunkt unterscheidet sich sein Wesen nicht von gewöhnlichen Klassenobjekten, außer dass es den Typ Klasse erbt.

Beim Generieren einer Instanz wird diese durch Aufrufen der Methode __init__ initialisiert. Tatsächlich wird zuvor die Methode __new__ aufgerufen, um die Instanz zu erstellen, und dann durch __init__ initialisiert, als ob __new__ für die Deklaration von Variablen verantwortlich wäre und __init__ ist für die Initialisierung deklarierter Variablen verantwortlich. Hier gilt die Regel, dass der Rückgabewert von __new__(cls,) eine Instanz des cls-Parameters sein muss, andernfalls wird __init__ nicht ausgelöst. Beispielsweise in der Definition von enum.Enum, da der Aufzählungstyp ein Singleton-Modus ist , also in der Definition von __new__ Wenn seine Instanz nicht zurückgegeben wird, wird sie nicht initialisiert:

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

Wenn Sie __new__ selbst definieren, müssen Sie normalerweise eine Instanz von cls erstellen, indem Sie aufrufen __new__-Methode der übergeordneten Klasse und auch beim Definieren der Metaklasse Beim Aufrufen der oben erwähnten Verwendung des Typs (da die Metaklasse vom Typ erbt):

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}

Zu diesem Zeitpunkt schauen wir bei der Enum-Klasse, die nicht mehr Typ ist, sondern deren Metaklasse MetaEnum:

type(Enum)
__main__.MetaEnum

Zusätzlich zur __new__-Methode, PEP 3115 Definiert auch das Attribut __prepare__ zum Festlegen des initialisierten Namespace (d. h. des dritten Parameters des Typs). Am Beispiel von enum.Enum müssen wir die Wiederverwendung der Attributnamen im Aufzählungstyp einschränken und dann das Verhalten einschränken der Klasse durch Metaklassen

# 定义新的字典类,在赋值新的 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

Alle Objekte sind Instanzen einer bestimmten Klasse oder eine Instanz eines bestimmten Metaklassentyps ist eine eigene Metaklasse und eine eigene Instanz.

Tiefer Einblick in Python-Klassen und Metaklassen II

Zusammenfassung

Metaklassen sind in Python relativ tiefschwarze Magie und werden in allgemeinen Alltagsanwendungen möglicherweise nicht häufig verwendet, aber das Verständnis der dahinter stehenden Prinzipien ist dafür unerlässlich Das Verständnis der objektorientierten Programmierung in Python ist hilfreich. Wenn Sie tiefgreifende Änderungen an einer Klasse vornehmen müssen, wissen Sie zumindest, wo Sie anfangen müssen.


Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn