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


102

이런 상황이 ...

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해야합니까?


왜 이런 짓을하는? 단순한 동료 관계의 문제점은 무엇입니까? 당신은 무언가를 "은폐"하려고합니까?
— S.Lott

1
이 시나리오의 예로는 일반적으로 외부 클래스에 액세스해야 하지만 첫 번째 클래스에서 파생 된 다른 클래스 (최상위 수준)를 만들어야 하는 하위 클래스가있는 클래스가있을 수 있습니다 . 이 경우, 두 번째 클래스의 하위 클래스를 사용하여 부모를 액세스하려고 할 것입니다 self.<original_parent_name>및 원래 클래스, 그들은에서 하위 클래스임을되지 새 클래스를 얻을 . 나는 이것을 읽는 사람들이이 어려운 시나리오를 시각화하고 이와 같은 질문의 요점을 볼 수 있기를 바랍니다.
— Edward

1
stackoverflow.com/questions/2278426 과 중복되며 좋은 대답이 있습니다.
— xmedeko

답변:


68

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

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

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


1
흠, Python은 Java / C ++보다 더 가볍습니다 ... 아래 내 대답을 참조하십시오. 우리가 보통처럼 머리카락을 쪼개고 있다면, "메서드 내의 중첩 클래스"가 내부 클래스로 간주되는지 여부를 실제로 말할 수 없습니다. 그러나이 시점에서 나는 오리 타이핑을 호출해야한다. 만약 그것이 내부 클래스가 할 수있는 모든 것을한다면 ... 파이썬적인 관점에서 보면 아마도 머리카락을 쪼개는 것에 지루해질 때일 것이다
— mike rodent

21
물론 내부 클래스는 외부 클래스와의 관계를 의미하며 일반적으로 내부 클래스 또는 조직 네임 스페이스의 암시 된 사용 범위와 관련이 있습니다.
— Acumenus

67

내부 클래스 인스턴스에서 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()

이것은 정답입니다. OP의 질문에 대한 해결책을 제공하므로 정답으로 선택해야합니다. 감사합니다!
— Daniel

29

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

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

1
이것은 나를 위해 작동합니다. 이 구조는 정확히 무엇이라고 부릅니까? 공장 기능? 폐쇄?
— nakedfanatic 2011 년

나는 그것이 무엇이라고 부르는지 모르겠습니다. 그러나 다른 포스터들이 이것을 보지 못한 이유는 아마도 파이썬의 대부분이 "자신을 포함하여 신성하지 않다는 것을 충분히 이해하지 못했기 때문"이라고 제안 할 수 있습니다. "(임의의 이름) 및 클래스-"일급 객체 "입니다. 이는 상당히 터무니없는 방식으로 조작 할 수 있음을 의미하는 것 같습니다
— mike rodent

훌륭한 일. 이 솔루션 아이디어와 약간의 생각에서 영감을 받아이 답변의 일부를 확장하여 더 많은 설명과 함께 아래에 또 다른 답변을 만들었습니다.
— Edward

1
@mikerodent 귀하의 의견 (내 답변에 대한) 때문에 이제 "커뮤니티 위키"설명을 제거하기 위해 내 답변을 편집했습니다. 당신처럼 저도 "커뮤니티 위키"답변에 대한 지식이 없기 때문에 실수를 고쳐 주셔서 감사합니다.
— Edward

@mikerodent 또한 위반은 없지만 "커뮤니티 위키"답변에 "커뮤니티 위키"태그가 없다고 생각합니다. 답의 유형일 뿐이어야합니다. 관심이 있으시면 Stack Overflow에 "커뮤니티 위키"답변에 대한 설명이있는 도움말 페이지가있을 것입니다.
— Edward

4

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

내부 클래스의 메서드 내에서 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.)


1
아마도 말이 안되는 이유는 내가 의도적으로 해키하기 때문입니다. Django의 QuerySet에 사용자 지정 메서드를 추가하려면 약간의 상용구 코드가 필요하며, 저는 상용구 코드를 템플릿 화하고 모델 코드에서 관련 부분을 작성하는 데 사용할 수있는 Python을 사용하여이를 수행하는 영리한 방법을 도출하려고했습니다.
— T. Stone

죄송합니다. 저는 Django를 모르기 때문에 상용구 코드를 템플릿 화하는 방법을 제안 할 수 없습니다.하지만 클래스 중첩을 시도 할 때 잘못된 트리를 짖는 것일 수 있습니다. 당신의 내부 클래스는 당신의 외부 클래스에서 아무것도 얻지 못합니다. Outer 내에서 중첩하는 것은 단순한 Inner가 아닌 Outer.Inner를 통해 액세스하도록 강제하는 것입니다.
— zenbot 2010 년

3

메타 클래스를 사용하여 외부 클래스에 쉽게 액세스 할 수 있습니다. 외부 클래스를 만든 후 모든 클래스에 대한 속성 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!

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


3

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

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하는 것입니다. 이는 분명히 어렵고 가치가 없습니다. 그것.


내 대답에 대한 귀하의 좋은 의견에 감사드립니다. 나는 "community wikis answer"태그에 대해 아무것도 몰랐고 이제 이것을 제거했습니다! 그래서 이것을 지적 해 주셔서 감사합니다. 미래의 독자들에게 혼란을주지 않도록 그에 따라 답변을 편집하는 것이 좋습니다!
— 마이크 설치류

3

내가 발견 이 .

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

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)

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

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


2

또 다른 가능성 :

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의 합리적 사고를 확장하면 외부 메서드가 정적 메서드 일 수 있습니다 .

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

이제 해당 라인은 실제로 호출 될 때까지 작동합니다.

위 코드의 마지막 줄은 Inner 클래스에 Outer 정적 메서드의 복제 본인 정적 메서드를 제공합니다.


이것은 두 가지 Python 기능을 활용합니다. 즉, 함수는 객체 이고 범위는 텍스트 입니다.

일반적으로 로컬 범위는 (텍스트로) 현재 함수의 로컬 이름을 참조합니다.

... 또는 우리의 경우 현재 클래스 . 따라서 Outer 클래스 ( Inner및 some_static_method) 의 정의에 "로컬"인 개체 는 해당 정의 내에서 직접 참조 될 수 있습니다.


1

파티에 늦게 몇 년 ....는 하지만에 확장 @mike rodent의 멋진 대답을, 나는 얼마나 유연한 그의 해결책이 보여주는 아래의 내 자신의 예를 제공하고, 그것이 있어야 (또는해야하는 이유 한 한 있었다) 허용 대답.

파이썬 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 # ....
)

산출:

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

다시 한 번, 멋진 대답, 마이크 소품!


-2

너무 간단합니다.

입력:

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

산출

클래스 A func1

클래스 B func1


2
흠, 질문에 유의하면 단어가 외부 클래스에 액세스 합니다. 행복하고, 아주 재미 있고, 우연의 일치로, 당신의 외부 및 내부 클래스는 모두 방법을 가지고 있습니다 func1. 그리고 똑같이 재미있는 점 A은의 생성자 내부에 생성합니다 B. 이는 A해당 특정 .NET의 외부 클래스 객체 가 절대 아닙니다 B.
— 마이크 설치류
당사 사이트를 사용함과 동시에 당사의 쿠키 정책과 개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.