Использование «новых» классов стиля (я нахожусь в python 3.2), есть ли способ разделить класс на несколько файлов? У меня большой класс (который действительно должен быть одним классом с точки зрения объектно-ориентированного дизайна, учитывая связь и т. Д., Но было бы неплохо разбить несколько файлов, просто для облегчения редактирования класса.Есть ли какой-либо эквивалент Python для частичных классов?
ответ
Если ваша проблема действительно работает с большим классом в редакторе, первое решение, которое я действительно искал, - лучший способ разбить проблему. Второе решение будет лучшим редактором, желательно с фальцованием кода.
Это говорит о том, что существует несколько способов разбить класс на несколько файлов. Python позволяет использовать папку в качестве модуля, помещая в нее __init__.py
, который затем может импортировать файлы из других файлов. Мы будем использовать эту возможность в каждом решении. Создайте папку, называемую, скажем, bigclass
.
В папке поместить различные
.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
, который будет выглядеть хорошо в ваших наследования графиков.Вы можете использовать множественное наследование для объединения различных частей вашего класса. В ваших отдельных модулях вы должны написать определение класса для
Bigclass
с частями класса. Затем в__init__.py
напиши:import classdef1, classdef2, classdef3 class Bigclass(classdef1.Bigclass, classdef2.Bigclass, classdef3.Bigclass): num = 42 # add more members if desired
Если множественное наследование становится проблемой, вы можете использовать одиночное наследование: просто каждый класс наследует от другого в цепи моды. Предполагая, что вы не определяете ничего более чем в одном классе, порядок не имеет значения. Например,
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
+1: Примерно так же, как и мой ответ, но гораздо более элегантный. – obmarg
+1 Мне нравится первый из ваших альтернатив, лучший из всех упомянутых здесь до сих пор. – wberry
Отметить этот ответ как решение, потому что другие ответы предполагают, что * обычно * большой класс не должен быть действительно одним классом. Но я утверждаю, что они происходят периодически, и этот ответ предлагает некоторые практические способы борьбы с ситуацией. –
Я не использовал его, но 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
. Хотя это может быть полным излишеством.
Могут быть другие (возможно, более эффективные) способы обойти это, но это те, что выскочили на ум.
Определения классов, содержащие сотни строк, встречаются «в дикой природе» (я видел некоторые из популярных фреймворков с открытым исходным кодом на основе Python), но я считаю, что если вы подумаете о том, что делают эти методы, это будет возможно чтобы уменьшить длину большинства классов до управляемой точки. Некоторые примеры:
- Ищите места, где чаще всего один и тот же код встречается более одного раза. Разбейте этот код на свой собственный метод и вызовите его из каждого места с аргументами.
- «Частные» методы, которые не используют какое-либо состояние объекта, могут быть выведены из класса в виде автономных функций.
- Методы, которые следует вызывать только при определенных условиях, могут указывать на необходимость размещения этих методов в подклассе.
Для прямого решения вашего вопроса можно разделить определение класса. Один из способов - это «обезьяна-патч» класса, определяя его, а затем добавляя к нему внешние функции как методы. Другим является использование встроенной функции type
для создания класса «вручную», предоставления его имени, любых базовых классов и его методов и атрибутов в словаре. Но я не рекомендую делать это только потому, что определение будет длинным в противном случае. На мой взгляд, такое лечение хуже, чем болезнь.
+1: ИМХО, это очень разумный совет. –
Я согласен с тем, что, как правило, большой класс предполагает, что он получил более одной ответственности и не должен.Однако они действительно происходят (даже после передовой практики). –
Действительно они делают. Недавно я написал для рациональной арифметики 362 строки, включая длинные docstrings, со всеми пустыми линиями. Однако у меня есть некоторые '@ staticmethod' с именами, начинающимися с' _', которые, по моим собственным советам, вероятно, должны быть вытеснены как функции. Это уменьшит длину примерно на половину. – wberry
Прежде всего, я хотел бы сказать, что что-то осложнило это, вероятно, не очень хорошая идея, просто чтобы легче найти свое место в классе - было бы лучше добавить комментарии, выделить разделы и т. Д.Тем не менее, я вижу два пути вы могли бы сделать это:
Написать класс в нескольких файлах, а затем прочитать их в виде текста, сцепить их и
exec
полученную строку.Создайте отдельный класс в каждом файле, а затем наследуйте их все в мастер-класс как mixins. Однако, если вы подклассифицируете другой класс, это может привести к проблемам MRO. Вы можете обойти это, создав метакласс для своего мастер-класса, который вручную разрешает MRO, но это может стать беспорядочным.
Самый простой вариант - это первый вариант.
Я раньше играл вокруг с чем-то подобным. Мой 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)
Ничего себе, отправьте методы! – wberry
Во-первых, я не вижу, как разделение класса на несколько файлов упрощает редактирование. Порядочная среда IDE должна иметь возможность легко найти любой метод, будь то в одном файле или несколько; если вы не используете достойную среду IDE, разделение класса означает, что сопровождающий должен угадать, к какому файлу относится данный метод, который звучит сложнее, а не проще.
Более фундаментально этот класс - настолько большой, что вы хотите использовать специальную языковую функцию только для поддержки своего веса - звуки принципиально нарушены. Сколько строк кода мы говорим? Почти наверняка, было бы лучше идея сделать один из:
- Refactor дублируется код в меньшем количестве, более общих примитивов
- Определить базовый класс и распространить его подклассов, как Кароль Хорват предлагает в комментариях (это самое близкое к «частичным классам», о котором вы просите, я бы одобрил)
- Определите несколько отдельных классов для инкапсуляции различных частей этой функциональности класса и создайте этот класс экземпляров этих меньших.
Я встретил такую же ситуацию - я хочу, чтобы мой класс был разбит на 2 файла. Причина в том, что - я хочу часть 1 для макета GUI, только макет и еще один файл сохраняет все функции. как частичный класс C#. один для XAML и еще один для функций.
Вы можете сделать это с помощью декораторов, как так:
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
вы можете поместить некоторые основные функции в базовом классе? –
Фразы «большой класс» и «один класс с точки зрения объектно-ориентированного дизайна» не слишком хорошо сочетаются. –
@ Никлас согласился, что они обычно не делают, а иногда делают. Большинство моих занятий довольно маленькие. –