Skip to content

Latest commit

 

History

History
245 lines (176 loc) · 10.1 KB

File metadata and controls

245 lines (176 loc) · 10.1 KB
title
Python / Decorator

Python / Decorator

  • Glossary — Python 3.7.3 documentation

    • A function RETURNING ANOTHER FUNCTION, usually applied as a FUNCTION TRANSFORMATION using the @wrapper syntax. Common examples for decorators are classmethod() and staticmethod().

      包含 decorator 自己,共有 3 個 function;而 decorator 的 input 跟 output 都是 function,通常是用 output function (outer) 將 input function (inner) 包裝一層,未來呼叫 outer function 時會間接呼叫 inner function,其間就是 decorator 可以安插一些邏輯的地方。

      Can Python decorators be used to return non-function objects? - Stack Overflow

    • The decorator syntax is merely SYNTACTIC SUGAR, the following two function definitions are semantically equivalent:

      def f(...):
          ...
      f = staticmethod(f)
      
      @staticmethod
      def f(...):
          ...
      
    • The same concept exists for classes, but is LESS COMMONLY USED THERE. See the documentation for function definitions and class definitions for more about decorators.

新手上路 {: #getting-started }

>>> def plus_five(func):
...     x = func()
...     return x + 5
...
>>> @plus_five
... def add_nums():
...     return 1 + 2
...
>>> type(add_nums) # <1>
<type 'int'>
>>> type(add_nums), add_nums
(<type 'int'>, 8)
  1. add_nums 的型態竟是 int。那是因為過程中已經發生 add_nums = plus_five(add_nums) 這件事,結果 add_nums 就是一個運算結果,而非包裝過一層的 function。

解法就是 plus_five 回傳一個 function 而非運算結果:

>>> def plus_five(func):
...     def inner(*args, **kwargs):
...         x = func(*args, **kwargs) + 5
...         return x
...     return inner
...
>>> @plus_five
... def add_nums(num1, num2):
...     return num1 + num2
>>> type(add_nums), add_nums
(<type 'function'>, <function inner at 0x7ff8b2e0c668>)
>>>

用 class 來寫的彈性比較大:

deco.py:

class decoargs():

    def __init__(self, arg):
        self.arg = arg

    def __call__(self, func):

        def wrapper(*args, **kwargs):
            print '[%s] before' % self.arg
            result = func(*args, **kwargs)
            print '[%s] after' % self.arg
            return result

        return wrapper

@decoargs('hello1')
def calc1(a, b):
    print '----------> calc1'
    return a + b

decoargs = decoargs('hello2')

@decoargs
def calc2(a, b):
    print '----------> calc2'
    return a + b

print calc1(1, 1)
print calc2(2, 2)

Python/Jython 都支援這樣的用法:

$ python deco.py
[hello1] before
----------> calc1
[hello1] after
2
[hello2] before
----------> calc2
[hello2] after
4

$ jython deco.py
[hello1] before
----------> calc1
[hello1] after
2
[hello2] before
----------> calc2
[hello2] after
4

參考資料:

不傳回 callable 可以嗎? {: #not-returning-callable }

  • 根據 Function definitions - 8. Compound statements — Python 3.7.3 documentation 的說法:

    Multiple decorators are applied in nested fashion. For example, the following code

    @f1(arg)
    @f2
    def func(): pass
    

    is roughly equivalent to

    def func(): pass
    func = f1(arg)(f2(func))
    

    except that the original function is not temporarily bound to the name func.

    f1() 傳回 non-function 沒什麼問題,但 f2() 傳回 non-function 可能就會讓預期收到 function 的外層 (f1) 出錯。

    這跟 Can Python decorators be used to return non-function objects? - Stack Overflow 中 mgilson 的想法/擔憂一致:

    However, doing so would be TERRIBLY UNCLEAR. People expect function decorators to return functions (or at least callable objects) and class decorators to return classes. If you stray from that pattern, DO SO AT YOUR OWN RISK


參考資料:

  • Can Python decorators be used to return non-function objects? - Stack Overflow #ril

    • mgilson: To quote PEP 0318:

      The current syntax for function decorators as implemented in Python 2.4a2 is:

      @dec2
      @dec1
      def func(arg1, arg2, ...):
          pass
      

      This is equivalent to:

      def func(arg1, arg2, ...):
          pass
      func = dec2(dec1(func))
      

      without the INTERMEDIATE ASSIGNMENT to the variable func.

      So, to answer your question, you CAN have a decorator return something which isn't a function and the behavior is COMPLETELY DEFINED. Otherwise, it wouldn't be equivalent to the code above.

      However, doing so would be TERRIBLY UNCLEAR. People expect function decorators to return functions (or at least callable objects) and class decorators to return classes. If you stray from that pattern, DO SO AT YOUR OWN RISK -- If your neighbor decides to knock down your cubicle out of anger and frustration, that's your problem :).

參考資料 {: #reference }

手冊: