摘要:然而,當(dāng)我們想要獲取被包裝函數(shù)的參數(shù)或源代碼時,同樣不能得到我們想要的結(jié)果。這是在中的,版本已被修復(fù),參考。如同上面我們所看到的,可以幫我們解決和的問題,但對于獲取函數(shù)的參數(shù)或源代碼則束手無策。
裝飾器基本概念
大家都知道裝飾器是一個很著名的設(shè)計模式,經(jīng)常被用于 AOP (面向切面編程)的場景,較為經(jīng)典的有插入日志,性能測試,事務(wù)處理,Web權(quán)限校驗, Cache等。
Python 語言本身提供了裝飾器語法(@),典型的裝飾器實現(xiàn)如下:
@function_wrapper def function(): pass
@實際上是 python2.4 才提出的語法糖,針對 python2.4 以前的版本有另一種等價的實現(xiàn):
def function(): pass function = function_wrapper(function)裝飾器的兩種實現(xiàn)
函數(shù)包裝器 - 經(jīng)典實現(xiàn)
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函數(shù)(function)自省
當(dāng)我們談到一個函數(shù)時,通常希望這個函數(shù)的屬性像其文檔上描述的那樣,是被明確定義的,例如__name__ 和__doc__ 。
針對某個函數(shù)應(yīng)用裝飾器時,這個函數(shù)的屬性就會發(fā)生變化,但這并不是我們所期望的。
def function_wrapper(wrapped): def _wrapper(*args, **kwargs): return wrapped(*args, **kwargs) return _wrapper @function_wrapper def function(): pass >>> print(function.__name__) _wrapper
python 標(biāo)準(zhǔn)庫提供了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
然而,當(dāng)我們想要獲取被包裝函數(shù)的參數(shù)(argument)或源代碼(source code)時,同樣不能得到我們想要的結(jié)果。
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)
當(dāng)包裝器(@function_wrapper)被應(yīng)用于@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在實現(xiàn)時,缺少functools.update_wrapper需要的某些屬性。這是functools.update_wrapper在 python2 中的 bug,3.2版本已被修復(fù),參考 http://bugs.python.org/issue3445。
然而,在 python3 下執(zhí)行,另一個問題出現(xiàn)了:
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
這是因為包裝器認(rèn)定被包裝的函數(shù)(@classmethod )是可以直接被調(diào)用的,但事實并不一定是這樣的。被包裝的函數(shù)實際上可能是描述符(descriptor ),意味著為了使其可調(diào)用,該函數(shù)(描述符)必須被正確地綁定到某個實例上。關(guān)于描述符的定義,可以參考 https://docs.python.org/2/howto/descriptor.html。
總結(jié) - 簡單并不意味著正確盡管大家實現(xiàn)裝飾器所用的方法通常都很簡單,但這并不意味著它們一定是正確的并且始終能正常工作。
如同上面我們所看到的,functools.wraps() 可以幫我們解決__name__ 和__doc__ 的問題,但對于獲取函數(shù)的參數(shù)(argument)或源代碼( source code )則束手無策。
以上問題,wrapt 都可以幫忙解決,詳細(xì)用法可參考其官方文檔:http://wrapt.readthedocs.org
本文作者系OneAPM 工程師曾靈敏 ,想閱讀更多好的技術(shù)文章,請訪問 OneAPM 官方技術(shù)博客。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/37523.html
摘要:進(jìn)階細(xì)節(jié)根據(jù)慕課網(wǎng)七月老師視頻整理一切皆對象對與來說,一切皆對象,包括函數(shù)。閉包的意義在于返回了一個現(xiàn)場,如果沒有閉包,很容易被外部的變量所影響。重復(fù)定義可見此時返回了不再是閉包了。注意該方法的返回值只能是布爾類型,即或。 Python進(jìn)階細(xì)節(jié) 根據(jù)慕課網(wǎng)七月老師視頻整理 一切皆對象 對與Python來說,一切皆對象,包括函數(shù)。在其他語言比如c++中,函數(shù)只是一段可執(zhí)行的代碼,只要你獲...
摘要:變量查找規(guī)則在中一個變量的查找順序是局部環(huán)境,閉包,全局,內(nèi)建閉包引用了自由變量的函數(shù)。閉包的作用閉包的最大特點是可以將父函數(shù)的變量與內(nèi)部函數(shù)綁定,并返回綁定變量后的函數(shù),此時即便生成閉包的環(huán)境父函數(shù)已經(jīng)釋放,閉包仍然存在。 導(dǎo)語:本文章記錄了本人在學(xué)習(xí)Python基礎(chǔ)之函數(shù)篇的重點知識及個人心得,打算入門Python的朋友們可以來一起學(xué)習(xí)并交流。 本文重點: 1、掌握裝飾器的本質(zhì)、功...
摘要:所以,有另一種說法認(rèn)為閉包是由函數(shù)和與其相關(guān)的引用環(huán)境組合而成的實體。 對于已經(jīng)對 閉包 或者 裝飾器有一定概念的,可以直接通過右側(cè)標(biāo)題目錄直接定位到相應(yīng)段落查看所需的內(nèi)容。 什么是裝飾器? 裝飾器(Decorator)相對簡單,咱們先介紹它:裝飾器的功能是將被裝飾的函數(shù)當(dāng)作參數(shù)傳遞給與裝飾器對應(yīng)的函數(shù)(名稱相同的函數(shù)),并返回包裝后的被裝飾的函數(shù),聽起來有點繞,沒關(guān)系,直接看示意圖,...
摘要:前言最近跟著流暢的和學(xué)習(xí),看到裝飾器部分,有些頭大倒不是因為概念難以理解,而是書和網(wǎng)上文章中有些地方有些矛盾之處在簡單學(xué)習(xí)和實踐之后,整理出我對裝飾器的理解如下裝飾器的定義在不同語境下,裝飾器有不一樣的含義,我大致認(rèn)為有種定義一種把另一個對 前言 最近跟著《流暢的Python》和《Python Cookbook》學(xué)習(xí),看到裝飾器部分,有些頭大倒不是因為概念難以理解,而是書和網(wǎng)上文章中有...
摘要:裝飾器的使用符合了面向?qū)ο缶幊痰拈_放封閉原則。三簡單的裝飾器基于上面的函數(shù)執(zhí)行時間的需求,我們就手寫一個簡單的裝飾器進(jìn)行實現(xiàn)。函數(shù)體就是要實現(xiàn)裝飾器的內(nèi)容。類裝飾器的實現(xiàn)是調(diào)用了類里面的函數(shù)。類裝飾器的寫法比我們裝飾器函數(shù)的寫法更加簡單。 目錄 前言 一、什么是裝飾器 二、為什么要用裝飾器 ...
閱讀 1880·2021-11-22 09:34
閱讀 1149·2021-10-09 09:44
閱讀 3006·2021-09-29 09:35
閱讀 3622·2021-09-14 18:01
閱讀 1487·2021-08-16 10:49
閱讀 1093·2019-08-29 14:11
閱讀 854·2019-08-29 12:47
閱讀 3078·2019-08-26 13:47