我正在尝试编写一个python脚本,它可以将两个python文件组合成一个混合,使用第二个文件中的方法覆盖第一个文件中的方法(这些方法可以是独立的,也可以存在于类中)。
例如,假设我有original.py:
from someclass import Class0
def fun1(a, b):
print "fun1", a, b
def fun2(a):
print "fun2", a
class Class1(Class0):
def __init__(self, a):
print "init", a
def fun3(self, a, b):
print "fun3", a, b
和new.py:
def fun2(a, b):
print "new fun2", a, b
class Class1(Class0):
def fun3(self, a, b):
print "new fun3"
并且在运行combine('original.py','new.py')之后我希望它生成一个如下所示的新文件:
import someclass
def fun1(a, b):
print "fun1", a, b
def fun2(a, b):
print "new fun2", a, b
class Class1:
def __init__(self, a):
print "init", a
def fun3(self, a, b):
print "new fun3"
我正在试图找出解决这个问题的最简洁方法。我一开始正在考虑使用正则表达式,但是跟踪我所处的缩进和当前级别(类中的一个方法会深一层,但是如果一个文件在类或其他方法中的方法中有类,那么当使用装饰器)听起来像是等待发生的灾难。我也在考虑使用tokenize模块生成一个语法树,然后尝试导航到两个树中的相同元素,用另一个树的部分替换一个树的部分(再次看起来这可能因为所有使用tokenize而变得复杂我看到的是修改单个python标记而不是整个方法)。你们有什么建议?这似乎是一项简单的任务,但我没有看到完成它的干净方法。
另外,我不关心第二个文件是完整还是有效(注意尽管从Class0继承而丢失了导入),但是如果使第二个文件有效则允许我使用一些内部python技巧(比如修改python import)使用来自不同文件的方法覆盖导入方法的逻辑,然后将导入的版本从内存中转储到新文件中),然后我也可以这样做。
编辑(更多解释为什么我尝试这样做而不是使用继承):
我想我的问题需要更好地解释我为什么要这样做。我有几个团体/客户我正在提供我的源代码。功能需要根据客户的要求而有所不同(在某些情况下,它就像调整参数文件一样简单,比如提到的sarnold;在其他情况下,用户要求的功能/钩子与其他组无关,并且只会增加更多的混淆用户界面)。另外,一些源代码是客户特定的和/或专有的(因此虽然我可以与某些组共享它,但我不允许与其他组共享),这就是我试图避免继承的原因。我想我仍然可以依赖正常继承,只要它是专有的子类版本而不是原始版本,但对于大多数功能,目前只有一个组没有priveledges(并且每个功能并不总是相同的组) )和其他团体一样。因此,如果我要使用继承,我可能需要一堆目录/文件,如“SecretFromA”,“SecretFromB”,......,“SecretFromZ”,然后需要“来自SecretFromZ的其他每个组” import *“对于每个模块,而对于我所描述的替换技术,我可以添加存根以用于我想要过滤的功能。这也是我将来会使用的很多东西,所以现在写这个脚本很痛苦,我觉得将来可以节省更多的东西而不必保持过多的“来自* import *“继承会强制我的类型文件(更不用说以后在客户决定移动功能时必须对每个版本进行区分)。
另外,为了回应sarnold的评论,我想我的描述太模糊了。这不是即时发生的事情。我会生成一次新的* .py文件(每个版本/交付)并将生成的源提供给客户。此外,仅当第二个文件(我的示例中为new.py)实际存在时才会进行修补,否则将复制原始文件。因此,与继承不同,我的特定于组的目录相对较小,最终版本部署到一个单独的目录,除了将其交付给客户之外,我根本不需要维护。
答案 0 :(得分:0)
通过新的改进描述,我可以提出一个我过去使用过的解决方案,以及其他团队,效果很好:使用源代码控制来通过分支机构或新的存储库来管理差异。
首先,请使用某种source control系统。更高级的内容,例如git
,hg
,Subversion,Bazaar,ClearCase,BitKeeper,Darcs,{ {3}}等等,都支持某种形式的合并跟踪,可以使这个特定问题更容易解决。 (无论您是否决定以这种方式使用分支机构,请使用源控制系统。)
您将维护一个主分支(如果您愿意,可以将其称为 trunk 或 master ),几乎所有的开发工作,错误修复等等。从这个特定于给定客户的所有代码分支。您可能更愿意基于功能而不是客户名称进行分支,因为您可能有10个客户需要SCTP支持,而其他20个客户则需要TCP支持。 (并且您希望为更具异国情调的协议收取更多费用 - 因此将其全部合并到配置选项中将是Bad For Business。)
在交付新产品时,您需要将分支(或存储库)更改为专用分支,git pull
(不同存储库)或git merge
(不同分支)或svn merge
或类似命令将更新从主开发主干拉入您的专用分支。根据您是否处理过冲突或不冲突的代码,您可能会遇到一些合并冲突需要修复,或者它可能会非常干净。修复任何合并冲突,提交到您的分支,然后转到下一个分支。
你最终可能会遇到一个看起来像这样的“树”:
I--J--K supremely magic feature
/ /
D--E--F--L--M--N magic feature
/ / /
A--B--C--G--H--O--P--Q main trunk
获得太多分支是疯狂的快速途径;如果可以,请确保将错误修复和共享功能提交到主干中,如果不能,尽可能接近它。这将使它更容易合并回所有后代分支。当你编写bug修复的新功能时,最好尽可能地完成所有的合并,因为那时你脑子里最新鲜。
维护多个版本的代码可能会让您感到疯狂 - 当您可以使代码更加模块化,并且只是向用户提供不同的“模块”代码时,您可以大大降低管理所需的复杂性。对模块化界面进行编程需要遵守纪律,前期工作量可能过高,但通常使用五个插件管理一个工具比为这些五个功能的5! == 120
不同组合更容易并从五个特征中进行选择。
您将认识到在每个客户分支中最有意义的各种修改以及应该作为主干中的模块维护的修改。案例并不总是明确的:采取导致最低总体复杂程度的方法,你可以判断得最好。