2016-04-15 5 views
4

Вот рабочий код:Как передать пустой параметр функции python?

def g(y=10): 
    return y**2 

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

print(f(5)) #->500 

Однако, давайте предположим, что мы не хотим помнить и скопировать значение по умолчанию параметра ключевого слова y для определения внешней функции (особенно если есть несколько слоев внешних функции). В приведенном выше примере это означает, что мы хотим использовать параметр, уже определенный в g.

Один из способов сделать это:

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

Но есть более чистый способ сделать то же самое? Что-то вроде:

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

ответ

3

Интересный вопрос! Вот еще одна возможность, однако для этого требуется передать второй параметр в качестве именованного параметра.

>>> def g(y=10): 
...  return y**2 
... 
>>> def f(x, **kwargs): 
...  return x * g(**kwargs) 
... 
>>> f(5) 
500 
>>> f(5, y=0) 
0 
4

Это возможно:

def g(y=10): 
    return y**2 

def f(x, y=g.__defaults__[0]): 
    return x * g(y) 

Но это вероятно, менее ясно, чем то, что вы были первоначально (недобросовестный y к None).

Опцион, который не ограничивает порядок определения о f и g, и должны продолжать работать, если функция по умолчанию g получает динамически изменяться:

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

Для питона 3.x: '__defaults__' используется вместо' func_defaults' – Nik

+0

Что делать, если вы _wanted_ передать 'у = None' к функции? –

+0

@ TadhgMcDonald-Jensen В этом случае это не имеет смысла, потому что 'None ** 2' будет повышаться. Но в общем случае, если вы хотите передать 'None' в функцию, то вы можете сделать инициализацию sentinel' default_y = object() 'во внешней области, а затем проверить на это в условном выражении. Гарантируется, что все, что передается, не будет случайно идентичным 'default_y'. – wim

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): ... 
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. Там не должно быть никаких других аргументов со значениями по умолчанию (или они получают переопределяется)

Родовое решение требует совсем немного кода но позволяет любой подписи, которые будут объединены в другую, например:

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 
0

Если вы можете изменить g, то это работает:

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

def f(x,y=None): 
    return x*g(y) 
Смежные вопросы