使用自定义值扩展HTTPStatus的最佳方法 [英] Best way to extend HTTPStatus with custom value
问题描述
我正在使用自定义值扩展 HTTPStatus
:
I am extending HTTPStatus
with a custom value:
from http import HTTPStatus
HTTPStatus.MY_CUSTOM_SERVICE_TIMEOUT = 573
我想知道为什么我在检查 HTTPStatus
时看不到该值:
I am wondering why I do not see that value when inspecting 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
是否发生了奇怪的事情?我应该采取不同的方法吗?
Is there something strange going on? Should I approach this differently?
推荐答案
这是因为 http.HTTPStatus
是 Enum
和Python并没有真正地将 Enum
作为泛型类型(这就是为什么您可以做些什么您正在执行的操作-实际上将 Enum
识别为特殊内容的语言不会让您像这样一般地弄乱它)。当然,Python会尽最大努力使枚举
表现出应有的表现(不可变,可迭代,可映射...)。
That's because http.HTTPStatus
is an Enum
and Python doesn't really, trully have Enum
as a generic type (which is why you can do what you're doing - languages that actually recognize Enum
as something special wouldn't let you mess with it like this in general). Of course, Python does its best to make Enums
behave as they should (immutable, iterable, mappable...).
实际上在创建新内容时( Enum._member_map _
)下面有一个 collections.OrderedDict
枚举
类型-读取成员,为重复项添加别名,并添加其他 value->枚举成员
映射为 Enum._value2member_map _
(所有操作均由 enum.EnumMeta
元类)。当您 dir()
枚举时-您会得到该映射(或更确切地说, Enum中可用的枚举名称列表。_member_names_
列表),并且您在运行时可能已应用的任何更改均不计入(否则,它不会是不变的)。换句话说,当您执行 HTTPStatus.MY_CUSTOM_SERVICE_TIMEOUT = 573
时,您并没有扩展 Enum
,您只是向有问题的 Enum
对象中添加动态属性。
There is actually a collections.OrderedDict
underneath (Enum._member_map_
) that gets created when you create a new Enum
type - it reads in the members, aliases the duplicates and adds an additional value -> enum member
map as Enum._value2member_map_
(all of that is done by the enum.EnumMeta
metaclass). When you dir()
an enum - you get that map (or more precisely, the enum names list available in the Enum._member_names_
list) and any changes you may have applied at runtime doesn't count (otherwise it wouldn't be immutable). In other words, when you do HTTPStatus.MY_CUSTOM_SERVICE_TIMEOUT = 573
you're not extending the Enum
, you're just adding a dynamic property to the Enum
object in question.
您应扩展 Enum
如果您想添加自定义成员,则键入常规的OOP方式,但是Python也不允许您这样做。因此,如果您真的坚持要在运行时进行操作,则可以修改内部结构,使Python相信您的枚举值始终存在:
You should extend your Enum
types the regular, OOP way if you want to add custom members... except Python won't let you do this either. So if you really insist doing it run-time you can, kind of, hack the internal structure to make Python believe your enum value was there all along:
# 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
现在您可以在运行时真正扩展 HTTPStatus
:
And now you can 'really' extend the HTTPStatus
at runtime:
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)
哪个会给你:
573 is not a valid HTTPStatus
False
True
HTTPStatus.MY_CUSTOM_SERVICE_TIMEOUT
573
Custom service timeout
请记住,如果您想任意扩展 Enum $ c,则此代码不会处理别名,重复数据删除和其他您绝对应该做的事情。 $ c>,因此请勿使用重复或无效的值,否则您将其破坏(在某种意义上,此后它将无法正常工作)。检查
enum.EnumMeta .__ new __()
中采取的其他步骤,以确保其有效性。
Keep in mind that this code doesn't handle aliasing, de-duplication and other nice things that you should absolutely be doing if you want to arbitrary extend an Enum
so don't use duplicate or invalid values or you'll break it (in a sense it won't work as expected afterwards). Check the additional steps taken in enum.EnumMeta.__new__()
to ensure its validity.
这篇关于使用自定义值扩展HTTPStatus的最佳方法的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!