ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

用于ORM目的的python枚举类

2019-07-01 00:42:47  阅读:342  来源: 互联网

标签:python enumeration sqlalchemy


编辑问题

我正在尝试创建一个类工厂,它可以生成具有以下属性的枚举类:

>从列表中初始化类
允许值(即,它)
自动生成!).
> 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
来源: https://codeday.me/bug/20190630/1341807.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有