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