Skip to main content
 首页 » 编程设计

python之使用 `is (CustomClass)` 可以安全地检测 Python 中的未初始化值

2025年05月04日21lonelyxmas

我正在创建一个装饰器来缓存可能抛出异常的昂贵函数的值,我想记录我们是否到达了初始化值的点。截至目前,我只是将该值初始化为“奇数”字符串 new_val = "__THIS_IS_UNINITIALIZED__" 但感觉很脏。

我想知道将 is 与自定义类(什么都不做)一起使用是否安全。

这是我现在拥有的:

class Cache: 
    _cached = {} 
    @staticmethod 
    def cache(prefix): 
        def wrapper(wrapped): 
            def inner(self, *args, **kwargs): 
                new_val = "__THIS_IS_UNINITIALIZED__" 
                key = Cache.make_key(*args, **kwargs) 
                if key in Cache._cached: 
                    print("cache hit") 
                    return Cache._cached[key] 
                print("cache miss") 
                try: 
                    # This can throw exceptions 
                    new_val = wrapped(self, *args, **kwargs) 
                    # Something below this can ALSO throw 
                    # exceptions, but the value has been 
                    # initialized at this point. 
                except: 
                    if new_val == "__THIS_IS_UNINITIALIZED__": 
                        print("we never got to initialize :_( ") 
                else: 
                    Cache._cache[key] = new_val 
            return inner 
        return wrapper 

我想知道我是否可以使用 if is Class 而不是 if new_val == "__THIS_IS_UNINITIALIZED__"

像这样:

class Uninitialized: 
    pass 
 
class Cache: 
    _cached = {} 
    @staticmethod 
    def cache(prefix): 
        def wrapper(wrapped): 
            def inner(self, *args, **kwargs): 
                new_val = Uninitialized 
                key = Cache.make_key(*args, **kwargs) 
                if key in Cache._cached: 
                    print("cache hit") 
                    return Cache._cached[key] 
                print("cache miss") 
                try: 
                    # This can throw exceptions 
                    new_val = wrapped(self, *args, **kwargs) 
                    # Something below this can ALSO throw 
                    # exceptions, but the value has been 
                    # initialized at this point. 
                except: 
                    if new_val is Uninitialized: 
                        print("we never got to initialize :_( ") 
                else: 
                    Cache._cache[key] = new_val 
            return inner 
        return wrapper 
 
    @staticmethod 
    def make_key(*args, **kwargs): 
        positional = ':'.join([str(s) for s in args]) 
        kw = ':'.join('%s=%s' % kf for kv in kwargs.items()) 
        return ':'.join([positional, kw]) 
 
class Test: 
    def __init__(self): 
        self.foo = 'hi' 
 
    @Cache.cache('api') 
    def test_cache(self, a, b): 
        raise Exception("foo") 
 
if __name__ == "__main__": 
    t = Test() 
    t.test_cache(1, 2) 

使用字符串 "__THIS_IS_UNINITIALIZED__" 到目前为止工作正常(并且在可预见的将来也将工作正常)。老实说,这主要是出于学习目的。

提前谢谢你。

请您参考如下方法:

标准的习惯用法是本质上做你所做的,但是创建一个 object 的实例,而不是为这样的哨兵定义一个新类。

 Uninitialized = object() 

因为你的类语句等同于

Uninitialized = type("Uninitialized", (object,), {}) 

唯一的区别是我实例化了 object 而不是 type。


更新(通过 Sentinel object and its applications? ):自定义类的实例可以提供更有用的表示,如 the dataclasses module 中的示例所示:

>>> from dataclasses import MISSING 
>>> repr(MISSING) 
'<dataclasses._MISSING_TYPE object at 0x10baeaf98>' 
>>> repr(object()) 
'<object object at 0x10b961200>' 

定义一个新类可以让您将类名用作简短的诊断消息或哨兵用途的描述。