在魔术方法上使用@StaticMethod或@ClassMethod装饰器

2 投票
2 回答
2147 浏览
提问于 2025-04-16 01:38

我正在尝试把魔法方法 __getitem__ 装饰成一个类方法。下面是我尝试的一个例子。我对使用类方法(classmethod)或静态方法(staticmethod)都没意见,但我不太确定该怎么做。以下是我尝试的代码:

import ConfigParser

class Settings(object):
   _env = None
   _config = None

   def __init__(self, env='dev'):
    _env = env
    # find the file
    filePath = "C:\\temp\\app.config"

    #load the file
    _config = ConfigParser.ConfigParser()
    _config.read(filePath)

   @classmethod
   def __getitem__(cls, key): 
    return cls._config.get(cls._env, key)

   @classmethod
   def loadEnv(cls, env): 
    cls._env = env

但是,当我尝试调用 Settings['database'] 时,出现了以下错误。

Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: expected Array[Type], got str

有没有人能告诉我我哪里做错了?另外,有人能建议我是否有更好的方法吗?我甚至尝试过使用元类(MetaClasses),但效果不太好(因为我对Python不太熟悉)。

 class Meta(type):
  def __getitem__(*args):
   return type.__getitem__(*args)

 class Settings(object):
  __metaclass__ = Meta

提前谢谢大家。

2 个回答

6

在Python中,查找__getitem__和其他特殊方法时,总是从类本身去查,而不是从实例去查。举个例子,如果你在一个元类中定义了__getitem__,那么你可以对进行索引(但是你不能通过委托给一个不存在的__getitem__来定义它,就像你不能通过委托给其他不存在的方法来定义任何东西一样;-)。

所以,如果你想对一个类,比如Settings,进行索引,你的自定义元类确实需要定义__getitem__,而且必须用明确的代码来执行你想要的操作——也就是你想要的return cls._config.get

编辑:让我给你一个简单的例子...:

>>> class MyMeta(type):
...   def __getitem__(cls, k):
...     return cls._config.get(k)
... 
>>> class Settings(metaclass=MyMeta):
...   _config = dict(foo=23, bar=45)
... 
>>> print(Settings['foo'])
23

当然,如果事情就这么简单,那把这段代码设计成“索引一个类”就显得有些傻了——一个类最好还是有实例,有状态和方法,否则你不如直接写一个模块;-)。至于为什么“正确”的访问方式是通过索引整个类而不是特定的实例等,这一点并不清楚。不过,我会假设你有一个很好的设计理由想要这样构建结构,然后就告诉你怎么实现这样的结构;-)。

1

除了Alex的回答(完全正确)之外,这里其实不太清楚你想要做什么。现在你是在创建这个类的时候加载配置。如果你把这个_config赋值给类对象,那就意味着这个类的所有实例都共享同一个配置(而且创建一个新的实例会让所有已有的实例都指向最新的配置)。你为什么要用来访问这个配置,而不是用类的某个具体实例呢?即使你只有一个配置,使用类的实例会更方便(也更容易理解!)。你甚至可以把这个实例存储在模块的全局变量中,叫它'设置'也可以:

class _Settings(object):
    def __init__(self, fname):
        self._config = ...
    ...

Settings = _Settings('/path/to/config.ini')

撰写回答