程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式師世界 >> 編程語言 >  >> 更多編程語言 >> Python

Python metaclass explanation

編輯:Python

新式類

New-style classes unify classes and types,如果objis an instance of a new-style class,則type(obj)等同於obj.__class__

>>> class Foo:
... pass
>>> obj = Foo()
>>> obj.__class__
<class '__main__.Foo'>
>>> type(obj)
<class '__main__.Foo'>
>>> obj.__class__ is type(obj)
True
>>> n = 5
>>> d = {
 'x' : 1, 'y' : 2 }
>>> class Foo:
... pass
...
>>> x = Foo()
>>> for obj in (n, d, x):
... print(type(obj) is obj.__class__)
...
True
True
True

Type and Class

在Python 3中,所有類都是新式類.因此,在Python 3中,It is reasonable to refer to an object's type and its class interchangeably.

注意:在Python 2中,Classes are old-style by default.在Python 2.2之前,New-style classes are not supported at all.從Python 2.2開始,它們可以創建,But it must be explicitly declared asnew-style.

請記住,在Python中,一切都是對象.類也是對象.因此,A class must have a type.class的類型是什麼?

考慮以下:

>>> class Foo:
... pass
...
>>> x = Foo()
>>> type(x)
<class '__main__.Foo'>
>>> type(Foo)
<class 'type'

該類型(type)x是class類Foo,如你所願.但是Foo,類本身的類型是type.通常,The type of any new-style class istype.

The types of built-in classes you are familiar with are also includedtype:

>>> for t in int, float, dict, list, tuple:
... print(type(t))
...
<class 'type'>
<class 'type'>
<class 'type'>
<class 'type'>
<class 'type'>

對於這個問題,類型type是type也(是對的):

>>> type (type )
<class'type'>

type是一個元類,where the class is the instance.Just like a normal object is an instance of a class,PythonAny new-style classes in ,以及Python 3中的任何類,都是type元類的一個實例.

在上述情況中:

  • x是一個類的實例Foo.
  • Foo是type元類的一個實例.
  • type也是type元類的一個實例,So it is itself an instance.

Dynamically define classes

type()當傳遞一個參數時,Built-in functions return the type of an object.對於新式類,Usually with objects__class__屬性相同:

>>> type(3)
<class 'int'>
>>> type(['foo', 'bar', 'baz'])
<class 'list'>
>>> t = (1, 2, 3, 4, 5)
>>> type(t)
<class 'tuple'>
>>> class Foo:
... pass
...
>>> type(Foo())
<class '__main__.Foo'>

你也可以type()Called with three arguments - type(<name>, <bases>, <dct>)

  • <name>指定類名.這成為__name__類的屬性.
  • <bases>A tuple specifying the base classes that the class inherits from.這成為__bases__類的屬性.
  • <dct>Specifies a namespace dictionary containing definitions for class bodies.這成為__dict__類的屬性.

type()Invoking in this way will createtypeA new instance of the metaclass.換句話說,It dynamically creates a new class.

在以下每個示例中,The top snippet defines a class dynamicallytype(),And the following code snippet uses thatclassThe statement defines the class in the usual way.在每種情況下,The two fragments are functionally equivalent.
例1

在第一個例子中,<bases><dct>通過參數type()都是空的.Inheritance from any parent class is not specified,and initially nothing is put in the namespace dictionary.This is the simplest class definition:

>>> Foo = type('Foo', (), {
})
>>> x = Foo()
>>> x
<__main__.Foo object at 0x04CFAD50>
>>> class Foo:
... pass
...
>>> x = Foo()
>>> x
<__main__.Foo object at 0x0370AD50>

例2

<bases>is a tuple with a single elementFoo,指定BarThe parent class from which to inherit.屬性, attrOriginally placed in the namespace dictionary:

>>> Bar = type('Bar', (Foo,), dict(attr=100))
>>> x = Bar()
>>> x.attr
100
>>> x.__class__
<class '__main__.Bar'>
>>> x.__class__.__bases__
(<class '__main__.Foo'>,)
>>> class Bar(Foo):
... attr = 100
...
>>> x = Bar()
>>> x.attr
100
>>> x.__class__
<class '__main__.Bar'>
>>> x.__class__.__bases__
(<class '__main__.Foo'>,)

例3

這一次,又<bases>是空的.通過<dct>The parameter puts two objects into the namespace dictionary.The first is an attribute named attr,The second is a function named attr_val,It becomes a method of the defined class:

>>> Foo = type(
... 'Foo',
... (),
... {

... 'attr': 100,
... 'attr_val': lambda x : x.attr
... }
... )
>>> x = Foo()
>>> x.attr
100
>>> x.attr_val()
100
>>> class Foo:
... attr = 100
... def attr_val(self):
... return self.attr
...
>>> x = Foo()
>>> x.attr
100
>>> x.attr_val()
100

例4

lambda在PythonOnly very simple functions can be defined in .在下面的示例中,Externally define slightly more complex functions,然後attr_valAssigned by name in the namespace dictionaryf:

>>> def f(obj):
... print('attr =', obj.attr)
...
>>> Foo = type(
... 'Foo',
... (),
... {

... 'attr': 100,
... 'attr_val': f
... }
... )
>>> x = Foo()
>>> x.attr
100
>>> x.attr_val()
attr = 100
>>> def f(obj):
... print('attr =', obj.attr)
...
>>> class Foo:
... attr = 100
... attr_val = f
...
>>> x = Foo()
>>> x.attr
100
>>> x.attr_val()
attr = 100

自定義元類

Consider this old example again:

class Foo:
… pass

f = Foo()

該表達式Foo()A new class instance is createdFoo.When encountered by the interpreterFoo(),會發生以下情況:

調用Foo的父類方法__call__().由於Foois a standard new-style class,它的父類是type元類,這樣type的__call__()方法被調用.

__call__()The method in turn calls the following:

  • __new__()
  • __init__()

如果Foo沒有定義__new__()__init__(),The default method is inheritanceFoo的祖先.但是如果Foo定義了這些方法,They override methods from ancestors,This allows for custom behavior at instantiation timeFoo.

在下面,A custom method is defined,並將new()It is specified as follows__new__()方法Foo:

>>> def new(cls):
... x = object.__new__(cls)
... x.attr = 100
... return x
...
>>> Foo.__new__ = new
>>> f = Foo()
>>> f.attr
100
>>> g = Foo()
>>> g.attr
100

This modifies the instantiation behavior of the classFoo:每次Foo創建一個實例時,By default a named attrproperty to initialize it,該屬性attr的值為100.(Such code usually appears in __init__()方法中,而不是典型的__new__().This example is designed for demonstration purposes.)

現在,As already reiterated,類也是對象.Suppose you like to customize the instantiation behavior similarly when you create your classFoo.If you are going to follow the pattern above,You will define a custom method again,and specify it__new__()為類Foo的實例的方法.Foo是type元類的一個實例,所以代碼看起來像這樣:

# Spoiler alert: This doesn't work!
>>> def new(cls):
... x = type.__new__(cls)
... x.attr = 100
... return x
...
>>> type.__new__ = new
Traceback (most recent call last):
File "<pyshell#77>", line 1, in <module>
type.__new__ = new
TypeError: can't set attributes of built-in/extension type 'type'

除此之外,正如您所看到的,You cannot reassigntype元類的__new__()方法.Python不允許.

This may be the same.typeis the metaclass from which all new style classes are derived.無論如何,You really shouldn't mess with it.但是,If you want to customize the instantiation of a class,那麼還有什麼辦法呢?

A possible solution is a custom metaclass.從本質上講,typeYou can define your own metaclass,instead of using metaclasses,但您可以typeUse it to make modifications.

The first step is to define a metaclass,派生自type,如下:

>>> class Meta(type):
... def __new__(cls, name, bases, dct):
... x = super().__new__(cls, name, bases, dct)
... x.attr = 100
... return x
...

Define the headerclass Meta(type):指定Meta派生自type.既然type是元類,那也是一個Meta元類.

請注意,__new__()A custom method is definedMeta.typeIt is not possible to do such operations directly on metaclasses.該__new__()方法執行以下操作:

  • via representativesuper()的__new__()A method of the parent metaclass(type)Actually create a new class
  • Assign custom propertiesattr給類,值為100
  • 返回新創建的類

Now it's the other half:定義一個新類Fooand specify that its metaclass is a custom metaclassMeta,instead of the standard metaclasstype.這是使用metaclassThe keyword in the class definition is done,如下所示:

>>> class Foo(metaclass=Meta):
... pass
...
>>> Foo.attr
100

瞧! Foo已經自動從MetaMetaclass gets propertiesattr了.當然,Any other classes you define will do the same:

>>> class Bar(metaclass=Meta):
... pass
...
>>> class Qux(metaclass=Meta):
... pass
...
>>> Bar.attr, Qux.attr
(100, 100)

In the same way that classes serve as templates for creating objects,Metaclasses are used as templates for creating classes.Metaclasses are sometimes called class factories.

Compare the following two examples:

對象工廠:

>>> class Foo:
... def __init__(self):
... self.attr = 100
...
>>> x = Foo()
>>> x.attr
100
>>> y = Foo()
>>> y.attr
100
>>> z = Foo()
>>> z.attr
100

類工廠:

>>> class Meta(type):
... def __init__(
... cls, name, bases, dct
... ):
... cls.attr = 100
...
>>> class X(metaclass=Meta):
... pass
...
>>> X.attr
100
>>> class Y(metaclass=Meta):
... pass
...
>>> Y.attr
100
>>> class Z(metaclass=Meta):
... pass
...
>>> Z.attr
100

這真的有必要嗎?

As simple as the class factory example above,It's the essence of how metaclasses work.They allow custom class instantiation.

盡管如此,為了attrAssign custom properties on each newly created class,It's still a fuss thing.Do you really need a metaclass?

在Python中,There are at least a few ways to do the same thing efficiently:

簡單繼承:

''' 學習中遇到問題沒人解答?小編創建了一個Python學習交流群:711312441 尋找有志同道合的小伙伴,互幫互助,群裡還有不錯的視頻學習教程和PDF電子書! '''
>>> class Base:
... attr = 100
...
>>> class X(Base):
... pass
...
>>> class Y(Base):
... pass
...
>>> class Z(Base):
... pass
...
>>> X.attr
100
>>> Y.attr
100
>>> Z.attr
100

類裝飾器

>>> def decorator(cls):
... class NewClass(cls):
... attr = 100
... return NewClass
...
>>> @decorator
... class X:
... pass
...
>>> @decorator
... class Y:
... pass
...
>>> @decorator
... class Z:
... pass
...
>>> X.attr
100
>>> Y.attr
100
>>> Z.attr
100

結論

Metaclasses are easy to turn to be“Find solutions to problems”的領域.Usually there is no need to create a custom metaclass.If the problem at hand can be solved in a simpler way,那麼它應該是.盡管如此,Understanding metaclasses is beneficial,So you can understandPython類,And can identify the tools that metaclasses are really good for.


  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式師世界 All Rights Reserved