0

我正在尝试在 python 中创建一个枚举。我见过几种解决方案(@alec thomas 的第二个答案 让我感兴趣),但我想让枚举不可变。我找到了一个不可变的python 配方,但我想要一个类似 dict 的键/值关联。

我试图使用打鸭子来向类添加属性,AttributeError如果您尝试调用fsetfdel属性,则会抛出一个。

fget我在定义属性的功能时遇到了麻烦。这是我到目前为止的代码:

def enum(*sequential, **named):
    # Build property dict
    enums = dict(zip(sequential, range(len(sequential))), **named)

    # Define an errorhandler function
    def err_func(*args, **kwargs):
        raise AttributeError('Enumeration is immutable!')

    # Create a base type
    t = type('enum', (object,), {})

    # Add properties to class by duck-punching
    for attr, val in enums.iteritems():
        setattr(t, attr, property(lambda attr: enums[attr], err_func, err_func))

    # Return an instance of the new class
    return t()

e = enum('OK', 'CANCEL', 'QUIT')
print e
print e.OK
print e.CANCEL
print e.QUIT

# Immutable?
e.OK = 'ASDF'  # Does throw the correct exception
print e.OK

输出是:

<__main__.enum object at 0x01FC8F70>
Traceback (most recent call last):
  File "enum.py", line 24, in <module>
    print e.OK
  File "enum.py", line 17, in <lambda>
    setattr(t, attr, property(lambda attr: enums[attr], err_func, err_func))
KeyError: <__main__.enum object at 0x01FC8F70>

也许这不是创建枚举的最佳方式,但它很短,我想更多地探索整个打鸭/打猴子的概念。

4

2 回答 2

1

您的直接问题是gettera作为唯一参数,而不是. 因此,您应该使用类似的东西。propertyselfattrlambda self: val

但是,这不起作用,因为lambda绑定了name val,它在迭代之间发生变化。所以你需要以某种方式包装它:

def getter(val):
    return lambda self: val

for attr, val in enums.iteritems():
    setattr(t, attr, property(getter(val), err_func, err_func))
于 2012-10-18T18:00:34.220 回答
0

最终实现(感谢@nneonneo)。

  • 检查重复的枚举键
  • 检查枚举是否为空
  • 不允许删除或修改枚举项

    def enum(*sequential, **named):
        # Check for duplicate keys
        names = list(sequential)
        names.extend(named.keys())
        if len(set(names)) != len(names):
            raise KeyError('Cannot create enumeration with duplicate keys!')
    
        # Build property dict
        enums = dict(zip(sequential, range(len(sequential))), **named)
        if not enums:
            raise KeyError('Cannot create empty enumeration')
    
        # Function to be called as fset/fdel
        def err_func(*args, **kwargs):
            raise AttributeError('Enumeration is immutable!')
    
        # function to be called as fget
        def getter(cls, val):
            return lambda cls: val
    
        # Create a base type
        t = type('enum', (object,), {})
    
        # Add properties to class by duck-punching
        for attr, val in enums.iteritems():
            setattr(t, attr, property(getter(t, val), err_func, err_func))
    
        # Return an instance of the new class
        return t()
    
于 2012-10-18T19:14:02.333 回答