【问题标题】:Organizing a large Python project that must share an internal state?组织一个必须共享内部状态的大型 Python 项目?
【发布时间】:2009-04-11 01:14:52
【问题描述】:

我目前正在移植一个相当大的 Perl 问题是它使用小的 Perl 技巧使其代码可用于useing。我对 Python 做了同样的事情,使代码库成为importing 的一个大模块。我已经牢牢掌握 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 存储库的链接。我的是here,我要移植的项目是here

【问题讨论】:

    标签: python project-management code-organization


    【解决方案1】:

    如果不能真正看到代码,真的很难判断,但您可能应该考虑在该模块中导入每个模块使用的项目。有很长的进口清单并不罕见 - 这是我自己网站上的一个例子:

    # 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
    

    或其他。

    【讨论】:

    • 哎呀!抱歉,我忘了添加 SVN 链接。我已经这样做了,现在。
    【解决方案2】:

    重组项目和重构代码以保持其当前访问其他所有内容的能力的最佳方式是什么?

    我认为你实际上已经很接近了,并且可能比许多 Python 项目更好,他们只是假设只有一个应用程序实例,并将应用程序特定的值存储在全局模块或单例模块中。

    (这对于许多简单的应用程序来说是可以的,但实际上最好能够将所有内容捆绑到一个应用程序对象中,该对象拥有所有需要了解应用程序状态的内部类和方法。)

    从上面的代码来看,我要做的第一件事是剔除那些不是应用程序核心能力的模块和类,这些东西不一定需要访问应用程序的状态.像“Utils”和“Misc”这样的名字听起来很可疑,因为它们的大部分内容并不是你的应用程序所特有的;它们也许可以重构为单独的独立模块,或者包的子模块,它们只有静态函数,不依赖于应用程序状态。

    接下来,我会将主要所有者 Application 类放在包的 __init__.py 中,而不是“主脚本”中。然后从您的运行脚本或解释器中,您可以获取应用程序的完整实例,如下所示:

    import myapplication
    
    a= myapplication.Application()
    

    您还可以考虑将任何基本部署设置从 Settings 类移到初始化程序中:

    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:我们不用太担心邪恶的程序员使用他们不应该使用的属性和方法,我们只是在名称的开头加上“_”,让它成为一个合适的警告给所有人。

    而且那些长链也会减慢代码速度。

    不是很明显。可读性是重要因素;其他任何事情都是过早的优化。

    【讨论】:

      猜你喜欢
      • 2010-12-14
      • 1970-01-01
      • 2017-07-03
      • 1970-01-01
      • 1970-01-01
      • 2022-01-17
      • 1970-01-01
      • 2023-03-04
      • 1970-01-01
      相关资源
      最近更新 更多