Python中type的使用和元类的理解二

元类可以用来自动为类的所有方法添加装饰,把所有使用的类注册到一个API,自动为类添加用户接口逻辑,

这个逻辑不会把类名重新绑定到一个装饰器可调用对象,而是把类自身的创建指向特定的逻辑

一、Python 3.0中以及在Python 2.6的新式类的特性

  • type是产生用户定义的类的一个类。
  • 元类是type类的一个子类。
  • 类对象是type类的一个实例,或一个子类。
  • 实例对象产生字一个类。

二、Class语句协议

class创建一个类对象:
    
调用type对象来创建class对象:
class A: pass  -> class = type(classname, superclasses, attributedict)
    
初始化类方法:
type.__new__(typeclass, classname, superclasses, attributedict)
type.__init__(class, classname, superclasses, attributedict)

  如下所示的类定义示例:

class Spam(Eggs): # Inherits from Eggs
        data = 1 # Class data attribute
        def meth(self, arg): # Class method attribute
            pass
        
 Python将会从内部运行嵌套的代码块来创建该类的两个属性(data和meth),然后在class语句的末尾调用type对象,产生class对象:
 Spam = type('Spam', (Eggs,), {'data': 1, 'meth': meth, '__module__': '__main__'})

三、元类初识,元类的定义

  3.1 元类声明:

  正如我们刚才看到的,类默认是type类创建的。要告诉Python用一个定制的元类来创建一个类,直接声明一个元类来拦截常规的类创建调用。怎么做到这点,依赖于你使用哪个Python版本。在Python 3.0中,在类标题中把想要的元类作为一个关键字参数列出来:

#声明元类
class
Spam(metaclass=Meta):

  3.2 超类继承:

  继承超类也可以列在标题中,在元类之前。例如,在下面的代码中,新的类Spam继承自Eggs,但也是Meta的一个实例并且由Meta创建:

#继承超类
class Spam(Eggs, metaclass=Meta)

  3.3 Mate的作用:

  当以上这些方式声明的时候,创建类对象的调用在class语句的底部运行,修改为调用元类而不是默认的type:

class = Meta(classname, superclasses, attributedict)
    
由于元类是type的一个子类,所以type类的__call__把创建和初始化新的类对象的调用
委托给元类,如果它定义了这些方法的定制版本:
Meta.__new__(Meta, classname, superclasses, attributedict)
Meta.__init__(class, classname, superclasses, attributedict)
    
示例分析:
class Spam(Eggs, metaclass=Meta)
    data = 1 
    def meth(self, arg): 
        pass
在这条class语句的末尾,Python内部运行如下的代码来创建class对象:
Spam = Meta('Spam', (Eggs,), {'data': 1, 'meth': meth, '__module__': '__main__'})

四、基本元类的创建

  通过继承自type的__new__方法而运行。它通常执行所需的任何定制并且调用type的超类的__new__方法来创建并运行新的类对象:

class Meta(type):
    def __new__(meta, classname, supers, classdict):
        # Run by inherited type.__call__
        return type.__new__(meta, classname, supers, classdict)
将元类接入元类钩子中以定制——由于元类在一条class语句的末尾调用,并且因为 type对象的__call__分派到了__new__和__init__方法

  4.1 创建一个最基本的元类:

class MetaOne(type):
    def __new__(meta, classname, supers, classdict):
        print('In MetaOne.new:', classname, supers, classdict, sep='\n...')
        return type.__new__(meta, classname, supers, classdict)


class Eggs(object):
    pass


print('making class')


class Spam(Eggs, metaclass=MetaOne):  # Inherits from Eggs, instance of Meta
    data = 1  # Class data attribute

    def meth(self, arg):  # Class method attribute
        pass


print('making instance')
X = Spam()
print('data:', X.data)


打印结果:
making class
In MetaOne.new:
...Spam
...(<class '__main__.Eggs'>,)
...{'__module__': '__main__', '__qualname__': 'Spam', 'data': 1, 'meth': <function Spam.meth at 0x000002B3585F3E18>}
making instance
data: 1

  4.2 定制构建和初始化(__init__方法):

  元类也可以接入__init__协议,由type对象的__call__调用:通常,__new__创建并返回了类对象,__init__初始化了已经创建的类。元类也可以用做在创建时管理类的钩子:

class MetaOne(type):
    def __new__(meta, classname, supers, classdict):
        print('In MetaOne.new: ', classname, supers, classdict, sep='\n...')
        return type.__new__(meta, classname, supers, classdict)

    def __init__(Class, classname, supers, classdict):
        print('In MetaOne init:', classname, supers, classdict, sep='\n...')
        print('...init class object:', list(Class.__dict__.keys()))


class Eggs:
    pass


print('making class')


class Spam(Eggs, metaclass=MetaOne):  # Inherits from Eggs, instance of Meta
    data = 1  # Class data attribute

    def meth(self, arg):  # Class method attribute
        pass


print('making instance')
X = Spam()
print('data:', X.data)


打印结果:
making class
In MetaOne.new: 
...Spam
...(<class '__main__.Eggs'>,)
...{'__module__': '__main__', '__qualname__': 'Spam', 'data': 1, 'meth': <function Spam.meth at 0x00000297605E3BF8>}
In MetaOne init:
...Spam
...(<class '__main__.Eggs'>,)
...{'__module__': '__main__', '__qualname__': 'Spam', 'data': 1, 'meth': <function Spam.meth at 0x00000297605E3BF8>}
...init class object: ['__module__', 'data', 'meth', '__doc__']
making instance
data: 1

五、用元类重载类创建调用(细谈元类创建)

  由于它们涉及常规的OOP机制,所以对于元类来说,也可能直接在一条class语句的末尾捕获创建调用,通过定义type对象的__call__。然而,所需的协议有点多:

class SuperMeta(type):
    def __call__(meta, classname, supers, classdict):
        print('In SuperMeta.call: ', classname, supers, classdict, sep='\n...')
        return type.__call__(meta, classname, supers, classdict)
class SubMeta(type, metaclass=SuperMeta):
    def __new__(meta, classname, supers, classdict):
        print('In SubMeta.new: ', classname, supers, classdict, sep='\n...')
        return type.__new__(meta, classname, supers, classdict)
    def __init__(Class, classname, supers, classdict):
        print('In SubMeta init:', classname, supers, classdict, sep='\n...')
        print('...init class object:', list(Class.__dict__.keys()))
class Eggs:
    pass
print('making class')
class Spam(Eggs, metaclass=SubMeta):
    data = 1
    def meth(self, arg):
        pass
print('making instance')
X = Spam()
print('data:', X.data)

当这段代码运行的时候,所有3个重新定义的方法都依次运行。这基本上就是type对象默认做的事情:
打印结果:
making class
In SuperMeta.call:
...Spam
...(<class '__main__.Eggs'>,)
...{'__module__': '__main__', 'data': 1, 'meth': <function meth at 0x02B7BA98>}
In SubMeta.new:
...Spam
...(<class '__main__.Eggs'>,)
...{'__module__': '__main__', 'data': 1, 'meth': <function meth at 0x02B7BA98>}
In SubMeta init:
...Spam
...(<class '__main__.Eggs'>,)
...{'__module__': '__main__', 'data': 1, 'meth': <function meth at 0x02B7BA98>}
...init class object: ['__module__', 'data', 'meth', '__doc__']
making instance
data: 1

六、实例与继承的关系

  1.元类继承自type类。尽管它们有一种特殊的角色元类,但元类是用c l a s s语句编写的,并且遵从Python中有用的OOP模型。例如,就像type的子类一样,它们可以重新定义type对象的方法,需要的时候重载或定制它们。元类通常重新定义type类的__new__和__init__,以定制类创建和初始化,但是,如果它们希望直接捕获类末尾的创建调用的话,它们也可以重新定义__c a l l__。尽管元类不常见,它们甚至是返回任意对象而不是type子类的简单函数。

  2.元类声明由子类继承。在用户定义的类中,metaclass=M声明由该类的子类继承,因此,对于在超类链中继承了这一声明的每个类的构建,该元类都将运行。

  3.元类属性没有由类实例继承。元类声明指定了一个实例关系,它和继承不同。由于类是元类的实例,所以元类中定义的行为应用于类,而不是类随后的实例。实例从它们的类和超类获取行为,但是,不是从任何元类获取行为。从技术上讲,实例属性查找通常只是搜索实例及其所有类的__dict__字典;元类不包含在实例查找中。

  自定义实例化对象不能调用元类的属性与方法:

class MetaOne(type):
  abc = 10
def __new__(meta, classname, supers, classdict): # Redefine type method print('In MetaOne.new:', classname) return type.__new__(meta, classname, supers, classdict) def toast(self): print('toast') class Super(metaclass=MetaOne): # Metaclass inherited by subs too def spam(self): # MetaOne run twice for two classes print('spam') class C(Super): # Superclass: inheritance versus instance def eggs(self): # Classes inherit from superclasses print('eggs') # But not from metclasses X = C() X.eggs() # Inherited from C X.spam() # Inherited from Super X.toast() # Not inherited from metaclass
print(X.abc) #Not inherited from attribute
当这段代码运行的时候,元类处理两个客户类的构建,并且实例继承类属性而不是元类属性: 打印结果: In MetaOne.new: Super In MetaOne.new: C eggs spam AttributeError: 'C' object has no attribute 'toast'
AttributeError: 'C' object has no attribute 'abc'

七、执行扩展的元类的静态工作实例

  像装饰器一样,我们在元类中做扩展,声明了元类的每个类都将统一且正确地扩展,并自动地接收未来做出的任何修改。如下的代码展示了这一点:

  这个示例中的元类仍然执行相当静态的工作:把两个已知的方法添加到声明了元类的每个类

def eggsfunc(obj):
    return obj.value * 4


def hamfunc(obj, value):
    return value + 'ham'


class Extender(type):
    def __new__(meta, classname, supers, classdict):
        classdict['eggs'] = eggsfunc
        classdict['ham'] = hamfunc
        return type.__new__(meta, classname, supers, classdict)


class Client1(metaclass=Extender):
    def __init__(self, value):
        self.value = value

    def spam(self):
        return self.value * 2


class Client2(metaclass=Extender):
    value = 'ni?'


X = Client1('Ni!')
print(X.spam())
print(X.eggs())
print(X.ham('bacon'))

Y = Client2()
print(Y.eggs())
print(Y.ham('bacon'))


打印结果:
两个客户类都使用新的方法扩展了,因为它们是执行扩展的元类的实例
Ni!Ni!
Ni!Ni!Ni!Ni!
baconham
ni?ni?ni?ni?
baconham

八、执行扩展的元类的动态工作实例

  定制元类的动态行为:主体类也可以基于运行时的任意逻辑配置

class MetaExtend(type):
    def __new__(meta, classname, supers, classdict):
        if sometest():
            classdict['eggs'] = eggsfunc1
        else:
            classdict['eggs'] = eggsfunc2
        if someothertest():
            classdict['ham'] = hamfunc
        else:
            classdict['ham'] = lambda *args: 'Not supported'
        return type.__new__(meta, classname, supers, classdict)

九、基于类装饰器的扩展实例与元类管理的对比

  类装饰器常常可以和元类一样充当类管理角色

  • 类装饰器可以管理类和实例
  • 元类可以管理类和实例,但是管理实例需要一些额外工作
def eggsfunc(obj):
    return obj.value * 4


def hamfunc(obj, value):
    return value + 'ham'


def Extender(aClass):
    aClass.eggs = eggsfunc  # Manages class, not instance
    aClass.ham = hamfunc  # Equiv to metaclass __init__
    return aClass


@Extender
class Client1:  # Client1 = Extender(Client1)
    def __init__(self, value):  # Rebound at end of class stmt
        self.value = value

    def spam(self):
        return self.value * 2


@Extender
class Client2:
    value = 'ni?'


X = Client1('Ni!')  # X is a Client1 instance
print(X.spam())
print(X.eggs())
print(X.ham('bacon'))
Y = Client2()
print(Y.eggs())
print(Y.ham('bacon'))

打印结果:
Ni!Ni!
Ni!Ni!Ni!Ni!
baconham
ni?ni?ni?ni?
baconham

十、基于类装饰器的扩展实例与元类管理的对比2

  10.1 类装饰器管理实例:

def Tracer(aClass): # On @ decorator
class Wrapper:
    def __init__(self, *args, **kargs): # On instance creation
        self.wrapped = aClass(*args, **kargs) # Use enclosing scope name
    def __getattr__(self, attrname):
        print('Trace:', attrname) # Catches all but .wrapped
        return getattr(self.wrapped, attrname) # Delegate to wrapped object
    return Wrapper
@Tracer
class Person: # Person = Tracer(Person)
    def __init__(self, name, hours, rate): # Wrapper remembers Person
        self.name = name
        self.hours = hours
        self.rate = rate # In-method fetch not traced
    def pay(self):
        return self.hours * self.rate
        
bob = Person('Bob', 40, 50) # bob is really a Wrapper
print(bob.name) # Wrapper embeds a Person
print(bob.pay())

打印结果:
Trace: name
Bob
Trace: pay
2000

  10.2 元类管理实例:

def Tracer(classname, supers, classdict): # On class creation call
    aClass = type(classname, supers, classdict) # Make client class
    class Wrapper:
        def __init__(self, *args, **kargs): # On instance creation
            self.wrapped = aClass(*args, **kargs)
        def __getattr__(self, attrname):
            print('Trace:', attrname) # Catches all but .wrapped
            return getattr(self.wrapped, attrname) # Delegate to wrapped object
        return Wrapper
class Person(metaclass=Tracer): # Make Person with Tracer
    def __init__(self, name, hours, rate): # Wrapper remembers Person
        self.name = name
        self.hours = hours
        self.rate = rate # In-method fetch not traced
    def pay(self):
        return self.hours * self.rate
bob = Person('Bob', 40, 50) # bob is really a Wrapper
print(bob.name) # Wrapper embeds a Person
print(bob.pay())

打印结果:
Trace: name
Bob
Trace: pay
2000

十一、基于类装饰器的扩展实例与元类管理的对比3

  装饰器弊端:我们在想要在类中跟踪的每个方法前面添加装饰语法,并且在不再想要跟踪的使用后删除该语法。如果想要跟踪一个类的每个方法,在较大的程序中,这会变得很繁琐
  元类创建跟踪方法:在构建一个类的时候运行,它们是把装饰包装器添加到一个类方法中的自然地方。通过扫描类的属性字典并测试函数对象,我们可以通过装饰器自动运行方法,并且把最初的名称重新绑定到结果。其效果与装饰器的自动方法名重新绑定是相同的。

class MetaTrace(type):
    def __new__(meta, classname, supers, classdict):
        for attr, attrval in classdict.items():
            if type(attrval) is FunctionType: # Method?
                classdict[attr] = tracer(attrval) # Decorate it
        return type.__new__(meta, classname, supers, classdict) # Make class
class Person(metaclass=MetaTrace):
    def __init__(self, name, pay):
        self.name = name
        self.pay = pay
    def giveRaise(self, percent):
        self.pay *= (1.0 + percent)
    def lastName(self):
        return self.name.split()[-1]
        
bob = Person('Bob Smith', 50000)
sue = Person('Sue Jones', 100000)
print(bob.name, sue.name)
sue.giveRaise(.10)
print(sue.pay)
print(bob.lastName(), sue.lastName())
打印结果: call
1 to __init__ call 2 to __init__ Bob Smith Sue Jones call 1 to giveRaise 110000.0 call 1 to lastName call 2 to lastName Smith Jones

  要对方法应用一种不同的装饰器,我们只要在类标题行替换装饰器名称,实用与多种装饰的方法:

from types import FunctionType
from mytools import tracer, timer
def decorateAll(decorator):
    class MetaDecorate(type):
        def __new__(meta, classname, supers, classdict):
            for attr, attrval in classdict.items():
                if type(attrval) is FunctionType:
                    classdict[attr] = decorator(attrval)
            return type.__new__(meta, classname, supers, classdict)
    return MetaDecorate
class Person(metaclass=decorateAll(tracer)): # Apply a decorator to all
    def __init__(self, name, pay):
        self.name = name
        self.pay = pay
    def giveRaise(self, percent):
        self.pay *= (1.0 + percent)
    def lastName(self):
        return self.name.split()[-1]
        
bob = Person('Bob Smith', 50000)
sue = Person('Sue Jones', 100000)
print(bob.name, sue.name)
sue.giveRaise(.10)
print(sue.pay)
print(bob.lastName(), sue.lastName())


打印结果:
call 1 to __init__
call 2 to __init__
Bob Smith Sue Jones
call 1 to giveRaise
110000.0
call 1 to lastName
call 2 to lastName
Smith Jones

  第一个接收了定时器的默认参数,第二个指定了标签文本:

class Person(metaclass=decorateAll(tracer)): # Apply tracer
class Person(metaclass=decorateAll(timer())): # Apply timer, defaults
class Person(metaclass=decorateAll(timer(label='**'))): # Decorator arguments

  总结:

  元类和类装饰器不仅常常可以交换,而且通常是互补的。它们都对于定制和管理类和实例对象,提供了高级但强大的方法,因为这二者最终都允许我们在类创建过程中插入代码。尽管某些高级应用可能用一种方式或另一种方式编码更好,但在很多情况下,我们选择或组合这两种工具的方法来使用。

 

posted @ 2019-07-12 22:18  Amorphous  阅读(532)  评论(0编辑  收藏  举报