Python에서 다른 클래스 내부에 클래스를 정의하면 이점이 있습니까?


106

여기서 제가 말하는 것은 중첩 클래스입니다. 기본적으로 모델링중인 두 개의 클래스가 있습니다. DownloadManager 클래스 및 DownloadThread 클래스. 여기서 명백한 OOP 개념은 구성입니다. 그러나 컴포지션이 반드시 중첩을 의미하는 것은 아닙니다.

다음과 같은 코드가 있습니다.

class DownloadThread:
    def foo(self):
        pass

class DownloadManager():
    def __init__(self):
        dwld_threads = []
    def create_new_thread():
        dwld_threads.append(DownloadThread())

하지만 지금은 중첩이 더 나은 상황이 있는지 궁금합니다. 다음과 같은 것 :

class DownloadManager():
    class DownloadThread:
        def foo(self):
            pass
    def __init__(self):
        dwld_threads = []
    def create_new_thread():
        dwld_threads.append(DownloadManager.DownloadThread())

답변:


121

"내부"클래스가 일회성이며 외부 클래스 의 정의 외부에서 사용되지 않을 때이 작업을 수행 할 수 있습니다 . 예를 들어 메타 클래스를 사용하려면 때때로 다음을 수행하는 것이 편리합니다.

class Foo(object):
    class __metaclass__(type):
        .... 

메타 클래스를 별도로 정의하는 대신 한 번만 사용하는 경우.

이와 같은 중첩 클래스를 사용한 유일한 다른 경우에는 외부 클래스를 네임 스페이스로만 사용하여 밀접하게 관련된 클래스를 그룹화했습니다.

class Group(object):
    class cls1(object):
       ...

    class cls2(object):
       ...

그런 다음 다른 모듈에서 Group을 가져 와서 Group.cls1, Group.cls2 등으로 참조 할 수 있습니다. 그러나 모듈을 사용하여 정확히 동일한 작업을 수행 할 수 있다고 주장 할 수 있습니다 (아마도 덜 혼란스러운 방식으로).


13
두 번째 경우에는 네임 스페이스로 설계된 모듈이나 패키지를 사용하여 더 나은 서비스를받을 수 있다고 주장합니다.
Matthew Trevor

5
이것은 환상적인 대답입니다. 요점은 간단하며 모듈을 사용하는 대체 방법을 언급합니다. +1
CornSmith 2013 년

5
기록을 위해, 코드를 계층 적 패키지와 적절한 모듈로 고집스럽게 거부하거나 구성 할 수없는 사람들에게 클래스를 네임 스페이스로 사용하는 것이 때때로 아무것도 사용하지 않는 것보다 낫습니다.
Acumenus 2014

19

저는 Python을 모르지만 귀하의 질문은 매우 일반적입니다. Python에만 국한된 경우 무시하십시오.

클래스 중첩은 모두 범위에 관한 것입니다. 한 클래스가 다른 클래스의 컨텍스트에서만 의미가 있다고 생각한다면 전자는 아마도 중첩 클래스가되기에 좋은 후보 일 것입니다.

헬퍼 클래스를 개인 중첩 클래스로 만드는 것은 일반적인 패턴입니다.


9
기록을 위해 private클래스 의 개념은 Python에서 그다지 적용되지 않지만 암시 적 사용 범위는 여전히 확실히 적용됩니다.
Acumenus 2014

7

향상된 기능이 특정 중첩 클래스에 캡슐화 된 상속 된 클래스를 생성하려는 경우 중첩 클래스에 대한 또 다른 사용법이 있습니다.

이 예를 참조하십시오.

class foo:

  class bar:
    ...  # functionalities of a specific sub-feature of foo

  def __init__(self):
    self.a = self.bar()
    ...

  ...  # other features of foo


class foo2(foo):

  class bar(foo.bar):
    ... # enhanced functionalities for this specific feature

  def __init__(self):
    foo.__init__(self)

의 생성자 foo에서 라인 self.a = self.bar()은 생성되는 foo.bar객체가 실제로 객체 인 경우 fooa를 foo2.bar생성하고 생성중인 객체가 실제로 객체 인 경우 객체를 생성 foo2합니다.

클래스 barfoo상속 된 버전 ( bar2예를 들어 호출 됨)뿐만 아니라 클래스 외부에서 정의 된 경우 새 클래스를 정의하는 foo2것이 훨씬 더 고통 스러울 foo2것입니다 self.a = bar2(). 이는 전체 생성자를 다시 작성하는 것을 의미합니다.


6

메타 클래스를 처리하는 경우를 제외하고는이 작업을 수행하는 데 아무런 이점이 없습니다.

클래스 : 스위트 룸은 실제로 당신이 생각하는 것과 다릅니다. 이상한 범위이고 이상한 일을합니다. 정말 수업도 안 해요! 클래스 이름,베이스, 속성 사전 및 메타 클래스와 같은 일부 변수를 수집하는 방법 일뿐입니다.

이름, 딕셔너리 및베이스는 모두 메타 클래스 인 함수에 전달 된 다음 class : suite가 있던 범위의 변수 'name'에 할당됩니다.

메타 클래스를 엉망으로 만들고 실제로 스톡 표준 클래스 내에 클래스를 중첩하여 얻을 수있는 것은 코드를 읽기 어렵고, 코드를 이해하기 어렵고, 왜 '클래스'인지 잘 알지 못하면 이해하기 매우 어려운 이상한 오류입니다. 범위는 다른 파이썬 범위와 완전히 다릅니다.


3
동의하지 않음 : 클래스의 사용자가 복잡한 가져 오기를 수행 할 필요없이 사용자 정의 예외를 확인할 수 있기 때문에 예외 클래스를 중첩하는 것은 매우 유용합니다. 예except myInstanceObj.CustomError, e:
RobM

2
@Jerub, 왜 나쁜가요?
Robert Siemer

5

클래스 생성기로 클래스를 사용할 수 있습니다. 처럼 (커프 코드에서 일부 :)

class gen(object):
    class base_1(object): pass
    ...
    class base_n(object): pass

    def __init__(self, ...):
        ...
    def mk_cls(self, ..., type):
        '''makes a class based on the type passed in, the current state of
           the class, and the other inputs to the method'''

이 기능이 필요할 때 매우 분명해 질 것 같습니다. 비슷한 일을 할 필요가 없다면 좋은 사용 사례가 아닐 것입니다.


1

아니요, 구성은 중첩을 의미하지 않습니다. 외부 클래스의 네임 스페이스에서 더 숨기려면 중첩 된 클래스를 갖는 것이 좋습니다.

어쨌든, 귀하의 경우 중첩에 대한 실질적인 사용이 보이지 않습니다. 코드를 읽기 (이해) 어렵게 만들며 들여 쓰기를 증가시켜 줄이 더 짧아지고 분할되기 쉽습니다.

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