Какова область действия функции, определенной вне класса? - PullRequest
1 голос
/ 28 января 2020
class player(object):
    def __init__(self, a, b):
        self.a = a
        self.b = b


def foo():
    x += 2
    obj.a += 10

obj = player(0,0)
x = 4

foo()

Я понимаю, что моя функция foo не может присвоить значение локальной переменной x, поскольку оно ранее было определено глобально вне области действия foo. Однако такая же проблема не возникнет для переменной a, которая является атрибутом экземпляра obj из класса player, который, в свою очередь, не имеет ничего общего с функцией foo(). Помимо ошибки с x += 2, я ожидал бы ошибку в obj.a, то есть я думаю, что foo() не сможет изменить obj.a, но это так! Он должен был выдать ту же ошибку, что и для x += 2, может быть, нет?

Ответы [ 5 ]

2 голосов
/ 28 января 2020

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

  1. Если левая сторона является именем, то x += y десугары до x = x.__iadd__(y). Поскольку это обычный оператор присваивания, синтаксический анализатор делает x локальной переменной (если ей не предшествует global x или nonlocal x). Независимо от области действия x уже должно иметь значение, чтобы можно было оценить правую часть. Другими словами, x не является свободной переменной.

  2. Если левая часть является индексированным именем, то x[i] += y десугарсится до x.__setitem__(i, x[i] + y). Если x еще не определено как локальная переменная, это свободная переменная, и поэтому она будет преобразована в значение x в первой охватывающей области.

  3. Если левая сторона - поиск атрибутов, затем x.a = y десугарс до x.__setattr__('a', x.a + y). Опять же, x является свободной переменной, если только локальная переменная x не существует.

1 голос
/ 28 января 2020

Ваше сравнение это яблоки с апельсинами. Пример, показывающий ожидаемое поведение:

class player(object):
    def __init__(self, a, b):
        self.a = a
        self.b = b
    def __add__(self, value):
        self.a += value

def foo():
    obj += 10

obj = player(0,0)
x = 4

foo()

Python позволяет вам читать глобальные переменные внутри foo, но не переопределять их. Оператор obj.a += 10 только читает переменную obj из глобальной области (даже если он устанавливает значение в экземпляре obj), поэтому это разрешено. x += 2 пытается переопределить значение x, которое является глобальным, поэтому оно не допускается. Это foo будет работать, потому что мы только читаем x, но не переопределяем его:

def foo():
    obj.a += 10
    y = x + 10
0 голосов
/ 28 января 2020

На самом деле, вы не можете позвонить x += 2, потому что x еще не определен в вашем коде. Вы можете прочитать x как глобальную переменную или изменить ее значение, используя global.

Если вы запустите свой код, вы получите ошибку:

UnboundLocalError: local variable 'x' referenced before assignment

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

Объявление его локально:

def foo():
    x = 0
    x += 2
    obj.a += 10

obj = player(0,0)
x = 4

foo()

При глобальном:

def foo():
    global x
    x += 2
    obj.a += 10

obj = player(0,0)
x = 4

foo()

Теперь, почему x.a += 10 работает?

Поскольку вы вообще не меняете объект x.

Как упоминал Джон в своем ответе, такая же ошибка возникнет, если вы измените сам объект, а не его свойства.

, если вы попытаетесь запустить приведенный ниже код:

def foo():
    obj += 10

obj = player(0,0)
x = 4

foo()

You Вы получите ту же ошибку, что и раньше:

UnboundLocalError: local variable 'obj' referenced before assignment
0 голосов
/ 28 января 2020

Без использования одного из ключевых слов global или nonlocal функция может читать , но не назначать новое значение для переменной за пределами собственной локальной области действия функции. Давайте рассмотрим эти два случая:

  • Для x += 2 это попытка присвоить новое значение глобальной переменной x, поэтому оно не разрешено.
  • Для obj.a += 10, это переменная obj, которая является глобальной; obj.a является атрибутом объекта, а не «переменной» в этом смысле. Способ оценки obj.a += 10 выглядит примерно так: сначала найдите объект, на который ссылается переменная obj, а затем добавьте 10 к атрибуту с именем a, принадлежащему этому объекту. Поскольку это только читает глобальную переменную obj и не пытается присвоить новое значение obj, это разрешено.
0 голосов
/ 28 января 2020

a является атрибутом экземпляра player, тогда как x является переменной или просто значением. Когда вы изменяете x в функции, она находится в локальной области видимости и приводит к NameError. Но obj - это объект в глобальной области видимости, и когда вы изменяете a, вы просто обновляете его атрибут, здесь вы не получите никакой ошибки.

...