[python] 내부 클래스에서 외부 클래스에 액세스하는 방법은 무엇입니까?

이런 상황이 …

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

Outer클래스에서 클래스의 메서드에 액세스하려면 어떻게 Inner해야합니까?



답변

중첩 클래스의 메서드는 외부 클래스의 인스턴스 속성에 직접 액세스 할 수 없습니다.

내부 클래스의 인스턴스를 만든 경우에도 외부 클래스의 인스턴스가 반드시 존재하는 것은 아닙니다.

실제로 중첩이 내부 클래스와 외부 클래스 간의 특정 관계를 의미하지 않기 때문에 중첩 클래스를 사용하지 않는 것이 좋습니다.


답변

내부 클래스 인스턴스에서 Outer의 클래스 인스턴스에 액세스하려고합니다. 따라서 팩토리 메서드를 사용하여 Inner 인스턴스를 빌드하고 Outer 인스턴스를 전달하십시오.

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()


답변

어쩌면 나는 화가 났지만 이것은 실제로 매우 쉬운 것처럼 보입니다-문제는 외부 클래스의 메서드 내부에 내부 클래스를 만드는 것입니다 …

def do_sthg( self ):
    ...

def messAround( self ):

    outerClassSelf = self

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

게다가 … “self”는 관례에 의해서만 사용되므로 다음과 같이 할 수 있습니다.

def do_sthg( self ):
    ...

def messAround( outerClassSelf ):

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

그런 다음 외부 클래스 외부에서이 내부 클래스를 만들 수 없다는 것이 반대 될 수 있지만 이것은 사실이 아닙니다.

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

그런 다음 어딘가에서 멀리 떨어져 있습니다.

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

보트를 약간 밀어 내고이 내부 클래스를 확장 (NB가 작동하도록하려면 mooble의 클래스 서명을 “class mooble (object)”로 변경해야 함)

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()

나중

mrh1997은이 기술을 사용하여 전달 된 내부 클래스의 일반적이지 않은 상속에 대해 흥미로운 점을 제기했습니다. 그러나 해결책은 매우 간단합니다.

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 ))


답변

이와 같은 중첩 클래스 대신 상속을 사용한다는 의미입니까? 당신이하는 일은 파이썬에서 의미가 없습니다.

내부 클래스의 메서드 내에서 Outer참조하여의 some_method에 액세스 할 수 Outer.some_method있지만 예상대로 작동하지 않습니다. 예를 들어 다음을 시도하면 :

class Outer(object):

    def some_method(self):
        # do something

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

Inner객체를 초기화 할 때 TypeError가 발생 합니다 . 첫 번째 인수로 인스턴스 Outer.some_method를받을 것으로 예상하기 때문 Outer입니다. (위의 예에서는 기본적 some_method으로의 클래스 메서드 로 호출하려고합니다 Outer.)


답변

메타 클래스를 사용하여 외부 클래스에 쉽게 액세스 할 수 있습니다. 외부 클래스를 만든 후 모든 클래스에 대한 속성 dict를 확인하고 (또는 필요한 모든 로직을 적용합니다.

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!

이 방법을 사용하면 서로간에 두 클래스를 쉽게 바인딩하고 참조 할 수 있습니다.


답변

나는 사용하는 일부 파이썬 코드를 생성 한 외부 클래스를 자사에서 내부 클래스 서로 좋은 아이디어를 기반으로, 답변 이 질문에 대한. 짧고 간단하며 이해하기 쉽다고 생각합니다.

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}

주 코드, “프로덕션 준비”(코멘트 없음 등). 꺾쇠 괄호 안에있는 모든 값 (예 :)을 <x>원하는 값 으로 바꿔야합니다 .

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>}

이 방법의 작동 방식에 대한 설명 (기본 단계) :

  1. (함수 내부에서 실행되는 코드에서) 상위 수준 클래스에 대한 참조 인 _subclass_container변수에 액세스하기위한 래퍼 역할을 하는 함수를 만듭니다 self.

    1. 이 함수 _parent_class의 변수 self에 대한 참조 이며의 하위 클래스가 _subclass_container액세스 할 수 있는 이름 이 지정된 변수를 만듭니다 ( 서브 클래스의 다른 self변수 와 이름 충돌 방지 ).

    2. _subclass_container함수를 호출하는 코드가 내부의 하위 클래스에 액세스 할 수 있도록 하위 클래스 / 하위 클래스를 사전 / 목록으로 반환합니다 .

  2. __init__상위 수준 클래스 내부 의 함수 (또는 필요한 경우)에서 반환 된 하위 클래스를 함수 _subclass_container에서 변수로 subclasses받습니다.

  3. subclasses변수에 저장된 하위 클래스를 상위 수준 클래스의 속성에 할당합니다 .

시나리오를 더 쉽게 만드는 몇 가지 팁 :

상위 레벨 클래스에 하위 클래스를 할당하는 코드를보다 쉽게 ​​복사하고 기능이 변경된 상위 레벨 클래스에서 파생 된 클래스에서 사용되도록합니다 __init__ .

기본 코드에서 12 행 앞에 삽입하십시오.

def _subclass_init(self):

그런 다음이 기능 라인 5-6 (기본 코드)에 삽입하고 4-7 라인을 다음 코드로 바꿉니다.

self._subclass_init(self)

하위 클래스가 많거나 알 수없는 수량이있을 때 상위 클래스에 하위 클래스를 할당 할 수 있도록합니다.

6 행을 다음 코드로 바꿉니다.

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

이 솔루션이 유용하고 상위 레벨 클래스 이름을 가져 오는 것이 불가능한 시나리오의 예 :

“a”( class a:) 라는 클래스 가 생성됩니다. 액세스해야하는 하위 클래스 (부모)가 있습니다. 하나의 서브 클래스는 “x1″이라고합니다. 이 서브 클래스에서 코드 a.run_func()가 실행됩니다.

그런 다음 “a”( ) 클래스 에서 파생 된 “b”라는 다른 클래스가 생성 class b(a):됩니다. 그 후 일부 코드가 실행됩니다 b.x1()(파생 된 하위 클래스 인 b의 하위 함수 “x1″호출). 이 함수는 “a”클래스에 정의 된 함수가 참조하도록 설정되어 있으므로 부모 “b”의 “run_func”함수가 아니라a.run_func() “a”클래스의 “run_func”함수를 호출하여 실행됩니다 . 클래스 “a”의 기능에 대한 것입니다.

이로 인해 문제가 발생할 수 있으며 (예 : 함수 a.run_func가 삭제 된 경우) 클래스에서 코드를 다시 작성하지 않는 유일한 해결책 은 클래스 “a”에서 파생 된 모든 클래스에 대해 업데이트 된 코드로 a.x1하위 클래스를 재정의 x1하는 것입니다. 이는 분명히 어렵고 가치가 없습니다. 그것.


답변

내가 발견 .

질문에 맞게 조정했습니다.

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)

나는 당신이 어떻게 든 이것 또는 무언가에 대한 데코레이터를 작성할 수 있다고 확신합니다

관련 : 파이썬 내부 클래스의 목적은 무엇입니까?