Make a membermethod a default parameter
我想做的是:
1 2 3 4 5 6 | class demo(object): def a(self): pass def b(self, param=self.a): #I tried demo.a as well after making a static param() |
很明显,问题是不能访问函数声明行中的类。有没有办法像C(++)那样添加原型?
现在我用一个难看的工具:
1 2 3 4 5 6 7 8 | def b(self, param=True): #my real function shall be able to use None, to skip the function call if param == True: param = self.a if param != None: #This explainds why I can't take None as default, #for param, I jsut needed something as default which was #neither none or a callable function (don't want to force the user to create dummy lambdas) param() |
号
那么,如果没有这个难看的工作安排,是否有可能达到上面描述的效果呢?注:我绑定到Jython,它大约是python 2.5(我知道有2.7,但我不能升级)
别告诉任何人我给你看了这个。
1 2 3 4 5 6 | class demo: def a(self): print(self,"called 'a'") def b(self, param): param(self) demo.b.__defaults__ = (demo.a,) demo().b() |
。
(在2.x中,
简短回答:不。
我认为最好的方法是创建一个自定义的占位符对象,例如,如果你想传递像
1 2 3 4 5 6 7 8 9 10 11 | default_value = object() class demo(object): def a(self): pass def b(self, param=default_value): if param is default_value: self.a() else: param() |
您可以使用funciton
1 | def b(self, param=a): |
号
只要在
我将再次回答这个问题,与我先前的回答相矛盾:
简短回答:是的!(有点)
在方法修饰器的帮助下,这是可能的。代码很长,有点难看,但是用法又短又简单。
问题是我们只能使用未绑定的方法作为默认参数。那么,如果我们创建一个包装函数——一个装饰器——它在调用真正的函数之前绑定参数呢?
首先,我们创建一个可以执行此任务的助手类。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | 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 |
因此,
现在我们可以使用这个类创建一个装饰器:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | 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 |
。
既然我们已经把丑陋抛在脑后,让我们来展示一下简单而漂亮的用法:
1 2 3 4 5 6 7 8 9 10 11 12 13 | 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,来自
我也更喜欢Lazyr的答案(我通常使用
1 2 3 | def b(self, **kwargs): param = kwargs.get('param', self.a) if param: param() |
您仍然可以使用
1 2 3 4 5 | demo.b() #demo.a() executed demo.b(param=some_func) #some_func() executed demo.b(param=None) #nothing executed. |
。
我喜欢Lazyr的回答,但也许你会更喜欢这个解决方案:
1 2 3 4 5 6 7 8 9 10 11 12 13 | class Demo(object): def a(self): pass def b(self, *args): if not args: param=self.a elif len(args)>1: raise TypeError("b() takes at most 1 positional argument") else: param=args[0] if param is not None: param() |
。
您可以将方法名放入函数定义中:
1 2 3 4 5 6 7 8 | class Demo(object): def a(self): print 'a' def b(self, param='a'): if param: getattr(self, param)() |
但是,您仍然需要检查