type在python的作用_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

passPython将会从内部运行嵌套的代码块来创建该类的两个属性(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

defmeth(self, arg):pass在这条class语句的末尾,Python内部运行如下的代码来创建class对象:

Spam= Meta('Spam', (Eggs,), {'data': 1, 'meth': meth, '__module__': '__main__'})

四、基本元类的创建

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

classMeta(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 创建一个最基本的元类:

classMetaOne(type):def __new__(meta, classname, supers, classdict):print('In MetaOne.new:', classname, supers, classdict, sep='\n...')return type.__new__(meta, classname, supers, classdict)classEggs(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)

打印结果:

makingclassIn MetaOne.new:

...Spam

...(,)

...{'__module__': '__main__', '__qualname__': 'Spam', 'data': 1, 'meth': }

making instance

data:1

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

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

classMetaOne(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()))classEggs: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)

打印结果:

makingclassIn MetaOne.new:

...Spam

...(,)

...{'__module__': '__main__', '__qualname__': 'Spam', 'data': 1, 'meth': }

In MetaOne init:

...Spam

...(,)

...{'__module__': '__main__', '__qualname__': 'Spam', 'data': 1, 'meth': }

...initclass object: ['__module__', 'data', 'meth', '__doc__']

making instance

data:1

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

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

classSuperMeta(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()))classEggs:pass

print('making class')class Spam(Eggs, metaclass=SubMeta):

data= 1

defmeth(self, arg):pass

print('making instance')

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

当这段代码运行的时候,所有3个重新定义的方法都依次运行。这基本上就是type对象默认做的事情:

打印结果:

makingclassIn SuperMeta.call:

...Spam

...(,)

...{'__module__': '__main__', 'data': 1, 'meth': }

In SubMeta.new:

...Spam

...(,)

...{'__module__': '__main__', 'data': 1, 'meth': }

In SubMeta init:

...Spam

...(,)

...{'__module__': '__main__', 'data': 1, 'meth': }

...initclass 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__字典;元类不包含在实例查找中。

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

classMetaOne(type):

abc = 10def __new__(meta, classname, supers, classdict): #Redefine type method

print('In MetaOne.new:', classname)return type.__new__(meta, classname, supers, classdict)deftoast(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'

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

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

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

defeggsfunc(obj):return obj.value * 4

defhamfunc(obj, value):return value + 'ham'

classExtender(type):def __new__(meta, classname, supers, classdict):

classdict['eggs'] =eggsfunc

classdict['ham'] =hamfuncreturn type.__new__(meta, classname, supers, classdict)class Client1(metaclass=Extender):def __init__(self, value):

self.value=valuedefspam(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

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

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

classMetaExtend(type):def __new__(meta, classname, supers, classdict):ifsometest():

classdict['eggs'] =eggsfunc1else:

classdict['eggs'] =eggsfunc2ifsomeothertest():

classdict['ham'] =hamfuncelse:

classdict['ham'] = lambda *args: 'Not supported'

return type.__new__(meta, classname, supers, classdict)

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

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

类装饰器可以管理类和实例

元类可以管理类和实例,但是管理实例需要一些额外工作

defeggsfunc(obj):return obj.value * 4

defhamfunc(obj, value):return value + 'ham'

defExtender(aClass):

aClass.eggs= eggsfunc #Manages class, not instance

aClass.ham = hamfunc #Equiv to metaclass __init__

returnaClass

@Extenderclass Client1: #Client1 = Extender(Client1)

def __init__(self, value): #Rebound at end of class stmt

self.value =valuedefspam(self):return self.value * 2@ExtenderclassClient2:

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

classWrapper: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

returnWrapper

@Tracerclass 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

defpay(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

classWrapper: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

returnWrapperclass 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

defpay(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: pay2000

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

装饰器弊端:我们在想要在类中跟踪的每个方法前面添加装饰语法,并且在不再想要跟踪的使用后删除该语法。如果想要跟踪一个类的每个方法,在较大的程序中,这会变得很繁琐

元类创建跟踪方法:在构建一个类的时候运行,它们是把装饰包装器添加到一个类方法中的自然地方。通过扫描类的属性字典并测试函数对象,我们可以通过装饰器自动运行方法,并且把最初的名称重新绑定到结果。其效果与装饰器的自动方法名重新绑定是相同的。

classMetaTrace(type):def __new__(meta, classname, supers, classdict):for attr, attrval inclassdict.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=paydefgiveRaise(self, percent):

self.pay*= (1.0 +percent)deflastName(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())

打印结果:

call1 to __init__call2 to __init__Bob Smith Sue Jones

call1to giveRaise110000.0call1to lastName

call2to lastName

Smith Jones

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

from types importFunctionTypefrom mytools importtracer, timerdefdecorateAll(decorator):classMetaDecorate(type):def __new__(meta, classname, supers, classdict):for attr, attrval inclassdict.items():if type(attrval) isFunctionType:

classdict[attr]=decorator(attrval)return type.__new__(meta, classname, supers, classdict)returnMetaDecorateclass Person(metaclass=decorateAll(tracer)): #Apply a decorator to all

def __init__(self, name, pay):

self.name=name

self.pay=paydefgiveRaise(self, percent):

self.pay*= (1.0 +percent)deflastName(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())

打印结果:

call1 to __init__call2 to __init__Bob Smith Sue Jones

call1to giveRaise110000.0call1to lastName

call2to 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

总结:

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值