Организация большого проекта Python, который должен иметь внутреннее состояние? - PullRequest
1 голос
/ 11 апреля 2009

Сейчас я на пороге портирования довольно большого Perl. Проблема в том, что он использует маленькие трюки Perl, чтобы сделать свой код доступным для use ing. Я сделал примерно то же самое с Python, сделав кодовую базу одним большим модулем для import ing. Я уже давно хорошо разбираюсь в Python, но у меня нет опыта работы с большими проектами, написанными на Python, которым требуется доступ к другим частям самого себя при сохранении внутреннего состояния.

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

self.Benchmark = Benchmark(self)

self.Exceptions = Exceptions

self.Settings = Settings(self)
self.Cache = Cache(self)

self.Deal = Deal(self)
self.Utils = Utils(self)
self.FileParsers = FileParsers(self)
self.Network = Network(self)
self.Plugins = Plugins(self)
self.Misc = Misc(self)

Это работает , но я не доволен этим. Прямо сейчас сценарий мастер-класса импортирует каждую часть модуля core и создает экземпляр содержащихся классов, передавая себя в качестве аргумента __init__ в этих классах. Вот так:

class FileParsers:
    def __init__(self, parent):
        self.parent = parent

Теперь код в этом классе может обращаться ко всей остальной кодовой базе через родительский класс.

self.parent.Settings.loadSysConfig()

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

РЕДАКТИРОВАТЬ: Ой, забыл эти: ссылки на репозитории SVN для обоих проектов. Мой здесь , и проект, который я портирую, здесь .

Ответы [ 2 ]

1 голос
/ 11 апреля 2009

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

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

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

Первое, что я хотел бы сделать из внешнего вида приведенного выше кода, это выделить какие-либо из тех модулей и классов, которые не являются основной компетенцией вашего приложения, вещи, которые не обязательно должны иметь доступ к состоянию приложения. , Такие названия, как «Utils» и «Misc», звучат подозрительно, так как большая часть их содержимого на самом деле не относится к вашему приложению; возможно, они могут быть реорганизованы в отдельные автономные модули или подмодули вашего пакета, которые имеют только статические функции, вещи, не зависящие от состояния приложения.

Далее, я бы поместил класс Application основного владельца в пакет __init__.py, а не в «главный скрипт». Затем из вашего run-скрипта или просто интерпретатора вы можете получить полный экземпляр приложения просто:

import myapplication

a= myapplication.Application()

Можно также рассмотреть возможность перемещения любых базовых параметров развертывания из класса «Настройки» в инициализатор:

a= myapplication.Application(basedir= '/opt/myapp', site= 'www.example.com', debug= False)

(Если у вас есть только один возможный набор настроек и каждый раз, когда вы создаете экземпляр Application (), вы получаете один и тот же, мало смысла в том, чтобы использовать всю эту возможность для инкапсуляции всего приложения; вы могли бы просто использовать глобальные переменные модуля). .)

Что я делаю с некоторыми из моих приложений, так это превращаю собственные классы в обезьяну-патч, превращая их в настоящих членов объекта-владельца:

# myapplication/__init__.py

class Application(object):
    def __init__(self, dbfactory, debug):
        # ...
        self.mailer= self.Mailer(self)
        self.webservice= self.Webservice(self)
        # ...

import myapplication.mailer, myapplication.webservice


# myapplication/mailer.py

import myapplication

class Mailer(object):
    def __init__(self, owner):
        self.owner= owner

    def send(self, message, recipients):
        # ...

myapplication.Application.Mailer= Mailer

Затем можно расширять, изменять или настраивать приложение извне, заменяя / подклассируя внутренние классы:

import myapplication

class MockApplication(myapplication.Application):
    class Mailer(myapplication.Application.Mailer):
        def send(self, message, recipients):
            self.owner.log('Mail send called (not actually sent)')
            return True

Меня не беспокоит целостность внутренних данных

Ну, нет, это Python, а не Java: мы не слишком беспокоимся о злых программистах, использующих свойства и методы, которые они не должны, мы просто помещаем '_' в начале имени, и пусть это будет подходящим предупреждением всем.

И эти длинные цепочки также замедляют код.

Не очень заметно. Читаемость является важным фактором; все остальное - преждевременная оптимизация.

1 голос
/ 11 апреля 2009

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

# standard
import inspect
import linecache
import neo_cgi
import neo_cs
import neo_util
import os
import random
import sys
import time
from _apache import SERVER_RETURN
from mod_python import apache
from mod_python import util
from mod_python.util import FieldStorage
from os.path import dirname, isfile, join, splitext

# set up path
pydir = dirname(__file__)
if pydir not in sys.path:
    sys.path.append(pydir)

# things I wrote
import auth
import handlers.accounts, handlers.publish, handlers.standard
import logger
import markup
import programs
import summarize
from auth import check_auth
from common import hdf_iterate, load_hdf_cgi_vars, load_hdf_common_vars
from common import hdf_insert_value, hdf_insert_list, hdf_insert_dict
from handlers import chain, farm, opt
from handlers import URIPrefixFilter
from handlers.standard import TabBarHandler

и я уверен, что многие более крупные модули имеют еще более длинные списки.

В вашем случае, возможно, есть модуль Settings с одноэлементным объектом (или с настройками в качестве свойств модуля) и выполните

import Settings

или что угодно.

...