如何为动态类型添加 __iter__?

2 投票
5 回答
549 浏览
提问于 2025-04-16 01:05

来源

def flags(*opts):
    keys = [t[0] for t in opts]
    words = [t[1] for t in opts]
    nums = [2**i for i in range(len(opts))]
    attrs = dict(zip(keys,nums))
    choices = iter(zip(nums,words))
    return type('Flags', (), dict(attrs))

Abilities = flags(
    ('FLY', 'Can fly'),
    ('FIREBALL', 'Can shoot fireballs'),
    ('INVISIBLE', 'Can turn invisible'),
)

问题

我该如何给 Abilities 添加一个 __iter__ 方法,这样我就可以遍历 choices 了?

为什么?

这样我就可以在代码中使用类似

hero.abilities = Abilities.FLY | Abilities.FIREBALL

if hero.abilities & Abilities.FIREBALL:

for k, v in Abilities:
    print k, v

的东西,而不需要使用任何神秘的数字或字符串,并且我还可以将这些标志的集合作为一个整数保存到数据库,或者以易读的格式显示列表。

欢迎其他改进建议。

5 个回答

0

如果你自己实现一个 __getattr__ 方法来访问动态字段,而不是通过 type 来处理 metaclass,那样会更符合 Python 的风格。

编辑:我不太明白你说的选择是什么意思,不过这里有一个例子:

class Abilities(object):
    def __init__(self, abilities):
        self.abilities = abilities

    def __getattr__(self, name):
        a = [x for x in self.abilities if x[0] == name]
        if len(a) != 1:
            raise AttributeError('attribute {0} not found'.format(name))
        title, id, help = a[0]
        return id

    def __iter__(self):
        return (id, help for title, id, help in self.abilities)

SPEC = [
    ('FLY', 10, 'Can fly'),
    ('FIREBALL', 13, 'Can shoot fireballs'),
    ('INVISIBLE', 14, 'Can turn invisible'),
]

abitilies = Abilities(SPEC)

hero.abilities = abilities.FLY | abilities.FIREBALL
for k, v in abilities:
    print k, v
1

你为什么要走弯路呢?如果你想要一个可以用__getattr__来重写的dict,那为什么不直接从一个现成的开始呢:

class Flags(dict):
    def __init__(self, *args):
        dict.__init__(self, args)
    def __getattr__(self, name):
         return self[name]
...

这样做还有一个好处,就是不会让人感到意外,因为dict.__iter__()会生成键,而dict.iteritems()会返回元组。

4

这里其实不需要用动态类型;我会把这个重构成一个简单的类,比如:

class flags(object):
    def __init__(self, *opts):
        keys = [t[0] for t in opts]
        words = [t[1] for t in opts]
        nums = [2**i for i in range(len(opts))]
        self.attrs = dict(zip(keys,nums))
        self.choices = zip(nums,words)

    def __getattr__(self, a):
        return self.attrs[a]

    def __iter__(self):
        return iter(self.choices)

Abilities = flags(
    ('FLY', 'Can fly'),
    ('FIREBALL', 'Can shoot fireballs'),
    ('INVISIBLE', 'Can turn invisible'),
)

print Abilities.FLY
for k, v in Abilities:
    print k, v

撰写回答