使用自定义值扩展HTTPStatus的最佳方法

时间:2017-07-11 08:15:13

标签: python-3.x enums

我正在使用自定义值扩展HTTPStatus

from http import HTTPStatus

HTTPStatus.MY_CUSTOM_SERVICE_TIMEOUT = 573

我想知道为什么在检查HTTPStatus时我没有看到这个值:

>>> dir(HTTPStatus)
['ACCEPTED', 'ALREADY_REPORTED', 'BAD_GATEWAY', 'BAD_REQUEST', 'CONFLICT', 'CONTINUE', 'CREATED', 'EXPECTATION_FAILED', 'FAILED_DEPENDENCY', 'FORBIDDEN', 'FOUND', 'GATEWAY_TIMEOUT', 'GONE', 'HTTP_VERSION_NOT_SUPPORTED', 'IM_USED', 'INSUFFICIENT_STORAGE', 'INTERNAL_SERVER_ERROR', 'LENGTH_REQUIRED', 'LOCKED', 'LOOP_DETECTED', 'METHOD_NOT_ALLOWED', 'MOVED_PERMANENTLY', 'MULTIPLE_CHOICES', 'MULTI_STATUS', 'NETWORK_AUTHENTICATION_REQUIRED', 'NON_AUTHORITATIVE_INFORMATION', 'NOT_ACCEPTABLE', 'NOT_EXTENDED', 'NOT_FOUND', 'NOT_IMPLEMENTED', 'NOT_MODIFIED', 'NO_CONTENT', 'OK', 'PARTIAL_CONTENT', 'PAYMENT_REQUIRED', 'PERMANENT_REDIRECT', 'PRECONDITION_FAILED', 'PRECONDITION_REQUIRED', 'PROCESSING', 'PROXY_AUTHENTICATION_REQUIRED', 'REQUESTED_RANGE_NOT_SATISFIABLE', 'REQUEST_ENTITY_TOO_LARGE', 'REQUEST_HEADER_FIELDS_TOO_LARGE', 'REQUEST_TIMEOUT', 'REQUEST_URI_TOO_LONG', 'RESET_CONTENT', 'SEE_OTHER', 'SERVICE_UNAVAILABLE', 'SWITCHING_PROTOCOLS', 'TEMPORARY_REDIRECT', 'TOO_MANY_REQUESTS', 'UNAUTHORIZED', 'UNPROCESSABLE_ENTITY', 'UNSUPPORTED_MEDIA_TYPE', 'UPGRADE_REQUIRED', 'USE_PROXY', 'VARIANT_ALSO_NEGOTIATES', '__class__', '__doc__', '__members__', '__module__']

价值本身可用:

>>> HTTPStatus.MY_CUSTOM_SERVICE_TIMEOUT
573

有什么奇怪的事吗?我应该采用不同的方法吗?

2 个答案:

答案 0 :(得分:2)

那是因为http.HTTPStatusEnum而Python并不是真的,因为Enum是一个通用类型(这就是为什么你可以做你正在做的事情 - 那些语言实际上认为Enum是特殊的东西不会让你像这样一般地混淆它。当然,Python尽最大努力使Enums表现得如此(不可变,可迭代,可映射......)。

在您创建新的collections.OrderedDict类型时,实际上会创建一个Enum._member_map_Enum) - 它会读取成员,为重复项添加别名并添加额外的value -> enum member Enum._value2member_map_ 1}} map为enum.EnumMeta(所有这些都由dir()元类完成)。当你Enum._member_names_枚举时 - 你得到那个地图(或者更确切地说,HTTPStatus.MY_CUSTOM_SERVICE_TIMEOUT = 573列表中可用的枚举名称列表),你在运行时可能已经应用的任何更改都不计算在内(否则它不会不可变的。换句话说,当您执行Enum时,您没有扩展Enum,而只是向有问题的Enum对象添加动态属性。

如果你想添加自定义成员,你应该以常规的OOP方式扩展你的# HERE BE DRAGONS! # DO NOT do this unless you absolutely have to. from http import HTTPStatus def add_http_status(name, value, phrase, description=''): # call our new member factory, it's essentially the `HTTPStatus.__new__` method new_status = HTTPStatus.__new_member__(HTTPStatus, value, phrase, description) new_status._name_ = name # store the enum's member internal name new_status.__objclass__ = HTTPStatus.__class__ # store the enum's member parent class setattr(HTTPStatus, name, new_status) # add it to the global HTTPStatus namespace HTTPStatus._member_map_[name] = new_status # add it to the name=>member map HTTPStatus._member_names_.append(name) # append the names so it appears in __members__ HTTPStatus._value2member_map_[value] = new_status # add it to the value=>member map 类型......除了Python也不允许你这样做。因此,如果你真的坚持做它的运行时你可以,有点,破解内部结构,让Python相信你的枚举值一直存在:

HTTPStatus

现在你可以'真正'在运行时扩展try: print(HTTPStatus(573)) except ValueError as e: print(e) print("MY_CUSTOM_SERVICE_TIMEOUT" in dir(HTTPStatus)) add_http_status("MY_CUSTOM_SERVICE_TIMEOUT", 573, "Custom service timeout") print("MY_CUSTOM_SERVICE_TIMEOUT" in dir(HTTPStatus)) print(HTTPStatus(573)) print(HTTPStatus.MY_CUSTOM_SERVICE_TIMEOUT.value) print(HTTPStatus(573).phrase)

Enum

哪个会给你:

573 is not a valid HTTPStatus
False
True
HTTPStatus.MY_CUSTOM_SERVICE_TIMEOUT
573
Custom service timeout

请记住,如果您想任意扩展enum.EnumMeta.__new__(),请不要使用重复或无效值,否则此代码不会处理别名,重复数据删除和其他您应该做的好事。你会打破它(从某种意义上说,它将无法按预期工作)。检查@CliCommand中采取的其他步骤,以确保其有效性。

答案 1 :(得分:1)

使用aenum library 1 中的extend_enum函数:

import aenum
import http

aenum.extend_enum(http.HTTPStatus, 'CustomTimeout', 537, 'more helpful phrase here')

结果是:

>>> list(http.HTTPStatus)
[<HTTPStatus.CONTINUE: 100>,
...,
<HTTPStatus.CustomTimeout: 537>]

1 披露:我是Python stdlib Enumenum34 backportAdvanced Enumeration (aenum)图书馆的作者。