Лучшая практика для установки значения по умолчанию параметра, который должен быть списком в Python?


У меня есть функция Python, которая принимает список в качестве параметра. Если я установлю значение параметра по умолчанию в пустой список, как это:

def func(items=[]):
    print items

Pylint сказал бы мне "опасное значение по умолчанию [] в качестве аргумента". Поэтому мне было интересно, какая здесь лучшая практика?

4 53

4 ответа:

использовать None в качестве значения по умолчанию:

def func(items=None):
    if items is None:
        items = []
    print items

проблема с изменяемым аргументом по умолчанию заключается в том, что он будет разделен между всеми вызовами функции-см. "важное предупреждение" в соответствующий раздел учебника Python.

Я только что столкнулся с этим в первый раз, и моя непосредственная мысль: "Ну, я все равно не хочу мутировать список, поэтому я действительно хочу по умолчанию использовать неизменяемый список, поэтому Python даст мне ошибку, если я случайно мутирую его."Неизменяемый список-это просто кортеж. Итак:

  def func(items=()):
      print items

конечно, если вы передадите его чему-то, что действительно хочет список (например, isinstance(items, list)), то это приведет вас к неприятностям. Но это все равно кодовый запах.

для изменяемого объекта в качестве параметра по умолчанию в объявлениях функций и методов проблема заключается в том, что оценка и создание происходят в один и тот же момент. Python-parser читает function-head и оценивает его в тот же момент.

большинство начинающих считают, что новый объект создается при каждом вызове, но это не так! Один объект (в вашем примере список) создается в момент объявления, а не по требованию, когда вы вызываете метод.

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

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

кроме того, а также чтобы лучше понять, что такое python, вот мой маленький тематический фрагмент:

from functools import wraps
def defaultFactories(func):
    'wraps function to use factories instead of values for defaults in call'
    defaults = func.func_defaults
    @wraps(func)
    def wrapped(*args,**kwargs):
        func.func_defaults = tuple(default() for default in defaults)
        return func(*args,**kwargs)
    return wrapped

def f1(n,b = []):
    b.append(n)
    if n == 1: return b
    else: return f1(n-1) + b

@defaultFactories
def f2(n,b = list):
    b.append(n)
    if n == 1: return b
    else: return f2(n-1) + b

>>> f1(6)
[6, 5, 4, 3, 2, 1, 6, 5, 4, 3, 2, 1, 6, 5, 4, 3, 2, 1, 6, 5, 4, 3, 2, 1, 6, 5, 4, 3, 2, 1, 6, 5, 4, 3, 2, 1]
>>> f2(6)
[1, 2, 3, 4, 5, 6]