四时宝库

程序员的知识宝库

python散装笔记——39:Metaclasses(元类)

通过替换新类默认使用的 type 元类,元类允许您深入修改 Python 类的行为(在如何定义、实例化、访问等方面)。

1: 基础元类

当调用带有三个参数的 type 时,它的行为就像(Meta)类一样,并创建一个新的实例,即产生一个新的类/类型。

Dummy = type('OtherDummy', (), dict(x=1))
Dummy.__class__ # 
Dummy().__class__.__class__ # 

可以对类型进行子类化,创建自定义元类。

class mytype(type):
  def __init__(cls, name, bases, dict):
    # 调用基础初始化器
    type.__init__(cls, name, bases, dict)

    # 执行自定义初始化...
    cls.__custom_attribute__ = 2

现在,我们有了一个新的自定义 mytype 元类,可以用与 type 相同的方式创建类。

MyDummy = mytype('MyDummy', (), dict(x=2))
MyDummy.__class__ # 
MyDummy().__class__.__class__ # 
MyDummy.__custom_attribute__ # 2

当我们使用 class 关键字创建一个新类时,默认情况下会根据基类选择元类。

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

>>> type(Foo)
type

在上面的例子中,唯一的基类是 object,所以我们的元类将是 object 的类型,即 type。我们可以覆盖默认值,但这取决于我们使用的是 Python 2 还是 Python 3:

可以使用特殊的类级属性 __metaclass__ 来指定元类。

class MyDummy(object):
  __metaclass__ = mytype
type(MyDummy) # 

一个特殊的元类关键字参数指定了元类。

class MyDummy(metaclass=mytype):
  pass
type(MyDummy) # 

类声明中的任何关键字参数(“元类 ”除外)都将传递给元类。因此 class MyDummy(metaclass=mytype, x=2) 将把 x=2 作为关键字参数传递给 mytype 构造函数。

请阅读 python 元类的深入介绍「链接」,了解更多详情。

2: 使用元类的单子

单例是一种将类的实例化限制为一个实例/对象的模式。有关 python 单例设计模式的更多信息,请参见此处 The Singleton  Python 3 Patterns, Recipes and Idioms

class SingletonType(type):
  def __call__(cls, *args, **kwargs):
    try:
      return cls.__instance
    except AttributeError:
      cls.__instance = super(SingletonType, cls).__call__(*args, **kwargs)
      return cls.__instance

Python 3.x Version ≥ 3.0

class MySingleton(metaclass=SingletonType):
  pass

MySingleton() is MySingleton() # True, only one instantiation occurs

3: 使用元类

元类语法

class MyClass(metaclass=SomeMetaclass):
  pass

Python 2 和 Python 3 与 six 的兼容性

import six

class MyClass(six.with_metaclass(SomeMetaclass)):
  pass

4: 元类简介

什么是元类? 在 Python 中,一切都是对象:整数、字符串、列表,甚至函数和类本身都是对象。每个对象都是一个类的实例。

要检查对象 x 的类,可以调用 type(x),所以

>>> type(5)

>>> type(str)

>>> type([1, 2, 3])


>>> class C(object):
...   pass
...
>>> type(C)

python 中的大多数类都是 type 的实例。type 本身也是一个类。这种实例也是类的类称为元类。

最简单的元类

好了,Python 中已经有了一个元类:type。我们能再创建一个吗?

class SimplestMetaclass(type):
  pass

class MyClass(object):
  __metaclass__ = SimplestMetaclass

这并没有增加任何功能,但它是一个新的元类,请看 MyClass 现在是 SimplestMetaclass 的实例:

>>> type(MyClass)

做某事的元类

执行某些操作的元类通常会覆盖 type 的 __new__,在调用创建类的原始 __new__ 之前修改要创建类的某些属性:

class AnotherMetaclass(type):
  def __new__(cls, name, parents, dct):
    # cls 是这个类
    # name 是要创建的类的名称
    # parents 是类的父类列表
    # dct 是类的属性(方法、静态变量)列表

    # 这里的所有属性都可以在创建类之前修改,例如

    dct['x'] = 8 # 现在,该类将有一个静态变量 x = 8

    # 返回值就是新的类。
    return super(AnotherMetaclass, cls).__new__(cls, name, parents, dct)

5: 使用元类实现自定义功能

元类中的功能可以更改,这样每当类被构建时,就会在标准输出中打印一个字符串,或者抛出一个异常。该元类将打印正在构建的类的名称。

class VerboseMetaclass(type):
  def __new__(cls, class_name, class_parents, class_dict):
    print("Creating class ", class_name)
    new_class = super().__new__(cls, class_name, class_parents, class_dict)
    return new_class

您可以像这样使用元类:

class Spam(metaclass=VerboseMetaclass):
  def eggs(self):
    print("[insert example string here]")
s = Spam()
s.eggs()

标准输出将是

Creating class Spam
[insert example string here]

6: 默认元类

您可能听说过 Python 中的一切都是对象。没错,所有对象都有一个类:

>>> type(1)

字面 1int 的一个实例。让我们声明一个类:

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

现在让我们将其实例化:

>>> bar = Foo()

bar类是什么类型?

>>> type(bar)

很好,bar 是 Foo 的一个实例。但 Foo 本身是什么类呢?

>>> type(Foo)

好吧,Foo 本身就是 type 的一个实例。那么类型本身呢?

>>> type(type)

那么什么是元类?现在,让我们把它当作一个类的别名。启示

  • 在 Python 中,万物皆对象,因此万物皆有类
  • 类的类称为元类
  • 默认的元类是 type,到目前为止它是最常用的元类

但为什么要了解元类?Python本身是非常 “可黑客化 ”的,如果你要进行元编程等高级操作,或者要控制类的初始化方式,元类的概念就非常重要。

发表评论:

控制面板
您好,欢迎到访网站!
  查看权限
网站分类
最新留言
    友情链接