1. 程式人生 > >PythonCookBook 筆記 chapter-09-超程式設計

PythonCookBook 筆記 chapter-09-超程式設計

1,裝飾器:就是一個函式,可以接受一個函式作為輸入並返回一個新的函式

from functools import wraps

class Point2D:
    def __init__(self, x, y):
        self.x = x
        self.y = y

    def __str__(self):
        return 'Point2D: [{!s}, {!s}]'.format(self.x, self.y)

# 裝飾器,對Point2D物件檢測,如果小於0 就置為0
def Add_Checker(func):
    @wraps(func)# 拷貝裝飾器的元資料
    def wrapper(a, b):
        if a.x < 0 or a.y < 0:
            a = Point2D(a.x if a.x > 0 else 0, a.y if a.y > 0 else 0)
        if b.x < 0 or b.y < 0:
            b = Point2D(b.x if b.x > 0 else 0, b.y if b.y > 0 else 0)
        ret = func(a, b)
        return ret
    return wrapper

def no_add(a, b):
    return Point2D(a.x + b.x, a.y + b.y)

@Add_Checker
def add(a, b):
    print(a, b)
    return Point2D(a.x + b.x, a.y + b.y)


a = Point2D(1,-2)
b = Point2D(-3, 4)
print(no_add(a, b))
print(add(a, b))
orig_add = add.__wrapped__ #對裝飾器進行解包裝
print(orig_add(a, b))

out:
    Point2D: [-2, 2]
    Point2D: [1, 0] Point2D: [0, 4]
    Point2D: [1, 4]
    Point2D: [1, -2] Point2D: [-3, 4]
    Point2D: [-2, 2]

2, 實現外部調整裝飾器的屬性,使得在執行時能夠控制裝飾器行為

from functools import wraps, partial


def attach_wrapper(obj, func=None):
    if func is None:
        return partial(attach_wrapper, obj)
    setattr(obj, func.__name__, func)
    return func


def Student(id, name=None, addr=None):
    def decorate(func):
        stud_id = id
        stud_name = name if name else 'XiaoMing'
        stud_addr = addr if addr else 'xxx'
        
        @wraps(func)
        def wrapper(*args, **kwargs):
            print(stud_id, ': ', stud_name, ':', stud_addr);
            return func(*args, **kwargs)

        #訪問器函式用來修改裝飾器的屬性
        @attach_wrapper(wrapper)
        def set_name(newname):
            nonlocal stud_name #通過對nonlocal變數賦值來調整內部引數
            stud_name = newname
            
        @attach_wrapper(wrapper)
        def set_addr(newaddr):
            nonlocal stud_addr
            stud_addr = newaddr

        return wrapper
    return decorate


@Student(1)
def do_what(what):
    print('do '+what)


do_what('homework')
do_what.set_name('Leon')
do_what('homework')
do_what.set_addr('PeopleSquare 15#')
do_what('homework')


out:
    1 :  XiaoMing : xxx
    do homework
    1 :  Leon : xxx
    do homework
    1 :  Leon : PeopleSquare 15#
    do homework

3,利用裝飾器對函式引數強制執行型別檢查

from inspect import signature
from functools import wraps

def typeassert(*ty_args, **ty_kwargs):
    def decorate(func):
        # 全域性變數__debug__被設為False時,返回未修改的函式
        if not __debug__:
            return func

        # 從一個可呼叫物件中提取出引數簽名信息
        sig = signature(func)
        # 對提供的型別到引數做部分繫結,建立了有序字典
        bound_types = sig.bind_partial(*ty_args, **ty_kwargs).arguments

        @wraps(func)
        def wrapper(*args, **kwargs):
            # 不允許出現缺失的引數
            bound_value = sig.bind(*args, **kwargs)
            for name, value in bound_value.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(x, y):
    print(x+y)

add(1, 2)
add(1, 'sdf')

out:
    3
    Traceback (most recent call last)...
    TypeError: Argument y must be <class 'int'>

4, 實現單例模式

#實現單例模式
class Singleton(type):
    def __init__(self, *args, **kwargs):
        self.__instance = None
        super().__init__(*args, **kwargs)

    def __call__(self, *args, **kwargs):
        if self.__instance is None:
            self.__instance = super().__call__(*args, **kwargs)
            return self.__instance
        else:
            return self.__instance

class Spam(metaclass = Singleton):
    def __init__(self):
        print('Creating Spam')

a = Spam()
b = Spam()

print (a == b)
out:
    Creating Spam
    True

#單例實現的另一種技巧
class _Spam:
    def __init__(self):
        print('Creating Spam')

_spam_instance = None
def Spam():
    global _spam_instance
    if _spam_instance is not None:
        return _spam_instance
    else:
        _spam_instance = _Spam()
        return _spam_instance

5,建立快取例項

方法一:

import weakref
class Spam():
    # 不能直接例項化該類
    def __init__(self, *args, **kwargs):
        raise RuntimeError('Cannot initialized directly')

    # 用類的方法實現建構函式的功能
    @classmethod
    def _new(cls, name):
        self = cls.__new__(cls)
        self.name = name
        return self

class CacheSpamMgr:
    def __init__(self):
        self._cache = weakref.WeakValueDictionary()

    def get_spam(self, name):
        if name not in self._cache:
            s = Spam._new(name)
            self._cache[name] = s
        else:
            s = self._cache[name]
        return s

mgr = CacheSpamMgr()    
a = mgr.get_spam('Leon')
b = mgr.get_spam('xxx')
c = mgr.get_spam('Leon')

print('a == b is', (a is b))
print('a == c is',(a is c))

out:
    a == b is False
    a == c is True

方法二:

import weakref
#利用元類實現建立快取例項
class Cached(type):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.__cache = weakref.WeakValueDictionary()

    def __call__(self, *args):
        if args in self.__cache:
            return self.__cache[args]
        else:
            obj = super().__call__(*args)
            self.__cache[args] = obj
        return obj

class Spam(metaclass=Cached):
    def __init__(self, name):
        print('Creating Spam ({!r})'.format(name))
        self.name = name

a = Spam('Leon')
b = Spam('x')
c = Spam('Leon')
print('a == b is', (a is b))
print('a == c is',(a is c))

out:
    Creating Spam ('Leon')
    Creating Spam ('x')
    a == b is False
    a == c is True