Вызов функции без дополнительных аргументов, если они отсутствуют.

Там функция, которая принимает необязательные аргументы.

def alpha(p1="foo", p2="bar"):
     print('{0},{1}'.format(p1, p2))

Позвольте мне повторить, что происходит, когда мы используем эту функцию по-разному:

>>> alpha()
foo,bar
>>> alpha("FOO")
FOO,bar
>>> alpha(p2="BAR")
foo,BAR
>>> alpha(p1="FOO", p2=None)
FOO,None

Теперь рассмотрим случай, когда я хочу называть его alpha("FOO", myp2) а myp2 будет либо содержать значение, которое нужно передать, либо быть None. Но даже если функция обрабатывает p2=None, я хочу использовать ее значение по умолчанию "bar".
Может быть, это сформулировано смутно, поэтому позвольте мне сказать следующее:

Если myp2 is None, вызовите alpha("FOO"). Иначе, вызовите alpha("FOO", myp2).

Различие имеет значение, потому что alpha("FOO", None) имеет другой результат, чем alpha("FOO").

Как я могу (но читающе) сделать это различие?

Одна из возможностей, как правило, заключалась бы в том, чтобы проверить отсутствие в alpha, что будет поощряться, потому что это сделает код более безопасным. Но предположим, что alpha используется в других местах, где он фактически должен обрабатывать None как он.

Я бы хотел обработать это на стороне вызывающего абонента.

Одна из возможностей - сделать различие в делах:

if myp2 is None:
    alpha("FOO")
else:
    alpha("FOO", myp2)

Но это может быстро стать большим количеством кода, когда есть несколько таких аргументов. (экспоненциально, 2 ^ n)

Другая возможность - просто выполнить alpha("FOO", myp2 or "bar"), но это требует, чтобы мы знали значение по умолчанию. Обычно, я бы, вероятно, пошел с таким подходом, но позже я мог бы изменить значения по умолчанию для alpha и этот вызов затем должен быть обновлен вручную, чтобы по-прежнему вызывать его с (новым) значением по умолчанию.

Я использую python 3.4, но было бы лучше, если бы ваши ответы могли обеспечить хороший способ работы в любой версии python.


Вопрос технически закончен здесь, но я снова пересказываю какое-то требование, так как первый ответ затушевал:
Я хочу, чтобы поведение alpha со значениями по умолчанию "foo", "bar" сохранялось в целом, поэтому (возможно) это не вариант изменения самой alpha.
В еще нескольких других словах предположим, что alpha используется где-то еще как alpha("FOO", None) где ожидается выход FOO,None.

Ответ 1

Передайте аргументы как kwargs из словаря, из которого вы отфильтровываете значения None:

kwargs = dict(p1='FOO', p2=None)

alpha(**{k: v for k, v in kwargs.items() if v is not None})

Ответ 2

хотя ** определенно является языковой функцией, она, безусловно, не создана для решения этой конкретной проблемы. Ваше предложение работает, так же как и мое. Какой из них лучше работает, зависит от остальной части кода OP. Однако по-прежнему нет способа написать f(x or dont_pass_it_at_all) - blue_note

Благодаря вашим замечательным ответам, я думал, что попытаюсь сделать именно это:

# gen.py
def callWithNonNoneArgs(f, *args, **kwargs):
    kwargsNotNone = {k: v for k, v in kwargs.items() if v is not None}
    return f(*args, **kwargsNotNone)

# python interpreter
>>> import gen
>>> def alpha(p1="foo", p2="bar"):
...     print('{0},{1}'.format(p1,p2))
...
>>> gen.callWithNonNoneArgs(alpha, p1="FOO", p2=None)
FOO,bar
>>> def beta(ree, p1="foo", p2="bar"):
...     print('{0},{1},{2}'.format(ree,p1,p2))
...
>>> beta('hello', p2="world")
hello,foo,world
>>> beta('hello', p2=None)
hello,foo,None
>>> gen.callWithNonNoneArgs(beta, 'hello', p2=None)
hello,foo,bar

Это, вероятно, не идеально, но, похоже, это работает: это функция, которую вы можете вызывать с помощью другой функции и ее аргументов, и она применяет отрицательный ответ, чтобы отфильтровать аргументы, которые являются None.

Ответ 3

Но предположим, что альфа используется в других местах, где он фактически должен обрабатывать None, как он.

Чтобы ответить на эту проблему, у меня, как известно, есть значение None -like, которое на самом деле не является None для этой конкретной цели.

_novalue = object()

def alpha(p1=_novalue, p2=_novalue):
    if p1 is _novalue:
        p1 = "foo"
    if p2 is _novalue:
        p2 = "bar"
    print('{0},{1}'.format(p1, p2))

Теперь аргументы по-прежнему являются необязательными, поэтому вы можете пренебречь тем, чтобы пройти любой из них. И функция обрабатывает None правильно. Если вы когда-либо хотите явно не передавать аргумент, вы можете передать _novalue.

>>> alpha(p1="FOO", p2=None)
FOO,None
>>> alpha(p1="FOO")
FOO,bar
>>> alpha(p1="FOO", p2=_novalue)
FOO,bar

и поскольку _novalue представляет собой специальную добавленную стоимость, созданную для этой прямой цели, любой, кто передает _novalue, безусловно, намеревается поведение "аргумент по умолчанию", в отличие от того, кто передает None который мог бы предположить, что значение должно интерпретироваться как литерал None.

Ответ 4

К сожалению, нет способа сделать то, что вы хотите. Даже широко распространенные библиотеки/рамки python используют ваш первый подход. Это дополнительная строка кода, но она вполне читаема.

Не используйте alpha("FOO", myp2 or "bar"), потому что, как вы сами alpha("FOO", myp2 or "bar"), он создает ужасную связь, поскольку для alpha("FOO", myp2 or "bar") функции требуется, чтобы вызывающий пользователь знал подробности о функции.

Что касается работы: вы можете сделать декоратор для вашей функции (используя модуль inspect), который проверяет переданные ему аргументы. Если один из них - None, он заменяет значение своим значением по умолчанию.

Ответ 5

Вы можете проверить значения по умолчанию через alpha.__defaults__ а затем использовать их вместо None. Таким образом вы обходите жесткое кодирование значений по умолчанию:

>>> args = [None]
>>> alpha('FOO', *[x if x is not None else y for x, y in zip(args, alpha.__defaults__[1:])])

Ответ 6

Я удивлен, что никто не поднял это

def f(p1="foo", p2=None):
    p2 = "bar" if p2 is None else p2
    print(p1+p2)

Вы назначаете None для p2 в качестве стандартного (или нет, но таким образом, у вас есть настоящий стандарт в одной точке вашего кода) и используйте встроенную if. Имо самый пифонический ответ. Еще одна вещь, которая приходит на ум, - это использовать обертку, но это было бы менее понятным.

EDIT: то, что я, вероятно, сделаю, это использовать манекен в качестве стандартного значения и проверить это. Так что-то вроде этого:

class dummy():
    pass

def alpha(p1="foo", p2=dummy()):
    if isinstance(p2, dummy):
        p2 = "bar"
    print("{0},{1}".format(p1, p2))

alpha()
alpha("a","b")
alpha(p2=None)

производит:

foo,bar
a,b
foo,None