Python - 通过字符串名称访问模块属性中的类实例

1 投票
3 回答
1364 浏览
提问于 2025-04-16 10:27

好的,让我尽量用简单的方式来解释这个问题。假设我有一个叫做 Foobar 的类:

class Foobar():
    def __init__(self, foo, bar, choo):
        self.foo = foo
        self.bar = bar
        self.choo = choo

    def doIt(self):
        return self.foo + self.bar

我想在我的应用程序中使用 10 种 Foobar 的实例。我的最终用户并不知道 foo、bar 和 choo 的具体值,也不在乎这些,但他们想从这 10 种可能性中选择一种,我会给这些选择贴上标签。

所以我想把他们的选择存储在数据库里(但我只想存储标签,这个标签代表了 Foobar 类的某个特定实例)。这样我就可以直接获取我需要的实例。

我尝试在一个模块中设置所有的实例,像这样(我们叫这个模块 "my_instances.py"):

import Foobar

label_one = Foobar("eenie", "meenie", "miney")
label_two = Foobar("teeny", "toony", "tiny")
...
label_ten = Foobar("biggie", "boogie", "baggie")

这样一来,当我从数据库中获取到代表他们选择的字符串时,我就可以像这样获取那个实例(我不讨论如何获取这个字符串,只是展示我是如何获取实例的)。

import my_instances

my_object = getattr(my_instances, 'label_one')
result = my_object.doIt()

但是我遇到了一个错误:TypeError: unbound method doIt() must be called with Foobar instance as first argument。

看起来我得到了 Foobar,但并不是它的真实实例。任何帮助都会非常感谢。我相信我已经把我的情况解释得够清楚了,如果你看到更简单的解决办法,请告诉我。

3 个回答

1

我无法重现你遇到的错误(使用 from foobar import Foobar),所以看起来你的代码中有一些我们没有看到的部分。不过,动态查找其实不是个好主意。为什么不创建一个工厂函数,返回一个包含多个foobars的字典呢?

def get_foobars(x, y, z):
    foobars = {}
    foobars["label_one"] = Foobar(x, y, z)
    foobars["label_two"] = Foobar(z, y, x)
    ...

或者,如果你不想每次都让这个函数创建新的foobars,

foobars = {}
foobars["label_one"] = Foobar(...

def get_foobars():
    return foobars

或者,更简洁一点,

foobars = {'label_one':Foobar("eenie", "meenie", "miney"), 
           'label_two':Foobar(...),
           ... } 
4

正如评论所说,你可能在类 Foobar 和模块 Foobar 之间发生了名字冲突。

不过,明确地定义一个查找表可能会更清晰:

foobars = {
  'label_one': Foobar("eenie", "meenie", "miney"),
  'label_two': Foobar("teeny", "toony", "tiny"),
  ...
  'label_ten': Foobar("biggie", "boogie", "baggie")
}

用法:

result = foobars['label_one'].do_it()

动态符号查找可能很聪明,但不一定更清晰。

1

@payne 的查找表创建了十个 Foobar 的实例,然后再返回你想用的那个;这看起来有点浪费。为什么不按需创建实例呢,像这样?

class Foobar():
    def __init__(self, foo, bar, choo):
        self.foo = foo
        self.bar = bar
        self.choo = choo

    def doIt(self):
        return self.foo + self.bar


makeFooLookup = {
    "first":  ("eenie", "meenie", "miney"),
    "second": ("teeny", "toonie", "tiny"),
    "third":  ("biggie", "baggie", "boogie")
}
def makeFoo(label, lookup=makeFooLookup):
    return Foobar(*lookup[label])

撰写回答