Bir iç sınıftan dış sınıfa nasıl erişilir?


105

Böyle bir durumum var ...

class Outer(object):

    def some_method(self):
        # do something

    class Inner(object):
        def __init__(self):
            self.Outer.some_method()    # <-- this is the line in question

Sınıftan Outersınıfın yöntemine nasıl erişebilirim Inner?


Bunu neden yapıyorsun? Basit akran ilişkilerinde yanlış olan ne? Bir şeyi "gizlemeye" mi çalışıyorsun?
S.Lott

1
Bu senaryonun bir örneği, normal olarak dış sınıfa erişmesi gereken, ancak daha sonra birinci sınıftan türetilmiş başka bir sınıf (üst düzey) oluşturmaya ihtiyaç duyan alt sınıflara sahip bir sınıfa sahip olmak olabilir . Bu durumda, ikinci sınıfın alt sınıfları, alt sınıf oldukları yeni sınıfı değil, orijinal sınıfı kullanarak ebeveyne erişmeye self.<original_parent_name>ve almaya çalışacaktır . Umarım bunu okuyan insanlar bu zor senaryoyu görselleştirebilir ve bunun gibi soruların amacını görebilirler.
Edward

1
Stackoverflow.com/questions/2278426 adresine çoğaltın ve güzel bir cevabı var.
xmedeko

Yanıtlar:


72

İç içe geçmiş bir sınıfın yöntemleri, dış sınıfın örnek özniteliklerine doğrudan erişemez.

İç sınıfın bir örneğini yaratmış olsanız bile dış sınıfın bir örneğinin var olmasının zorunlu olmadığını unutmayın.

Aslında, yuvalama, iç ve dış sınıflar arasında belirli bir ilişki anlamına gelmediğinden, genellikle yuvalanmış sınıfların kullanılmaması önerilir.


1
Hmm, Python Java / C ++ 'dan daha zordur ... aşağıdaki cevabıma bakın. Genelde yaptığımız gibi tüyleri ayırıyorsak, "yöntem içinde yuvalanmış sınıfımın" bir iç sınıf olarak sayılıp sayılmayacağını size gerçekten söyleyemem. Bu noktada, yine de ördek yazmayı çağırmalıyım: eğer bir iç sınıfın yapabileceği her şeyi yaparsa ... Pythonic bakış açısına göre, muhtemelen saçları
ayırmaktan

22
Elbette bir iç sınıf, dış sınıfla bir ilişkiyi ima eder, tipik olarak iç sınıfın ima edilen kullanım kapsamı veya başka türlü bir örgütsel ad alanı ile ilgilidir.
Acumenus

75

İç sınıf örneğinden Outer'un sınıf örneğine erişmeye çalışıyorsunuz. Bu nedenle, Inner örneğini oluşturmak için fabrika yöntemini kullanın ve Dış örneği ona iletin.

class Outer(object):

    def createInner(self):
        return Outer.Inner(self)

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

        def inner_method(self):
            self.outer_instance.anothermethod()

Bu tam olarak doğru cevap - OP'nin sorusuna bir çözüm sağladığı için doğru cevap olarak seçilmelidir - çok teşekkürler!
Daniel

29

belki kızgınım ama bu gerçekten çok kolay görünüyor - mesele, iç sınıfınızı dış sınıfın bir yönteminin içine sokmak ...

def do_sthg( self ):
    ...

def messAround( self ):

    outerClassSelf = self

    class mooble():
        def do_sthg_different( self ):
            ...
            outerClassSelf.do_sthg()

Artı ... "öz" yalnızca kural tarafından kullanılır, bu nedenle bunu yapabilirsiniz:

def do_sthg( self ):
    ...

def messAround( outerClassSelf ):

    class mooble():
        def do_sthg_different( self ):
            ...
            outerClassSelf.do_sthg()

O zaman bu iç sınıfı dış sınıfın dışından yaratamayacağınıza itiraz edilebilir ... ama bu doğru değil:

class Bumblebee():

    def do_sthg( self ):
        print "sthg"

    def giveMeAnInnerClass( outerClassSelf ):

        class mooble():
            def do_sthg_different( self ):
                print "something diff\n"
                outerClassSelf.do_sthg()
        return mooble

sonra, millerce uzakta bir yerde:

blob = Bumblebee().giveMeAnInnerClass()()
blob.do_sthg_different()    

hatta tekneyi biraz dışarı itin ve bu iç sınıfı genişletin (NB to get super () çalışması için mooble'ın sınıf imzasını "class mooble (object)" olarak değiştirmeniz gerekir.

class InnerBumblebeeWithAddedBounce( Bumblebee().giveMeAnInnerClass() ):
    def bounce( self ):
        print "bounce"

    def do_sthg_different( self ):
        super( InnerBumblebeeWithAddedBounce, self ).do_sthg_different()
        print "and more different"


ibwab = InnerBumblebeeWithAddedBounce()    
ibwab.bounce()
ibwab.do_sthg_different()

sonra

mrh1997, bu teknik kullanılarak teslim edilen iç sınıfların ortak olmayan kalıtımı hakkında ilginç bir noktaya değindi. Ancak çözümün oldukça basit olduğu görülüyor:

class Fatty():
    def do_sthg( self ):
        pass

    class InnerFatty( object ):
        pass

    def giveMeAnInnerFattyClass(self):
        class ExtendedInnerFatty( Fatty.InnerFatty ):
            pass
        return ExtendedInnerFatty

fatty1 = Fatty()
fatty2 = Fatty()

innerFattyClass1 = fatty1.giveMeAnInnerFattyClass()
innerFattyClass2 = fatty2.giveMeAnInnerFattyClass()

print ( issubclass( innerFattyClass1, Fatty.InnerFatty ))
print ( issubclass( innerFattyClass2, Fatty.InnerFatty ))

1
Bu benim için çalışıyor. Bu yapının adı tam olarak nedir? Bir fabrika işlevi mi? Kapatma mı?
nakedfanatic

Adının ne olduğuna dair bir fikrim yok ... ama diğer afişlerin bunu görmemesinin sebebinin Python'daki çoğu şeyin kutsal olmadığı, belki de tam olarak takdir edilmediğini söyleyebilir miyim? "(rastgele ad) ve sınıflar - bunlar" birinci sınıf nesnelerdir ", bu da onları oldukça çirkin şekillerde manipüle edebileceğiniz anlamına geliyor gibi görünüyor
mike rodent

Harika iş. Bu çözüm fikrinden ve biraz düşünmeden esinlenerek, bu cevabın bazı kısımlarını aşağıda biraz daha açıklama ile başka bir cevap oluşturmak için genişlettim.
Edward

1
@mikerodent Yorumunuz için (cevabıma), şimdi "topluluk wiki" açıklamasını kaldırmak için cevabımı düzenledim. Sizin gibi "topluluk wiki" cevapları hakkında hiçbir bilgim yok, bu yüzden hatanızı düzeltmeniz iyi oldu.
Edward

@mikerodent Ayrıca, alınma ama "topluluk wiki" yanıtlarının "topluluk wiki" etiketi olduğunu düşünmüyorum, sadece bir tür yanıt olmalılar. Eğer ilgilenirseniz, Stack Overflow'da muhtemelen "topluluk wiki" yanıtlarının açıklamasını içeren bir yardım sayfası olacaktır.
Edward

3

Bunun gibi sınıfları iç içe yerleştirmek yerine kalıtımı mı kullanmak istiyorsunuz? Yaptığınız şey Python'da bir anlam ifade etmiyor.

OuterBazı_yöntemlere yalnızca Outer.some_methodiç sınıfın yöntemlerine başvurarak erişebilirsiniz , ancak beklediğiniz gibi çalışmayacaktır. Örneğin, şunu denerseniz:

class Outer(object):

    def some_method(self):
        # do something

    class Inner(object):
        def __init__(self):
            Outer.some_method()

... bir Innernesneyi başlatırken bir TypeError Outer.some_methodalacaksınız , çünkü Outerilk argüman olarak bir örneği almayı bekler . (Yukarıdaki örnekte, temelde some_methodbir sınıf yöntemi olarak çağırmaya çalışıyorsunuz Outer.)


1
Muhtemelen mantıklı gelmemesinin nedeni, kasıtlı olarak saldırgan olmamdır. Django'da bir QuerySet'e özel yöntemler eklemek için biraz standart kod gerekiyor ve bunu python kullanarak yapmanın, ortak kodu şablonlamama ve Model kodumdaki ilgili parçaları yazmamı sağlayan akıllıca bir yol bulmaya çalışıyordum.
T. Stone

Özür dilerim - Django'yu bilmiyorum ve bu yüzden ortak kod için bir şablon öneremem, ancak sınıflarınızı iç içe geçirmeye çalışırken yanlış ağacı havlıyor olabilirsiniz. İç sınıfınız Dış sınıfınızdan hiçbir şey almıyor. Onu Outer'in yaptığı tüm iç içe yerleştirme, sizi sadece düz İçten ziyade Dış.İç yoluyla erişmeye zorlar.
zenbot

3

Metasınıf kullanarak dış sınıfa kolayca erişebilirsiniz: dış sınıf oluşturulduktan sonra, herhangi bir sınıf için öznitelik diktesini kontrol edin (veya ihtiyacınız olan herhangi bir mantığı uygulayın - benimki sadece önemsiz bir örnektir) ve karşılık gelen değerleri ayarlayın:

import six
import inspect


# helper method from `peewee` project to add metaclass
_METACLASS_ = '_metaclass_helper_'
def with_metaclass(meta, base=object):
    return meta(_METACLASS_, (base,), {})


class OuterMeta(type):
    def __new__(mcs, name, parents, dct):
        cls = super(OuterMeta, mcs).__new__(mcs, name, parents, dct)
        for klass in dct.values():
            if inspect.isclass(klass):
                print("Setting outer of '%s' to '%s'" % (klass, cls))
                klass.outer = cls

        return cls


# @six.add_metaclass(OuterMeta) -- this is alternative to `with_metaclass`
class Outer(with_metaclass(OuterMeta)):
    def foo(self):
        return "I'm outer class!"

    class Inner(object):
        outer = None  # <-- by default it's None

        def bar(self):
            return "I'm inner class"


print(Outer.Inner.outer)
>>> <class '__main__.Outer'>
assert isinstance(Outer.Inner.outer(), Outer)

print(Outer().foo())
>>> I'm outer class!
print(Outer.Inner.outer().foo())
>>> I'm outer class!
print(Outer.Inner().outer().foo())
>>> I'm outer class!
print(Outer.Inner().bar())
>>> I'm inner class!

Bu yaklaşımı kullanarak, iki sınıfı birbirine kolayca bağlayabilir ve bunlara atıfta bulunabilirsiniz.


3

Bu soru için başka bir cevaptan alınan iyi bir fikre dayanarak, kendi iç sınıfından bir dış sınıfı kullanmak için bazı Python kodu oluşturdum . Kısa, basit ve anlaşılması kolay olduğunu düşünüyorum.

class higher_level__unknown_irrelevant_name__class:
    def __init__(self, ...args...):
        ...other code...
        # Important lines to access sub-classes.
        subclasses = self._subclass_container()
        self.some_subclass = subclasses["some_subclass"]
        del subclasses # Free up variable for other use.

    def sub_function(self, ...args...):
        ...other code...

    def _subclass_container(self):
        _parent_class = self # Create access to parent class.
        class some_subclass:
            def __init__(self):
                self._parent_class = _parent_class # Easy access from self.
                # Optional line, clears variable space, but SHOULD NOT BE USED
                # IF THERE ARE MULTIPLE SUBCLASSES as would stop their parent access.
                #  del _parent_class
        class subclass_2:
            def __init__(self):
                self._parent_class = _parent_class
        # Return reference(s) to the subclass(es).
        return {"some_subclass": some_subclass, "subclass_2": subclass_2}

Ana kod, "üretime hazır" (yorumsuz, vb.). Açılı parantez içindeki her bir değeri (örneğin <x>) istenen değerle değiştirmeyi unutmayın.

class <higher_level_class>:
    def __init__(self):
        subclasses = self._subclass_container()
        self.<sub_class> = subclasses[<sub_class, type string>]
        del subclasses

    def _subclass_container(self):
        _parent_class = self
        class <sub_class>:
            def __init__(self):
                self._parent_class = _parent_class
        return {<sub_class, type string>: <sub_class>}

Bu yöntemin nasıl çalıştığının açıklaması (temel adımlar):

  1. _subclass_containerDeğişkene erişmek için bir sarmalayıcı görevi görecek şekilde adlandırılmış bir işlev oluşturun self, üst düzey sınıfa bir referans (işlev içinde çalışan koddan).

    1. Adında bir değişken oluşturma _parent_classdeğişkeni için bir referans selfalt sınıfları olduğunu, bu fonksiyonun _subclass_containerkullanıcılar erişebilir (kaçınır diğer çakışmaları isim selfalt sınıflara değişkenler).

    2. Alt sınıfı / alt sınıfları bir sözlük / liste olarak döndürün, böylece _subclass_containerişlevi çağıran kod, içindeki alt sınıflara erişebilir.

  2. Daha __init__yüksek seviye sınıfın içindeki fonksiyonda (veya ihtiyaç duyulan her yerde), fonksiyondan döndürülen alt sınıfları _subclass_containerdeğişkene alın subclasses.

  3. subclassesDeğişkende depolanan alt sınıfları daha yüksek seviye sınıfın özniteliklerine atayın .

Senaryoları kolaylaştırmak için birkaç ipucu:

Alt sınıfları daha yüksek seviyeli sınıfa atamak için kodun kopyalanması ve işlevi değiştirilen daha yüksek seviyeli sınıftan türetilen sınıflarda kullanılmasını sağlamak __init__ :

Ana kodda 12. satırdan önce ekleyin:

def _subclass_init(self):

Sonra bu işlev satırlarına 5-6 (ana kodun) ekleyin ve 4-7 satırlarını aşağıdaki kodla değiştirin:

self._subclass_init(self)

Alt sınıfların çok / bilinmeyen miktarları olduğunda daha yüksek seviyeli sınıfa alt sınıf atamayı mümkün kılmak.

6. satırı aşağıdaki kodla değiştirin:

for subclass_name in list(subclasses.keys()):
    setattr(self, subclass_name, subclasses[subclass_name])

Bu çözümün nerede yararlı olacağına ve daha üst düzey sınıf adının elde edilmesinin imkansız olması gerektiğine dair örnek senaryo:

"A" ( class a:) adlı bir sınıf oluşturulur. Erişmesi gereken alt sınıfları vardır (ebeveyn). Bir alt sınıfa "x1" adı verilir. Bu alt sınıfta kod a.run_func()çalıştırılır.

Daha sonra "a" ( ) sınıfından türetilen "b" adlı başka bir sınıf oluşturulur class b(a):. Bundan sonra, bazı kodlar çalışır b.x1()(türetilmiş bir alt sınıf olan b'nin "x1" alt işlevini çağırır). Bu işlev a.run_func(), "a" sınıfının "run_func" işlevini çağırarak çalışır , üst işlevinin "run_func" işlevini değil , "b" (olması gerektiği gibi) işlevini çağırır, çünkü "a" sınıfında tanımlanan işlev başvurmak üzere ayarlanmıştır. "a" sınıfının işlevi, çünkü bu onun ebeveyniydi.

Bu, sorunlara neden olabilir (örneğin, işlev a.run_funcsilinmişse) ve sınıftaki kodu yeniden yazmadan tek çözüm a.x1, alt x1sınıfı "a" sınıfından türetilen tüm sınıflar için güncellenmiş kodla yeniden tanımlamak olacaktır ki bu açıkça zor ve değersiz olacaktır. o.


Cevabıma güzel yorumunuz için teşekkürler. "Topluluk wiki yanıtı" etiketleri hakkında hiçbir şey bilmiyordum ve şimdi bunu kaldırdım! Bu yüzden bunu işaret ettiğiniz için de teşekkürler. Gelecekteki okuyucuların kafa karışıklığını önlemek için cevabınızı buna göre düzenlemek isteyebilirsiniz!
mike rodent

3

Bulduğum bu .

Sorunuzu uygun hale getirmek için ayarlandı:

class Outer(object):
    def some_method(self):
        # do something

    class _Inner(object):
        def __init__(self, outer):
            outer.some_method()
    def Inner(self):
        return _Inner(self)

Eminim bunun için bir dekoratör falan yazabilirsin

related: python'un iç sınıflarının amacı nedir?


2

Başka bir olasılık:

class _Outer (object):
    # Define your static methods here, e.g.
    @staticmethod
    def subclassRef ():
        return Outer

class Outer (_Outer):
    class Inner (object):
        def outer (self):
            return _Outer

        def doSomething (self):
            outer = self.outer ()
            # Call your static mehthods.
            cls = outer.subclassRef ()
            return cls ()

1

@ Tsnorri'nin ikna edici düşüncesini, yani dış yöntemin statik bir yöntem olabileceğini genişleterek :

class Outer(object):

    @staticmethod
    def some_static_method(self):
        # do something

    class Inner(object):
        def __init__(self):
            self.some_static_method()    # <-- this will work later

    Inner.some_static_method = some_static_method

Şimdi söz konusu hat, aslında çağrıldığı zamana kadar çalışmalıdır.

Yukarıdaki kodun son satırı, Inner sınıfına Outer statik yönteminin bir klonu olan statik bir yöntem verir.


Bu, işlevlerin nesne ve kapsamın metinsel olduğu iki Python özelliğinden yararlanır .

Genellikle, yerel kapsam, mevcut işlevin (metinsel olarak) yerel adlarına başvurur.

... veya bizim durumumuzda mevcut sınıf . Bu nedenle, Outer sınıfının ( Innerve some_static_method) tanımına "yerel" nesneler , doğrudan bu tanım içinde başvurulabilir.


1

Geç partiye Birkaç yıl .... ama genişletmek için @mike rodent'ın harika cevabı burada ne esnek onun çözüm olduğunu gösterir altında kendi örneğini sağladı ve olması gerektiği (veya neden ettik gelmiş oldu) kabul Cevap.

Python 3.7

class Parent():

    def __init__(self, name):
        self.name = name
        self.children = []

    class Inner(object):
        pass

    def Child(self, name):
        parent = self
        class Child(Parent.Inner):
            def __init__(self, name):
                self.name = name
                self.parent = parent
                parent.children.append(self)
        return Child(name)



parent = Parent('Bar')

child1 = parent.Child('Foo')
child2 = parent.Child('World')

print(
    # Getting its first childs name
    child1.name, # From itself
    parent.children[0].name, # From its parent
    # Also works with the second child
    child2.name,
    parent.children[1].name,
    # Go nuts if you want
    child2.parent.children[0].name,
    child1.parent.children[1].name
)

print(
    # Getting the parents name
    parent.name, # From itself
    child1.parent.name, # From its children
    child2.parent.name,
    # Go nuts again if you want
    parent.children[0].parent.name,
    parent.children[1].parent.name,
    # Or insane
    child2.parent.children[0].parent.children[1].parent.name,
    child1.parent.children[1].parent.children[0].parent.name
)


# Second parent? No problem
parent2 = Parent('John')
child3 = parent2.Child('Doe')
child4 = parent2.Child('Appleseed')

print(
    child3.name, parent2.children[0].name,
    child4.name, parent2.children[1].name,
    parent2.name # ....
)

Çıktı:

Foo Foo World World Foo World
Bar Bar Bar Bar Bar Bar Bar
Doe Doe Appleseed Appleseed John

Yine, harika bir cevap, size mikrofon!


-2

Çok basit:

Giriş:

class A:
    def __init__(self):
        pass

    def func1(self):
        print('class A func1')

    class B:
        def __init__(self):
            a1 = A()
            a1.func1()

        def func1(self):
            print('class B func1')

b = A.B()
b.func1()

Çıktı

A sınıfı func1

B sınıfı func1


3
Hmm, soruyu not edin ve kelimeler dış sınıfa erişiyor . Mutlu, oldukça eğlenceli bir tesadüfle, hem dış hem de iç sınıflarınızın bir yöntemi var func1. Ve aynı derecede eğlenceli bir şekilde A, kurucusunun içini yaratırsınız Bki bu kesinlikle o özelliğin dış sınıf Anesnesi DEĞİLDİR B.
mike kemirgen
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.