Here is the working code:
def g(y=10):
return y**2
def f(x,y=10):
return x*g(y)
print(f(5)) #->500
However, let's suppose we don't want to remember and copy a default value of keyword parameter y
to the definition of external function (especially if there are several layers of external functions). In the above example it means that we want to use parameter, already defined in g
.
One way to do that:
def f(x,y=None):
if y==None: return x*g()
else: return x*g(y)
But is there a cleaner way to do the same? Something like:
def f(x,y=empty()):
return x*g(y)
答案 0 :(得分:4)
This is possible:
def g(y=10):
return y**2
def f(x, y=g.__defaults__[0]):
return x * g(y)
But it is arguably less clear than what you had originally (defaulting y
to None
).
An option which doesn't restrict the definition order of f
and g
, and should remain working if the function default of g
gets changed dynamically:
def f(x, y=None):
kwargs = {}
if y is None:
kwargs['y'] = y
return x * g(**kwargs)
答案 1 :(得分:4)
Interesting question! Here's another possibility, however this requires handing in the second parameter as a named parameter.
>>> def g(y=10):
... return y**2
...
>>> def f(x, **kwargs):
... return x * g(**kwargs)
...
>>> f(5)
500
>>> f(5, y=0)
0
答案 2 :(得分:4)
def f(x, y=None)
或def f(x, **kwargs)
等签名的限制是读者必须深入了解源代码或文档,以了解y
发生了什么。坚持简单明了的事情:
DEFAULT_Y = 10
def g(y=DEFAULT_Y): ...
def f(x, y=DEFAULT_Y): ...
答案 3 :(得分:1)
我首先要说的是,如果参数只是关键字,那么这将是所以简单:
def f(*, x="x", y= "y",z="z"):
print(x,y,z)
def g(*, x,y,z):
print(x,y,z,"from g!!")
if g.__kwdefaults__ is None: #completely override defaults
g.__kwdefaults__ = f.__kwdefaults__
else: #if there are already some defaults then update
g.__kwdefaults__.update(f.__kedefaults__)
g()
如果您使用位置参数,它并不是那么容易,尽管您的示例是以相同方式工作的特定情况之一:
def g(y=10): #last argument is y
return y**2
def f(x,y): #last argument is y
return x*g(y)
f.__defaults__ = g.__defaults__ #copies the end of the defaults to f
print(f(5)) #->500
但这是一个非常具体的案例:
通用解决方案需要相当多的代码,但允许任何签名合并到另一个,例如:
def f(x,y,z=0,reverse=True):
pass
@copy_defaults(f)
def g(a,b, #arguments for g
x,y,z, #arguments to forward to f
c=None, d="test", #some optional arguments for g
*,reverse): #only take reverse as a keyword
pass
>>> inspect.signature(g)
<Signature (a, b, x, y, z=0, c=None, d='test', *, reverse=True)>
这可以通过以下代码实现(我无法找到一种更简单的方法来处理上述情况)
import inspect
def copy_defaults(original_f):
"creates wrapper for DefaultArgs(original_f).copy_defaults(dest_f)"
def wrapper(dest_f):
return DefaultArgs(original_f).copy_defaults(dest_f)
return wrapper
class DefaultArgs(dict):
def __init__(self,func):
spec = inspect.getfullargspec(func)
if spec.defaults:
dict.__init__(self,
zip(reversed(spec.args),
reversed(spec.defaults)
))
else:
dict.__init__(self) #I'm not sure this is necessary
if spec.kwonlydefaults:
self.update(spec.kwonlydefaults)
def get_kwdefaults(self,keywords):
return {k:v for k,v in self.items() if k in keywords}
def gen_pos_defaults(self,args,defaults=None):
if defaults is None:
defaults = ()
found_default = False
for i,arg in enumerate(args,start=len(defaults)-len(args)):
if arg in self:
yield self[arg]
found_default = True
elif i>=0:
yield defaults[i]
elif found_default: #if an argument does not have a default but is after one that does
raise TypeError("non-default argument %r follows default argument"%arg)
def copy_defaults(self,func):
spec = inspect.getfullargspec(func)
new_kwargs = self.get_kwdefaults(spec.kwonlyargs)
if func.__kwdefaults__ is not None:
func.__kwdefaults__.update(new_kwargs)
else:
func.__kwdefaults__ = new_kwargs
func.__defaults__ = tuple(self.gen_pos_defaults(spec.args,spec.defaults))
return func
答案 4 :(得分:0)
如果你可以修改g
,那么这可行:
def g(y=None):
if y is None:
y = 10
return y**2
def f(x,y=None):
return x*g(y)