Порядок аргументов default и non-default

в Python я понимаю, что аргументы по умолчанию приходят в конце и что аргументы не по умолчанию не могут следовать аргументу по умолчанию. Это прекрасно. Например:

>>> def foo(x=0, y):
        return x, y
SyntaxError: non-default argument follows default argument

это нормально, как и ожидалось.

однако, как насчет случая, когда я хочу, чтобы первый аргумент должен быть по умолчанию? Например, как видно из приведенного выше кода, x должен быть первым аргументом, и он должен иметь значение по умолчанию 0.

возможно ли это сделать? Я спрашиваю, потому что даже в , Я предполагаю, что это что-то вроде этого:

def range(start=0, end):
    pass

Итак, как это делается, и если это невозможно, как это реализуется range? Обратите внимание, что я настаиваю на том, чтобы первый аргумент был по умолчанию, то есть весь смысл. Я использую range в качестве примера, потому что она идеально подходит для моей проблемы. Конечно можно реализовать range as def range(end, start=0), но дело не в этом.

5 ответов


Ну range это код на C, который может сделать это немного лучше. В любом случае, вы можете сделать это:

def range(start, stop=None):
    if stop is None: # only one arg, treat stop as start ...
        stop = start
        start = 0
    ...

и документировать функцию соответственно.


Он не реализован range. Вы можете использовать *args или **args и лечить кортеж или дикт, как вы хотите. Например:

def f(*args):
  if len(args) == 1:
     print "assuming the first is default"
  elif len(args) == 2:
     print "two arguments were passed"
  else:
     print "Complaining"

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

def range1(value, end=None):
    if end == None:
        end = value
        value = 0
    return _generate_range_values(value, end)

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

def range2(*args):
    if len(args) == 1:
        start = 0
        end = int(args[0])
    elif len(args) == 2:
        start = int(args[0])
        end = int(args[1])
    return _generate_range_values(start, end)

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

def range3(end, start=0):
    return _generate_range_values(start, end)

тогда пользователи будут вызывать его с именованным аргументом start, когда им нужно что-то кроме 0. (Хотя именованный аргумент не требуется, он сохраняет код ясным.

for i in range3(55, start=12)

вы можете обрабатывать исключения самостоятельно, если вы действительно этого хотите

def Range(start=0, end=None):
    if end is None:
        raise AttributeError("end value not specified")
     pass

У меня нет кода для диапазона, но я уверен, что он выполняет такой трюк:

def range(start, stop=None, step=1):
    if stop is None:
        start, stop = 0, start
    ...

edit: код исправлен в комментарии Мартино.