如何将__iter__添加到动态类型?

时间:2010-07-10 03:59:26

标签: python

来源

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,或以可读格式显示列表。

欢迎其他改进。

5 个答案:

答案 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