2.6. 元类
2.6.1. 在函数上添加包装器
import time
from functools import wraps
def addtime(func):
@wraps(func)
def wrapper(*args,**kw):
start =time.time()
result = func(*args,**kw)
end = time.time()
print(f'{func.__name__} run time: {end-start}')
return result
return wrapper
def addtimev2(func):
def wrapper(*args,**kw):
start =time.time()
result = func(*args,**kw)
end = time.time()
print(f'{func.__name__} run time: {end-start}')
return result
return wrapper
@addtime
def add(a,b):
return a+b
print(add(1,2))
@addtimev2
def add2(a,b):
return a+b
print(add2(1,2))
print
2.6.2. 解除一个装饰器
2.6.3. 带可选参数的装饰器
import logging
from functools import wraps
def logged(level,name=None,message=None):
def decorate(func):
logname = name if name is not None else func.__module__
log = logging.getLogger(logname)
log_msg = message if message is not None else func.__name__
@wraps(func)
def wrapper(*args,**kwargs):
log.log(level,log_msg)
return func(*args,**kwargs)
return wrapper
return decorate
@logged(logging.CRITICAL,'example','Adding two numbers')
def add(a,b):
return a+b
@logged(logging.CRITICAL)
def add2(a,b):
return a+b
add(1,2)
add2(1,2)
2.6.4. 利用装饰器强制函数上的类型检查
from inspect import signature
from functools import wraps
def typeassert(*ty_args, **ty_kwargs):
def decorate(func):
# If in optimized mode, disable type checking
if not __debug__:
return func
# Map function argument names to supplied types
print(ty_args)
print(ty_kwargs)
sig = signature(func)
bound_types = sig.bind_partial(*ty_args, **ty_kwargs).arguments
print(bound_types)
@wraps(func)
def wrapper(*args, **kwargs):
bound_values = sig.bind(*args, **kwargs)
print(bound_types)
print(bound_values.arguments.items())
for name, value in bound_values.arguments.items():
if name in bound_types:
if not isinstance(value, bound_types[name]):
raise TypeError(
'Argument {} must be {}'.format(name, bound_types[name])
)
return func(*args, **kwargs)
return wrapper
return decorate
@typeassert(int,int)
def add(a,b):
return a+b
print(add(1,2))
def spam(x:int, y, z:int = 42):
print(x,y,z)
2.6.5. 将装饰器定义为类的一部分
class A(object):
def decorator1(self,func):
def wrapper(*args,**kw):
print('decorator1')
return func(*args,**kw)
return wrapper
@classmethod
def decorator2(cls,func):
def wrapper(*args,**kw):
print('decorator2')
return func(*args,**kw)
return wrapper
a = A()
@a.decorator1
def add(a,b):
return a+b
@A.decorator2
def add2(a,b):
return a+b
print(add(1,2))
print(add2(2,1))
2.6.6. 为类和静态方法提供装饰器
需要我们将类的装饰器放到最前面
class Spam:
@classmethod
@timethis
def class_method(cls, n):
print(cls, n)
while n > 0:
n -= 1
2.6.7. 装饰器为被包装函数增加参数
from functools import wraps
def optional_debug(func):
@wraps(func)
def wrapper(*args, debug=False, **kwargs):
if debug:
print('Debug Calling', func.__name__)
return func(*args, **kwargs)
return wrapper
@optional_debug
def add(a,b):
return a+b
add(1,2,debug=True)
2.6.8. 使用装饰器扩充类的功能
def log_getattribute(cls):
# Get the original implementation
orig_getattribute = cls.__getattribute__
# Make a new definition
def new_getattribute(self, name):
print('getting:', name)
return orig_getattribute(self, name)
# Attach to the class and return
cls.__getattribute__ = new_getattribute
return cls
# Example use
@log_getattribute
class A:
def __init__(self,x):
self.x = x
def spam(self):
pass
a =A(1)
a.spam()
2.6.9. 使用元类控制实例的创建
class NoInstances(type):
def __call__(self, *args, **kwargs):
raise TypeError("Can't instantiate directly")
# Example
class Spam(metaclass=NoInstances):
@staticmethod
def grok(x):
print('Spam.grok')
class Singleton(type):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.__instance = None
def __call__(self, *args, **kwargs):
if self.__instance is None:
self.__instance = super().__call__(*args, **kwargs)
return self.__instance
class Student(metaclass=Singleton):
pass
# Example
s = Student()
s2 = Student()
print(s is s2)
2.6.10. 定义有可选参数的元类
class MyMeta(type):
# Optional
@classmethod
def __prepare__(cls, name, bases, *, debug=False, synchronize=False):
return super().__prepare__(name, bases)
# Required
def __new__(cls, name, bases, ns, *, debug=False, synchronize=False):
return super().__new__(cls, name, bases, ns)
# Required
def __init__(self, name, bases, ns, *, debug=False, synchronize=False):
super().__init__(name, bases, ns)
class Spam(metaclass=MyMeta, debug=True, synchronize=True):
pass
s = Spam()
2.6.11. *args和**kwargs的强制参数签名
from inspect import Signature, Parameter
# Make a signature for a func(x, y=42, *, z=None)
sig = Signature(
[
Parameter('x',Parameter.POSITIONAL_OR_KEYWORD),
Parameter('y',Parameter.POSITIONAL_OR_KEYWORD,default=42),
Parameter('z', Parameter.KEYWORD_ONLY, default=None)
]
)
print(sig)
def func(*args, **kwargs):
bound_values = sig.bind(*args, **kwargs)
for name, value in bound_values.arguments.items():
print(name,value)
func(1,2,z=3)
def make_sig(*names):
return Signature([Parameter(name, Parameter.POSITIONAL_OR_KEYWORD) for name in names])
class StructureMeta(type):
def __new__(cls, clsname, bases, clsdict):
clsdict['__signature__'] = make_sig(*clsdict.get('_fields',[]))
return super().__new__(cls, clsname, bases, clsdict)
class Structure(metaclass=StructureMeta):
def __init__(self, *args, **kwargs):
bound_values = self.__signature__.bind(*args, **kwargs)
for name, value in bound_values.arguments.items():
setattr(self, name, value)
class Point(Structure):
_fields = ['x','y']
p = Point(1,2)
print(p.x,p.y)
2.6.12. 在类上强制使用编程规约
class NoMixedCaseMeta(type):
def __new__(cls, clsname, bases, clsdict):
for name in clsdict:
if name.lower() != name:
raise TypeError('Bad attribute name: ' + name)
return super().__new__(cls, clsname, bases, clsdict)
class NoMixedCase(metaclass=NoMixedCaseMeta):
def a(self):
pass
def AbcD(self):
pass
c = NoMixedCase()
c.AbcD()
2.6.13. 以编程方式定义类
# stock.py
# Example of making a class manually from parts
# Methods
def __init__(self, name, shares, price):
self.name = name
self.shares = shares
self.price = price
def cost(self):
return self.shares * self.price
cls_dict = {
'__init__' : __init__,
'cost' : cost,
}
# Make a class
import types
Stock = types.new_class('Stock', (), {}, lambda ns: ns.update(cls_dict))
Stock.__module__ = __name__
s = Stock('ACME', 50, 91.1)
print(s.cost())
2.6.14. 利用函数注解实现方法重载
2.6.15. 避免重复的属性方法
class Person:
def __init__(self, name ,age):
self.name = name
self.age = age
@property
def name(self):
return self._name
@name.setter
def name(self, value):
if not isinstance(value, str):
raise TypeError('name must be a string')
self._name = value
@property
def age(self):
return self._age
@age.setter
def age(self, value):
if not isinstance(value, int):
raise TypeError('age must be an int')
self._age = value
class Person2:
def __init__(self, name ,age):
self.name = name
self.age = age
def __setattr__(self, name, value):
if name == 'name':
if not isinstance(value, str):
raise TypeError('name must be a string')
elif name == 'age':
if not isinstance(value, int):
raise TypeError('age must be an int')
class Descriptor:
def __init__(self, name=None, **opts):
self.name = name
for key, value in opts.items():
setattr(self, key, value)
def __set__(self, instance, value):
instance.__dict__[self.name] = value
class Typed(Descriptor):
expected_type = type(None)
def __set__(self, instance, value):
if not isinstance(value, self.expected_type):
raise TypeError('expected ' + str(self.expected_type))
super().__set__(instance, value)
class String(Typed):
expected_type = str
class Interger(Typed):
expected_type = int
class Person3:
name = String()
age = Interger()
def __init__(self, name, age):
self.name = name
self.age = age
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
class Person4:
name = typed_property('name', str)
age = typed_property('age', int)
def __init__(self, name, age):
self.name = name
self.age = age