python2.7在对象imp上执行代码

2024-04-29 01:01:13 发布

您现在位置:Python中文网/ 问答频道 /正文

我正在尝试创建一个弃用系统,允许代码对普通用户透明地运行,但在开发人员模式下标记弃用的对象。你知道吗

我遇到的一个问题是,即使处于开发人员模式,也可以将不推荐使用的对象导入到另一个模块中。这意味着我缺少使用不推荐对象的地方。你知道吗

例如,在module1.py中:

class MyObject(object):
    pass
MyObject = MyObject if not dev_mode() else DeprecatedObject

然后在module2.py中:

from module1 import MyObject

我已经设置了DeprecatedObject,因此任何与它的交互都会引发DeprecationWarning—有没有任何方法可以使它在导入时出错?即使导入module2.py也会引发异常。你知道吗

我在想象这样的事情:

import warnings

class DeprecatedObject(object):
    ...
    def __onimport__(self):
        warnings.warn("deprecated", DeprecationWarning)

Tags: 对象代码pyimportobject开发人员系统模式
3条回答

首先,我建议查看内置的^{}模块。它有专门为这类事情制作的工具。有一个非致命的警告比引发一个异常更有意义。你知道吗

现在,对于您的情况,一种可能的做法是用函数“替换”不推荐使用的类。这意味着将类重命名为其他名称,并使用原始名称的函数检查是否启用了开发人员模式并相应地执行操作。结果是:

class MyDeprecatedClass:
    pass

def MyClass(*args, **kwargs):
    if dev_mode():
        raise DeprecationWarning
    else:
        return MyDeprecatedClass(*args, **kwargs)

或者,带有警告:

def MyClass(*args, **kwargs):
    from warnings import warn
    if dev_mode():
        warn("Dont use this!!!!!!!!!")
    else:
        return MyDeprecatedClass(*args, **kwargs)

它检查是否启用了开发人员模式,并且只在启用时引发异常(或警告)。否则,它会将给定给它的所有参数传递给重命名类的构造函数,这意味着所有依赖它的旧类都可以正常工作。你知道吗

模块级__getattr__特性允许模块级名称在导入时进行正确的弃用过程。python3.7中提供了这个特性,请参见PEP 562以了解详细信息(因为您已经使用python2.7进行了标记,所以它对您没有帮助,但我提到它是为了将来读者的利益)。你知道吗

在Python2.7上,有两个较差的选项:

  • 在对象__init__中触发弃用警告。你知道吗
  • 导入后,使用Guido's hack将模块替换为自身的修补版本。围绕模块包装代理对象可以控制名称解析。你知道吗

你最初的方法几乎和我建议的完全一样,只是你允许两种类型的对象同时存在。我将从模块中一个完整的if语句开始,它一次只允许定义一个对象。更像是:

if dev_mode():
    class MyObject:
        # Define deprecated version here
        ...
else:
    class MyObject:
        # Define production version here
        ...

如果不推荐使用的版本和未推荐使用的版本之间的区别很简单,例如,可以使用函数或类装饰器轻松完成(如发出警告),则可以将上面的代码简化为:

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

使用模块级if来避免类的多个版本四处浮动是这里的基本工具。您可以向流程添加任意数量的复杂层。我见过一种类似的技术(类的特定版本取决于一些导入时间条件,如操作系统),就是创建一个名为module1的包,并在不同的模块中实现类的两个不同版本。包结构如下所示:

module1/
|
+  __init__.py
|
+  _development.py
|
+  _production.py

_development_production定义的名称相同,但版本不同。模块名称前面的下划线意味着永远不应该直接导入它们。将module1作为模块公开,而不是使用其__init__文件作为包公开,该文件如下所示:

__all__ = ['MyModule']

if dev_mode():
    from ._development import MyModule
else:
    from ._production import MyModule

如果有很多名称,可以使用__init__中的__all__自动执行公共导入:

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))

这种分离形式允许您测试产品版本和开发版本,而不需要两个单独的测试流。您的测试可以直接导入私有模块。你知道吗

相关问题 更多 >