Обязательный параметр, если присутствует другой аргумент - PullRequest
2 голосов
/ 03 марта 2020

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

def f(a, b, c):
    if a == 'method1':
        c = 0
    return b + c

В этой функции параметр c не требуется, если выполняется условие a='method1'. Тем не менее, я могу вызвать функцию с помощью f(a='method1', b=1, c=2), что будет иметь тот же эффект, что и f(a='method1', b=1, c=0).

. Решение этой проблемы - установить для параметра c default значение 0

def f(a, b, c=0):
    if a == 'method1':
        c = 0
    return b + c

Теперь я могу позвонить f(a='method1',b=1), это именно то, что я хочу. Проблема в том, что я все еще могу изменить параметр c в вызове f(a='method1',b=1,c=1), что я не хочу, чтобы пользователь мог.

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

Что-то вроде

def f(a, b, c = 0 if a == 'method1', else c is required):
    return b + c

Заранее спасибо.

Ответы [ 2 ]

1 голос
/ 04 марта 2020

a, b и c назначаются динамически во время выполнения. Вы не можете восполнить это в подписи. Его нужно обнаружить во время выполнения, и вы можете сделать это в if, как и везде. Вы можете специализироваться на уровне имени функции, и python позаботится об определении количества параметров.

def f(b,c):
    return b + c

def f_method1(b):
    return f(b, 0)

def f_method2(half_a_c):
    return f(0, half_a_c*2)
1 голос
/ 04 марта 2020

Хм ... это почти похоже на то, что вы должны делать с functools.singledispatch и typing.Literal, но я не могу заставить их работать вместе, по крайней мере, в python 3.7 (с * 1003). * исходя из typing_extensions модуля). Я думаю, что в целом singledispatch, вероятно, единственный инструмент, который действительно получит то, о чем вы просили, поскольку разные зарегистрированные функции могут иметь совершенно разные подписи. Однако для этого наши методы должны быть разных классов.

from functools import singledispatch

class Method1():
    pass

class OtherMethods():
    pass

@singledispatch
def f(method, b, c):
    return b + c

@f.register(Method1)
def _(method, b):
    return b

f(Method1(), 12)  # returns 12
f(Method1(), 12, 7) # raises an error
f(OtherMethods(), 12) # raises an error
f(OtherMethods(), 12, 7) # returns 19

Теперь это не совсем то, что вы просили, но аргументы применяются в подписи.

Если кто-то, кто знает больше о реализации singledispatch и Literal, возможно, сможет объяснить взаимодействие между ними.

Одна более простая вещь, которую можно сделать, это просто определить c по умолчанию для некоторого недопустимого значения.

def f(a, b, c=None):
    if a == 'method1':
        c = 0
    return b + c

Это решает проблему, заключающуюся в том, что если пользователь забудет установить c для метода, кроме method1, он получит (возможно, crypti * 1025). *) сообщение об ошибке. Однако это не исправляет тот факт, что если они установят c при использовании method1, это значение будет игнорироваться и, возможно, приведет к путанице.

...