2019年1月31日 星期四

[Python] 裝飾器 Decorator

Decorator裝飾器使用

Decorator
裝飾器能讓你在被裝飾的函式之前與之後執行裝飾器的程式, 延伸函式的呼叫行為, 將共用的功能獨立出來, 不用修改函式
, 加上去即可使用 ,不限制一個數量, 內建裝飾器如@property, @staticmethod各有不同功能:
使用場合:
  • 紀錄log
  • 管控權限與身份認證
  • 在呼叫API時很適合搭配使用
  • 在沒明確定義參數數量時使用 *args : 回傳tuple, **kwargs : 回傳Dictionary
import functools
def upercase(func):
    functools.wraps(func)
    def wrapper(*args, **kwargs):
        print args
        print kwargs
        return func().upper()
    return wrapper

@uppercase
def hi('boy',name='Charles'):
    return 'Hello'

>>> hi()
('boy')
{name : 'Charles'}
'HELLO'

這裡還有一些方法可以參考 例如快取,debug......
import functools
def cache(func):
    memo = {}

    @wraps(func)
    def _wrapper(*args):
        res = memo.get(args, None)
        if res is not None:
            return res
        else:
            res = func(*args)
            memo[args] = res
        return res
    return _wrapper


@cache
def fib(n):
    if n <= 1:
        return n
    else:
        return fib(n-1) + fib(n-2)

print([fib(i) for i in list(range(31))])

def debug(fn):
  def wrapper(*args, **kwargs):
    print(fn.__name__, 'called!')
    print(sorted(args), tuple(sorted(kwargs.items())))
    res = fn(*args, **kwargs)
    print(res)
    return res
  return wrapper
class cache(object):

  def __init__(self, fn):
    self.fn = fn
    self._cache = {}
    functools.update_wrapper(self, fn)

  def __call__(self, *args, **kwargs):
    key = str(args) + str(kwargs)
    if key in self._cache:
      ret = self._cache[key]
    else:
      ret = self._cache[key] = self.fn(*args, **kwargs)

    return ret

  def clear_cache(self):
    self._cache = {}


# from http://stackoverflow.com/questions/3627793/best-output-type-and-encoding-practices-for-repr-functions
def stdout_encode(u, default='UTF8'):
  encoding = sys.stdout.encoding or default
  if sys.version_info > (3, 0):
    return u.encode(encoding).decode(encoding)
  return u.encode(encoding)

計算時間decorator(python3.5+)
def timeit_wrapper(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        start = time.perf_counter()  # Alternatively, you can use time.process_time()
        func_return_val = func(*args, **kwargs)
        end = time.perf_counter()
        print('{0:<10 -="" :="" end="" format="" func.__module__="" func.__name__="" func_return_val="" pre="" return="" start="" wrapper="">

沒有留言:

張貼留言