我将再次回答这个问题,与我之前的回答相矛盾:
简短的回答:是的!(有点)
在方法装饰器的帮助下,这是可能的。代码很长,有点难看,但是用法很短很简单。
问题是我们只能使用未绑定的方法作为默认参数。那么,如果我们创建一个包装函数——一个装饰器——在调用真正的函数之前绑定参数呢?
首先,我们创建一个可以执行此任务的辅助类。
from inspect import getcallargs
from types import MethodType
from functools import wraps
class MethodBinder(object):
def __init__(self, function):
self.function = function
def set_defaults(self, args, kwargs):
kwargs = getcallargs(self.function, *args, **kwargs)
# This is the self of the method we wish to call
method_self = kwargs["self"]
# First we build a list of the functions that are bound to self
targets = set()
for attr_name in dir(method_self):
attr = getattr(method_self, attr_name)
# For older python versions, replace __func__ with im_func
if hasattr(attr, "__func__"):
targets.add(attr.__func__)
# Now we check whether any of the arguments are identical to the
# functions we found above. If so, we bind them to self.
ret = {}
for kw, val in kwargs.items():
if val in targets:
ret[kw] = MethodType(val, method_self)
else:
ret[kw] = val
return ret
因此,实例MethodBinder
与方法(或者更确切地说将成为方法的函数)相关联。MethodBinder
s 方法set_defaults
可以被赋予用于调用关联方法的参数,它将绑定关联方法的任何未绑定方法,self
并返回可用于调用关联方法的 kwargs dict。
现在我们可以使用这个类创建一个装饰器:
def bind_args(f):
# f will be b in the below example
binder = MethodBinder(f)
@wraps(f)
def wrapper(*args, **kwargs):
# The wrapper function will get called instead of b, so args and kwargs
# contains b's arguments. Let's bind any unbound function arguments:
kwargs = binder.set_defaults(args, kwargs)
# All arguments have been turned into keyword arguments. Now we
# may call the real method with the modified arguments and return
# the result.
return f(**kwargs)
return wrapper
现在我们已经把丑陋抛在脑后,让我们展示一下简单而漂亮的用法:
class demo(object):
def a(self):
print("{0}.a called!".format(self))
@bind_args
def b(self, param=a):
param()
def other():
print("other called")
demo().b()
demo().b(other)
这个秘籍使用了一个相当新的 Python 补充,getcallargs
来自inspect
. 它仅在较新版本的 python2.7 和 3.1 中可用。