How to pass an empty parameter to a python function?

时间:2016-04-15 14:59:42

标签: python python-3.x

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)

5 个答案:

答案 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

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

  1. 继承默认值的参数必须与原始参数的顺序相同。
  2. 继承默认值
  3. 后,不得有任何位置参数
  4. 不得有任何其他参数的默认值(或它们被覆盖)
  5. 通用解决方案需要相当多的代码,但允许任何签名合并到另一个,例如:

    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)