Чи є користь визначити клас всередині іншого класу в Python?


106

Про що я тут говорю - це вкладені класи. По суті, у мене є два класи, які я моделюю. Клас 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())

Відповіді:


121

Ви можете зробити це, коли "внутрішній" клас є одноразовим, який ніколи не буде використовуватися поза визначенням зовнішнього класу. Наприклад, використовувати метаклас, це іноді зручно зробити

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

замість того, щоб визначати метаклас окремо, якщо ви використовуєте його лише один раз.

Єдиний раз, коли я використовував такі вкладені класи, я використовував зовнішній клас лише як простір імен, щоб згрупувати купу тісно пов'язаних класів разом:

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

    class cls2(object):
       ...

Потім з іншого модуля ви можете імпортувати групу і посилатися на них як Group.cls1, Group.cls2 і т.д.


13
Я-можу стверджувати, що у другому випадку вам, безумовно, краще послужити, використовуючи модуль або пакет, призначений для простору імен.
Меттью Тревор

5
Це фантастична відповідь. Простий, до речі, і згадує альтернативний метод використання модулів. +1
CornSmith

5
Що стосується запису, для тих, хто вперто відмовляється або не може організувати свій код в ієрархічний пакет і належні модулі, використання класів як простору імен часом може бути краще, ніж нічого не використовувати.
Acumenus

19

Я не знаю Python, але ваше запитання здається дуже загальним. Ігноруйте мене, якщо це специфічно для Python.

Класове введення - це все про сферу застосування. Якщо ви думаєте, що один клас матиме сенс лише в контексті іншого, то перший, мабуть, хороший кандидат, щоб стати вкладеним класом.

Це загальна модель зробити класи помічників приватними, вкладеними класами.


9
Зауважимо для запису, що концепція privateкласів не так сильно застосовується в Python, але загальна сфера використання все одно безумовно застосовується.
Acumenus

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()буде побудовано a, foo.barколи об'єкт, що будується, є фактично fooоб'єктом, а foo2.barоб'єкт, коли об'єкт, що будується, є фактично foo2об'єктом.

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


6

Користуватися цим дійсно немає, за винятком випадків, коли ви маєте справу з метакласами.

клас: люкс насправді не те, що ви думаєте, що це таке. Це дивна сфера, і вона робить дивні речі. Це насправді навіть не робить клас! Це лише спосіб збору деяких змінних - назва класу, бази, невеликий словник атрибутів та метаклас.

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

Що ви можете отримати, возившись з метакласами, і, власне, вклавши класи у ваших стандартних класах, складніше читати код, важче зрозуміти код та непарні помилки, які дуже важко зрозуміти, не будучи глибоко знайомими з тим, чому "клас" Обсяг повністю відрізняється від будь-якої іншої області python.


3
Не погоджуючись: Класи виключень внесення дуже корисні, оскільки користувачі вашого класу можуть перевіряти ваші користувальницькі винятки, не потребуючи жодного брудного імпорту. Напр.except myInstanceObj.CustomError, e:
RobM

2
@Jerub, чому це погано?
Роберт Сімер

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

Ні, композиція не означає гніздування. Було б сенс мати вкладений клас, якщо ви хочете більше приховати його в просторі імен зовнішнього класу.

У будь-якому випадку, я не бачу у вашому випадку практичного використання для гніздування. Це ускладнить читання (розуміння) коду, а також збільшить відступ, що зробить лінії коротшими та більш схильними до розщеплення.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.