如何为动态类型添加 __iter__?
来源
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