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


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 클래스 ( Innersome_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.