İç içe geçmiş sınıfların kapsamı?


116

Python'daki yuvalanmış sınıfların kapsamını anlamaya çalışıyorum. İşte örnek kodum:

class OuterClass:
    outer_var = 1
    class InnerClass:
        inner_var = outer_var

Sınıfın oluşturulması tamamlanmıyor ve şu hatayı alıyorum:

<type 'exceptions.NameError'>: name 'outer_var' is not defined

Denemek inner_var = Outerclass.outer_varişe yaramıyor. Alırım:

<type 'exceptions.NameError'>: name 'OuterClass' is not defined

Ben statik erişmeye çalışıyorum outer_vardan InnerClass.

Bunu yapmanın bir yolu var mı?

Yanıtlar:


105
class Outer(object):
    outer_var = 1

    class Inner(object):
        @property
        def inner_var(self):
            return Outer.outer_var

Bu, diğer dillerde benzer şeylerin çalışmasıyla tamamen aynı değildir ve erişim kapsamını belirlemek yerine genel arama kullanır outer_var. (Adın Outerbağlı olduğu nesneyi değiştirirseniz , bu kod bir sonraki çalıştırıldığında o nesneyi kullanır.)

Bunun yerine, tüm Innernesnelerin bir referans niteliğine sahip olmasını istiyorsanız , Outerçünkü outer_vargerçekten bir örnek niteliğidir:

class Outer(object):
    def __init__(self):
        self.outer_var = 1

    def get_inner(self):
        return self.Inner(self)
        # "self.Inner" is because Inner is a class attribute of this class
        # "Outer.Inner" would also work, or move Inner to global scope
        # and then just use "Inner"

    class Inner(object):
        def __init__(self, outer):
            self.outer = outer

        @property
        def inner_var(self):
            return self.outer.outer_var

Sınıfların iç içe geçmesinin Python'da pek yaygın olmadığına ve sınıflar arasında otomatik olarak herhangi bir özel ilişki anlamına gelmediğine dikkat edin. Yuvalanmasan daha iyi. (Sen hala class özelliği ayarlayabilirsiniz Outeriçin Inneristerseniz.)


2
Cevabınızın hangi python sürümleriyle çalışacağını eklemeniz faydalı olabilir.
AJ.

1
Bunu aklımda 2.6 / 2.x ile yazdım ama baktığımda 3.x'te aynı şekilde çalışmayacak hiçbir şey görmüyorum.

Bu bölümde ne demek istediğinizi tam olarak anlamıyorum, "(Outer adının hangi nesneye bağlı olduğunu değiştirirseniz, bu kod bir sonraki çalıştırıldığında o nesneyi kullanacaktır.)" Lütfen anlamama yardım eder misiniz?
batbrat

1
@batbrat bu, yaptığınız Outerher seferinde başvurunun yeniden arandığı anlamına gelir Inner.inner_var. Dolayısıyla, adı Outeryeni bir nesneye yeniden bağlarsanız, Inner.inner_varbu yeni nesneyi döndürmeye başlayacaktır.
Felipe

45

Sanırım şunları yapabilirsiniz:

class OuterClass:
    outer_var = 1

    class InnerClass:
        pass
    InnerClass.inner_var = outer_var

Karşılaştığınız sorunun nedeni şudur:

Blok, birim olarak çalıştırılan bir Python program metnidir. Aşağıdakiler bloklardır: bir modül, bir işlev gövdesi ve bir sınıf tanımı.
(...)
Kapsam, bir blok içindeki bir adın görünürlüğünü tanımlar.
(...)
Bir sınıf bloğunda tanımlanan isimlerin kapsamı, sınıf bloğu ile sınırlıdır; yöntemlerin kod bloklarını kapsamaz - bu, bir işlev kapsamı kullanılarak gerçekleştirildikleri için jeneratör ifadelerini içerir. Bu, aşağıdakilerin başarısız olacağı anlamına gelir:

   class A:  

       a = 42  

       b = list(a + i for i in range(10))

http://docs.python.org/reference/executionmodel.html#naming-and-binding

Yukarıdaki şu anlama gelir:
bir işlev gövdesi bir kod bloğudur ve bir yöntem bir işlevdir, bu durumda bir sınıf tanımında bulunan işlev gövdesinden tanımlanan adlar işlev gövdesini kapsamaz.

Bunu sizin durumunuz için
başka bir deyişle : bir sınıf tanımı bir kod bloğudur, daha sonra bir dış sınıf tanımında bulunan iç sınıf tanımından tanımlanan adlar iç sınıf tanımını kapsamaz.


2
İnanılmaz. Örneğiniz "genel ad 'a' tanımlı değil" iddiasıyla başarısız oluyor. Yine de bir liste kavrayışının yerini [a + i for i in range(10)]almak, Ab'yi beklenen listeye başarıyla bağlar [42..51].
George

6
@George Not A sınıfı örnek benim değil, bağlantı verdiğim Python resmi belgesine ait . Bu örnek başarısız olur ve bu örnekte gösterilmek istenen şey bu başarısızlıktır. Aslında list(a + i for i in range(10))olduğunu list((a + i for i in range(10)))yani list(a_generator). Bir jeneratörün işlev kapsamına benzer bir kapsamda uygulandığını söylüyorlar .
eyquem

@George Benim için bu, işlevlerin bir modülde veya bir sınıfta olup olmadıklarına göre farklı hareket ettiği anlamına gelir. İlk durumda, bir fonksiyon serbest bir tanımlayıcıya bağlı nesneyi bulmak için dışarı çıkar. İkinci durumda, bir fonksiyon, yani bir yöntem, bedeninin dışına çıkmaz. Bir modüldeki işlevler ve bir sınıftaki yöntemler gerçekte iki tür nesnedir. Yöntemler yalnızca sınıftaki işlevler değildir. Bu benim fikrim.
eyquem

5
@George: FWIW, ne list(...)Python 3'te çağrı ne de anlama çalışması belgeleri PY3 için de bu yansıtan biraz farklıdır. Şimdi diyor ki " Bir sınıf bloğunda tanımlanan isimlerin kapsamı sınıf bloğu ile sınırlıdır; yöntemlerin kod bloklarını kapsamaz - bu, bir işlev kapsamı kullanılarak uygulandıkları için anlamaları ve üreteç ifadelerini içerir . " (Vurgu benim ).
martineau

Listenin (aralıktaki i için Aa + i (10)) neden çalışmadığını merak ediyorum, burada a'yı Aa ile düzelttim A'nın küresel bir isim olabileceğini düşünüyorum.
gaoxinge

20

İç içe sınıflar kullanmazsanız daha iyi durumda olabilirsiniz. Yuvalama yapmanız gerekiyorsa, şunu deneyin:

x = 1
class OuterClass:
    outer_var = x
    class InnerClass:
        inner_var = x

Veya iç içe geçmeden önce her iki sınıfı da bildirin:

class OuterClass:
    outer_var = 1

class InnerClass:
    inner_var = OuterClass.outer_var

OuterClass.InnerClass = InnerClass

(Bundan sonra gerekirse yapabilirsiniz del InnerClass.)


3

En kolay çözüm:

class OuterClass:
    outer_var = 1
    class InnerClass:
        def __init__(self):
            self.inner_var = OuterClass.outer_var

Açık olmanızı gerektirir, ancak fazla çaba gerektirmez.


NameError: 'OuterClass' adı tanımlanmadı - -1
Mr_and_Mrs_D

3
Buradaki amaç, ona Outer sınıf kapsamından erişmektir - ayrıca bunu Outer içindeki statik bir kapsamdan çağırırsanız benzer bir hata oluşur. Bu
gönderiyi

1

Python'da, değişken nesneler referans olarak aktarılır, böylece dış sınıfın bir referansını iç sınıfa aktarabilirsiniz.

class OuterClass:
    def __init__(self):
        self.outer_var = 1
        self.inner_class = OuterClass.InnerClass(self)
        print('Inner variable in OuterClass = %d' % self.inner_class.inner_var)

    class InnerClass:
        def __init__(self, outer_class):
            self.outer_class = outer_class
            self.inner_var = 2
            print('Outer variable in InnerClass = %d' % self.outer_class.outer_var)

2
Lütfen burada bir referans döngünüz olduğunu ve bu sınıfın bazı senaryolarında bu sınıfın serbest bırakılmayacağını unutmayın. Bir örnek, cPython ile, __del__ yöntemi tanımladıysanız , çöp toplayıcı referans döngüsünü idare edemez ve nesneler içine girer gc.garbage. Yukarıdaki kod, olduğu gibi, sorunlu değildir. Bununla başa çıkmanın yolu zayıf bir referans kullanmaktır . Zayıf ref (2.7) veya zayıfref (3.5) ile
guyarad

0

Tüm açıklamalar Python Belgeleri The Python Eğitimi'nde bulunabilir.

İlk hatanız için <type 'exceptions.NameError'>: name 'outer_var' is not defined. Açıklama şu şekildedir:

Yöntemlerin içinden veri özniteliklerine (veya diğer yöntemlere!) Atıfta bulunmak için bir kısaltma yoktur. Bunun aslında yöntemlerin okunabilirliğini artırdığını görüyorum: bir yönteme göz atarken yerel değişkenleri ve örnek değişkenlerini karıştırmanın hiçbir şansı yoktur.

The Python Tutorial 9.4'ten alıntı

İkinci hatan için <type 'exceptions.NameError'>: name 'OuterClass' is not defined

Bir sınıf tanımı normal olarak bırakıldığında (uçtan), bir sınıf nesnesi oluşturulur.

The Python Tutorial 9.3.1'den alıntı

Yani denediğinizde inner_var = Outerclass.outer_var, Quterclasshenüz yaratılmadı, bu yüzdenname 'OuterClass' is not defined

İlk hatanız için daha ayrıntılı ama sıkıcı bir açıklama:

Sınıfların kapsayıcı işlevlerin kapsamlarına erişimleri olmasına rağmen, sınıf içinde yuvalanmış koda yönelik kapsayıcı kapsamlar olarak hareket etmezler: Python, başvurulan isimleri kapsayan işlevleri arar, ancak hiçbir zaman kapsayıcı sınıfları aramaz. Diğer bir deyişle, bir sınıf yerel bir kapsamdır ve yerel kapsamları çevreleyen erişime sahiptir, ancak daha fazla iç içe geçmiş kod için çevreleyen bir yerel kapsam işlevi görmez.

alıntı Learning.Python (5) .Mark.Lutz

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.