중첩 클래스의 범위?


116

파이썬에서 중첩 된 클래스의 범위를 이해하려고합니다. 다음은 내 예제 코드입니다.

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

클래스 생성이 완료되지 않고 오류가 발생합니다.

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

시도 inner_var = Outerclass.outer_var는 작동하지 않습니다. 나는 얻다:

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

나는 정적에 액세스하려고 outer_var에서을 InnerClass.

이를 수행하는 방법이 있습니까?

답변:


105
class Outer(object):
    outer_var = 1

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

이것은 유사한 것들이 다른 언어에서 작동하는 것과 완전히 같지 않으며,에 대한 액세스 범위를 지정하는 대신 전역 조회를 사용합니다 outer_var. (이름 Outer이 바인딩 된 개체를 변경하면 이 코드는 다음에 실행될 때 해당 개체를 사용합니다.)

대신 모든 Inner객체가 Outer왜냐하면 outer_var실제로 인스턴스 속성 이기 때문에에 대한 참조를 갖기 를 원하는 경우 :

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

중첩 클래스는 Python에서 다소 드물며 클래스 간의 특별한 관계를 자동으로 암시하지 않습니다. 중첩하지 않는 것이 좋습니다. ( 원하는 경우 클래스 속성을 Outer로 설정할 수 있습니다 Inner.)


2
답변이 작동하는 파이썬 버전을 추가하는 것이 도움이 될 수 있습니다.
AJ.

1
2.6 / 2.x를 염두에두고이 글을 작성했지만, 3.x에서 똑같이 작동하지 않는 것은 아무것도 보이지 않습니다.

"(Outer라는 이름이 바인딩 된 객체를 변경하면이 코드는 다음에 실행될 때 해당 객체를 사용합니다.)"라는 부분에서 의미하는 바를 잘 이해하지 못합니다. 이해하도록 도와 주시겠습니까?
batbrat 2014 년

1
@batbrat Outer당신이 할 때마다 참조 가 새로 조회된다는 것을 의미합니다 Inner.inner_var. 따라서 이름 Outer을 새 개체에 Inner.inner_var다시 바인딩하면 에서 새 개체를 반환하기 시작합니다.
Felipe 2014

45

나는 당신이 간단히 할 수 있다고 생각합니다.

class OuterClass:
    outer_var = 1

    class InnerClass:
        pass
    InnerClass.inner_var = outer_var

발생한 문제는 다음과 같습니다.

블록은 단위로 실행되는 Python 프로그램 텍스트의 일부입니다. 다음은 블록입니다 : 모듈, 함수 본문 및 클래스 정의.
(...)
범위는 블록 내에서 이름의 가시성을 정의합니다.
(...)
클래스 블록에 정의 된 이름의 범위는 클래스 블록으로 제한됩니다. 메소드의 코드 블록으로 확장되지 않습니다. 여기에는 함수 범위를 사용하여 구현되기 때문에 생성기 표현식이 포함됩니다. 이것은 다음이 실패 함을 의미합니다.

   class A:  

       a = 42  

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

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

위의 의미 :
함수 본문은 코드 블록이고 메서드는 함수입니다. 그러면 클래스 정의에있는 함수 본문에서 정의 된 이름이 함수 본문으로 확장되지 않습니다.

귀하의 경우에 이것을 의역 :
클래스 정의는 코드 블록이며 외부 클래스 정의에 존재하는 내부 클래스 정의에서 정의 된 이름은 내부 클래스 정의로 확장되지 않습니다.


2
놀랄 만한. "글로벌 이름 'a'가 정의되지 않았습니다."라고 주장하는 예제가 실패합니다. 그러나 목록 이해력을 대체하면 [a + i for i in range(10)]Ab가 예상 목록에 성공적으로 바인딩됩니다 [42..51].
George

6
@George 클래스 A 의 예제 는 내 것이 아니라 내가 링크를 준 Python 공식 문서에서 가져온 것입니다. 이 예제는 실패하고 그 실패는이 예제에서 보여주고 자하는 것입니다. 사실 list(a + i for i in range(10))list((a + i for i in range(10)))즉 말을하는 것입니다 list(a_generator). 그들은 생성기 가 기능의 범위와 유사한 범위로 구현 되었다고 말합니다 .
eyquem

@George 나를 위해 그것은 함수가 모듈이나 클래스에 있는지에 따라 다르게 작동한다는 것을 의미합니다. 첫 번째 경우 함수는 외부로 나가 자유 식별자에 바인딩 된 개체를 찾습니다. 두 번째 경우, 함수, 즉 메서드라고하는 것은 몸 밖으로 나가지 않습니다. 모듈의 함수와 클래스의 메서드는 실제로 두 종류의 객체입니다. 메서드는 단순히 클래스의 함수가 아닙니다. 그게 내 생각입니다.
eyquem

5
@George : FWIW, list(...)호출도 이해도 Python 3에서 작동하지 않습니다. Py3에 대한 문서 도이를 반영하여 약간 다릅니다. 이제 " 클래스 블록에 정의 된 이름의 범위는 클래스 블록으로 제한됩니다. 메서드의 코드 블록으로 확장되지 않습니다. 여기에는 함수 범위를 사용하여 구현되기 때문에 이해 및 생성기 표현식 이 포함됩니다 . "(강조 ).
마티

왜 list (Aa + i for i in range (10))가 작동하지 않는지 궁금합니다. 여기서 A로 a를 수정했습니다. A는 글로벌 이름 일 수 있습니다.
gaoxinge

20

중첩 클래스를 사용하지 않는 것이 더 나을 수 있습니다. 중첩해야하는 경우 다음을 시도하십시오.

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

또는 중첩하기 전에 두 클래스를 모두 선언하십시오.

class OuterClass:
    outer_var = 1

class InnerClass:
    inner_var = OuterClass.outer_var

OuterClass.InnerClass = InnerClass

(그 후에 del InnerClass필요한 경우 할 수 있습니다 .)


3

가장 쉬운 솔루션 :

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

명시 적이어야하지만 많은 노력이 필요하지 않습니다.


NameError : 'OuterClass'이름이 정의되지 않았습니다
.--

3
요점은 Outer 클래스 범위에서 액세스하는 것입니다. 또한 Outer 내부의 정적 범위에서 이것을 호출하면 유사한 오류가 발생합니다. 이 게시물을 삭제하는 것이 좋습니다
Mr_and_Mrs_D

1

파이썬에서 변경 가능한 객체는 참조로 전달되므로 외부 클래스의 참조를 내부 클래스에 전달할 수 있습니다.

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
여기에 참조주기가 있으며 일부 시나리오에서는이 클래스의 인스턴스가 해제되지 않습니다. cPython을 사용하는 한 가지 예는 __del__ 메서드를 정의한 경우 가비지 수집기가 참조주기를 처리 할 수 ​​없으며 개체는 gc.garbage. 위의 코드는있는 그대로 문제가되지 않습니다. 이를 처리하는 방법은 약한 참조를 사용하는 것입니다 . weakref (2.7) 또는 weakref (3.5)
guyarad

0

모든 설명은 Python 문서 The Python Tutorial 에서 찾을 수 있습니다.

첫 번째 오류 <type 'exceptions.NameError'>: name 'outer_var' is not defined. 설명은 다음과 같습니다.

메서드 내에서 데이터 속성 (또는 다른 메서드!)을 참조하는 약어는 없습니다. 이것이 실제로 메서드의 가독성을 증가 시킨다는 것을 발견했습니다. 메서드를 살펴볼 때 지역 변수와 인스턴스 변수를 혼동 할 가능성이 없습니다.

The Python Tutorial 9.4 에서 인용

두 번째 오류 <type 'exceptions.NameError'>: name 'OuterClass' is not defined

클래스 정의가 정상적으로 종료되면 (끝까지) 클래스 객체가 생성됩니다.

Python Tutorial 9.3.1 에서 인용

그래서 당신이 시도했을 때 inner_var = Outerclass.outer_var, Quterclass아직 생성되지 않은 것입니다.name 'OuterClass' is not defined

첫 번째 오류에 대한 더 자세하지만 지루한 설명 :

비록 클래스가 둘러싸는 함수의 범위에 접근 할 수 있지만, 그것들은 클래스 내에 중첩 된 코드에 대한 둘러싸는 범위의 역할을하지 않습니다. 파이썬은 참조 된 이름에 대해 둘러싸는 함수를 검색하지만 둘러싸는 클래스는 검색하지 않습니다. 즉, 클래스는 로컬 범위이고 둘러싸는 로컬 범위에 액세스 할 수 있지만 추가 중첩 코드에 대한 둘러싸는 로컬 범위 역할을하지 않습니다.

Learning.Python (5th) .Mark.Lutz 에서 인용

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.