Вызовите другую функцию и, возможно, сохраните аргументы по умолчанию
У меня есть функция с одним необязательным аргументом, например:
def funA(x, a, b=1):
return a+b*x
Я хочу написать новую функцию, которая вызывает funA
, а также имеет необязательный аргумент, но если аргумент не передан, я хочу сохранить значение по умолчанию в funA
.
Я думал примерно так:
def funB(x, a, b=None):
if b:
return funA(x, a, b)
else:
return funA(x, a)
Есть ли более питонический способ сделать это?
Ответы
Ответ 1
Я заменил бы if b
на if b is not None
, так что если вы передадите b=0
(или любое другое значение "ложь" ) в качестве аргумента в funB
, оно будет передано в funA
.
Кроме того, для меня это кажется довольно pythonic: ясным и явным. (хотя, может быть, немного бесполезно, в зависимости от того, что вы пытаетесь сделать!)
Немного более загадочный способ, который полагается на вызов funB
с правильными аргументами ключевого слова (например, funB(3, 2, b=4)
:
def funB(x, a, **kwargs):
return funA(x, a, **kwargs)
Ответ 2
def funA(x, a, b=1):
return a+b*x
def funB(x, a, b=1):
return funA(x, a, b)
Сделать значение по умолчанию b=1
в funB()
, а затем передать его всегда на funA()
Ответ 3
То, как вы это сделали, прекрасно. Другой способ: для funB
иметь те же значения по умолчанию, что и funA
, поэтому вы можете передать те же параметры прямо. Например, если вы выполняете def funB(x, a, b=1)
, вы всегда можете вызвать return funA(x, a, b)
именно так.
В простых случаях вышеуказанное будет работать нормально. Для более сложных случаев вы можете использовать *args
и **kwargs
(пояснил здесь и здесь). В частности, вы можете передать все свои аргументы ключевого слова в виде словаря (условно называемого kwargs
). В этом случае каждая функция будет устанавливать свои собственные независимые значения по умолчанию, и вы просто передадите весь словарь через:
def funA(x, a, **kwargs):
b = kwargs.get("b", 1)
return a+b*x
def funB(x, a, **kwargs):
return funA(x, a, **kwargs)
Если kwargs
пуст при передаче в funB
(b
не указан), он будет установлен по умолчанию в funA
по выражению b = kwargs.get("b", 1)
. Если указан b
, он будет передан через as-is. Обратите внимание, что в funB
вы можете получить доступ к b
со своим собственным независимым значением по умолчанию и по-прежнему получать поведение, которое вы ищете.
Хотя это может показаться излишним для вашего примера, извлечение пары аргументов в начале функции не имеет большого значения, если функция достаточно сложная. Это также дает вам гораздо большую гибкость (например, избегая многих обычных gotchas).
Ответ 4
Используя FunctionType
из types
, вы можете просто взять функцию и создать новую, определяющую значения по умолчанию во время выполнения. Вы можете поместить все это в декоратор, чтобы в точке, где вы пишете свой код, он будет держать вещи в порядке, в то же время давая читателю понять, что вы пытаетесь выполнить. Он также позволяет точно такую же сигнатуру вызова для funB
, как funA
- все аргументы могут быть позиционными, или все аргументы могут быть ключевыми словами или любым допустимым их сочетанием, а любые аргументы со значениями по умолчанию являются необязательными. Должен хорошо играть с позиционными аргументами (*args
) и аргументами ключевого слова (**kwargs
).
import inspect
from types import FunctionType
def copy_defaults(source_function):
def decorator(destination_function):
"""Creates a wrapper for the destination function with the exact same
signature as source_function (including defaults)."""
# check signature matches
src_sig = inspect.signature(source_function)
dst_sig = inspect.signature(destination_function)
if list(src_sig.parameters) != list(dst_sig.parameters):
raise ValueError("src func and dst func do not having matching " \
"parameter names / order")
return FunctionType(
destination_function.__code__,
destination_function.__globals__,
destination_function.__name__,
source_function.__defaults__, # use defaults from src
destination_function.__closure__
)
return decorator
def funA(x, a, b=1):
return a+b*x
@copy_defaults(funA)
def funB(x, a, b):
"""this is fun B"""
return funA(x, a, b)
assert funA(1, 2) == funB(1, 2)
assert funB.__name__ == "funB"
assert funB.__doc__ == "this is fun B"
Ответ 5
Используя inspect.getargspec
, вы можете получить значения по умолчанию (четвертый элемент возвращаемого набора = defaults
):
import inspect
def funA(x, a, b=1):
return a + b * x
# inspect.getargspec(funA) =>
# ArgSpec(args=['x', 'a', 'b'], varargs=None, keywords=None, defaults=(1,))
def funcB(x, a, b=inspect.getargspec(funA)[3][0]):
return funA(x, a, b)
ИЛИ (в Python 2.7 +)
def funcB(x, a, b=inspect.getargspec(funA).defaults[0]):
return funA(x, a, b)
В Python 3.5+ рекомендуется вместо inspect.signature
:
def funcB(x, a, b=inspect.signature(funA).parameters['b'].default):
return funA(x, a, b)
Ответ 6
Вы также можете использовать:
def funA(x, a, b=1):
return a+b*x
def funB(x, a, b=None):
return funA(*filter(lambda o: o is not None, [x, a, b]))
Версия, которая не будет терпеть неудачу, если x или - None:
def funB(x, a, b=None):
return funA(*([x, a]+filter(lambda o: o is not None, [b])))