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'),
)
如何向__iter__
添加Abilities
方法,以便我可以迭代choices
?
这样我可以使用像
这样的东西hero.abilities = Abilities.FLY | Abilities.FIREBALL
if hero.abilities & Abilities.FIREBALL:
for k, v in Abilities:
print k, v
在我的代码中,无需使用任何幻数或字符串,我也可以将标记集保存为DB作为单个int,或以可读格式显示列表。
欢迎其他改进。
答案 0 :(得分: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
答案 1 :(得分:1)
你为什么这么做?如果您希望dict
__getattr__
覆盖为什么不从一个开始:
class Flags(dict):
def __init__(self, *args):
dict.__init__(self, args)
def __getattr__(self, name):
return self[name]
...
这也有最小的优势,因为dict.__iter__()
会生成密钥,而dict.iteritems()
会产生元组。
答案 2 :(得分:0)
如果您实现自己的__getattr__
方法来访问动态字段而不是通过type
处理元类,那么这将是一个更Pythonic的解决方案。
编辑:我不清楚选择是什么意思,但这是一个例子:
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
答案 3 :(得分:0)
您需要进行两项重要更改 - flags
的最后一行应为:
choices = iter(zip(nums,words))
attrs['__iter__'] = lambda self: choices
return type('Flags', (), dict(attrs))()
请注意,我在__iter__
中添加了一行()
,和尾随return
到实例化 type(要在类型上循环,你必须使用自定义元类 - 方式矫枉过正,不需要)。
flags
的最后一行应该是:
return type('Flags', (), attrs)()
因为没有理由复制attrs
,这已经是dict
(但这是一个无害的冗余,而不是一个杀手锏的错误; - )。
答案 4 :(得分:0)
根据你们的建议,我想出了这个:
class enumerable(object):
def __init__(self, func, *opts):
keys = func(len(opts))
self.attrs = dict(zip([t[0] for t in opts], keys))
self.opts = zip(keys, [t[1] for t in opts])
def __getattr__(self, a):
return self.attrs[a]
def __len__(self):
return len(self.opts)
def __iter__(self):
return iter(self.opts)
def __deepcopy__(self, memo):
return self
class enum(enumerable):
def __init__(self, *opts):
return super(enum, self).__init__(range, *opts)
class flags(enumerable):
def __init__(self, *opts):
return super(flags, self).__init__(lambda l: [1<<i for i in range(l)], *opts)
### tests:
Abilities = enum(
('FLY', 'Can fly'),
('FIREBALL', 'Can shoot fireballs'),
('INVISIBLE', 'Can turn invisible'),
('FROST_NOVA', 'Can call down an ice storm'),
('BLINK', 'Can teleport short distances'),
)
print 'Fireball = %d' % Abilities.FIREBALL
print 'Number of options = %d' % len(Abilities)
for k, v in Abilities:
print '%d: %s' % (k, v)
Fireball = 1
Number of options = 5
0: Can fly
1: Can shoot fireballs
2: Can turn invisible
3: Can call down an ice storm
4: Can teleport short distances
无论出于何种原因,我的特定应用程序需要__deepcopy__
来实现。由于这些类用于构建“常量”,因此在创建之后不应更改它们的任何属性;因此,我希望只返回self
。