<p>你最初的方法几乎和我建议的完全一样,只是你允许两种类型的对象同时存在。我将从模块中一个完整的<code>if</code>语句开始,它一次只允许定义一个对象。更像是:</p>
<pre><code>if dev_mode():
class MyObject:
# Define deprecated version here
...
else:
class MyObject:
# Define production version here
...
</code></pre>
<p>如果不推荐使用的版本和未推荐使用的版本之间的区别很简单,例如,可以使用函数或类装饰器轻松完成(如发出警告),则可以将上面的代码简化为:</p>
<pre><code>if dev_mode():
def function_decorator(func, cls=None):
# You can use the second argument when calling manually from a class decorator
name = func.__name__ is cls is None else cls.__name__ + '.' + func.__name__
warnings.warn("Importing deprecated function: {}".format(name))
return func
def class_decorator(cls):
warnings.warn("Importing deprecated class: {}".format(cls.__name__))
# Make additional modifications here (like adding function_decorator to all the class methods)
return cls
else:
def function_decorator(func):
return func
def class_decorator(cls):
return cls
@class_decorator
class MyClass:
pass
</code></pre>
<p>使用模块级<code>if</code>来避免类的多个版本四处浮动是这里的基本工具。您可以向流程添加任意数量的复杂层。我见过一种类似的技术(类的特定版本取决于一些导入时间条件,如操作系统),就是创建一个名为<code>module1</code>的包,并在不同的模块中实现类的两个不同版本。包结构如下所示:</p>
<pre><code>module1/
|
+ __init__.py
|
+ _development.py
|
+ _production.py
</code></pre>
<p><code>_development</code>和<code>_production</code>定义的名称相同,但版本不同。模块名称前面的下划线意味着永远不应该直接导入它们。将<code>module1</code>作为模块公开,而不是使用其<code>__init__</code>文件作为包公开,该文件如下所示:</p>
<pre><code>__all__ = ['MyModule']
if dev_mode():
from ._development import MyModule
else:
from ._production import MyModule
</code></pre>
<p>如果有很多名称,可以使用<code>__init__</code>中的<code>__all__</code>自动执行公共导入:</p>
<pre><code>import importlib, sys
__all__ = ['MyClass']
self = sys.modules[__name__]
sub = importlib.import_module('_development' if dev_mode() else '_production')
for name in __all__:
setattr(self, name, getattr(sub, name))
</code></pre>
<p>这种分离形式允许您测试产品版本和开发版本,而不需要两个单独的测试流。您的测试可以直接导入私有模块。你知道吗</p>