[toc]
9 元編程
http://python-3-patterns-idioms-test.readthedocs.io/en/latest/Metaprogramming.html
http://pythoncentral.io/how-metaclasses-work-technically-in-python-2-and-3/
http://eli.thegreenplace.net/2011/08/14/python-metaclasses-by-example
三種特殊方法的理解
__new__, __init__, __call__
- 無(wú)論在元類(lèi)和普通類(lèi)中者三者都存在,無(wú)論meta類(lèi)還是普通類(lèi)new, super()調(diào)用父類(lèi)方法并return .
__call__
- 在元類(lèi)中必須調(diào)用super(),才能使子類(lèi)正常。
三種特殊方法的作用
new: 它是創(chuàng)建對(duì)象時(shí)調(diào)用牙躺,會(huì)返回當(dāng)前對(duì)象的一個(gè)實(shí)例愁憔;
init: 它是創(chuàng)建對(duì)象后調(diào)用,對(duì)當(dāng)前對(duì)象的一些實(shí)例初始化述呐,無(wú)返回值
call: 子類(lèi)(不嚴(yán)謹(jǐn))調(diào)用中起作用惩淳,
class Singleton(type):
def __new__(mcl, *args, **kwargs):
print "meta__new__"
return type.__new__(mcl, *args, **kwargs)
def __init__(cls, *args, **kwargs):
print "mew__init__"
cls.instance = None # 類(lèi)屬性
super(Singleton, cls).__init__(*args, **kwargs)
def __call__(cls, *args, **kwargs):
# Spam()觸發(fā)
print "meta__call__"
if cls.instance is None:
# 觸發(fā) spam 中的__mew__,__init__,完成實(shí)例化
cls.instance = super(Singleton, cls).__call__(*args, **kwargs)
return cls.instance
else:
return cls.instance
class Spam(object):
__metaclass__ = Singleton
def __call__(self, *args, **kwargs):
# spam()()觸發(fā)
return 123
def __init__(self):
print('Creating Spam')
def __new__(cls,*args,**kwargs):
print "instanc__new__"
return super(Spam, cls).__new__(cls,*args,**kwargs)
print Spam()()
>>>
meta__new__
meta__init__
meta__call__
instance__new__
instance__inta__
123
元編程的一點(diǎn)理解:
- 元類(lèi)是實(shí)例化出類(lèi)的.因此在元類(lèi)里面定義普通方法,相當(dāng)于類(lèi)方法
- 元類(lèi)中的new 方法和init 不同,init方法不能直接操作name, bases, attrs. 以及solt屬性.而new則無(wú)所不能(所有起作用的都是通過(guò)type類(lèi)).
- 執(zhí)行順序,類(lèi)定義好方法以后,再去跑對(duì)應(yīng)元類(lèi)里面的 new ,后init.
from pprint import pprint
class Tag1: pass
class Tag2: pass
class Tag3:
def tag3_method(self): pass
class MetaBase(type):
def __new__(mcl, name, bases, nmspc):
print('2MetaBase.__new__\n')
return super(MetaBase, mcl).__new__(mcl, name, bases, nmspc)
def __init__(cls, name, bases, nmspc):
print('MetaBase.__init__\n')
super(MetaBase, cls).__init__(name, bases, nmspc)
class MetaNewVSInit(MetaBase):
def __new__(mcls, name, bases, dict):
# 分配物理地址,準(zhǔn)備構(gòu)建類(lèi)的材料(類(lèi)有哪些元素組成,父類(lèi),類(lèi)名稱(chēng)是什么,屬性字典有木有),創(chuàng)建類(lèi)
print('MetaNewVSInit.__new__')
for x in (mcls, name, bases, dict): pprint(x)
print('1')
if 'foo' in dict: dict.pop('foo')
name += '_x'
bases += (Tag1,)
dict['baz'] = 42
return super(MetaNewVSInit, mcls).__new__(mcls, name, bases, dict)
def __init__(cls, name, bases, dict):
# 初始化類(lèi),不能直接修改類(lèi)的基類(lèi),名字.字典等
print('MetaNewVSInit.__init__')
for x in (cls, name, bases, dict): pprint(x)
print('3')
if 'bar' in dict: dict.pop('bar') # No effect
name += '_y' # No effect
bases += (Tag2,) # No effect
dict['pi'] = 3.14159 # No effect
# These do work because they operate on the class object:
# 只能修改類(lèi)的屬性
super(MetaNewVSInit, cls).__init__(name, bases, dict) #所有這句話在不在都一樣
cls.__name__ += '_z'
cls.__bases__ += (Tag3,)
cls.e = 2.718
class Test(object):
__metaclass__ = MetaNewVSInit
def __init__(self):
print('Test.__init__')
def foo(self): print('foo still here')
def bar(self): print('bar still here')
print 4
t = Test()
$關(guān)于裝飾器$
不管是類(lèi)裝飾器還是函數(shù)裝飾器
func = warp(func) # 兩層
func = warp(*agrs,**kwargs )(func) # 三層
用wraps可以保留被包裝函數(shù)的原信息乓搬,如函數(shù)的name 和doc的()
函數(shù)裝飾器
- 最基礎(chǔ)裝飾器
def decorator(func):
print '定義裝飾器時(shí)執(zhí)行'
@wraps(func)
def wrapper(*args, **kwargs):
print '調(diào)用原函數(shù)執(zhí)行'
return func(*args, **kwargs)
return wrapper
# @decorator
def add(x, y):
return x + y
add = decorator(add) ## add就是wrapper了
- 帶參數(shù)裝飾器
對(duì)于函數(shù)裝飾器來(lái)說(shuō)思犁,裝飾方法和類(lèi)是一樣的
def decorator_args(*args,**kwargs):
print '定義裝飾器時(shí)執(zhí)行1'
a = args
b = kwargs
def decorator(func):
print '定義裝飾器時(shí)執(zhí)行2'
@wraps(func)
def wrapper(*args, **kwargs):
print a,b
print '調(diào)用原函數(shù)執(zhí)行'
return func(*args, **kwargs)
return wrapper
return decorator
class Human(object):
# @decorator_args(1,2,3)
def add(self, x, y):
return x + y
add = decorator_args(123)(add)
Human().add(1 ,2)
類(lèi)裝飾器
如何將外部函數(shù),綁定為類(lèi)的方法??
如果將函數(shù)自己賦值給類(lèi)的屬性,這樣是不行的
class MyObj(object):
def __init__(self, val):
self.val = val
def new_method(self, value):
return self.val + value
obj = MyObj(3)
obj.method = new_method
obj.method(11)
##會(huì)出錯(cuò),屬性指向一個(gè)函數(shù),無(wú)法隱式傳入self
正確的方法
import types
obj.method = types.MethodType(new_method, obj, MyObj) #將一個(gè)函數(shù)和實(shí)例,類(lèi)綁定為方法
obj.method(5)
進(jìn)一步解釋 types.MethodType
import types
class MyObj1(object):
def __init__(self, val):
self.val = val
class MyObj2(object):
def __init__(self,func):
self.func = new_method
def __call__(self, *args, **kwargs):
print args
self.func(*args)
return self.func(*args, **kwargs)
def new_method(self, value):
return self.val + value
instance = MyObj1(3)
obj_method = types.MethodType(MyObj2(new_method), instance)
obj_method = types.MethodType(new_method, instance)
## 調(diào)用方法obj_method時(shí)进肯,隱形傳入instance
print obj_method(100) #(<__main__.MyObj1 object at 0x0000000002FA1160>, 100)
下面開(kāi)始解釋類(lèi)的裝飾器舉例
import types
class Decorator(object):
def __init__(self, func)
self.func = func
def __call__(self,*args,**kwargs):
# self為Decorator()實(shí)例激蹲,而arg里面有instance即h
print "調(diào)用時(shí)執(zhí)行2"
return self.func(*args,**kwargs)
def __get__(self, instance, cls):
print "調(diào)用時(shí)執(zhí)行1"
if instance is None:
return self
else:
return types.MethodType(self, instance)
class Human(object):
def __init__(self):
self.val = 1
# @decorator_args(1,2,3)
def add(self, x, y):
return x + y + self.val
add = Decorator(add)
print Human().add(1,2)
## h.add --->types.MethodType(self, instance)
## types.MethodType(self, instance) return 一個(gè)對(duì)象,這個(gè)對(duì)象調(diào)用的時(shí)候江掩,隱形的傳入實(shí)例
## return self 学辱,self()調(diào)用 觸發(fā)Decorator的__call__,
## 因此Decorator()()則會(huì)訪問(wèn)__call__
## 因此Decorator()會(huì)訪問(wèn)meta類(lèi)中的__call__
- 帶參數(shù)的類(lèi)裝飾器 加一層類(lèi)的嵌套
import types
class Decorator(object):
def __init__(self, *args, **kwargs):
self.args = args
self.kwargs = kwargs
def __call__(self, func):
class META(object):
def __init__(self, func, *args, **kwargs):
self.func = func
self.args = args
self.kwargs = kwargs
def __get__(self, instance, cls):
if instance is None:
return self
return types.MethodType(self, instance)
def __call__(self, *args, **kwargs):
print self.args
print self.kwargs
return self.func(*args, **kwargs)
return META(func, *self.args, **self.kwargs)
class Human(object):
def __init__(self):
self.val = 1
@Decorator(1, 2, 3)
def add(self, x, y):
return x + y + self.val
# add = Decorator(1, 2, 3)(add)
h = Human()
print h.add(44, 55)
@Decorator(1, 2, 3)
def func():
print 'im function'
func()
9.13 使用元類(lèi)控制實(shí)例的創(chuàng)建
限制類(lèi)實(shí)例化环形,clall函數(shù)定義普通類(lèi)里面策泣,實(shí)例調(diào)用的時(shí)候觸發(fā),call函數(shù)定義在元類(lèi)里面抬吟,則在類(lèi)實(shí)例化時(shí)調(diào)用萨咕。
class NoInstances(type):
def __call__(self, *args, **kwargs):
raise TypeError("Can't instantiate directly")
class Spam(object):
__metaclass__ = NoInstances
@staticmethod
def grok(x):
print('Spam.grok')
s = Spam()
單例模式
class Singleton(type):
def __init__(self, *args, **kwargs):
self.instance = None # 類(lèi)屬性
super(Singleton,self).__init__(*args, **kwargs)
def __call__(self, *args, **kwargs):
if self.instance is None:
# 類(lèi)里面的調(diào)用Spam()觸發(fā)元類(lèi)中的__call__,默認(rèn)的元類(lèi)中__call__方法(應(yīng)該是再次觸發(fā)類(lèi)中的__init__方法).在這里被override了.
# 所以,無(wú)法進(jìn)行下一步的操作,需要調(diào)用父類(lèi)的__call__正常實(shí)例化即為Spam().
#元類(lèi)中的__call__相當(dāng)于實(shí)例化的開(kāi)關(guān).
self.instance = super(Singleton,self).__call__(*args, **kwargs)
return self.instance
else:
return self.instance
# Example
class Spam(object):
__metaclass__ = Singleton
# 實(shí)例的a()觸發(fā)
def __call__(self, *args, **kwargs):
return 123
def __init__(self):
print('Creating Spam')
print Spam.__dict__
a = Spam()
print Spam.instance
print Spam.__dict__
b = Spam()
print b()
甚至可以做到,一次實(shí)例化,化出來(lái)多個(gè)實(shí)例,即元類(lèi)可以花樣定制類(lèi)的實(shí)例化過(guò)程
class Singleton(type):
def __call__(cls, *args, **kwargs):
x = super(Singleton, cls).__call__(2)
y = super(Singleton, cls).__call__(3)
return x,y
class Spam(object):
__metaclass__ = Singleton
def __init__(self,value):
self.value = value
print('Creating Spam')
(a,b)=Spam()
print a.value
print b.value
用元類(lèi)的方法緩存實(shí)例8.25小節(jié)
緩存意思是指,有一樣的東西,就去調(diào)用存在的,不一樣就再生成
class Cached(type):
def __init__(self, *args, **kwargs):
super(Cached,self).__init__(*args, **kwargs)
self.__cache = weakref.WeakValueDictionary()
def __call__(self, *args):
if args in self.__cache:
return self.__cache[args]
else:
obj = super(Cached,self).__call__(*args)
self.__cache[args] = obj
return obj
# Example
class Spam(object):
__metaclass__ = Cached
def __init__(self, name):
print('Creating Spam({!r})'.format(name))
self.name = name
a = Spam("jin")
b = Spam("jin")
.14 捕獲類(lèi)的屬性定義順序 (todo)
2版本中沒(méi)有prepare ,可以看一下django中的字段順序form
http://stackoverflow.com/questions/350799/how-does-django-know-the-order-to-render-form-fields
9.17 類(lèi)上強(qiáng)制使用編程規(guī)約
在元類(lèi)中定義,類(lèi)方法或者屬性不能用大寫(xiě)
class NoMixedCaseMeta(type):
def __new__(mcs, name, bases, attrs):
for key in attrs:
if key != key.lower():
raise TypeError('Bad attirbute name' + name)
return super(NoMixedCaseMeta, mcs).__new__(mcs, name, bases, attrs)
class Root(object):
__metaclass__ = NoMixedCaseMeta
pass
class A(Root):
def foo_bar(self):
pass
class B(Root):
def Foo_bar(self):
pass
9.18 以編程方式定義類(lèi)
無(wú)types.new_class
9.19 在定義的時(shí)候初始化類(lèi)的成員
帶有名稱(chēng)的tuple
import operator
class StructTupleMeta(type):
def __init__(cls, *args, **kwargs):
super(StructTupleMeta,cls).__init__(*args, **kwargs)
for n, name in enumerate(cls._fields):
# operator.itemgetter() return 一個(gè)切片函數(shù)火本,必須是描述器才能傳入實(shí)例
setattr(cls, name, property(operator.itemgetter(n)))
# 變成了類(lèi)屬性
class StructTuple(tuple):
__metaclass__ = StructTupleMeta
_fields = []
# 繼承不可變類(lèi)型時(shí)危队,改寫(xiě)new
def __new__(cls, *args):
if len(args) != len(cls._fields):
raise ValueError('{} arguments required'.format(len(cls._fields)))
# 注意不是×args, tuple只能接受一個(gè)參數(shù)钙畔。tuple([1,2,3,4])
return super(StructTuple,cls).__new__(cls,args)
class Stock(StructTuple):
_fields = ['name', 'shares', 'price']
class Point(StructTuple):
_fields = ['x', 'y']
s = Stock('ACME', 50, 91.1)
print s.__dict__ # {} 無(wú)實(shí)例屬性則調(diào)用類(lèi)屬性
print s.name, s.shares, s.price
print tuple([1,2,3,4])
9.20 略
9.21 批量的制造描述器
#制造函數(shù)茫陆,批量的return 描述器
def typed_property(name, expected_type):
storage_name = '_' + name
@property
def prop(self):
return getattr(self, storage_name)
@prop.setter
def prop(self, value):
if not isinstance(value, expected_type):
raise TypeError('{} must be a {}'.format(name, expected_type))
setattr(self, storage_name, value)
return prop
# Example use
class Person:
name = typed_property('name', str)
age = typed_property('age', int)
def __init__(self, name, age):
self.name = name
self.age = age