編輯問題
我正在嘗試創建一個類工廠,它可以生成具有以下屬性的枚舉類:
>從列表中初始化類
允許值(即,它)
自動生成!).
> Class創建自己的一個實例
對于每個允許的值.
>類不允許創建
任何其他實例一旦
上述步驟已完成(任何嘗試
這樣做會導致異常).
>類實例提供了一種方法
這個,給定一個值,返回一個
參考相應的
實例.
>類實例只有兩個
屬性:id和value.該
屬性id自動遞增
每個新實例;屬性
value是實例的值
代表.
>類是可迭代的.我更喜歡
使用the accepted
answer to another SO question實現這一點
(具體來說,通過利用課程
注冊表和定義iter
我的元類中的方法
枚舉類是實例).
這就是我正在尋找的.請將原始文本(下方)視為問題的背景.很抱歉從一開始就不清楚.
更新的答案
我對aaronasterling的非常有用的答案做了一些修改.我以為我會在這里展示,以便其他人可以受益,所以如果我做錯了,我會收到更多評論:)
我做的修改是:
(0)移植到p3k(iteritems – > items,元類 – >’metaclass =’,無需指定對象作為基類)
(1)將實例方法更改為@classmethod(現在我不需要對象來調用它,只需要類)
(2)我不是一舉填充_registry,而是每次構造一個新元素時都更新它.這意味著我可以使用它的長度來設置id,所以我擺脫了_next_id屬性.它也適用于我計劃的擴展(見下文).
(3)從enum()中刪除了classname參數.畢竟,該類名將是一個本地名稱;無論如何,全局名稱必須單獨設置.所以我使用了一個虛擬’XXX’作為本地類名.我有點擔心第二次調用該函數時會發生什么,但它似乎有效.如果有人知道原因,請告訴我.如果這是一個壞主意,我當然可以在每次調用時自動生成一個新的本地類名.
(4)擴展此類以允許用戶添加新的枚舉元素的選項.具體來說,如果使用不存在的值調用instance(),則會創建相應的對象,然后由該方法返回.如果我從解析文件中獲取大量枚舉值,這將非常有用.
def enum(values):
class EnumType(metaclass = IterRegistry):
_registry = {}
def __init__(self, value):
self.value = value
self.id = len(type(self)._registry)
type(self)._registry[value] = self
def __repr__(self):
return self.value
@classmethod
def instance(cls, value):
return cls._registry[value]
cls = type('XXX', (EnumType, ), {})
for value in values:
cls(value)
def __new__(cls, value):
if value in cls._registry:
return cls._registry[value]
else:
if cls.frozen:
raise TypeError('No more instances allowed')
else:
return object.__new__(cls)
cls.__new__ = staticmethod(__new__)
return cls
原文
我使用SQLAlchemy作為對象關系映射工具.它允許我將類映射到SQL數據庫中的表.
我有幾節課.一個類(Book)是具有一些實例數據的典型類.其他(流派,類型,封面等)都是基本的枚舉類型;例如,流派只能是’科幻’,’浪漫’,’漫畫’,’科學’;封面只能是“硬”,“軟”;等等. Book與其他每個類之間存在多對一的關系.
我想半自動生成每個枚舉樣式的類.請注意,SQLAlchemy要求’scifi’表示為類Genre的實例;換句話說,簡單地定義Genre.scifi = 0,Genre.romance = 1等是行不通的.
我嘗試編寫一個元類枚舉,它接受類的名稱和允許值列表作為參數.我希望如此
Genre = enum('Genre', ['scifi', 'romance', 'comic', 'science'])
會創建一個允許這些特定值的類,并且還可以創建我需要的每個對象:類型(‘科幻’),流派(‘浪漫’)等.
但我被卡住了.一個特別的問題是,在ORM意識到這個類之前我無法創建Genre(‘scifi’);另一方面,當ORM知道Genre時,我們不再是類構造函數了.
另外,我不確定我的方法是好的開始.
任何意見,將不勝感激.
解決方法:
基于更新的新答案
我認為這滿足了您所有指定的要求.如果沒有,我們可以添加你需要的任何東西.
def enum(classname, values):
class EnumMeta(type):
def __iter__(cls):
return cls._instances.itervalues()
class EnumType(object):
__metaclass__ = EnumMeta
_instances = {}
_next_id = 0
def __init__(self, value):
self.value = value
self.id = type(self)._next_id
type(self)._next_id += 1
def instance(self, value):
return type(self)._instances[value]
cls = type(classname, (EnumType, ), {})
instances = dict((value, cls(value)) for value in values)
cls._instances = instances
def __new__(cls, value):
raise TypeError('No more instances allowed')
cls.__new__ = staticmethod(__new__)
return cls
Genre = enum('Genre', ['scifi', 'comic', 'science'])
for item in Genre:
print item, item.value, item.id
assert(item is Genre(item.value))
assert(item is item.instance(item.value))
Genre('romance')
老答案
回應你對Noctis Skytower答案的評論,其中你說你想要Genre.comic =流派(‘漫畫’)(未經測試):
class Genre(GenreBase):
genres = ['comic', 'scifi', ... ]
def __getattr__(self, attr):
if attr in type(self).genres:
self.__dict__[attr] = type(self)(attr)
return self.__dict__[attr]
這將創建一個類型實例,以響應嘗試訪問它并將其附加到請求它的實例.如果您希望將它附加到整個類,請替換該行
self.__dict__[attr] == type(self)(attr)
同
type(self).__dict__[attr] = type(self)(attr)
這使得所有子類都創建子類的實例以響應請求.如果您希望子類創建Genre的實例,請將type(self)(attr)替換為Genre(attr)
標簽:python,enumeration,sqlalchemy