摘要:然而,當我們想要獲取被包裝函數的參數或源代碼時,同樣不能得到我們想要的結果。這是在中的,版本已被修復,參考。如同上面我們所看到的,可以幫我們解決和的問題,但對于獲取函數的參數或源代碼則束手無策。
裝飾器基本概念
大家都知道裝飾器是一個很著名的設計模式,經常被用于 AOP (面向切面編程)的場景,較為經典的有插入日志,性能測試,事務處理,Web權限校驗, Cache等。
Python 語言本身提供了裝飾器語法(@),典型的裝飾器實現如下:
@function_wrapper def function(): pass
@實際上是 python2.4 才提出的語法糖,針對 python2.4 以前的版本有另一種等價的實現:
def function(): pass function = function_wrapper(function)裝飾器的兩種實現
函數包裝器 - 經典實現
def function_wrapper(wrapped): def _wrapper(*args, **kwargs): return wrapped(*args, **kwargs) return _wrapper @function_wrapper def function(): pass
類包裝器 - 易于理解
class function_wrapper(object): def __init__(self, wrapped): self.wrapped = wrapped def __call__(self, *args, **kwargs): return self.wrapped(*args, **kwargs) @function_wrapper def function(): pass函數(function)自省
當我們談到一個函數時,通常希望這個函數的屬性像其文檔上描述的那樣,是被明確定義的,例如__name__ 和__doc__ 。
針對某個函數應用裝飾器時,這個函數的屬性就會發生變化,但這并不是我們所期望的。
def function_wrapper(wrapped): def _wrapper(*args, **kwargs): return wrapped(*args, **kwargs) return _wrapper @function_wrapper def function(): pass >>> print(function.__name__) _wrapper
python 標準庫提供了functools.wraps(),來解決這個問題。
import functools def function_wrapper(wrapped): @functools.wraps(wrapped) def _wrapper(*args, **kwargs): return wrapped(*args, **kwargs) return _wrapper @function_wrapper def function(): pass >>> print(function.__name__) function
然而,當我們想要獲取被包裝函數的參數(argument)或源代碼(source code)時,同樣不能得到我們想要的結果。
import inspect def function_wrapper(wrapped): ... @function_wrapper def function(arg1, arg2): pass >>> print(inspect.getargspec(function)) ArgSpec(args=[], varargs="args", keywords="kwargs", defaults=None) >>> print(inspect.getsource(function)) @functools.wraps(wrapped) def _wrapper(*args, **kwargs): return wrapped(*args, **kwargs)包裝類方法(@classmethod)
當包裝器(@function_wrapper)被應用于@classmethod時,將會拋出如下異常:
class Class(object): @function_wrapper @classmethod def cmethod(cls): pass Traceback (most recent call last): File "", line 1, in File " ", line 3, in Class File " ", line 2, in wrapper File ".../functools.py", line 33, in update_wrapper setattr(wrapper, attr, getattr(wrapped, attr)) AttributeError: "classmethod" object has no attribute "__module__"
因為@classmethod在實現時,缺少functools.update_wrapper需要的某些屬性。這是functools.update_wrapper在 python2 中的 bug,3.2版本已被修復,參考 http://bugs.python.org/issue3445。
然而,在 python3 下執行,另一個問題出現了:
class Class(object): @function_wrapper @classmethod def cmethod(cls): pass >>> Class.cmethod() Traceback (most recent call last): File "classmethod.py", line 15, inClass.cmethod() File "classmethod.py", line 6, in _wrapper return wrapped(*args, **kwargs) TypeError: "classmethod" object is not callable
這是因為包裝器認定被包裝的函數(@classmethod )是可以直接被調用的,但事實并不一定是這樣的。被包裝的函數實際上可能是描述符(descriptor ),意味著為了使其可調用,該函數(描述符)必須被正確地綁定到某個實例上。關于描述符的定義,可以參考 https://docs.python.org/2/howto/descriptor.html。
總結 - 簡單并不意味著正確盡管大家實現裝飾器所用的方法通常都很簡單,但這并不意味著它們一定是正確的并且始終能正常工作。
如同上面我們所看到的,functools.wraps() 可以幫我們解決__name__ 和__doc__ 的問題,但對于獲取函數的參數(argument)或源代碼( source code )則束手無策。
以上問題,wrapt 都可以幫忙解決,詳細用法可參考其官方文檔:http://wrapt.readthedocs.org
本文作者系OneAPM 工程師曾靈敏 ,想閱讀更多好的技術文章,請訪問 OneAPM 官方技術博客。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/37523.html
摘要:進階細節根據慕課網七月老師視頻整理一切皆對象對與來說,一切皆對象,包括函數。閉包的意義在于返回了一個現場,如果沒有閉包,很容易被外部的變量所影響。重復定義可見此時返回了不再是閉包了。注意該方法的返回值只能是布爾類型,即或。 Python進階細節 根據慕課網七月老師視頻整理 一切皆對象 對與Python來說,一切皆對象,包括函數。在其他語言比如c++中,函數只是一段可執行的代碼,只要你獲...
摘要:變量查找規則在中一個變量的查找順序是局部環境,閉包,全局,內建閉包引用了自由變量的函數。閉包的作用閉包的最大特點是可以將父函數的變量與內部函數綁定,并返回綁定變量后的函數,此時即便生成閉包的環境父函數已經釋放,閉包仍然存在。 導語:本文章記錄了本人在學習Python基礎之函數篇的重點知識及個人心得,打算入門Python的朋友們可以來一起學習并交流。 本文重點: 1、掌握裝飾器的本質、功...
摘要:所以,有另一種說法認為閉包是由函數和與其相關的引用環境組合而成的實體。 對于已經對 閉包 或者 裝飾器有一定概念的,可以直接通過右側標題目錄直接定位到相應段落查看所需的內容。 什么是裝飾器? 裝飾器(Decorator)相對簡單,咱們先介紹它:裝飾器的功能是將被裝飾的函數當作參數傳遞給與裝飾器對應的函數(名稱相同的函數),并返回包裝后的被裝飾的函數,聽起來有點繞,沒關系,直接看示意圖,...
摘要:前言最近跟著流暢的和學習,看到裝飾器部分,有些頭大倒不是因為概念難以理解,而是書和網上文章中有些地方有些矛盾之處在簡單學習和實踐之后,整理出我對裝飾器的理解如下裝飾器的定義在不同語境下,裝飾器有不一樣的含義,我大致認為有種定義一種把另一個對 前言 最近跟著《流暢的Python》和《Python Cookbook》學習,看到裝飾器部分,有些頭大倒不是因為概念難以理解,而是書和網上文章中有...
摘要:裝飾器的使用符合了面向對象編程的開放封閉原則。三簡單的裝飾器基于上面的函數執行時間的需求,我們就手寫一個簡單的裝飾器進行實現。函數體就是要實現裝飾器的內容。類裝飾器的實現是調用了類里面的函數。類裝飾器的寫法比我們裝飾器函數的寫法更加簡單。 目錄 前言 一、什么是裝飾器 二、為什么要用裝飾器 ...
閱讀 1918·2021-11-22 09:34
閱讀 1158·2021-10-09 09:44
閱讀 3046·2021-09-29 09:35
閱讀 3626·2021-09-14 18:01
閱讀 1494·2021-08-16 10:49
閱讀 1096·2019-08-29 14:11
閱讀 859·2019-08-29 12:47
閱讀 3081·2019-08-26 13:47