有人能詳細解釋下面的代碼的最後一行:Python的方法定義使用裝飾
def myMethod(self):
# do something
myMethod = transformMethod(myMethod)
你爲什麼想要通過定義爲通過其他方法的方法?那又如何呢?提前致謝!
有人能詳細解釋下面的代碼的最後一行:Python的方法定義使用裝飾
def myMethod(self):
# do something
myMethod = transformMethod(myMethod)
你爲什麼想要通過定義爲通過其他方法的方法?那又如何呢?提前致謝!
這是一個函數環繞的例子,當你有一個函數接受一個函數作爲參數,並且返回一個新的函數來修改原始函數的行爲。
下面是如何這可能是使用的例子,這是一個簡單的包裝它只是打印「確認」,並在每次調用「退出」:
def wrapper(func):
def wrapped():
print 'Enter'
result = func()
print 'Exit'
return result
return wrapped
,這裏是你怎麼可能一個例子使用此:
>>> def say_hello():
... print 'Hello'
...
>>> say_hello() # behavior before wrapping
Hello
>>> say_hello = wrapper(say_hello)
>>> say_hello() # behavior after wrapping
Enter
Hello
Exit
爲方便起見,Python提供了decorator syntax這僅僅是函數封裝的簡寫版本做同樣的事情,在函數定義的時候,這裏是如何可以用:
>>> @wrapper
... def say_hello():
... print 'Hello'
...
>>> say_hello()
Enter
Hello
Exit
爲什麼要通過另一種方法傳遞方法的定義?
因爲你想修改它的行爲。
那該怎麼辦?
完美的是,因爲函數是Python中的第一類。
def decorator(f):
def wrapper(*args, **kwargs):
print 'Before!'
res = f(*args, **kwargs)
print 'After!'
return res
return wrapper
def somemethod():
print 'During...'
somemethod = decorator(somemethod)
somemethod()
爲什麼不修改方法的實際源代碼?你能舉一個例子,通過方法的定義會非常有益嗎?最後,如何編寫transformMethod來修改另一個方法?對於所有的問題抱歉,我從來沒有見過這種類型的語法,所以我很困惑。 –
「爲什麼不修改方法的實際源代碼?」因爲您可能沒有*源代碼,或者您需要將其應用於多個功能。 –
@ user1495015或者,也許所做的修改總是相同的,只有實際修改的部分是不同的。爲此,您可以愉快地使用裝飾器。 – glglgl
你描述的是一個裝飾,方法/函數修改的一種形式,可以實現與decorators特殊的語法容易得多。
你描述的是在@staticmethod
形式,@classmethod
,@functools.wraps()
,非常廣泛地使用,例如相當於
@transformMethod
def myMethod(self):
# do something
裝飾@contextlib.contextmanager
等等,等等等等
由於有一定的Python版本(我認爲它是2.6),課程也可以進行裝飾。
這兩種裝飾愉快地允許返回甚至不是函數或類的對象。例如,你可以用一種將它變成字典,集合或其他的方式來裝飾生成器函數。
apply = lambda tobeapplied: lambda f: tobeapplied(f())
@apply(dict)
def mydict():
yield 'key1', 'value1'
yield 'key2', 'value2'
print mydict
@apply(set)
def myset():
yield 1
yield 2
yield 1
yield 4
yield 2
yield 7
print myset
我在這裏做什麼?
我創建了一個函數,它接受一個「要應用的東西」,然後返回另一個函數。
這個「內部」函數需要對函數進行修飾,調用它並將其結果放入外部函數並返回此結果。
f()
返回一個發生器對象,然後將其放入dict()
或set()
。
不,它*是一個裝飾器。對於完全相同的操作,「@」只是語法糖。 –
@ IgnacioVazquez-Abrams你是對的。我對Python的定義太接近了。 – glglgl
@ IgnacioVazquez-Abrams是的,但大多數人看到裝飾相反。不一定作爲一種通用的方法轉換。 –
你需要明白,在Python中,一切都是一個對象。函數是一個對象。你可以用一個函數對象來做同樣的事情,你可以用其他類型的對象來做:存儲在列表中,存儲在字典中,從函數調用中返回等等。
像你這樣的代碼的常見原因顯示是「包裝」其他功能對象。例如,這裏是一個打印由函數返回的值的包裝器。
def print_wrapper(fn):
def new_wrapped_fn(*args):
x = fn(*args)
print("return value of %s is: %s" % (fn.__name__, repr(x)))
return x
return new_wrapped_fn
def test(a, b):
return a * b
test = print_wrapper(test)
test(2, 3) # prints: return value of test is: 6
這是一個非常有用的任務,也是一個很普遍的任務,Python對它有特別的支持。 Google搜索「Python裝飾器」。
在您的原始問題中,您問「爲什麼要通過其他方法傳遞方法的定義?」然後,在評論中,您問「爲什麼不修改方法的實際源代碼?」實際上,我認爲這是一個非常好的問題,如果不用手揮手就很難回答,因爲裝飾者只有在代碼達到一定的複雜程度時纔會變得非常有用。但是,我認爲如果你考慮以下兩個功能裝飾的點將變得更加清晰:
def add_x_to_sequence(x, seq):
result = []
for i in seq:
result.append(i + x)
return result
def subtract_x_from_sequence(x, seq):
result = []
for i in seq:
result.append(i - x)
return result
現在,這兩個例子功能有一定的缺陷 - 在現實生活中,例如,你可能只是重寫他們作爲列表解析 - 但讓我們忽略目前的明顯缺陷,並假裝我們必須這樣寫,因爲for
循環遍歷序列。我們現在面臨的問題是,我們的兩個功能差不多是一樣的,只在一個關鍵時刻有所不同。這意味着我們在這裏重複自己!這是一個問題。現在我們必須維護更多的代碼行,爲bug出現留出更多空間,並且在bug出現後隱藏更多空間。
一個經典方法這個問題可能是創建一個函數,需要的功能,和整個序列應用它,就像這樣:
def my_map(func, x, seq):
result = []
for i in seq:
result.append(func(i, x))
return result
現在我們需要做的就是定義特定funcs中傳遞給my_map
(這實際上只是內置的map
函數的專用版本)。
def sub(a, b):
return a - b
def add(a, b):
return a + b
而且我們可以使用它們像這樣:
added = my_map(sub, x, seq)
但這種方法有它的問題。例如,閱讀比我們最初的獨立功能要難一些;每次我們想從項目列表中加上或減去x
,我們必須指定函數和值作爲參數。如果我們這麼做的話,我們寧願有一個單一的函數名稱,它總是指向同一個動作 - 這將提高可讀性,並使我們更容易理解代碼中發生的事情。我們可以包裹上面另一功能...
def add_x_to_sequence(x, seq):
return my_map(add, x, seq)
但現在我們再次重複自己!而且我們也在創造一個功能繁多的混亂的名字空間。
裝飾者提供了一個解決這些問題的方法。而不是每次將函數傳遞給另一個函數,我們可以傳遞一次。首先,我們定義了一個包裝函數:
def vectorize(func):
def wrapper(x, seq):
result = []
for i in seq:
result.append(func(i, x))
return result
return wrapper
現在我們需要做的就是定義一個函數,並把它傳遞給上面,它環繞:
def add_x_to_sequence(a, b):
return a + b
add_x_to_sequence = vectorize(add_x_to_sequence)
或者,使用修飾語法:
@vectorize
def add_x_to_sequence(a, b):
return a + b
現在我們可以寫出許多不同vectorize
d功能,並且我們for
爲所有這些的邏輯在短短一個發生PL高手。現在我們不必分別修復或優化許多不同的功能;我們所有的循環相關的錯誤和循環相關的優化發生在同一個地方;而且我們仍然可以獲得特定功能的所有可讀性優勢。
非常感謝您的幫助!我非常感謝它有多清楚。 –