子类化模块以弃用模块级变量/常量?

MSe*_*ert 5 python module

假设我有一个模块,并且我想弃用该模块中的某些内容。这对于函数来说非常简单,本质上这可以使用装饰器来完成:

import warnings

def deprecated(func):
    def old(*args, **kwargs):
        warnings.warn("That has been deprecated, use the new features!", DeprecationWarning)
        return func(*args, **kwargs)
    return old

@deprecated
def func():
    return 10

func()
Run Code Online (Sandbox Code Playgroud)

DeprecationWarning:该功能已被弃用,请使用新功能!

10

但是,如果我想弃用一个不重要的常量,则无法将装饰器应用于变量。我正在玩弄,似乎可以对模块进行子类化并用于__getattribute__发出警告:

我这里使用NumPy只是为了说明原理:

import numpy as np

class MyMod(type(np)):   # I could also subclass "types.ModuleType" instead ...
    def __getattribute__(self, name):
        if name in {'float', 'int', 'bool', 'complex'}:
            warnings.warn("that's deprecated!", DeprecationWarning)
        return object.__getattribute__(self, name)

np.__class__ = MyMod

np.float
Run Code Online (Sandbox Code Playgroud)

弃用警告:已弃用!

漂浮

然而,这似乎不可能从包内完成(至少在顶层),因为我无法访问自己的模块。我必须创建另一个包来对主包进行猴子修补。

有没有更好的方法来“弃用”从包中访问变量,而不是子类化“模块”类和/或使用对另一个包的顶级模块进行猴子修补的元包?

MSe*_*ert 4

PEP 562已被接受并将添加到 Python 3.7(在撰写本文时尚未发布)中,这将允许(或至少大大简化)弃用模块级常量。

__getattr__它的工作原理是在模块中添加一个函数。例如在本例中:

import builtins
import warnings

def __getattr__(name):
    if name == 'float':
        warnings.warn("That has been deprecated, use the new features!", DeprecationWarning)
        return builtins.float
    raise AttributeError(f"module {__name__} has no attribute {name}")
Run Code Online (Sandbox Code Playgroud)

这基本上是 PEP 中针对该问题稍加修改的示例。