如何在Python中标记全局变量为弃用?

13 投票
4 回答
8448 浏览
提问于 2025-04-15 11:53

我见过一些装饰器,可以标记一个函数为“已弃用”,这样每次使用这个函数时就会发出警告。我想做的事情是对全局变量也做类似的处理,但我想不出怎么检测全局变量的访问。我知道有一个叫globals()的函数,我可以检查它的内容,但这只能告诉我这个全局变量是否被定义(即使它被标记为已弃用,只要没有完全删除,它还是会存在),而不能告诉我它是否真的被使用了。我想到的最佳替代方案是这样的:

# myglobal = 3
myglobal = DEPRECATED(3)

但是除了如何让DEPRECATED像'3'那样工作的问题,我不太确定DEPRECATED能做什么来检测每次访问它的情况。我觉得它能做的最好就是遍历所有全局变量的方法(因为在Python中一切都是对象,连'3'都有方法,比如转换成字符串等),然后把这些方法都标记为已弃用。但这并不是最理想的办法。

有没有什么好主意?有没有人解决过这个问题?

4 个回答

5

看这里:

代码

from types import *

def wrapper(f, warning):
    def new(*args, **kwargs):
        if not args[0].warned:
            print "Deprecated Warning: %s" % warning
            args[0].warned = True
        return f(*args, **kwargs)
    return new

class Deprecated(object):
    def __new__(self, o, warning):
        print "Creating Deprecated Object"
        class temp(o.__class__): pass
        temp.__name__ = "Deprecated_%s" % o.__class__.__name__
        output = temp.__new__(temp, o)

        output.warned = True
        wrappable_types = (type(int.__add__), type(zip), FunctionType)
        unwrappable_names = ("__str__", "__unicode__", "__repr__", "__getattribute__", "__setattr__")

        for method_name in dir(temp):
            if not type(getattr(temp, method_name)) in wrappable_types: continue
            if method_name in unwrappable_names: continue

            setattr(temp, method_name, wrapper(getattr(temp, method_name), warning))

        output.warned = False

        return output

输出

>>> a=Deprecated(1, "Don't use 1")
Creating Deprecated Object
>>> a+9
Deprecated Warning: Don't use 1
10
>>> a*4
4
>>> 2*a
2

这部分代码显然可以进一步优化,但大致意思就是这样。

5

你可以把你的模块做成一个类(可以参考一下这个问题),然后把那个不推荐使用的全局变量变成一个属性。这样,当有人访问这个属性时,你就可以执行一些代码,并给出你想要的警告。不过,这样做似乎有点过于复杂了。

14

你不能直接这样做,因为没有办法拦截模块的访问。不过,你可以用一个你自己选择的对象来替代那个模块,这个对象可以充当一个代理,专门用来监控对某些属性的访问:

import sys, warnings

def WrapMod(mod, deprecated):
    """Return a wrapped object that warns about deprecated accesses"""
    deprecated = set(deprecated)
    class Wrapper(object):
        def __getattr__(self, attr):
            if attr in deprecated:
                warnings.warn("Property %s is deprecated" % attr)

            return getattr(mod, attr)

        def __setattr__(self, attr, value):
            if attr in deprecated:
                warnings.warn("Property %s is deprecated" % attr)
            return setattr(mod, attr, value)
    return Wrapper()

oldVal = 6*9
newVal = 42

sys.modules[__name__] = WrapMod(sys.modules[__name__], 
                         deprecated = ['oldVal'])

现在,你可以这样使用它:

>>> import mod1
>>> mod1.newVal
42
>>> mod1.oldVal
mod1.py:11: UserWarning: Property oldVal is deprecated
  warnings.warn("Property %s is deprecated" % attr)
54

缺点是,当你访问这个模块时,现在需要进行两次查找,所以会稍微影响性能。

撰写回答