问题描述
我想创建装饰器,以显示将哪些参数传递给函数和方法.我已经写了函数代码,但是方法让我头疼.
I want to create decorator that shows which parameters were passed to function and methods. I have already written the code for functions, but methods are giving me a headaches.
这是按预期工作的函数修饰器:
This is function decorator that works as intended:
from functools import update_wrapper
class _PrintingArguments:
def __init__(self, function, default_comment, comment_variable):
self.function = function
self.comment_variable = comment_variable
self.default_comment = default_comment
update_wrapper(wrapped=function, wrapper=self)
def __call__(self, *args, **kwargs):
comment = kwargs.pop(self.comment_variable, self.default_comment)
params_str = [repr(arg) for arg in args] + ["{}={}".format(k, repr(v)) for k, v in kwargs.items()]
function_call_log = "{}({})".format(self.function.__name__, ", ".join(params_str))
print("Function execution - '{}'\n\t{}".format(comment, function_call_log))
function_return = self.function(*args, **kwargs)
print("\tFunction executed\n")
return function_return
def function_log(_function=None, default_comment="No comment.", comment_variable="comment"):
if _function is None:
def decorator(func):
return _PrintingArguments(function=func, default_comment=default_comment, comment_variable=comment_variable)
return decorator
else:
return _PrintingArguments(function=_function, default_comment=default_comment, comment_variable=comment_variable)
# example use:
@function_log
def a(*args, **kwargs):
pass
@function_log(default_comment="Hello World!", comment_variable="comment2")
def b(*args, **kwargs):
pass
a(0, x=1, y=2)
a(0, x=1, y=2, comment="Custom comment!")
b("a", "b", "c", asd="something")
b("a", "b", "c", asd="something", comment2="Custom comment for b!")
代码执行的输出:
Function execution - 'No comment.'
a(0, y=2, x=1)
Function executed
Function execution - 'Custom comment!'
a(0, y=2, x=1)
Function executed
Function execution - 'Hello World!'
b('a', 'b', 'c', asd='something')
Function executed
Function execution - 'Custom comment for b!'
b('a', 'b', 'c', asd='something')
Function executed
我已经为方法尝试了完全相同的装饰器:
I have tried the exactly same decorator for methods:
class A:
def __init__(self):
pass
@function_log
def method1(self, *args, **kwargs):
print("\tself = {}".format(self))
@function_log(default_comment="Something", comment_variable="comment2")
def method2(self, *args, **kwargs):
print("\tself = {}".format(self))
a_obj = A()
a_obj.method1(0, 1, p1="abc", p2="xyz")
a_obj.method1(0, 1, p1="abc", p2="xyz", comment="My comment")
a_obj.method2("a", "b", p1="abc", p2="xyz")
a_obj.method2("a", "b", p1="abc", p2="xyz", comment="My comment 2")
输出为:
Function execution - 'No comment.'
method1(0, 1, p2='xyz', p1='abc')
self = 0
Function executed
Function execution - 'My comment'
method1(0, 1, p2='xyz', p1='abc')
self = 0
Function executed
Function execution - 'Something'
method2('a', 'b', p2='xyz', p1='abc')
self = a
Function executed
Function execution - 'Something'
method2('a', 'b', comment='My comment 2', p2='xyz', p1='abc')
self = a
Function executed
我的装饰器未将参数"self"传递给该方法.
我想编写第二个修饰符'method_log',其作用与'function_log'非常相似.对于代码:
Parameter 'self' is not passed by my decorator to the method.
I want to write second decorator 'method_log' that would work pretty similar as 'function_log'.For code:
class A:
def __init__(self):
pass
@method_log
def method1(self, *args, **kwargs):
print("\tself = {}".format(self))
@fmethod_log(default_comment="Something", comment_variable="comment2")
def method2(self, *args, **kwargs):
print("\tself = {}".format(self))
a_obj = A()
a_obj.method1(0, 1, p1="abc", p2="xyz")
a_obj.method1(0, 1, p1="abc", p2="xyz", comment="My comment")
a_obj.method2("a", "b", p1="abc", p2="xyz")
a_obj.method2("a", "b", p1="abc", p2="xyz", comment="My comment 2")
我想要输出:
Method execution - 'No comment.'
method1(<__main__.A instance at ...>, 0, 1, p2='xyz', p1='abc')
self = <__main__.A instance at ...> #
Function executed
Method execution - 'My comment'
method1(<__main__.A instance at ...>, 0, 1, p2='xyz', p1='abc')
self = <__main__.A instance at ...>
Function executed
Method execution - 'Something'
method2(<__main__.A instance at ...>, 'a', 'b', p2='xyz', p1='abc')
self = <__main__.A instance at ...>
Function executed
Method execution - 'Something'
method2(<__main__.A instance at ...>, 'a', 'b', comment='My comment 2', p2='xyz', p1='abc')
self = <__main__.A instance at ...>
Function executed
推荐答案
由于类在Python中的工作方式,它不适用于您当前的设计.
It's not working with you current design because of how classes work in Python.
实例化一个类时,其上的函数将绑定到实例-它们成为绑定方法,因此self
被自动传递.
When a class is instantiated, the functions on it get bound to the instance -they become bound methods, so that self
is automatically passed.
您可以看到它发生了:
class A:
def method1(self):
pass
>>> A.method1
<function A.method1 at 0x7f303298ef28>
>>> a_instance = A()
>>> a_instance.method1
<bound method A.method1 of <__main__.A object at 0x7f303a36c518>>
实例化A时,method1
从a神奇地转化function
转换为bound method
.
When A is instantiated, method1
is magically transformed from afunction
into a bound method
.
您的装饰器替换了method1
-而不是实函数,现在它是_PrintingArguments
的实例.魔术将函数转换为绑定方法的方法不适用于随机方法对象,即使它们定义了__call__
以便它们的行为也像函数一样. (但是,如果您的类实现了Descriptor协议,则可以应用魔术 ,请参见ShadowRanger的答案!).
Your decorator replaces method1
- instead of a real function,it is now an instance of _PrintingArguments
. The magicthat turns functions into bound methods is not applied to randomobjects, even if they define __call__
so that they behave like a function. (But that magic can be applied, if your class implements the Descriptor protocol, see ShadowRanger's answer!).
class Decorator:
def __init__(self, func):
self.func = func
def __call__(self, *args, **kwargs):
return self.func(*args, **kwargs)
class A:
@Decorator
def method1(self):
pass
>>> A.method1
<__main__.Decorator object at 0x7f303a36cbe0>
>>> a_instance = A()
>>> a_instance.method1
<__main__.Decorator object at 0x7f303a36cbe0>
没有魔术. A实例上的method1
不是绑定方法,它只是带有__call__
方法的随机对象,不会self
自动通过.
There is no magic. method1
on the instance of A is not a bound method,it's just a random object with a __call__
method, which will not haveself
passed automatically.
如果要修饰方法,则必须替换修饰的函数带有另一个实函数,使用__call__
的任意对象都不会.
If you want to decorate methods you have to replace the decorated functionwith another real function, an arbitrary object with __call__
will not do.
您可以修改当前代码以返回实函数:
You could adapt your current code to return a real function:
import functools
class _PrintingArguments:
def __init__(self, default_comment, comment_variable):
self.comment_variable = comment_variable
self.default_comment = default_comment
def __call__(self, function):
@functools.wraps(function)
def decorated(*args, **kwargs):
comment = kwargs.pop(self.comment_variable, self.default_comment)
params_str = [repr(arg) for arg in args] + ["{}={}".format(k, repr(v)) for k, v in kwargs.items()]
function_call_log = "{}({})".format(function.__name__, ", ".join(params_str))
print("Function execution - '{}'\n\t{}".format(comment, function_call_log))
function_return = function(*args, **kwargs)
print("\tFunction executed\n")
return function_return
return decorated
def function_log(_function=None, default_comment="No comment.", comment_variable="comment"):
decorator = _PrintingArguments(
default_comment=default_comment,
comment_variable=comment_variable,
)
if _function is None:
return decorator
else:
return decorator(_function)
这篇关于在Python中进行方法装饰时传递'self'参数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!