я хотел бы начать с того, если аргументы были ключевым слово только это было бы так легко:
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
Для питона 3.x: '__defaults__' используется вместо' func_defaults' – Nik
Что делать, если вы _wanted_ передать 'у = None' к функции? –
@ TadhgMcDonald-Jensen В этом случае это не имеет смысла, потому что 'None ** 2' будет повышаться. Но в общем случае, если вы хотите передать 'None' в функцию, то вы можете сделать инициализацию sentinel' default_y = object() 'во внешней области, а затем проверить на это в условном выражении. Гарантируется, что все, что передается, не будет случайно идентичным 'default_y'. – wim