描述符
描述符是一种属性托管的方法,一个类只要实现了__get__、__set__、__del__(未必全部实现),并且该类的实例是另一个类的类属性,那么该类就称为描述符。
描述符可以方便的对属性进行类型检查、保护属性不被修改、为属性增加依赖等。
一个简单的描述符的例子:
class a_des(object):
def __init__(self):
self.__value = 'a_des'
def __get__(self, instance, owner):
print('>>>> get: %s owner: %s' % (self.__value, owner))
return self.__value
def __set__(self, instance, value):
print('>>>> set')
if not isinstance(value, str):
raise 'invalid value type'
self.__value = 'i am data des ' + value
class TestDes(object):
a = a_des()
def __init__(self, value):
self.a = value
t = TestDes('instance a')
>>>> set
>>>> get: i am data des instance a owner:
i am data des instance a
注意,实例属性的访问顺序:数据描述符 > 实例属性 > 非数据描述符 > __getattri__
装饰器
装饰器(decorator)是一个返回函数的高阶函数,是python中一种在代码运行期间动态增加功能的方式。
举个 fibonacci
def fibonacci(n):
if n == 1 or n == 2:
return 1
else:
return fibonacci(n-1) + fibonacci(n-2)
分别打印函数运用前后的传参和返回值
def log(func):
def wrapper(*args, **kw):
print('func parameter %r - %r', args, kw)
result = func(*args, **kw)
print('func result %r', result)
return result
return wrapper
调用 fibonacci 函数时只需如下修饰即可
@log
def fibonacci(n):
....
pass
>> fibonacci(3)
func parameter %r - %r (3,) {}
func parameter %r - %r (2,) {}
func result %r 1
func parameter %r - %r (1,) {}
func result %r 1
func result %r 2
把 @log 放在 fibonacci 函数的定义处,相等于执行
fibonacci = log(fibonacci)
由于log()是一个decorator,返回一个函数,所以,原来的fibonacci()函数仍然存在,只是现在同名的fibonacci变量指向了新的函数,于是调用fibonacci()将执行新函数,即在log()函数中返回的wrapper()函数。
如果decorator本身需要传入参数,那就需要编写一个返回decorator的高阶函数
def log(text):
def decorator(func):
def wrapper(*args, **kw):
print('decorator text %s', text)
print('func parameter %r - %r', args, kw)
result = func(*args, **kw)
print('func result %r', result)
return result
return wrapper
return decorator
@log('test_decorator')
def fibonacci(n):
if n == 1 or n == 2:
return 1
else:
return fibonacci(n-1) + fibonacci(n-2)
note, 由于函数也是对象,有__name__等属性。将函数修饰后,函数变成wrapper,所以:
>> fibonacci.__name__
wrapper
这在一些可能使用函数属性的地方造成错误,好在python内置模块functools.wraps可以消除装饰器的影响,使用如下
import functools
def log(func):
@functools.wraps(func)
def wrapper(*args, **kw):
print('func parameter %r - %r', args, kw)
result = func(*args, **kw)
print('func result %r', result)
return result
return wrapper
>> fibonacci.__name__
fibonacci
自定义实现装饰器方法
staticmethod
class DDDStaticMethod(object):
def __init__(self, func):
self.func = func
super(DDDStaticMethod, self).__init__()
def __get__(self, isinstance, owner):
return self.func
def dddstaticmethod(func):
return DDDStaticMethod(func)
classmethod
class DDDClassMethod(object):
def __init__(self, func):
self.func = func
super(DDDClassMethod, self).__init__()
def __get__(self, isinstance, owner):
def method(*args, **kwargs):
return self.func(owner, *args, **kwargs)
return method
def dddclassmethod(func):
return DDDClassMethod(func)
property
class DDDPropertyMethod(object):
def __init__(self, fset=None, fget=None, fdel=None):
self.fset = fset
self.fget = fget
self.fdel = fdel
super(DDDPropertyMethod, self).__init__()
def __get__(self, instance, owner):
if not self.fget:
raise AttributeError('')
res = self.fget(instance)
return res
def __set__(self, instance, value):
if not self.fset:
raise AttributeError('')
self.fset(instance, value)
def setter(self, fset):
return type(self)(fset=fset, fget=self.fget)
def getter(self, fget):
return type(self)(fset=self.fset, fget=fget)
dddpropertymethod = DDDPropertyMethod()
测试
class TestDes(object):
def __init__(self, value):
self._pvalue = None
super(TestDes, self).__init__()
@staticmethod
def ori_static_method(*args, **kwargs):
print('>>> %s %s' % (args, kwargs))
print('>>> ori_static_method func invoked')
@classmethod
def ori_class_method(cls, *args, **kwargs):
print('>>> %s %s %s' % (cls, args, kwargs))
print('>>> ori_class_method func invoked')
@dddstaticmethod
def my_static_method(*args, **kwargs):
print('>>> %s %s' % (args, kwargs))
print('>>> my_static_method func invoked')
@dddclassmethod
def my_class_method(cls, *args, **kwargs):
print('>>> %s %s %s' % (cls, args, kwargs))
print('>>> my_class_method func invoked')
@dddpropertymethod.getter
def my_x(self):
return self._pvalue
@my_x.setter
def my_x(self, value):
self._pvalue = value
t = TestDes('instance a')
TestDes.ori_static_method(1)
t.ori_static_method(1)
TestDes.my_static_method(1)
t.my_static_method(1)
TestDes.ori_class_method(1)
t.ori_class_method(1)
TestDes.my_class_method(1)
t.my_class_method(1)
t.my_x = 'myx'
print(t.my_x)
>>> 输出
>>> (1,) {}
>>> ori_static_method func invoked
>>> (1,) {}
>>> ori_static_method func invoked
>>> (1,) {}
>>> my_static_method func invoked
>>> (1,) {}
>>> my_static_method func invoked
>>> (1,) {}
>>> ori_class_method func invoked
>>> (1,) {}
>>> ori_class_method func invoked
>>> (1,) {}
>>> my_class_method func invoked
>>> (1,) {}
>>> my_class_method func invoked
>>> myx