Python中的元類是什麼?



6 Answers

類作為對象

在理解元類之前,您需要掌握Python中的類。 Python從Smalltalk語言中藉用了一些非常奇怪的概念,

在大多數語言中,類只是描述如何生成對象的代碼段。 這在Python中也是如此:

>>> class ObjectCreator(object):
...       pass
...

>>> my_object = ObjectCreator()
>>> print(my_object)
<__main__.ObjectCreator object at 0x8974f2c>

但是類不僅僅是Python。 類也是對象。

是的,對象。

只要您使用關鍵字class ,Python就會執行它並創建一個OBJECT。 指令

>>> class ObjectCreator(object):
...       pass
...

在內存中創建一個名為“ObjectCreator”的對象。

這個對象(類)本身能夠創建對象(實例),這就是為什麼它是一個類

但是,它仍然是一個客體,因此:

  • 你可以將它分配給一個變量
  • 你可以復制它
  • 你可以給它添加屬性
  • 你可以將它作為函數參數傳遞

例如:

>>> print(ObjectCreator) # you can print a class because it's an object
<class '__main__.ObjectCreator'>
>>> def echo(o):
...       print(o)
...
>>> echo(ObjectCreator) # you can pass a class as a parameter
<class '__main__.ObjectCreator'>
>>> print(hasattr(ObjectCreator, 'new_attribute'))
False
>>> ObjectCreator.new_attribute = 'foo' # you can add attributes to a class
>>> print(hasattr(ObjectCreator, 'new_attribute'))
True
>>> print(ObjectCreator.new_attribute)
foo
>>> ObjectCreatorMirror = ObjectCreator # you can assign a class to a variable
>>> print(ObjectCreatorMirror.new_attribute)
foo
>>> print(ObjectCreatorMirror())
<__main__.ObjectCreator object at 0x8997b4c>

動態創建類

由於類是對象,因此可以像任何對像一樣快速創建它們。

首先,您可以使用class在函數中創建一個class

>>> def choose_class(name):
...     if name == 'foo':
...         class Foo(object):
...             pass
...         return Foo # return the class, not an instance
...     else:
...         class Bar(object):
...             pass
...         return Bar
...
>>> MyClass = choose_class('foo')
>>> print(MyClass) # the function returns a class, not an instance
<class '__main__.Foo'>
>>> print(MyClass()) # you can create an object from this class
<__main__.Foo object at 0x89c6d4c>

但它並不那麼活躍,因為你還得自己寫全班。

由於類是對象,它們必須由某些東西生成。

當你使用class關鍵字時,Python會自動創建這個對象。 但是與Python中的大多數事情一樣,它為您提供了一種手動執行的方法。

記住功能type ? 良好的舊功能,讓你知道什麼類型的對像是:

>>> print(type(1))
<type 'int'>
>>> print(type("1"))
<type 'str'>
>>> print(type(ObjectCreator))
<type 'type'>
>>> print(type(ObjectCreator()))
<class '__main__.ObjectCreator'>

那麼, type具有完全不同的能力,它也可以即時創建類。 type可以將類的描述作為參數,並返回一個類。

(我知道,根據傳遞給它的參數,相同的函數可能有兩種完全不同的用法,這很愚蠢,由於Python的向後兼容性,這是一個問題)

type工作方式如下:

type(name of the class,
     tuple of the parent class (for inheritance, can be empty),
     dictionary containing attributes names and values)

例如:

>>> class MyShinyClass(object):
...       pass

可以通過以下方式手動創建:

>>> MyShinyClass = type('MyShinyClass', (), {}) # returns a class object
>>> print(MyShinyClass)
<class '__main__.MyShinyClass'>
>>> print(MyShinyClass()) # create an instance with the class
<__main__.MyShinyClass object at 0x8997cec>

您會注意到我們使用“MyShinyClass”作為類的名稱,並將其用作保存類引用的變量。 它們可以不同,但​​沒有理由使事情複雜化。

type接受一個字典來定義類的屬性。 所以:

>>> class Foo(object):
...       bar = True

可以翻譯成:

>>> Foo = type('Foo', (), {'bar':True})

並用作普通班級:

>>> print(Foo)
<class '__main__.Foo'>
>>> print(Foo.bar)
True
>>> f = Foo()
>>> print(f)
<__main__.Foo object at 0x8a9b84c>
>>> print(f.bar)
True

當然,你可以繼承它,所以:

>>>   class FooChild(Foo):
...         pass

將會:

>>> FooChild = type('FooChild', (Foo,), {})
>>> print(FooChild)
<class '__main__.FooChild'>
>>> print(FooChild.bar) # bar is inherited from Foo
True

最終你會想要添加方法到你的班級。 只需使用正確的簽名定義一個函數,並將其作為屬性進行分配。

>>> def echo_bar(self):
...       print(self.bar)
...
>>> FooChild = type('FooChild', (Foo,), {'echo_bar': echo_bar})
>>> hasattr(Foo, 'echo_bar')
False
>>> hasattr(FooChild, 'echo_bar')
True
>>> my_foo = FooChild()
>>> my_foo.echo_bar()
True

在動態創建類後,您可以添加更多方法,就像向通常創建的類對像中添加方法一樣。

>>> def echo_bar_more(self):
...       print('yet another method')
...
>>> FooChild.echo_bar_more = echo_bar_more
>>> hasattr(FooChild, 'echo_bar_more')
True

你可以看到我們要去的地方:在Python中,類是對象,並且可以動態地動態創建類。

這就是Python在使用關鍵字class時所做的事情,它通過使用元類來實現。

什麼是元類(最後)

元類是創建類的'東西'。

你可以定義類來創建對象,對嗎?

但是我們了解到Python類是對象。

好吧,元類是創建這些對象的東西。 他們是班級的班級,你可以這樣描繪他們:

MyClass = MetaClass()
my_object = MyClass()

你已經看到這種type可以讓你做這樣的事情:

MyClass = type('MyClass', (), {})

這是因為函數type實際上是一個元類。 type是Python用於在幕後創建所有類的元類。

現在你想知道為什麼它是用小寫寫的,而不是Type

那麼,我想這是一個與str一致的問題,創建字符串對象的類以及創建整數對象的類。 type只是創建類對象的類。

你可以通過檢查__class__屬性來看到。

一切,我的意思是一切,都是Python中的一個對象。 包括整數,字符串,函數和類。 所有這些都是對象。 所有這些都是從一個班級創建的:

>>> age = 35
>>> age.__class__
<type 'int'>
>>> name = 'bob'
>>> name.__class__
<type 'str'>
>>> def foo(): pass
>>> foo.__class__
<type 'function'>
>>> class Bar(object): pass
>>> b = Bar()
>>> b.__class__
<class '__main__.Bar'>

現在,什麼是__class____class__

>>> age.__class__.__class__
<type 'type'>
>>> name.__class__.__class__
<type 'type'>
>>> foo.__class__.__class__
<type 'type'>
>>> b.__class__.__class__
<type 'type'>

所以,元類就是創建類對象的東西。

如果你願意,你可以稱它為“班級工廠”。

type是Python使用的內置元類,但是當然,您可以創建自己的元類。

__metaclass__屬性

編寫一個類時,您可以添加__metaclass__屬性:

class Foo(object):
    __metaclass__ = something...
    [...]

如果你這樣做,Python將使用元類創建類Foo

小心,這很棘手。

您先寫class Foo(object) ,但類對象Foo不會在內存中創建。

Python會在類定義中尋找__metaclass__ 。 如果發現它,它將使用它來創建對像類Foo 。 如果沒有,它將使用type來創建類。

多讀幾次。

當你這樣做時:

class Foo(Bar):
    pass

Python執行以下操作:

Foo是否有__metaclass__屬性?

如果是,則在內存中創建一個類對象(我說的是一個類對象,留在這裡),名稱為Foo通過使用__metaclass__

如果Python找不到__metaclass__ ,它將在MODULE級別尋找__metaclass__ ,並嘗試做同樣的事情(但只適用於不繼承任何東西的類,基本上是舊式類)。

然後,如果它根本找不到任何__metaclass__ ,它將使用Bar的(第一個父)自己的元類(可能是默認type )來創建類對象。

請注意, __metaclass__屬性不會被繼承,父類( Bar.__class__ )的元類將會被繼承。 如果Bar使用了一個__metaclass__屬性,該屬性使用type() (而不是type.__new__() )創建Bar ,則該子類不會繼承該行為。

現在最大的問題是,你可以在__metaclass____metaclass__什麼?

答案是:可以創建一個類的東西。

什麼可以創建一個班級? type或任何子類或使用它的東西。

自定義元類

元類的主要目的是在創建時自動更改類。

您通常對API進行此操作,您需要創建與當前上下文匹配的類。

想像一個愚蠢的例子,你決定你模塊中的所有類都應該用大寫字母來寫屬性。 有幾種方法可以做到這一點,但一種方法是在模塊級設置__metaclass__

這樣,這個模塊的所有類都將使用這個元類創建,並且我們只需要告訴元類將所有屬性都轉換為大寫。

幸運的是, __metaclass__實際上可以是任何可調用的,它不需要是一個正式的類(我知道,名稱中有'class'的東西不需要成為類,但是這很有用)。

所以我們將從一個簡單的例子開始,通過使用一個函數。

# the metaclass will automatically get passed the same argument
# that you usually pass to `type`
def upper_attr(future_class_name, future_class_parents, future_class_attr):
    """
      Return a class object, with the list of its attribute turned
      into uppercase.
    """

    # pick up any attribute that doesn't start with '__' and uppercase it
    uppercase_attr = {}
    for name, val in future_class_attr.items():
        if not name.startswith('__'):
            uppercase_attr[name.upper()] = val
        else:
            uppercase_attr[name] = val

    # let `type` do the class creation
    return type(future_class_name, future_class_parents, uppercase_attr)

__metaclass__ = upper_attr # this will affect all classes in the module

class Foo(): # global __metaclass__ won't work with "object" though
    # but we can define __metaclass__ here instead to affect only this class
    # and this will work with "object" children
    bar = 'bip'

print(hasattr(Foo, 'bar'))
# Out: False
print(hasattr(Foo, 'BAR'))
# Out: True

f = Foo()
print(f.BAR)
# Out: 'bip'

現在,讓我們做同樣的事情,但使用一個真正的類作為元類:

# remember that `type` is actually a class like `str` and `int`
# so you can inherit from it
class UpperAttrMetaclass(type):
    # __new__ is the method called before __init__
    # it's the method that creates the object and returns it
    # while __init__ just initializes the object passed as parameter
    # you rarely use __new__, except when you want to control how the object
    # is created.
    # here the created object is the class, and we want to customize it
    # so we override __new__
    # you can do some stuff in __init__ too if you wish
    # some advanced use involves overriding __call__ as well, but we won't
    # see this
    def __new__(upperattr_metaclass, future_class_name,
                future_class_parents, future_class_attr):

        uppercase_attr = {}
        for name, val in future_class_attr.items():
            if not name.startswith('__'):
                uppercase_attr[name.upper()] = val
            else:
                uppercase_attr[name] = val

        return type(future_class_name, future_class_parents, uppercase_attr)

但這不是真正的OOP。 我們直接調用type ,並且不會覆蓋或調用父類__new__ 。 我們開始做吧:

class UpperAttrMetaclass(type):

    def __new__(upperattr_metaclass, future_class_name,
                future_class_parents, future_class_attr):

        uppercase_attr = {}
        for name, val in future_class_attr.items():
            if not name.startswith('__'):
                uppercase_attr[name.upper()] = val
            else:
                uppercase_attr[name] = val

        # reuse the type.__new__ method
        # this is basic OOP, nothing magic in there
        return type.__new__(upperattr_metaclass, future_class_name,
                            future_class_parents, uppercase_attr)

你可能已經註意到了額外的參數upperattr_metaclass 。 沒有什麼特別的: __new__總是接收它定義的類,作為第一個參數。 就像你有接受實例作為第一個參數的普通方法或類方法的定義類一樣。

當然,為了清楚起見,我在這裡使用的名字很長,但是對於self ,所有的參數都有常規名稱。 所以一個真正的生產元類將如下所示:

class UpperAttrMetaclass(type):

    def __new__(cls, clsname, bases, dct):

        uppercase_attr = {}
        for name, val in dct.items():
            if not name.startswith('__'):
                uppercase_attr[name.upper()] = val
            else:
                uppercase_attr[name] = val

        return type.__new__(cls, clsname, bases, uppercase_attr)

我們可以通過使用super來使它更加清潔,這將減輕繼承(因為是的,你可以有元類,繼承自類繼承的元類):

class UpperAttrMetaclass(type):

    def __new__(cls, clsname, bases, dct):

        uppercase_attr = {}
        for name, val in dct.items():
            if not name.startswith('__'):
                uppercase_attr[name.upper()] = val
            else:
                uppercase_attr[name] = val

        return super(UpperAttrMetaclass, cls).__new__(cls, clsname, bases, uppercase_attr)

而已。 關於元類沒有什麼更多。

使用元類代碼的複雜性背後的原因不是因為元類,這是因為你通常使用元類來依靠內省來做扭曲的東西,操縱繼承,__ __dict__等變量。

事實上,元類對於做黑魔法特別有用,因此也是複雜的東西。 但是它們本身很簡單:

  • 攔截一個類的創建
  • 修改班級
  • 返回修改的類

為什麼你會使用元類而不是函數?

由於__metaclass__可以接受任何可調用,為什麼你會使用一個類,因為它顯然更複雜?

這樣做有幾個原因:

  • 意圖很清楚。 當你閱讀UpperAttrMetaclass(type) ,你知道會發生什麼
  • 你可以使用OOP。 元類可以從元類繼承,覆蓋父類方法。 元類甚至可以使用元類。
  • 如果您指定了元類別類,但不具有元類函數,則類的子類將是其元類的實例。
  • 你可以更好地構建你的代碼。 你從來沒有像上面的例子那樣使用元類作為微不足道的東西。 這通常是複雜的。 有能力製作多個方法並將它們分組到一個類中,這對於使代碼更易於閱讀非常有用。
  • 你可以掛鉤__new__ __init____init____call__ 。 這將允許你做不同的事情。 即使通常您可以在__new__完成所有__new__ ,但有些人使用__init__更加舒適。
  • 這些被稱為元類,該死的! 它一定意味著什麼!

為什麼你會使用元類?

現在是個大問題。 為什麼你會使用一些不太容易出錯的錯誤特徵?

那麼,通常你不會:

元類是更深的魔法,99%的用戶不應該擔心。 如果你想知道你是否需要他們,你就不會(實際需要他們的人確切地知道他們需要他們,並且不需要為什麼解釋)。

Python大師蒂姆彼得斯

元類的主要用例是創建一個API。 一個典型的例子是Django ORM。

它允許你定義這樣的東西:

class Person(models.Model):
    name = models.CharField(max_length=30)
    age = models.IntegerField()

但是,如果你這樣做:

guy = Person(name='bob', age='35')
print(guy.age)

它不會返回一個IntegerField對象。 它將返回一個int ,甚至可以直接從數據庫中獲取它。

這是可能的,因為models.Model定義了__metaclass__ ,它使用了一些魔法,將您剛剛用簡單語句定義的Person變成複雜的數據庫字段鉤子。

Django通過暴露一個簡單的API並使用元類來創建一些複雜的外觀,從這個API重新創建代碼來完成幕後的真實工作。

最後一個字

首先,你知道類是可以創建實例的對象。

事實上,類本身就是實例。 元類。

>>> class Foo(object): pass
>>> id(Foo)
142630324

一切都是Python中的一個對象,它們都是類的實例或元類的實例。

除了type

type實際上是它自己的元類。 這不是您可以在純Python中復制的東西,而是通過在實現級別上作弊而完成的。

其次,元類很複雜。 你可能不希望將它們用於非常簡單的課程改動。 您可以通過使用兩種不同的技術來改變課程:

99%的時間你需要改變班級,你最好使用這些。

但98%的時間,你根本不需要改變班級。

Question

什麼是元類,我們用它們做什麼?




The tl;dr version

The type(obj) function gets you the type of an object.

The type() of a class is its metaclass .

To use a metaclass:

class Foo(object):
    __metaclass__ = MyMetaClass



Python 3 update

There are (at this point) two key methods in a metaclass:

  • __prepare__ , and
  • __new__

__prepare__ lets you supply a custom mapping (such as an OrderedDict ) to be used as the namespace while the class is being created. You must return an instance of whatever namespace you choose. If you don't implement __prepare__ a normal dict is used.

__new__ is responsible for the actual creation/modification of the final class.

A bare-bones, do-nothing-extra metaclass would like:

class Meta(type):

    def __prepare__(metaclass, cls, bases):
        return dict()

    def __new__(metacls, cls, bases, clsdict):
        return super().__new__(metacls, cls, bases, clsdict)

一個簡單的例子:

Say you want some simple validation code to run on your attributes -- like it must always be an int or a str . Without a metaclass, your class would look something like:

class Person:
    weight = ValidateType('weight', int)
    age = ValidateType('age', int)
    name = ValidateType('name', str)

As you can see, you have to repeat the name of the attribute twice. This makes typos possible along with irritating bugs.

A simple metaclass can address that problem:

class Person(metaclass=Validator):
    weight = ValidateType(int)
    age = ValidateType(int)
    name = ValidateType(str)

This is what the metaclass would look like (not using __prepare__ since it is not needed):

class Validator(type):
    def __new__(metacls, cls, bases, clsdict):
        # search clsdict looking for ValidateType descriptors
        for name, attr in clsdict.items():
            if isinstance(attr, ValidateType):
                attr.name = name
                attr.attr = '_' + name
        # create final class and return it
        return super().__new__(metacls, cls, bases, clsdict)

A sample run of:

p = Person()
p.weight = 9
print(p.weight)
p.weight = '9'

生產:

9
Traceback (most recent call last):
  File "simple_meta.py", line 36, in <module>
    p.weight = '9'
  File "simple_meta.py", line 24, in __set__
    (self.name, self.type, value))
TypeError: weight must be of type(s) <class 'int'> (got '9')

Note : This example is simple enough it could have also been accomplished with a class decorator, but presumably an actual metaclass would be doing much more.

The 'ValidateType' class for reference:

class ValidateType:
    def __init__(self, type):
        self.name = None  # will be set by metaclass
        self.attr = None  # will be set by metaclass
        self.type = type
    def __get__(self, inst, cls):
        if inst is None:
            return self
        else:
            return inst.__dict__[self.attr]
    def __set__(self, inst, value):
        if not isinstance(value, self.type):
            raise TypeError('%s must be of type(s) %s (got %r)' %
                    (self.name, self.type, value))
        else:
            inst.__dict__[self.attr] = value



我認為ONLamp對元類編程的介紹寫得很好,並且儘管已經有幾年的歷史,但對這個主題有很好的介紹。

http://www.onlamp.com/pub/a/python/2003/04/17/metaclasses.html

簡而言之:類是創建實例的藍圖,元類是創建類的藍圖。 可以很容易地看出,在Python類中也需要成為第一類對象才能實現這種行為。

我從來沒有自己寫過,但我認為在Django框架中可以看到元類的最好用法之一。 模型類使用元類方法來實現編寫新模型或表單類的聲明式風格。 當元類創建類時,所有成員都可以自定義類。

剩下要說的是:如果你不知道什麼是元類,那麼你不需要它們的概率是99%。




元類的一個用途是自動向實例添加新的屬性和方法。

例如,如果你看看Django模型 ,他們的定義看起來有點混亂。 它看起來好像只是定義類屬性:

class Person(models.Model):
    first_name = models.CharField(max_length=30)
    last_name = models.CharField(max_length=30)

但是,在運行時,Person對象會充滿各種有用的方法。 看到一些驚人的metaclassery source




Role of a metaclass's __call__() method when creating a class instance

If you've done Python programming for more than a few months you'll eventually stumble upon code that looks like this:

# define a class
class SomeClass(object):
    # ...
    # some definition here ...
    # ...

# create an instance of it
instance = SomeClass()

# then call the object as if it's a function
result = instance('foo', 'bar')

The latter is possible when you implement the __call__() magic method on the class.

class SomeClass(object):
    # ...
    # some definition here ...
    # ...

    def __call__(self, foo, bar):
        return bar + foo

The __call__() method is invoked when an instance of a class is used as a callable. But as we've seen from previous answers a class itself is an instance of a metaclass, so when we use the class as a callable (ie when we create an instance of it) we're actually calling its metaclass's __call__() method. At this point most Python programmers are a bit confused because they've been told that when creating an instance like this instance = SomeClass() you're calling it's __init__() method. Some who've dug a bit deeper know that before __init__() there's __new__() . Well, today another layer of truth is being revealed, before __new__() there's the metaclass's __call__() .

Let's study the method call chain from specifically the perspective of creating an instance of a class.

This is a metaclass that logs exactly the moment before an instance is created and the moment it's about to return it.

class Meta_1(type):
    def __call__(cls):
        print "Meta_1.__call__() before creating an instance of ", cls
        instance = super(Meta_1, cls).__call__()
        print "Meta_1.__call__() about to return instance."
        return instance

This is a class that uses that metaclass

class Class_1(object):

    __metaclass__ = Meta_1

    def __new__(cls):
        print "Class_1.__new__() before creating an instance."
        instance = super(Class_1, cls).__new__(cls)
        print "Class_1.__new__() about to return instance."
        return instance

    def __init__(self):
        print "entering Class_1.__init__() for instance initialization."
        super(Class_1,self).__init__()
        print "exiting Class_1.__init__()."

And now let's create an instance of Class_1

instance = Class_1()
# Meta_1.__call__() before creating an instance of <class '__main__.Class_1'>.
# Class_1.__new__() before creating an instance.
# Class_1.__new__() about to return instance.
# entering Class_1.__init__() for instance initialization.
# exiting Class_1.__init__().
# Meta_1.__call__() about to return instance.

The code above doesn't actually do anything other than logging the task and then delegating the actual work to the parent (ie keeping the default behavior). So with type being Meta_1 's parent class, we can imagine that this would be the pseudo implementation of type.__call__() :

class type:
    def __call__(cls, *args, **kwarg):

        # ... maybe a few things done to cls here

        # then we call __new__() on the class to create an instance
        instance = cls.__new__(cls, *args, **kwargs)

        # ... maybe a few things done to the instance here

        # then we initialize the instance with its __init__() method
        instance.__init__(*args, **kwargs)

        # ... maybe a few more things done to instance here

        # then we return it
        return instance

We can see that the metaclass's __call__() method is the one that's called first. It then delegates creation of the instance to the class's __new__() method and initialization to the instance's __init__() . It's also the one that ultimately returns the instance.

From the above it stems that the metaclass's __call__() is also given the opportunity to decide whether or not a call to Class_1.__new__() or Class_1.__init__() will eventually be made. Over the course of its execution it could actually return an object that hasn't been touched by either of these methods. Take for example this approach to the singleton pattern:

class Meta_2(type):
    singletons = {}

    def __call__(cls, *args, **kwargs):
        if cls in Meta_2.singletons:
            # we return the only instance and skip a call to __new__() 
            # and __init__()
            print ("{} singleton returning from Meta_2.__call__(), "
                   "skipping creation of new instance.".format(cls))
            return Meta_2.singletons[cls]

        # else if the singleton isn't present we proceed as usual
        print "Meta_2.__call__() before creating an instance."
        instance = super(Meta_2, cls).__call__(*args, **kwargs)
        Meta_2.singletons[cls] = instance
        print "Meta_2.__call__() returning new instance."
        return instance

class Class_2(object):

    __metaclass__ = Meta_2

    def __new__(cls, *args, **kwargs):
        print "Class_2.__new__() before creating instance."
        instance = super(Class_2, cls).__new__(cls)
        print "Class_2.__new__() returning instance."
        return instance

    def __init__(self, *args, **kwargs):
        print "entering Class_2.__init__() for initialization."
        super(Class_2, self).__init__()
        print "exiting Class_2.__init__()."

Let's observe what happens when repeatedly trying to create an object of type Class_2

a = Class_2()
# Meta_2.__call__() before creating an instance.
# Class_2.__new__() before creating instance.
# Class_2.__new__() returning instance.
# entering Class_2.__init__() for initialization.
# exiting Class_2.__init__().
# Meta_2.__call__() returning new instance.

b = Class_2()
# <class '__main__.Class_2'> singleton returning from Meta_2.__call__(), skipping creation of new instance.

c = Class_2()
# <class '__main__.Class_2'> singleton returning from Meta_2.__call__(), skipping creation of new instance.

a is b is c # True



The type() function can return the type of an object or create a new type,

for example, we can create a Hi class with the type() function and do not need to use this way with class Hi(object):

def func(self, name='mike'):
    print('Hi, %s.' % name)

Hi = type('Hi', (object,), dict(hi=func))
h = Hi()
h.hi()
Hi, mike.

type(Hi)
type

type(h)
__main__.Hi

In addition to using type() to create classes dynamically, you can control creation behavior of class and use metaclass.

According to the Python object model, the class is the object, so the class must be an instance of another certain class. By default, a Python class is instance of the type class. That is, type is metaclass of most of the built-in classes and metaclass of user-defined classes.

class ListMetaclass(type):
    def __new__(cls, name, bases, attrs):
        attrs['add'] = lambda self, value: self.append(value)
        return type.__new__(cls, name, bases, attrs)

class CustomList(list, metaclass=ListMetaclass):
    pass

lst = CustomList()
lst.add('custom_list_1')
lst.add('custom_list_2')

lst
['custom_list_1', 'custom_list_2']

Magic will take effect when we passed keyword arguments in metaclass, it indicates the Python interpreter to create the CustomList through ListMetaclass. new (), at this point, we can modify the class definition, for example, and add a new method and then return the revised definition.




Related