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


2.6.16. 定义上下文管理器的简单方法