奇淫技巧实现 Python 注册器

本文涉及知识点包括装饰器、类的继承等,如有需要请自行搜索,下面是本文所参考的文章:

三篇文章中都使用了继承 dict 类,而且重写了其部分方法,但是经检验不重写也行,所以本文提供的代码略有不同。

首先看看奇淫技巧的体现版本,仅三行代码:

class Register(dict):
    def __call__(self, obj):
        return self.setdefault(obj.__name__, obj) if callable(obj) else lambda x: self.setdefault(obj, x)

使用方法上面的三篇文章都有介绍,简单来说就是:

# Instantiate a Register instance.
registry = Register()

# Auto register via decorator.
@registry
def test():
    pass

# Add a customize name.
@registry('name')
class eman:
    pass

# Manually register.
anonymous_function = lambda: print('I am a function and I have no name')  # Not recommended defination.
registry('anonymous_function')(anonymous_function)

print(registry)
# {'test': , 'name': __main__.eman, '': ()>, 'anonymous_function': ()>}

细心的人可能会发现 Register 类的第三行代码中有 callable 函数用于测试传入对象是否可调用,但是这里其实有 Bug。如果哪个大聪明这么用,那 register 里可能会被传入一些不可调用的对象:

registry = Register()
registry('A')('B')

print(registry)
# {'A': 'B'}

很明显,你不能通过 register['A']() 调用该对象,因为它是 str 类型的。

所以上面这个你当我耍耍酷就行,真正要用的话,可能会害了你(注册时不会报错,等到调用时:TypeError: 'XXX' object is not callable)。

所以还是用下面这个比较稳妥:

class Register(dict):
    def __call__(self, obj):
        def add_item(k, v):
            if callable(v):
                return self.setdefault(k, v)
            else:
                raise TypeError(f'{repr(type(v).__name__)} object is not callable!')

        if callable(obj):
            return add_item(obj.__name__, obj)
        else:
            return lambda x: add_item(obj, x)
阅读剩余
THE END