2012-03-09 3 views
29

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

+0

вы можете поместить некоторые основные функции в базовом классе? –

+5

Фразы «большой класс» и «один класс с точки зрения объектно-ориентированного дизайна» не слишком хорошо сочетаются. –

+0

@ Никлас согласился, что они обычно не делают, а иногда делают. Большинство моих занятий довольно маленькие. –

ответ

33

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

Это говорит о том, что существует несколько способов разбить класс на несколько файлов. 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

+0

+1: Примерно так же, как и мой ответ, но гораздо более элегантный. – obmarg

+1

+1 Мне нравится первый из ваших альтернатив, лучший из всех упомянутых здесь до сих пор. – wberry

+0

Отметить этот ответ как решение, потому что другие ответы предполагают, что * обычно * большой класс не должен быть действительно одним классом. Но я утверждаю, что они происходят периодически, и этот ответ предлагает некоторые практические способы борьбы с ситуацией. –

2

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

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

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

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

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, а затем добавьте m в качестве атрибутов на C. Хотя это может быть полным излишеством.

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

7

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

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

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

+0

+1: ИМХО, это очень разумный совет. –

+0

Я согласен с тем, что, как правило, большой класс предполагает, что он получил более одной ответственности и не должен.Однако они действительно происходят (даже после передовой практики). –

+0

Действительно они делают. Недавно я написал для рациональной арифметики 362 строки, включая длинные docstrings, со всеми пустыми линиями. Однако у меня есть некоторые '@ staticmethod' с именами, начинающимися с' _', которые, по моим собственным советам, вероятно, должны быть вытеснены как функции. Это уменьшит длину примерно на половину. – wberry

-1

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

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

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

Самый простой вариант - это первый вариант.

+3

Будьте осторожны с 'exec'. Вы должны указать пространство имен, которое код должен выполнить «in». Риски безопасности и т. Д. – wberry

+0

+1 Хороший комментарий. – aquavitae

5

Я раньше играл вокруг с чем-то подобным. Мой usecase был иерархией классов узлов в абстрактном синтаксическом дереве, а затем я хотел поместить все, например. 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) 
+0

Ничего себе, отправьте методы! – wberry

0

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

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

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

Я встретил такую ​​же ситуацию - я хочу, чтобы мой класс был разбит на 2 файла. Причина в том, что - я хочу часть 1 для макета GUI, только макет и еще один файл сохраняет все функции. как частичный класс C#. один для XAML и еще один для функций.

3

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

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 

 Смежные вопросы

  • Нет связанных вопросов^_^