Python 静态方法的魔法调用

1 投票
1 回答
950 浏览
提问于 2025-04-18 06:05

我正在尝试创建一个 Service 类,用来保存 Registry 实例。RegistryService 类的代码如下:

class Registry:

    def __init__(self, registrar):
        self.registrar = registrar

    def get(self, key):
        layers = key.split('.')
        value = self.registrar
        for item in layers:
            value = value[item]
        return value

class Service:

    instance = None

    @classmethod
    def boot(cls, object, configuration = {}):
        if cls.instance is None:
            cls.instance = object(configuration)

我现在想要的是,在调用 boot 函数后,Service 类可以静态地访问 Registry 的功能,而不需要在 Service 类中定义这些函数。所以,在用以下代码启动后,我想直接使用 get 函数。

Service.boot(Config, {'a' : 'b'});
Service.get('a');

注意:Config 对象是 Registry 的一个扩展

我尝试过使用 __getattr__ 这个魔法方法,但它在静态方法上不起作用。我也试过 __call__,但这样的话我就需要调用 Service().get(),这不是我想要的。

所以,在 Python 中,这种情况可以实现吗?我不想在 Service 层重新定义类方法。

注意:在 PHP 中,有一个 __callStatic() 函数,正好实现了我想要的功能。不过,我找不到在 Python 中的实现。

1 个回答

2

如果你能稍微调整一下你的调用方式,我觉得这样是可行的:

cfg = Service.boot(Config, {'a' : 'b'})
cfg.get('a')

这样的话,如果你让 boot() 返回一个提供这些方法的对象,你就可以完美地使用 __getattr__ 了。

如果不行,你可以试试以下几种方法:

  1. 直接调用 cfg.instance.get('a')
  2. Service 加一个元类:

    class Registry:
    
        def __init__(self, registrar):
            self.registrar = registrar
    
        def get(self, key):
            layers = key.split('.')
            value = self.registrar
            for item in layers:
                value = value[item]
            return value
    
    class DeflectToInstance(type):
        def __getattr__(selfcls, a): # selfcls in order to make clear it is a class object (as we are a metaclass)
            try:
                # first, inquiry the class itself
                return super(Meta, selfcls).__getattr__(a)
            except AttributeError:
                # Not found, so try to inquiry the instance attribute:
                return getattr(selfcls.instance, a)
    
    class Service:
        __metaclass__ = DeflectToInstance
        instance = None
    
        @classmethod
        def boot(cls, NewCls, configuration={}):
            if cls.instance is None:
                cls.instance = NewCls(configuration)
    
    Service.boot(Registry, {'a': 'b'})
    print Service.get('a')
    

    这里的元类提供了一种合适的方法,可以把对类属性的访问转发到它的某个属性上。

    要注意的是,在 Python 3 中,语法稍微有点不同。如果我没记错的话,应该是

    class Service(metaclass=DeflectToInstance):
    

(不过,我觉得仅仅为了这种封装使用 Service 类有点不太合适。)

你可以在某个地方做 cfg = boot(...),然后后面就可以用 cfg 来访问所有内容。你甚至可以(虽然有点误导)把它命名为 Config

class Registry:

    def __init__(self, registrar):
        self.registrar = registrar

    def get(self, key):
        layers = key.split('.')
        value = self.registrar
        for item in layers:
            value = value[item]
        return value

cfg = Registry(...)

或者

Config = Registry(...)

然后在你想要的地方随时使用 cfg(或者 Config)。

撰写回答