2015-10-29 1 views
11

В Python 3.5.0 этот код:Почему я получаю этот NameError в генераторе в определении класса Python?

a = (1,2) 
class Foo(object): 
    b = (3,4) 
    c = tuple((i,j) for j in b for i in a) 
    d = tuple((i,j) for i in a for j in b) 

производит:

Traceback (most recent call last): 
    File "genexprtest.py", line 2, in <module> 
    class Foo(object): 
    File "genexprtest.py", line 5, in Foo 
    d = tuple((i,j) for i in a for j in b) 
    File "genexprtest.py", line 5, in <genexpr> 
    d = tuple((i,j) for i in a for j in b) 
NameError: name 'b' is not defined 

Почему я получаю эту ошибку? И почему я не получаю эту ошибку в предыдущей строке?

+1

Поскольку выражения генераторов и определения классов являются их собственными областями – jonrsharpe

+0

Но если они оба находятся в своем собственном объеме, то почему черт делает доступ к b в предыдущей строке (c = ...) успешным? – Wangnick

+0

В первом примере 'b' повторяется в самом внешнем выражении' for', которое оценивается немедленно - см., Например, https://www.python.org/dev/peps/pep-0289/#early-binding-versus-late- привязка для обоснования. Аналогично, если вы измените пример в документах на 'b = list (i для i в диапазоне (a))' он отлично работает и 'd = tuple ((i, j) для i, j в itertools.product (b, a)) 'будет работать в любом случае. – jonrsharpe

ответ

0

Я провел эксперименты, и у меня есть теория о том, почему вы получаете эту ошибку. Я не уверен, но это объясняет, почему он работает для c, а не для d. Я надеюсь, что это поможет, комментарий, если вы не согласны :)

def Tuple(this): 
    print(a) # this always works 
    try: 
     print(b) # this always gives an error 
    except NameError: 
     print("...b is not defined") 
    try: 
     return tuple(this) # this only gives an error for d and e 
    except NameError: 
     print("...couldn't make it a tuple") 


a = (1,2)  
class Foo(object): 
    b = (3,4) 
    c = Tuple((i,j) for j in b for i in a) 
    d = Tuple((i,j) for i in a for j in b) 
    e = Tuple((i,j,k) for i in a for j in b for k in (5, 6)) 
    f = Tuple((i,j,k) for j in b for i in (5, 6) for k in a) 

    print("\nc:", c,"\nd:", d,"\ne:", e,"\nf:", f) 

Что случилось: каждый раз, когда я назвал функцию Tuple(), b не был определен, но a всегда определен. Это объясняет, почему вы получите ошибку для d и e, но это не объясняет, почему c и f работа, даже если b является «не определен»

Моя теория: Первый for цикл вычисляется перед всем вещь преобразованный в кортеж. Например, если вы пытались сделать это: Tuple((a, b, c) for a in loop1, for b in loop2 for c in loop3), в классе Foo было бы вычислить for a in loop1 первым, то он будет двигаться к обув и рассчитать петли 2 и 3.

В итоге:

  1. делает первый цикл
  2. переходит к функции кортежа
  3. делает оставшиеся петли
  4. ошибка возникает, если переменная в 2-м или 3-м цикле в классе Foo
0

По-моему, ошибка возникает из-за того, что b определяется как переменная класса. Чтобы правильно использовать его, вам нужно рассматривать его как таковое (self.b). Кроме того, вы должны использовать конструктор:

a = (1, 2) 

class Foo(object): 
    def __init__(self): 
     self.b = (3, 4) 
     self.c = tuple((i, j) for j in self.b for i in a) 
     self.d = tuple((i, j) for i in a for j in self.b) 

Это является более ясным кодом. И он ведет себя правильно. Надеюсь, поможет.

EDIT: если вы не хотите использовать __init__, есть также возможность получить c и d методы с использованием:

a = (1, 2) 

class Foo(object): 
    b = (3, 4) 

    def get_c(self): 
     return tuple((i, j) for j in self.b for i in a) 

    def get_d(self): 
     return tuple((i, j) for i in a for j in self.b) 

Это также работает прекрасно. Вы можете попробовать оба реализации, как это:

inst = Foo() 
# 1st one 
print(inst.c) 
print(inst.d) 
# 2nd one 
print(inst.get_c()) 
print(inst.get_d()) 
0

Это потому, что выражение for i in a имеет локальные переменные сферы, и выражение for j in b находится внутри сферы, таким образом, не b не найден.
На самом деле, если вы напишете c = tuple((i, j) for i in a for j in b, то это будет исключение.

Решение помещается b в объем определения класса и ссылается на него self.b.

0

решение в вашем конкретном случае является использование itertools:

d = tuple(itertools.product(a, b)) 

объяснение для, казалось бы, неожиданное поведение имеет два аспекта:

  1. Bare атрибуты класса, такие как b являются доступный только в области корневого класса. См. pep 227:

    Имена в классе не доступны. Имена разрешаются в самой внутренней области приложения. Если определение класса происходит в цепочке вложенных областей, процесс разрешения пропускает определения классов.

  2. Вложенные циклы в генераторах не функционируют, как вы могли бы ожидать. Первый цикл на самом деле является самым внешним, а второй - самым внутренним. От python docs:

    Последующие предложения не могут быть оценены немедленно, поскольку они могут зависеть от предыдущего цикла цикла. Например: (x * y для x в диапазоне (10) для y в bar (x)).

Второй момент можно проиллюстрировать с добавлением разрывов строк.

d = tuple((i,j) 
    for i in a 
     for j in b) 

Это означает, что на самом деле b ссылку из внутреннего цикла (вложенная область видимости) и, таким образом NameError выбрасываются. Однако в первом генераторе ссылка находится во внешнем, которая работает нормально.