首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >python-2.7在对象导入时执行代码

python-2.7在对象导入时执行代码
EN

Stack Overflow用户
提问于 2018-01-13 02:04:44
回答 3查看 96关注 0票数 3

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

我遇到的一个问题是,即使我处于开发人员模式,我也可以将弃用的对象导入到另一个模块中。这意味着我遗漏了使用弃用对象的地方。

例如在module1.py中:

代码语言:javascript
复制
class MyObject(object):
    pass
MyObject = MyObject if not dev_mode() else DeprecatedObject

然后在module2.py中:

代码语言:javascript
复制
from module1 import MyObject

我已经设置了DeprecatedObject,所以与它的任何交互都会引发一个DeprecationWarning --有没有办法让它在导入时出错?即。即使导入module2.py也会引发异常。

我的想象是这样的:

代码语言:javascript
复制
import warnings

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

回答 3

Stack Overflow用户

发布于 2018-01-13 02:12:35

模块级__getattr__功能允许模块级名称在导入时经历正确的弃用过程。这个特性将在Python3.7中出现,详情请参见PEP 562 (因为您已经使用Python2.7进行了标记,所以它不能帮助您,但我提到它是为了方便将来的读者)。

在Python 2.7上,您有两个较低级别的选项:

object __init__中的

  • 触发器弃用警告。导入后,
  • 使用Guido's hack将模块替换为其自身的修补版本。通过将代理对象包装在模块周围,您可以控制名称解析。
票数 2
EN

Stack Overflow用户

发布于 2018-01-13 02:11:51

首先,我建议研究一下内置的warnings模块。它有专门为这类事情制作的工具。有一个非致命的警告比引发异常更有意义。

现在,对于您的情况,一种可能的操作过程是用一个函数“替换”已弃用的类。这意味着将类重命名为其他名称,并具有一个具有原始名称的函数,该函数检查是否启用了开发人员模式并相应地执行操作。结果将类似于:

代码语言:javascript
复制
class MyDeprecatedClass:
    pass

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

或者,带有警告:

代码语言:javascript
复制
def MyClass(*args, **kwargs):
    from warnings import warn
    if dev_mode():
        warn("Dont use this!!!!!!!!!")
    else:
        return MyDeprecatedClass(*args, **kwargs)

这样做的目的是检查是否启用了开发人员模式,如果启用了,则仅引发异常(或警告)。否则,它会将所有提供给它的参数传递给重命名类的构造函数,这意味着所有依赖它的旧类都可以正常工作。

票数 1
EN

Stack Overflow用户

发布于 2018-01-16 03:28:11

除了允许两种类型的对象同时存在之外,您的初始方法几乎与我建议的完全相同。我将从模块中的一条完整的if语句开始,该语句一次只允许定义一个对象。更像是:

代码语言:javascript
复制
if dev_mode():
    class MyObject:
        # Define deprecated version here
        ...
else:
    class MyObject:
        # Define production version here
        ...

如果弃用版本和非弃用版本之间的区别很简单,例如,可以通过函数或类装饰器轻松完成(比如发出警告),那么您可以将上面的代码简化为以下代码:

代码语言:javascript
复制
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来避免类的多个版本浮动。您可以在流程中添加任意数量的复杂性层。我见过的一种用于类似目的的技术(其中类的特定版本取决于某些导入时条件,如OS),是创建一个名为module1的包,并在不同的模块中完全实现类的两个不同版本。包结构将如下所示:

代码语言:javascript
复制
module1/
|
+-- __init__.py
|
+-- _development.py
|
+-- _production.py

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

代码语言:javascript
复制
__all__ = ['MyModule']

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

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

代码语言:javascript
复制
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))

这种形式的分离允许您同时测试生产版本和开发版本,而无需两个单独的测试流程。您的测试可以直接导入私有模块。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/48231794

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档