当前位置: 代码网 > it编程>前端脚本>Python > Python项目中5个Enum枚举巧妙操作详解

Python项目中5个Enum枚举巧妙操作详解

2025年10月15日 Python 我要评论
刚开始在别人的代码里看到枚举,我其实觉得有点多余。用字符串或者数字常量不也一样吗?为啥非要整个enum出来,感觉是把简单问题复杂化了。但后来我接手了一个后端项目,里面有大量的常量定义,我才慢慢体会到e

刚开始在别人的代码里看到枚举,我其实觉得有点多余。用字符串或者数字常量不也一样吗?为啥非要整个enum出来,感觉是把简单问题复杂化了。但后来我接手了一个后端项目,里面有大量的常量定义,我才慢慢体会到enum的好处。它不仅能让代码更整洁,还能强制保证代码的一致性,避免了很多低级错误。

在开始之前,先提一嘴我的开发环境。我现在用的是servbay,堪称神器。

以前要部署python环境,版本切来切去特别麻烦,尤其是有时候要维护一个python 2的老项目,同时又要写python 3的新功能。servbay能一键部署好python环境,而且可以自由切换不同的python版本,甚至让多个版本同时运行。不管是python 2.x的老古董还是最新的python 3.x,它都能轻松搞定。

环境干净又卫生,我才能把心思花在琢磨代码本身上,比如怎么用好enum这种细节。

下面我就分享几个我在实际项目里用过的enum模式,没什么高深理论,都是些实实在在帮我解决过问题的小技巧。

用enum替换字符串

这是enum最基础,也是最常见的用法。以前,我的代码里可能有很多这样的硬编码字符串:

# 不推荐的写法
def check_ticket_status(status_string):
    if status_string == "open":
        print("工单开启")
    elif status_string == "in_progress":
        print("工单处理中")
    elif status_string == "closed":
        print("工单已关闭")

这么写也能跑,但问题很大。status参数可以传入任何字符串,一不小心手滑,把closed写成了closed,这个条件就匹配不上了,bug就这么悄无声息地出现了。

换成enum之后,代码就严谨多了:

from enum import enum

class ticketstate(enum):
    open = "open"
    in_progress = "in_progress"
    closed = "closed"

def handle_ticket(state: ticketstate):
    if state == ticketstate.open:
        print("工单当前状态:开启")
    elif state == ticketstate.in_progress:
        print("工单当前状态:处理中")
    elif state == ticketstate.closed:
        print("工单当前状态:已关闭")

# 调用时必须使用enum成员,非常安全
handle_ticket(ticketstate.in_progress)

现在,如果有人想传入一个ticketstate里没有定义的值,静态类型检查工具(比如mypy)直接就会报警,代码在运行前就能发现问题。这就从根源上杜绝了拼写错误导致的bug。在处理后端服务各种复杂状态流转时,这个模式特别有用。

值为整数的enum

我之前维护过一个很老的系统,对接的api返回的状态码全是数字,比如1代表成功,2代表失败。代码里到处是if status_code == 1:这种数字,过段时间别说别人,我自己都看不懂了。

如果用enum改造了这部分代码,可读性大大提高:

from enum import intenum

# 使用intenum,它继承自int,行为更像整数
class apierrorcode(intenum):
    ok = 0
    invalid_params = 1001
    server_error = 2001
    auth_failure = 3001

def process_api_response(code: int):
    try:
        error_code = apierrorcode(code)
    except valueerror:
        print(f"收到未知的错误码: [code]")
        return

    if error_code == apierrorcode.ok:
        print("请求成功")
    elif error_code == apierrorcode.server_error:
        print("服务端内部错误")
    else:
        print(f"业务错误: {error_code.name}")

# 示例调用
process_api_response(0)
process_api_response(3001)
process_api_response(9999) # 测试未知错误码

这里用了intenum,它是enum的一个变体,成员的值都是整数,并且可以直接和整数进行比较。通过apierrorcode(code)可以方便地把整数转换为enum成员,如果转换失败则说明收到了一个未定义的错误码,逻辑清晰了很多。

在数据库模型里使用enum

在数据库里存一些固定选项的字段时,enum也特别好用。比如,文章的发布状态,可能只有“草稿”、“已发布”、“已归档”三种。直接在数据库里存字符串当然可以,但在代码的模型层用enum来约束会更安全。

from enum import enum
from pydantic import basemodel
from typing import list

class articlestatus(str, enum):
    draft = "draft"
    published = "published"
    archived = "archived"

class article(basemodel):
    id: int
    title: str
    status: articlestatus
    tags: list[str]

# 创建一个文章实例
new_article = article(
    id=101, 
    title="python enum使用技巧", 
    status=articlestatus.published,
    tags=["python", "enum"]
)

# articlestatus.published可以直接作为字符串参与json序列化
print(new_article.model_dump_json())
# 输出: {"id":101,"title":"python enum使用技巧","status":"published","tags":["python","enum"]}

注意这里的写法:class articlestatus(str, enum):。通过多继承一个str,这个enum的成员就拥有了字符串的所有特性。这样做的好处是,它能和pydantic、sqlalchemy这类库无缝集成,序列化成json或者存入数据库时,它表现得就像一个普通的字符串,但在代码层面,它又保留了enum的类型安全和代码补全等优点。

给enum添加方法

这个玩法是我后来才发现的,在enum内部直接定义方法。一开始我觉得挺奇怪的,enum不是用来放常量的吗?但如果某些逻辑和这个enum强绑定,那把方法写在里面就非常合适。

from enum import enum

class environment(enum):
    dev = "development"
    test = "testing"
    prod = "production"

    @property
    def is_production(self) -> bool:
        return self == environment.prod

    def get_db_url(self) -> str:
        # 实际项目中,这里可能会从配置中读取
        db_urls = {
            "development": "sqlite:///dev.db",
            "testing": "sqlite:///test.db",
            "production": "postgresql://user:pass@host/prod_db"
        }
        return db_urls[self.value]

# 使用起来非常直观
current_env = environment.prod
print(f"当前环境: {current_env.value}")
print(f"是否为生产环境: {current_env.is_production}")
print(f"数据库连接: {current_env.get_db_url()}")

每个environment的成员自己就知道对应的数据库连接,还能判断自己是不是生产环境。这比在外面写一堆if current_env == "production":要优雅和内聚得多。

自动赋值的enum

有时候,enum成员的名字和值是一样的,一个个手动敲赋值会显得很啰嗦。比如,我们要定义一组操作权限。

enum模块里的auto()可以帮我们偷懒:

from enum import enum, auto

class permission(enum):
    # 重写这个特殊方法,让auto()返回成员的大写字符串名
    def _generate_next_value_(name, start, count, last_values):
        return name.upper()

    read = auto()
    write = auto()
    execute = auto()
    delete = auto()

# 验证一下
print(f"read permission value: {permission.read.value}")  # 输出: read permission value: read
print(f"delete permission value: {permission.delete.value}") # 输出: delete permission value: delete

通过重写内部的_generate_next_value_方法,我们可以自定义auto()的行为。这里我让它自动将成员名转为大写作为值,这样定义permission时就非常简洁,完全不用写等号右边的部分。

总结一下

以上就是我实际在项目中用得比较多的五种enum模式。刚开始我觉得enum是形式主义,但随着项目越来越复杂,我越来越喜欢用它。它让代码变得更严格,但也更清晰。

当然,如果代码很简单,或者只有一两个常量,我可能还是会直接用字符串。但对于中大型项目,特别是多人协作的时候,enum真的能帮我们避免很多因为常量写错、不一致导致的傻瓜式错误。

总的来说,enum不是什么高深的技巧,但用在对的地方,确实能让代码质量上一个台阶。就像一个好用的开发工具 servbay 能帮我们摆平环境配置的烦恼一样,好的代码习惯能让我们在几个月后回头看自己的代码时,少一些头疼,多一份舒心。

以上就是python项目中5个enum枚举巧妙操作详解的详细内容,更多关于python enum枚举的资料请关注代码网其它相关文章!

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2025  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com