Есть ли преимущество в определении класса внутри другого класса в Python? - PullRequest
88 голосов
/ 17 сентября 2008

Я говорю здесь о вложенных классах. По сути, у меня есть два класса, которые я моделирую. Класс DownloadManager и класс DownloadThread. Очевидная концепция ООП здесь - это композиция. Однако композиция не обязательно означает вложение, верно?

У меня есть код, который выглядит примерно так:

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

Ответы [ 6 ]

106 голосов
/ 17 сентября 2008

Возможно, вы захотите сделать это, когда «внутренний» класс является одноразовым, который никогда не будет использоваться вне определения внешнего класса. Например, чтобы использовать метакласс, иногда удобно сделать

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

вместо отдельного определения метакласса, если вы используете его только один раз.

Единственный раз, когда я использовал подобные вложенные классы, я использовал внешний класс только в качестве пространства имен для группировки нескольких тесно связанных классов:

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

    class cls2(object):
       ...

Затем из другого модуля вы можете импортировать Group и ссылаться на них как Group.cls1, Group.cls2 и т. Д. Однако можно утверждать, что вы можете сделать то же самое (возможно, менее запутанно), используя модуль.

19 голосов
/ 17 сентября 2008

Я не знаю Python, но ваш вопрос кажется очень общим. Проигнорируйте меня, если это определенно для Питона.

Вложение классов - все о сфере действия. Если вы думаете, что один класс будет иметь смысл только в контексте другого, то первый, вероятно, является хорошим кандидатом на то, чтобы стать вложенным классом.

Это распространенный шаблон создания вспомогательных классов как частных, вложенных классов.

6 голосов
/ 17 сентября 2008

В этом нет никакой пользы, кроме случаев, когда вы имеете дело с метаклассами.

класс: набор на самом деле не то, что вы думаете. Это странная сфера, и она делает странные вещи. Это действительно даже не делает класс! Это просто способ собрать некоторые переменные - имя класса, базы, небольшой словарь атрибутов и метакласс.

Имя, словарь и базы передаются в функцию, которая является метаклассом, а затем присваиваются переменной 'name' в области видимости класса: suite.

То, что вы можете получить, связываясь с метаклассами и даже вкладывая классы в стандартные классы, сложнее для чтения, сложнее для понимания кода и странных ошибок, которые ужасно трудно понять, не зная, почему Область видимости 'class' полностью отличается от любой другой области видимости Python.

5 голосов
/ 17 сентября 2008

Вы можете использовать класс в качестве генератора классов. Как (в некоторых не манжете код:)

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

Я чувствую, что когда вам понадобится эта функциональность, она будет вам очень понятна. Если вам не нужно делать что-то подобное, вероятно, это не очень хороший вариант использования.

4 голосов
/ 13 апреля 2018

Существует еще одно применение для вложенного класса, когда требуется создать унаследованные классы, расширенные функциональные возможности которых заключены в конкретный вложенный класс.

См. Этот пример:

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, когда конструируемый объект на самом деле является foo объектом, и foo2.bar, когда конструируемый объект на самом деле foo2 объект.

Если бы класс bar был определен за пределами класса foo, а также его унаследованной версии (которая, например, называлась бы bar2), то определение нового класса foo2 было бы гораздо более болезненным потому что конструктору foo2 необходимо заменить первую строку на self.a = bar2(), что подразумевает перезапись всего конструктора.

1 голос
/ 17 сентября 2008

Нет, композиция не означает вложение. Было бы целесообразно иметь вложенный класс, если вы хотите скрыть его больше в пространстве имен внешнего класса.

Во всяком случае, я не вижу практического использования для вложения в вашем случае. Это затруднило бы чтение (понимание) кода и увеличило бы отступ, что сделало бы строки более короткими и более склонными к расщеплению.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...