Есть ли Python, эквивалентный частичным классам? - PullRequest
36 голосов
/ 09 марта 2012

Используя "новые" классы стилей (я в Python 3.2), есть ли способ разделить класс на несколько файлов? У меня есть большой класс (который действительно должен быть единственным классом с точки зрения объектно-ориентированного проектирования, с учетом связывания и т. Д., Но было бы неплохо разделить несколько файлов только для простоты редактирования класса.

Ответы [ 8 ]

38 голосов
/ 09 марта 2012

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

Тем не менее, есть несколько способов разбить класс на несколько файлов.Python позволяет использовать папку в качестве модуля, поместив в нее __init__.py, которая затем может импортировать объекты из других файлов.Мы будем использовать эту возможность в каждом решении.Сначала создайте папку с именем, скажем, bigclass.

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

    class Bigclass(object):
    
        from classdef1 import foo, bar, baz, quux
        from classdef2 import thing1, thing2
        from classdef3 import magic, moremagic
        # unfortunately, "from classdefn import *" is an error or warning
    
        num = 42   # add more members here if you like
    

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

  2. Вы можете использовать множественное наследование для объединения различных частей вашего класса.В ваших отдельных модулях вы бы написали определение класса для Bigclass с частями класса.Тогда в вашей __init__.py напишите:

    import classdef1, classdef2, classdef3
    
    class Bigclass(classdef1.Bigclass, classdef2.Bigclass, classdef3.Bigclass):
        num = 42   # add more members if desired
    
  3. Если множественное наследование становится проблемой, вы можете использовать одиночное наследование: просто пусть каждый класс наследует от другогоодин в цепочке моды.Предполагая, что вы не определяете ничего более чем в одном классе, порядок не имеет значения.Например, classdef2.py будет выглядеть так:

    import classdef1
    class Bigclass(classdef1.Bigclass):
         # more member defs here
    

    classdef3 будет импортировать Bigclass из classdef2 и добавлять к нему, и так далее.Ваш __init__.py будет просто импортировать последний:

    from classdef42 import Bigclass
    

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

Чтобы использовать класс в любом из этих сценариев, вы можете просто импортировать его, используя имя папки в качестве имени модуля: from bigclass import Bigclass

7 голосов
/ 09 марта 2012

Определения классов, содержащие сотни строк, встречаются «в дикой природе» (я видел некоторые из них в популярных основанных на Python фреймворках с открытым исходным кодом), но я считаю, что если вы задумаетесь над тем, что делают методы, то можно будетуменьшить длину большинства классов до управляемой точки.Некоторые примеры:

  • Найдите места, где один и тот же код встречается более одного раза.Разбейте этот код на его собственный метод и вызывайте его из каждого места с аргументами.
  • «Приватные» методы, которые не используют ни одно из состояний объекта, могут быть выведены из класса как автономные функции.
  • Методы, которые следует вызывать только при определенных условиях, могут указывать на необходимость помещения этих методов в подкласс.

Чтобы непосредственно ответить на ваш вопрос, можно разбить определениекласс.Один из способов заключается в том, чтобы «обезопасить» класс, определив его, а затем добавив в него внешние функции в качестве методов.Другой способ - использовать встроенную функцию type для создания класса «вручную», предоставляя его имя, любые базовые классы, а также его методы и атрибуты в словаре.Но я не рекомендую делать это только потому, что определение было бы длинным в противном случае.По моему мнению, такое лекарство хуже болезни.

6 голосов
/ 09 марта 2012

Я раньше играл с чем-то похожим.Мой сценарий использования представлял собой иерархию классов узлов в абстрактном синтаксическом дереве, и затем я хотел поместить все, например, prettyprinting функции, в отдельный файл prettyprint.py, но все еще иметь их в качестве методов в классах.попытался использовать декоратор, который помещает декорированную функцию в качестве атрибута в указанный класс.В моем случае это будет означать, что prettyprint.py содержит множество def prettyprint(self), украшенных различными @inclass(...)

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

def inclass(kls):
    """
    Decorator that adds the decorated function
    as a method in specified class
    """
    def _(func):
        setattr(kls,func.__name__, func)
        return func
    return _

## exampe usage
class C:
    def __init__(self, d):
        self.d = d

# this would be in a separate file.
@inclass(C)
def meth(self, a):
    """Some method"""
    print "attribute: %s - argument: %s" % (self.d, a)

i = C(10)
print i.meth.__doc__
i.meth(20)
5 голосов
/ 12 июня 2013

Вы можете сделать это с помощью декораторов следующим образом:

class Car(object):

    def start(self):
    print 'Car has started'


def extends(klass):
    def decorator(func):
      setattr(klass, func.__name__, func)
      return func
    return decorator

#this can go in a different module/file
@extends(Car)
def do_start(self):
    self.start()


#so can this
car = Car()
car.do_start()

#=> Car has started
3 голосов
/ 09 марта 2012

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

Похоже, есть и несколько других способов реализовать это самостоятельно.

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

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

a.py:

def AFunc( self, something ):
    # Do something
    pass

b.py:

def BFunc( self, something ):
    # Do something else
    pass

c.py:

import a, b

class C:
    AFunc = a.AFunc
    BFunc = b.BFunc

Вы могли бы даже пойти настолько далеко, чтобы автоматизировать этот процесс, если вы действительно этого хотите - пройтись по всем функциям, предоставляемым модулями a и b, а затем добавить их в качестве атрибутов на C. Хотя это может быть полным перебором.

Могут быть и другие (возможно, лучшие) способы сделать это, но вот те 2, которые всплыли в уме.

1 голос
/ 09 августа 2012

Я столкнулся с той же ситуацией - я хочу сдвинуть свой класс на 2 файла. причина в том, что я хочу часть 1 для макета GUI, только макет и другой файл сохраняет все функции. как частичный класс c #. один для XAML и другой для функций.

0 голосов
/ 09 марта 2012

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

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

  • Преобразуйте дублированный код в меньшее количество более общих примитивов
  • Определите базовый класс и добавьте в него подклассы, как предлагает Кароли Хорват в комментариях (это наиболее близкая вещь к «частичным классам», о которых вы просите, я бы одобрил)
  • Определите несколько отдельных классов для инкапсуляции различных частей этого функциональные возможности класса, и составляют этот класс экземпляров тех меньшие.
0 голосов
/ 09 марта 2012

Сначала я хотел бы сказать, что что-то настолько сложное, что, вероятно, не очень хорошая идея, чтобы упростить поиск вашего места в классе - было бы лучше добавить комментарии, выделить разделы и т. Д. Однако я вижу два способаможет сделать это:

  1. Запишите класс в несколько файлов, затем прочитайте их как текст, объедините их и exec полученную строку.

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

Самым простым будет первый вариант.

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