Quantcast
Channel: CodeSection,代码区,Python开发技术文章_教程 - CodeSec
Viewing all articles
Browse latest Browse all 9596

理解 Python 中的元类

$
0
0
理解 python 中的元类

1小时前来源:开发者头条

元类 (metaclass) 是创建类的类,类是元类的实例,只有 type 及其派生类才可能充当元类。正如我们要先创建一个类,然后才能创建实例,我们先要创建元类,然后才能创建类。

好吧上面这段话确实很绕,下面我们一步一步来看

首先在 Python 中类也是一个对象

class A(object): pass

由于类也是一种对象(类可以称为类型对象),所以它们也是通过什么东西来生成的。

In [3]: a = A In [4]: a.__class__ Out[4]: __main__.A In [5]: a.__class__.__class__ Out[5]: type

答案是 type

object is the base of every object, type is the class of every type

那 type 的类型又是什么呢

In [10]: type(type) Out[10]: type

type 的类型就是他自身

type 比较特殊。它不只可以查看对象的类型,还可以接受一个类的描述作为参数,然后返回一个类

Parent = type('Parent', (object,), {'bar': 1}) def echo(self): print self.bar Child = type('Child', (Parent,), {'echo': echo}) child = Child child.echo

type 的参数依次为:

类名; 继承的父类集合; 包含属性的字典;

但这种方法没有使用 class 关键字定义类来的直观

到这里,我们再来回想本文的开始处所讲:元类是用来创建类的。type 实际上就是一个元类,Python 使用 type 作为元类来创建所有的类。

元类实际上控制了类的创建行为,所以我们可以通过元类在类的创建时进行修饰。

可以使用 metaclass 关键字指定自定义元类

class UpperAttrMetaclass(type): def __init__(cls, name, bases, attrs): print 'metaclass __init__' super(UpperAttrMetaclass, cls).__init__(name, bases, attrs) def __call__(self, *args): print 'metaclass __call__', args # return type.__call__(self, *args) return super(UpperAttrMetaclass, self).__call__(*args) def __new__(cls, name, bases, dct): print 'metaclass __new__' 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, name, bases, uppercase_attr) return super(UpperAttrMetaclass, cls).__new__(cls, name, bases, uppercase_attr) # For Python3 # class A(metaclass=UpperAttrMetaclass): # pass class A(object): __metaclass__ = UpperAttrMetaclass def __init__(self, bar): print('A __init__') self.bar = bar def echo(self): print(self.bar) print 'Create Instance' a = A(6) b = A(12) if hasattr(a, 'echo'): print 'instance has echo' elif hasattr(a, 'ECHO'): print 'instance has ECHO' else: print 'instance has nothing'

Output

metaclass __new__ metaclass __init__ Create Instance metaclass __call__ (6,) A __init__ metaclass __call__ (12,) A __init__ instance has ECHOUpperAttrMetaclass.__new__是用来生成类A的类型对象, 我们可以在调用type.__new__之前更改 dct 变量来对类A进行修饰. UpperAttrMetaclass.__init__是在生成类A的类型对象后被调用进行初始化. 第一个参数是已经生成的类A的类型对象. UpperAttrMetaclass.__call__是在生成类A的实例对象时被调用的, 通过调用type.__call__可以生成该实例对象obj, 之后我们可以直接修改obj来实现实例对象的自定义.元类的__init__和__new__只在创建类A时调用一次,而创建A的实例时,每次都会调用元类的__call__方法Python 在创建类的过程中,会在类的定义中寻找__metaclass__,如果存在则用其创建类,否则使用内建的 type 来创建类。对于继承的情况,如果当前类没有找到,会继续在父类中寻找__metaclass__,直到所有父类中都没有找到才使用 type 创建类。所以元类可以隐式地继承到子类,但子类自己却感觉不到class A(object): __metaclass__ = UpperAttrMetaclass def __init__(self, bar): print('A __init__') self.bar = bar def echo(self): print(self.bar) class B(A): pass

Output

metaclass __new__ metaclass __init__ metaclass __new__ metaclass __init__

元类可以用来干很多事,比如单例模式

class Singleton(type): _instances = {} def __call__(cls, *args, **kwargs): if cls not in cls._instances: cls._instances[cls] = super(Singleton, cls).__call__(*args, **kwargs) return cls._instances[cls] class A(object): __metaclass__ = Singleton class Achild(A): pass a1 = Achild a2 = Achild print id(a1) == id(a2) # True

为了彻底搞懂,再来看一个例子

abc 是 Python 的一个内建库,用于模拟抽象基类(Abstract Base Classes)。开发者可以使用 abc.abstractmethod 装饰器,将指定了元类为 abc.ABCMeta 的类的方法定义成抽象方法,同时这个类也成了抽象基类。

from abc import ABCMeta, abstractmethod class A(object): __metaclass__ = ABCMeta @abstractmethod def method(self, bar): pass a = A

Output

Traceback (most recent call last): File "test.py", line 12, in <module> a = A TypeError: Can't instantiate abstract class A with abstract methods method

我们来看一下这个是如何实现的 abc.py 在/usr/lib/python2.7目录中

def abstractmethod(funcobj): """A decorator indicating abstract methods. Requires that the metaclass is ABCMeta or derived from it. A class that has a metaclass derived from ABCMeta cannot be instantiated unless all of its abstract methods are overridden. The abstract methods can be called using any of the normal 'super' call mechanisms. Usage: class C: __metaclass__ = ABCMeta @abstractmethod def my_abstract_method(self, ...): ... """ funcobj.__isabstractmethod__ = True return funcobjabstractmethod装饰器给类中的方法添加了__isabstractmethod__ = Trueclass ABCMeta(type): """Metaclass for defining Abstract Base Classes (ABCs). Use this metaclass to create an ABC. An ABC can be subclassed directly, and then acts as a mix-in class. You can also register unrelated concrete classes (even built-in classes) and unrelated ABCs as 'virtual subclasses' -- these and their descendants will be considered subclasses of the registering ABC by the built-in issubclass function, but the registering ABC won't show up in their MRO (Method Resolution Order) nor will method implementations defined by the registering ABC be callable (not even via super). """ # A global counter that is incremented each time a class is # registered as a virtual subclass of anything. It forces the # negative cache to be cleared before its next use. _abc_invalidation_counter = 0 def __new__(mcls, name, bases, namespace): cls = super(ABCMeta, mcls).__new__(mcls, name, bases, namespace) # Compute set of abstract method names abstracts = set(name for name, value in namespace.items if getattr(value, "__isabstractmethod__", False)) for base in bases: for name in getattr(base, "__abstractmethods__", set): value = getattr(cls, name, None) if getattr(value, "__isabstractmethod__", False): abstracts.add(name) cls.__abstractmethods__ = frozenset(abstracts) # Set up inheritance registry cls._abc_registry = WeakSet cls._abc_cache = WeakSet cls._abc_negative_cache = WeakSet cls._abc_negative_cache_version = ABCMeta._abc_invalidation_counter return cls # 省略部分方法可以看到ABCMeta.__new__将所要修饰的类与其抽象父类中的__isabstractmethod__为True的方法都添加到abstracts这个集合中,并添加了__abstractmethods__这个属性,然后返回修饰完成的类from abc import ABCMeta, abstractmethod class A(object): __metaclass__ = ABCMeta @abstractmethod def say(self): pass @abstractmethod def hi(self): pass class B(A): @abstractmethod def bye(self): pass print B.__abstractmethods__

Output

frozenset(['bye', 'say', 'hi'])

Viewing all articles
Browse latest Browse all 9596

Latest Images

Trending Articles