2016-07-12 10 views
10

, mc ödev Python 2'de çalışıyor ve 3.Bir Python sınıfında liste anlama kapsam belirleme kuralları nelerdir? Aşağıdaki kodda

bir sınıf içinde aynı liste anlama kullanır cc atama, Python 2 çalışır ancak

Python 3. başarısız olur Bu davranışı ne açıklar?

(default-3.5) snafu$ python2 /tmp/z.py 
('mc = ', ['a1', 'a2', 'a3', 'b1', 'b2', 'b3', 'c1', 'c2', 'c3']) 
('cc = ', ['a1', 'a2', 'a3', 'b1', 'b2', 'b3', 'c1', 'c2', 'c3']) 

(default-3.5) snafu$ python3 /tmp/z.py 
Traceback (most recent call last): 
    File "/tmp/z.py", line 5, in <module> 
    class Foo(object): 
    File "/tmp/z.py", line 11, in Foo 
    cc = [ i1 + i2 for i1 in cl1 for i2 in cl2 ] 
    File "/tmp/z.py", line 11, in <listcomp> 
    cc = [ i1 + i2 for i1 in cl1 for i2 in cl2 ] 
NameError: name 'cl2' is not defined 

Neden sınıf değişkeni cl2 tanımlanmadı:
ml1 = "a b c".split() 
ml2 = "1 2 3".split() 
mc = [ i1 + i2 for i1 in ml1 for i2 in ml2 ] 

class Foo(object): 
    cl1 = ml1 
    cl2 = ml2 

    cc1 = [ i1 for i1 in cl1 ] 
    cc2 = [ i2 for i2 in cl2 ] 
    cc = [ i1 + i2 for i1 in cl1 for i2 in cl2 ] 


print("mc = ", mc) 
foo = Foo() 
print("cc = ", foo.cc) 

Ben bu olsun? cc1, cc2 atama işleminin düzgün çalıştığını unutmayın. ,

Python 3
(default-3.5) snafu$ python2 --version 
Python 2.7.11+ 
(default-3.5) snafu$ python3 --version 
Python 3.5.1+ 
+0

Bu, sınıf değişkenleri ve örnek değişkenler, anlama kapsamı ile ilgili değildir. – TigerhawkT3

+0

@ TigerhawkT3: Aslında, gerçekten sınıf düzeyi anlamada değişken kapsam ile ilgilidir. – user2357112

+2

http://stackoverflow.com/questions/20136955/python3-nested-list-comprehension-scope –

cevap

2

, liste comprehensions kendi kapsama sahip. Anlama cl1 ve cl2 takas ikinci döngü istisna değil, cl2 başına)

sürümleri tetikler biri olduğunu gösterir Bir işlev kapsamı olarak aynı kuralları izler. Sınıfın yöntemlerinin, değişken arama için otomatik olarak sınıf kapsamına nasıl bakmadığını biliyor musunuz?

class Example: 
    var = 1 
    def this_fails(self): 
     print(var) 
Example().this_fails() # NameError 

Aynı liste anlama kapsamı içeren bir sınıf kapsam, içine yuvalanmış herhangi bir işlev kapsamı için de geçerlidir. Listedeki cl2 arayışı, sınıf kapsamını atlar ve doğrudan globals'a gider. Bu etkili şöyle çalışır: cl1 arama çalışıyor o

class Foo(object): 
    ... 
    def make_cc(outer_iterable): 
     result = [] 
     for i1 in outer_iterable: 
      for i2 in cl2: # This fails 
       result.append(i1 + i2) 
     return result 
    cc = make_cc(cl1) # cl1 is evaluated outside the comprehension scope, for reasons 

Not, bu sözdizimsel anlama içine yuvalanmış rağmen, anlama dışında, sınıf kapsamda olur çünkü. Python, genexps'i tanıttığı zaman, They made that decision geri döndü, çünkü daha önce birkaç yaygın genek hatası yakaladı. Ayrıca, cc1 ve cc2 liste kavramalarının çalışmasının nedeni de budur; sınıf düzeyi değişkenlerinin tek kullanımı onların dış (sadece) for tekrarlanabilir olmasıdır.

Bir sınıf deyimindeki anlama ve üretme ifadelerini kullanmak bir karmaşadır. Olmamalı, ama öyle. Normal döngülere sadık olun ya da sınıf ifadesinin dışındaki kavrayışları çalıştırın, böylece semantikler daha açık olur.

+1

Bu kulağa mantıklı geliyor, ancak, neden bana açıklamıyor? Cc1 = [i1 in cl1 için] Çalışır, ancak cc = [i1 + i2 için cl1'de i1 için cl2'de '] yapmaz. Kapsam belirleme kuralları aynı olmaz mı? – mhawke

+0

@mhawke: İlk "için" iterable yineleme kapsamı dışında değerlendirilir ve büyük olasılıkla daha önce birkaç yaygın hataları yakaladığı için bu şekilde çalışan jeneratör ifadeleri ile simetri için bir argüman olarak anonim işlevine geçirilir. – user2357112

+0

"Bir sınıf ifadesinde anlama ve jeneratör ifadelerini kullanmak bir karmaşadır." Katılıyorum: Başarılı 'cc1' ve 'cc2' ödevleri ve başarısız cc' ataması, şaşırtıcı olmayan ve beklenmedik görünmektedir. – Reece