終于解決了使用Python裝飾器中的一個(gè)痛點(diǎn)
前言
如何給裝飾器的參數(shù)傳參,這個(gè)問題曾經(jīng)困擾我好久,雖然Python版本的更新,現(xiàn)在這個(gè)問題終于解決了,特此記錄。
疑問
首先我有一個(gè)這樣的裝飾器文件路徑helper/log_helper.py
- import traceback
- from functools import wraps
- from loguru import logger
- def my_logger(count):
- def step1(foo):
- @wraps(foo)
- def step2(*args, **kwargs):
- try:
- result = foo(*args, **kwargs)
- logger.info(f"{result=},{count=}")
- except Exception:
- logger.exception(traceback.format_exc())
- return step2
- return step1
然后我有個(gè)文件需要引用這個(gè)裝飾器demo.py
- from helper.log_helper import my_logger
- class Demo:
- @my_logger(count=2)
- def main(self):
- return "in main function"
- if __name__ == '__main__':
- d = Demo()
- d.main()
輸出結(jié)果如下
- 2020-10-16 11:43:12.001 | INFO | helper.log_helper:step2:18 - result='in main function',count=2
這個(gè)裝飾器的作用很簡(jiǎn)單,就是獲取當(dāng)前函數(shù)的返回值,和傳入的count值。
好,現(xiàn)在問題來了?
如果給裝飾器的參數(shù)傳值呢,也就是說我的count=2,是通過傳值的形式。你想到可能是這樣
- from helper.log_helper import my_logger
- COUNT=2
- class Demo:
- @my_logger(count=COUNT)
- def main(self):
- return "in main function"
- if __name__ == '__main__':
- d = Demo()
- d.main()
ok,這樣確實(shí)可以,我們還可以使用再簡(jiǎn)化一步
- from functools import partial
- from helper.log_helper import my_logger
- COUNT=2
- my_logger = partial(my_logger,count=2)
- class Demo:
- @my_logger()
- def main(self):
- return "in main function"
- if __name__ == '__main__':
- d = Demo()
- d.main()
暫時(shí)來看我們搞定了傳參數(shù)的問題,這時(shí)候我們想如果外界調(diào)用了Demo類的main方法,并且向指定count的值怎么辦呢?
我們知道外界調(diào)用Demo類傳參的唯一途徑就是向__init__里進(jìn)行傳參數(shù),按照這個(gè)思路我們只能這么寫了,
- class Demo:
- def __init__(self):
- count =2
- @my_logger(count=self.count)
- def main(self):
- return "in main function"
但是這樣并不可以,我們得到錯(cuò)誤信息
- NameError: name 'self' is not defined
在裝飾器中無法使用self.形式的參數(shù),難道這個(gè)問題解決不了么?
問題解決
在Python3.7之前確實(shí)沒什么可行的方案。
我們知道在Python3.7的時(shí)候引入了dataclasses,我們可以通過它來簡(jiǎn)化__init__。
改下我們的代碼
- from functools import partial
- from helper.log_helper import my_logger
- from dataclasses import dataclass
- @dataclass()
- class Demo:
- count: int = 2
- logger: my_logger = partial(my_logger, count)
- @logger()
- def main(self):
- return "in main function"
- if __name__ == '__main__':
- d = Demo()
- d.main()
如果使用Python3.8那么可以直接忽略掉dataclass
- class Demo:
- count: int = 2
- logger: my_logger = partial(my_logger, count)
- @logger()
- def main(self):
- return "in main function"
這樣我們就成功的解決了這個(gè)問題,突然想起來之前遇到的這個(gè)難題,現(xiàn)在算是解決了,希望對(duì)你有幫助。