如何将空参数传递给python函数?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/36650583/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-19 18:10:23  来源:igfitidea点击:

How to pass an empty parameter to a python function?

pythonpython-3.x

提问by Nik

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 yto 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.

但是,假设我们不想记住并将关键字参数的默认值复制y到外部函数的定义中(特别是如果有多层外部函数时)。在上面的例子中,这意味着我们要使用已经在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)

回答by dorian

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

回答by wim

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 clearthan what you had originally (defaulting yto None).

但可以说它不如你最初的清晰(默认yNone)。

An option which doesn't restrict the definition order of fand g, and should remain working if the function default of ggets changed dynamically:

不限制fand的定义顺序的选项,g如果g动态更改函数默认值,则应保持工作状态:

def f(x, y=None):
    kwargs = {}
    if y is None:
        kwargs['y'] = y
    return x * g(**kwargs)

回答by Alex Hall

A limitation of signatures such as def f(x, y=None)or def f(x, **kwargs)is that readers have to dig into source code or documentation to find out what's going on with y. Stick to something simple and straightforward:

诸如def f(x, y=None)或之类的签名的局限性def f(x, **kwargs)在于,读者必须深入研究源代码或文档才能了解y. 坚持简单明了的事情:

DEFAULT_Y = 10
def g(y=DEFAULT_Y): ...
def f(x, y=DEFAULT_Y): ...

回答by Tadhg McDonald-Jensen

I'd like to start by saying if the arguments were keyword only this would be soeasy:

我想首先说如果参数是关键字,这将非常简单:

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()

if you are using positional arguments it isn't quite as easy although your example is one of the specific cases that works the same way:

如果您使用位置参数,虽然您的示例是以相同方式工作的特定情况之一,但它并不那么容易:

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

But this is a very specific case:

但这是一个非常具体的案例:

  1. The arguments to inherit the defaults must be in the same order as the original.
  2. There must not be any positional arguments after the ones with inherited defaults
  3. There must not be any other arguments with default values (or they get overridden)
  1. 继承默认值的参数必须与原始参数的顺序相同。
  2. 在具有继承默认值的参数之后不能有任何位置参数
  3. 不能有任何其他带有默认值的参数(否则它们会被覆盖)

The generic solution requires quite a bit of code but allows any signature to be merged into another, for example:

通用解决方案需要相当多的代码,但允许将任何签名合并到另一个签名中,例如:

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)>

This can be achieved with the following code (I can't find a simpler way to do it that works with above case)

这可以通过以下代码来实现(我找不到一种更简单的方法来处理上述情况)

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

回答by BallpointBen

If you can modify g, then this works:

如果你可以修改g,那么这有效:

def g(y=None):
    if y is None:
        y = 10
    return y**2

def f(x,y=None):
    return x*g(y)