UnboundLocalError при использовании += в списке. Почему здесь необходим нелокальный режим, когда прямой вызов __iadd__ работает нормально?

Рассмотрим этот код:

def main():
    l = []

    def func():
        l += [1]

    func()
    print(l)

if __name__ == '__main__':
    main()

Это произведет:

Traceback (most recent call last):
  File "/Users/tahsmith/Library/Preferences/PyCharm2017.1/scratches/scratch_21.py", line 14, in <module>
    main()
  File "/Users/tahsmith/Library/Preferences/PyCharm2017.1/scratches/scratch_21.py", line 11, in main
    func()
  File "/Users/tahsmith/Library/Preferences/PyCharm2017.1/scratches/scratch_21.py", line 9, in func
    l += [1]
UnboundLocalError: local variable 'l' referenced before assignment

Это само по себе можно исправить, используя nonlocal l в начале func или используя __iadd__ прямо вместо +=,

Вопрос: почему nonlocal нужен здесь?

Это очень удивительно для меня.

2 ответа

Решение

+= является оператором расширенного присваивания; это примерно означает:

def func():
    l = l + [1]

Если бы вы должны были заменить l += [1] с призывом к object.__iadd__(), вы не можете игнорировать возвращаемое значение этого вызова, если вы используете его правильно:

def func():
    l = l.__iadd__([1])

Оба этих перевода также нуждаются в nonlocal заявление, потому что оба доступа l и назначить обратно l,

Вы можете избежать игнорирования возвращаемого значения object.__iadd__ потому что объекты списка изменчивы; список видоизменен на месте. Но тогда вы можете также использовать list.extend() позвоните в этом случае:

def func():
    l.extend([1])

list.__iadd__()под прикрытием звонков list.extend() перед возвращением self,

Потому что под одеялом l += [1] приводит к:

l = l + [1]

который ссылается на имя l до того, как это назначено; вот почему вы получите UnboundLocalError,

l.__iadd__с другой стороны, это простой вызов функции; он не выполняет назначение, тем самым сводя на нет необходимость nonlocal чтобы помочь в том, где искать имя l,

Другие вопросы по тегам