python中使用duck的不可变枚举

python中使用duck的不可变枚举,python,Python,我正在尝试用python创建一个枚举。我已经看到了几种解决方案(第二个答案是@alec thomas,我最感兴趣),但我想让枚举不可变。我发现一个是不可变的,但我希望有一个类似dict的键/值关联 我试图使用duck punching向类添加属性,如果您试图调用属性上的fset或fdel,该属性将抛出AttributeError 我在定义属性的fget函数时遇到了问题。以下是我目前掌握的代码: def enum(*sequential, **named): # Build propert

我正在尝试用python创建一个枚举。我已经看到了几种解决方案(第二个答案是@alec thomas,我最感兴趣),但我想让枚举不可变。我发现一个是不可变的,但我希望有一个类似dict的键/值关联

我试图使用duck punching向类添加属性,如果您试图调用属性上的
fset
fdel
,该属性将抛出
AttributeError

我在定义属性的
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>

回溯(最近一次呼叫最后一次):
文件“enum.py”,第24行,在
打印e.OK
文件“enum.py”,第17行,在
setattr(t,attr,property(lambda attr:enums[attr],err_func,err_func))
关键错误:

也许这不是创建枚举的最佳方法,但它很短,我想进一步探讨打鸭子/打猴子这一概念。

您当前的问题是
属性的
getter
self
作为唯一的参数,而不是
attr
。因此,您应该使用类似于
lambda self:val
的内容

但是,这不起作用,因为
lambda
绑定了名称
val
,该名称随着迭代而变化。因此,您需要以某种方式包装它:

def getter(val):
    return lambda self: val

for attr, val in enums.iteritems():
    setattr(t, attr, property(getter(val), err_func, err_func))
最终实现(感谢@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()
    

谢谢!最终的解决方案如下:
def getter(cls,val):返回lambda cls:val
。属性是这样插入的:
setattr(t,attr,property(getter(t,val),err_func,err_func))