[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__
- 无论在元类和普通类中者三者都存在,无论meta类还是普通类new, super()调用父类方法并return .
__call__
- 在元类中必须调用super(),才能使子类正常。
三种特殊方法的作用
new: 它是创建对象时调用,会返回当前对象的一个实例;
init: 它是创建对象后调用,对当前对象的一些实例初始化,无返回值
call: 子类(不严谨)调用中起作用,
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 # 类属性
super(Singleton, cls).__init__(*args, **kwargs)
def __call__(cls, *args, **kwargs):
# Spam()触发
print "meta__call__"
if cls.instance is None:
# 触发 spam 中的__mew__,__init__,完成实例化
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()()触发
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
元编程的一点理解:
- 元类是实例化出类的.因此在元类里面定义普通方法,相当于类方法
- 元类中的new 方法和init 不同,init方法不能直接操作name, bases, attrs. 以及solt属性.而new则无所不能(所有起作用的都是通过type类).
- 执行顺序,类定义好方法以后,再去跑对应元类里面的 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):
# 分配物理地址,准备构建类的材料(类有哪些元素组成,父类,类名称是什么,属性字典有木有),创建类
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):
# 初始化类,不能直接修改类的基类,名字.字典等
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:
# 只能修改类的属性
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()
$关于装饰器$
不管是类装饰器还是函数装饰器
func = warp(func) # 两层
func = warp(*agrs,**kwargs )(func) # 三层
用wraps可以保留被包装函数的原信息,如函数的name 和doc的()
函数装饰器
- 最基础装饰器
def decorator(func):
print '定义装饰器时执行'
@wraps(func)
def wrapper(*args, **kwargs):
print '调用原函数执行'
return func(*args, **kwargs)
return wrapper
# @decorator
def add(x, y):
return x + y
add = decorator(add) ## add就是wrapper了
- 带参数装饰器
对于函数装饰器来说,装饰方法和类是一样的
def decorator_args(*args,**kwargs):
print '定义装饰器时执行1'
a = args
b = kwargs
def decorator(func):
print '定义装饰器时执行2'
@wraps(func)
def wrapper(*args, **kwargs):
print a,b
print '调用原函数执行'
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)
类装饰器
如何将外部函数,绑定为类的方法??
如果将函数自己赋值给类的属性,这样是不行的
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)
##会出错,属性指向一个函数,无法隐式传入self
正确的方法
import types
obj.method = types.MethodType(new_method, obj, MyObj) #将一个函数和实例,类绑定为方法
obj.method(5)
进一步解释 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)
## 调用方法obj_method时,隐形传入instance
print obj_method(100) #(<__main__.MyObj1 object at 0x0000000002FA1160>, 100)
下面开始解释类的装饰器举例
import types
class Decorator(object):
def __init__(self, func)
self.func = func
def __call__(self,*args,**kwargs):
# self为Decorator()实例,而arg里面有instance即h
print "调用时执行2"
return self.func(*args,**kwargs)
def __get__(self, instance, cls):
print "调用时执行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 一个对象,这个对象调用的时候,隐形的传入实例
## return self ,self()调用 触发Decorator的__call__,
## 因此Decorator()()则会访问__call__
## 因此Decorator()会访问meta类中的__call__
- 带参数的类装饰器 加一层类的嵌套
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 使用元类控制实例的创建
限制类实例化,clall函数定义普通类里面,实例调用的时候触发,call函数定义在元类里面,则在类实例化时调用。
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 # 类属性
super(Singleton,self).__init__(*args, **kwargs)
def __call__(self, *args, **kwargs):
if self.instance is None:
# 类里面的调用Spam()触发元类中的__call__,默认的元类中__call__方法(应该是再次触发类中的__init__方法).在这里被override了.
# 所以,无法进行下一步的操作,需要调用父类的__call__正常实例化即为Spam().
#元类中的__call__相当于实例化的开关.
self.instance = super(Singleton,self).__call__(*args, **kwargs)
return self.instance
else:
return self.instance
# Example
class Spam(object):
__metaclass__ = Singleton
# 实例的a()触发
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()
甚至可以做到,一次实例化,化出来多个实例,即元类可以花样定制类的实例化过程
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
用元类的方法缓存实例8.25小节
缓存意思是指,有一样的东西,就去调用存在的,不一样就再生成
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 捕获类的属性定义顺序 (todo)
2版本中没有prepare ,可以看一下django中的字段顺序form
http://stackoverflow.com/questions/350799/how-does-django-know-the-order-to-render-form-fields
9.17 类上强制使用编程规约
在元类中定义,类方法或者属性不能用大写
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 以编程方式定义类
无types.new_class
9.19 在定义的时候初始化类的成员
带有名称的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 一个切片函数,必须是描述器才能传入实例
setattr(cls, name, property(operator.itemgetter(n)))
# 变成了类属性
class StructTuple(tuple):
__metaclass__ = StructTupleMeta
_fields = []
# 继承不可变类型时,改写new
def __new__(cls, *args):
if len(args) != len(cls._fields):
raise ValueError('{} arguments required'.format(len(cls._fields)))
# 注意不是×args, tuple只能接受一个参数。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__ # {} 无实例属性则调用类属性
print s.name, s.shares, s.price
print tuple([1,2,3,4])
9.20 略
9.21 批量的制造描述器
#制造函数,批量的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