Как реализовать метод python с сигнатурой типа ([start,] stop [, step]), то есть аргумент ключевого слова по умолчанию слева - PullRequest
4 голосов
/ 26 декабря 2011

Поскольку в python 3.X функция build-id range() возвращает больше не список, а итеративный код, некоторый старый код дает сбой, так как я использую range() для удобного создания нужных мне списков.

Поэтому я пытаюсь реализовать свою собственную функцию lrange следующим образом:

def lrange(start = 0, stop, step = 1):
    ret = []
    while start < stop:
        ret.append(start)
        start += step
    return ret

ошибка интерпретатора "аргумент не по умолчанию следует за аргументом по умолчанию".

Если я посмотрю на range Python () , то это возможно.

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

Ответы [ 5 ]

9 голосов
/ 06 января 2014

Быстрый ответ

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

def list_range(start, stop=None, step=1):
    if stop is None:
         start, stop = 0, start
    return list(range(start, stop, step))

Я думаю, что это решение немного более элегантно, чем использование всех аргументов ключевых слов или *args.

Объяснение

Используйте Стража

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

None, будучи нулевым значением Python, является хорошим передовым примером, и идиоматический способ проверить это с ключевым словом is, так как это одиночный код.

Пример с правильной строкой документации с объявлением подписи / API

def list_range(start, stop=None, step=1):
    '''
    list_range(stop) 
    list_range(start, stop, step)

    return list of integers from start (default 0) to stop,
    incrementing by step (default 1).

    '''
    if stop is None:
         start, stop = 0, start
    return list(range(start, stop, step))

Демонстрация

>>> list_range(10)
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> list_range(5, 10)
[5, 6, 7, 8, 9]
>>> list_range(2, 10, 2)
[2, 4, 6, 8]

И возникает ошибка, если не приводятся аргументы, в отличие от решения с ключевыми словами здесь.

Протест

Кстати, я надеюсь, что читатель рассматривает это только с теоретической точки зрения, я не верю, что эта функция заслуживает обслуживания, если только она не используется в центральном каноническом месте для обеспечения кросс-совместимости кода между Python 2 и 3. В Python довольно просто материализовать диапазон в список с помощью встроенных функций:

Python 3.3.1 (default, Sep 25 2013, 19:29:01) 
[GCC 4.7.3] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> list(range(10))
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
5 голосов
/ 26 декабря 2011

На самом деле я никогда не задумывался об этом, но, прежде всего, чтобы решить вашу проблему, вы просто должны иметь возможность заключить диапазон (...) в список (range (...)) в своем коде.

Используя аргументы ключевых слов, вы можете реализовать такую ​​сигнатуру, поскольку вам не требуется указывать фактический ключ при вызове

def f(x=None, y=None, z=None):
    print x, y, z

f(1, 2, 3)
#output: 1 2 3

Затем вы можете проверить значения, чтобы определить, как выдолжен справиться с ними.Таким образом, чтобы эмулировать диапазон

def f(x=None, y=None, z=None):
    if z is not None: # then all values were assigned
        return range(x, y, z)
    elif y is not None: # then a start stop was set
        return range(x, y):
    else: # only one value was given
        return range(x)

Дело здесь не в том, чтобы быть оберткой для диапазона (как выше, просто используйте список), а в том, чтобы дать некоторое представление о том, действительно ли кто-то пытался эмулировать встроенный диапазонподпись для чего-то нестандартного.

Также имейте в виду, что это не полное решение, f(z=1) может вызвать проблемы с вышеперечисленным, поэтому вы хотите предоставить разумные значения по умолчанию для каждого [kwarg], проверяя при этом необходимые kwarg

def f(x=0, y=None, z=1):
    if y is None:
        raise Exception()
    return range(x, y, z)

было бы немного более проницательным для метода python с такой сигнатурой, как ([start], stop, [step])

2 голосов
/ 26 декабря 2011

А как же:

>>> def my_range(*args):
...     return list(range(*args))
... 
>>> my_range(0, 10, 2)
[0, 2, 4, 6, 8]
1 голос
/ 26 декабря 2011

Вы можете использовать функции *args и **kwargs:

def myrange(*args, **kwargs):
  start = 0
  stop = None
  if (len(args) == 1):
    stop = args[0]
  if (len(args) >= 2 ):
    start = args[0]
    stop = args[1]
  start = kwargs.get("start", start)
  stop = kwargs.get("stop", stop)
  return list(range(start, stop))

Вам необходимо добавить еще одну проверку ошибок и поддержать оператор шага.

Inв этом случае вам, вероятно, лучше реализовать его следующим образом:

def myrange(*args):
  return list(range(*args))

Обратите внимание, что функция builitn range не поддерживает kwargs.

1 голос
/ 26 декабря 2011

Вы можете попробовать что-то вроде этого:

def lrange(*args):
    # Default values
    start = 0
    step = 1

    # Assign variables based on args length
    if len(args) == 1:
        stop, = args
    elif len(args) == 2:
        start, stop = args
    elif len(args) == 3:
        start, stop, step = args
    else:
        raise TypeError('lrange expected at most 3 arguments, got {0}'
                        .format(len(args)))
     ...

Если вы попытаетесь использовать range в интерпретаторе, вы увидите, что он не принимает аргументы ключевого слова, поэтому он, безусловно, подыгрывает переменному числу аргументов, как в примере выше.

...