通过';自我';Python中方法修饰期间的参数
我想创建一个装饰器,显示哪些参数被传递给函数和方法。我已经为函数编写了代码,但是方法让我头疼 这是按预期工作的函数装饰器:通过';自我';Python中方法修饰期间的参数,python,python-2.7,python-decorators,method-declaration,class-decorator,Python,Python 2.7,Python Decorators,Method Declaration,Class Decorator,我想创建一个装饰器,显示哪些参数被传递给函数和方法。我已经为函数编写了代码,但是方法让我头疼 这是按预期工作的函数装饰器: from functools import update_wrapper class _PrintingArguments: def __init__(self, function, default_comment, comment_variable): self.function = function self.comment_
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
我尝试了完全相同的装饰方法:
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”传递给该方法。
我想编写第二个decorator“method\u log”,它的工作原理与“function\u log”非常相似。 代码:
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
方法执行-“无注释”
方法1(,0,1,p2='xyz',p1='abc')
自我=#
执行的功能
方法执行-“我的评论”
方法1(,0,1,p2='xyz',p1='abc')
自我=
执行的功能
方法执行-“某物”
方法2(,'a','b',p2='xyz',p1='abc')
自我=
执行的功能
方法执行-“某物”
方法2(,'a','b',comment='My comment 2',p2='xyz',p1='abc')
自我=
执行的功能
由于类在Python中的工作方式,当前的设计无法使用它
当一个类被实例化时,它上面的函数被绑定到实例-
它们成为绑定方法,以便自动传递self
你可以看到它发生了:
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>>
没有魔法<实例上的代码>方法1不是绑定方法,
它只是一个随机对象,具有\uuuu调用\uuuu
方法,该方法不会
self
自动传递
如果要修饰方法,必须替换修饰函数
对于另一个实函数,具有\uuuu调用\uuu
的任意对象将不起作用
您可以调整当前代码以返回实际函数:
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可以用于从任何可调用对象创建绑定方法,给定一个要绑定到的实例,因此我们使用它来实现描述符的
\uuuu get\uuu
:
import types
class _PrintingArguments:
# __init__ and __call__ unchanged
def __get__(self, instance, owner):
if instance is None:
return self # Accessed from class, return unchanged
return types.MethodType(self, instance) # Accessed from instance, bind to instance
这在Python3()上可以正常工作。在Python2上更简单(因为存在未绑定的方法,所以可以无条件地调用types.MethodType
):
为了获得更好的性能(仅在Python 2上),您可以改为:
class _PrintingArguments(object): # Explicit inheritance from object needed for new-style class on Py2
# __init__ and __call__ unchanged
# Defined outside class, immediately after dedent
_PrintingArguments.__get__ = types.MethodType(types.MethodType, None, _PrintingArguments)
它将
\uuuu get\uuuu
的实现移动到C层,方法是从类型中生成一个未绑定的方法。MethodType
本身,从每次调用中移除字节码解释器开销。@That1Guy尽管标题类似,但问题实际上是完全不同的。@JBChouinard,这就是我没有彻底阅读it的原因,lolFYI,这种魔力不会自动应用于可调用类,但您可以通过实现描述符协议手动打开它(def
-ed函数自动实现描述符协议,但任何类都可以这样做)。遗憾的是,在Py2和Py3之间执行此操作的代码略有不同,但是,没有对现有代码进行任何更改(除了显式继承Py2上的object
),只有新代码。@ShadowRanger我不知道这一点,非常酷。@ShadowRanger描述符是解决我使用元类所做某些事情的更好方法。是的,我通常发现,除了极为复杂的情况(例如,enum.EnumMeta
和abc.ABCMeta
)的少数例外,只要半频繁地使用元类的任何给定特性,该语言就会提供一个非基于元类的解决方案。按声明顺序存储类属性过去需要一个元类,现在它们是免费排序的;类装饰器替换它们进行后期类修改\uuuu class\u getitem\uuuu
将其替换为类型泛型;描述符协议的不同寻常之处在于它与元类一起发布;这不是后来添加的。我怀疑这种倾向的很大一部分是为元类已经可以做的事情提供一个更有针对性的解决方案,因为:1)基于元类的解决方案需要为每个实现类提供一个(相对简单,但仍然相当详细)自定义元类,或者是一个非常复杂的元类如果你想为许多行为迥异的实现类重用元类,2)元类的速度要慢得多,而且最重要的是,3)更多的元类使用会显著增加由于多重继承而发生元类冲突的风险。旁注:为了避免需要为每个类显式地从对象继承以使其成为新样式,您可以将\uuuuuuuuuuuuuuuuuu=type
添加到您想要成为统一新样式的每个源文件的顶部;该源文件中定义的每个类都将自动成为新样式,而无需从对象
显式继承。
import types
class _PrintingArguments(object): # Explicit inheritance from object needed for new-style class on Py2
# __init__ and __call__ unchanged
def __get__(self, instance, owner):
return types.MethodType(self, instance, owner) # Also pass owner
class _PrintingArguments(object): # Explicit inheritance from object needed for new-style class on Py2
# __init__ and __call__ unchanged
# Defined outside class, immediately after dedent
_PrintingArguments.__get__ = types.MethodType(types.MethodType, None, _PrintingArguments)