I have an enum defined like this:
def enum(**enums):
return type('Enum', (), enums)
Status = enum(
STATUS_OK=0,
STATUS_ERR_NULL_POINTER=1,
STATUS_ERR_INVALID_PARAMETER=2)
I have a function that returns status as Status enum.
How can I get the name of the enum value, and not just value?
>>> cur_status = get_Status()
>>> print(cur_status)
1
I would like to get STATUS_ERR_NULL_POINTER, instead of 1
You'd have to loop through the class attributes to find the matching name:
name = next(name for name, value in vars(Status).items() if value == 1)
The generator expression loops over the attributes and their values (taken from the dictionary produced by the vars() function) then returns the first one that matches the value 1.
Enumerations are better modelled by the enum library, available in Python 3.4 or as a backport for earlier versions:
from enum import Enum
class Status(Enum):
STATUS_OK = 0
STATUS_ERR_NULL_POINTER = 1
STATUS_ERR_INVALID_PARAMETER = 2
giving you access to the name and value:
name = Status(1).name # gives 'STATUS_ERR_NULL_POINTER'
value = Status.STATUS_ERR_NULL_POINTER.value # gives 1
2021 update:
These answers are out of date. Using Python's standard Enum class,
cur_status.name
will return the name. (STATUS_ERR_NULL_POINTER)
To look up the enum knowing the name:
s = Status['STATUS_ERR_NULL_POINTER']
Not sure which python version it was introduced, but the hidden attribute _value2member_map_ gives you what you want.
class Status(Enum):
STATUS_OK=0
STATUS_ERR_NULL_POINTER=1
STATUS_ERR_INVALID_PARAMETER=2
str(Status._value2member_map_[1])
Out:
'Status.STATUS_ERR_NULL_POINTER'
You don't need to loop through the Enum class but just access _member_map_.
>>> Status._member_map_['STATUS_OK']
<Status.STATUS_OK: 0>
For some reason, most of the methods above did not work for me. All methods return the Enum type as an integer. I'm working with Python 3.7.
In my solution, I defined class function to handle this. It's not purely pythonic, but worked well enough for my case.
from enum import Enum
class Status(Enum):
STATUS_OK = 0
STATUS_ERR_NULL_POINTER = 1
STATUS_ERR_INVALID_PARAMETER = 2
#classmethod
def name(cls,val):
return { v:k for k,v in dict(vars(cls)).items() if isinstance(v,int)}.get(val,None)
# test it
stat = Status.STATUS_OK
print(Status.name(stat))
Prints: 'STATUS_OK'
It may seem obvious that we asked for the status after giving it the status, but in my case, this is set programmatically elsewhere
Related
I need to reassign the attribute value in Enum.
from enum import Enum
class Number(Enum):
number = "1"
Number.number = "2" # AttributeError: cannot reassign member 'number'
I tried to reassign the attribute, but I got:
AttributeError: cannot reassign member 'number'
Author's note: This is a horrible idea.
Let's just delete the string "1" from Python and replace it with "2"
from ctypes import c_byte
from enum import Enum
from sys import getsizeof
def change_enum_value(old: object, new: object) -> None:
"""
Assigns contents of new object to old object.
The size of new and old objection should be identical.
Args:
old (Any): Any object
new (Any): Any object
Raises:
ValueError: Size of objects don't match
Faults:
Segfault: OOB write on destination
"""
src_s, des_s = getsizeof(new), getsizeof(old)
if src_s != des_s:
raise ValueError("Size of new and old objects don't match")
src_arr = (c_byte * src_s).from_address(id(new))
des_arr = (c_byte * des_s).from_address(id(old))
for index in range(len(des_arr)):
des_arr[index] = src_arr[index]
class Number(Enum):
number = "1"
change_enum_value(Number.number.value, "2")
print(Number.number.value) # 2
You don't have the "1" anymore, quite literally.
>>> "1"
'2'
>>>
which sure is a tad concerning...
When using Enum, your number = "1" is considered as one of the enum entry.
So, firstly, it is thought to be used this way, for example :
from enum import Enum
class Number(Enum):
numberOne = "1"
numberTwo = "2"
By the way, when you access to Number.number, you access to the "enum item", which is more than just the value. The item has, indeed, a name (number) and a value ("1").
So, in theory, the good way to change the value should be :
Number.number.value = "2"
But, in any was, an Enum is made to not be mutable. So you can't do that anyway.
Conceptually, an Enum is a way to give "nicer names" to "constants" and then use the nicer names in your code rather than using the constants.
This intent is implemented by Python by making the enum members as "functionally constant" ... See Python Enum Documentation.
The names associated with Enums are not like variables.
Hence you cannot change the value of the name.
If you read the enum Python documentation:
The attributes, in your case Number.number, etc., are enumeration members (or members) and are functionally constants.
I have a dataclass and I use it as a constant store.
#dataclass
class MyClass:
CONSTANT_1 = "first"
CONSTANT_2 = "second"
I have a function:
def my_func(value: ?):
print(value)
I want to add annotation to my function to specify that possible value is one of attribute of MyClass
How to do it (I am using python 3.10) ?
Hopefully I not misunderstand the ask, please let me know if so. But I think in this case is best to use Enum type in python.
Here is a simple example:
from enum import Enum
class MyEnum(Enum):
CONSTANT_1 = "first"
CONSTANT_2 = "second"
Then to answer the second part, for annotation the ? becomes a MyEnum. This means any enum member of this type, but not the type (class) itself.
def my_func(value: MyEnum):
print(value, value.name, value.value)
Putting it all together, it becomes like:
from enum import Enum
class MyEnum(Enum):
CONSTANT_1 = "first"
CONSTANT_2 = "second"
def my_func(value: MyEnum):
# technically you can remove this check
if not isinstance(value, MyEnum):
return
print(value, value.name, value.value)
# note below: only type checker or ide complain, but code still runs fine
my_func('hello') # not OK!
my_func('second') # not OK!
my_func(MyEnum) # not OK!
my_func(MyEnum.CONSTANT_1) # OK
I think you're asking an XY problem. From your response in the comments, it seems like what you want is rather:
Have a class-like interface to hold a bunch of constant values.
Constraint the argument to only take the above values.
As as mentioned in rv.kvetch's answer, the conventional way of doing this is to use enums. I'm not sure what you mean by "wanting to skip .value", the value field of an enum simply gives you what's associated with that enum, and I would say that it's not important at all. Here's an example:
class StrEnum(enum.Enum):
FIRST = "first"
SECOND = "second"
class StrEnum2(enum.Enum):
FIRST = "first"
SECOND = "second"
print(StrEnum.FIRST.value) # first
print(StrEnum2.FIRST.value) # first
print(StrEnum.FIRST.value == StrEnum2.FIRST.value) # True
print(StrEnum.FIRST == StrEnum2.FIRST) # False
class IntEnum(enum.Enum):
FIRST = enum.auto()
SECOND = enum.auto()
print(IntEnum.FIRST.value) # 1
print(IntEnum.SECOND.value) # 2
What I want to show with this example are two things:
You don't really need .value at all if you're just comparing enums.
You don't even need to manually assign values to the enums; you can use enum.auto() to auto-assign a unique value to it.
Because at the end of the day, enums themselves already represent a choice among valid choices, so it doesn't matter what values it has.
That said, if what you want is just to put a type constraint on what values an argument can type, and not have to use enums, then you can use the Literal type. See this answer for details. For your example, you could do something like:
from typing import Literal, Final
def my_func(value: Literal["first", "second"]):
print(value)
my_func("first") # ok
my_func("not first") # bad
x = "first"
y: Final = "first"
my_func(x) # bad, because `x` is not final
my_func(y) # ok
But note that type annotations don't really prevent you from calling a function with an invalid value, it's just a hint for IDEs and type checkers.
Is it possible to have something like
class MyAbstract {
final int myFieldSomebodyHasToDefine;
}
class MyAbstractImplementation extends MyAbstract {
final int myFieldSomebodyHasToDefine = 5;
}
using dataclasses in python?
If you are working with a python interpreter before version 3.8, there is no straightforward way. However, since python 3.8, the final decorator has been added to the language. After importing it from the typing module in python, you can use it for methods and classes.
You may also use FINAL type for values.
Here is an example
from typing import final, Final
#final
class Base:
#final
def h(self)->None:
print("old")
class Child(Base):
# Bad overriding
def h(self) -> None:
print("new")
if __name__ == "__main__":
b = Base()
b.h()
c = Child()
c.h()
RATE: Final = 3000
# Bad value assignment
RATE = 7
print(RATE)
Important note: Python does not force the developer with final and FINAL. You can yet change the values upon your wish. The decorators of mostly informative for developers.
For more information, you may visit: https://peps.python.org/pep-0591/
Update: This is also an instance for dataclass
#dataclass
class Item:
"""Class for keeping track of an item in inventory."""
price: float
quantity_on_hand: int = 0
name:Final[str] = "ItemX"
def total_cost(self) -> float:
return self.unit_price * self.quantity_on_hand
As you can see, name is a final field. However, you must put the final values with a default value below all of the fields without an initial value.
While learning about how classes work in Python I came across a class definition example which behaved kind of strangely in my eyes.
The purpose of the example was to demonstrate how the behaviour of a static variable can be achieved in Python. The example was written as follows:
class MemberCounter:
members = 0
def init(self):
MemberCounter.members += 1
m1 = MemberCounter()
m1.init()
m2 = MemberCounter()
m2.init()
after setting up the class and creating the objects, I printed the values of the 'members' attribute. These were the results:
MemberCounter.members = 2
m1.members = 2
m2.members = 2
And that's when I got confused. While I was expecting for 'MemberCounter.members = 2' the two other results made no sense to me - why would both of 'm1' and 'm2' objects' 'members' value be equal to 2? I thought that both of the values should have been 0 - if the only attribute that was chaged is the 'members' attribute which was attached to the MemberCounter class why would it cause any change to the own unique 'members' value of each of the class' objects. It looks like the fact that the 'members' attribute is addresed like 'MemberCounter.members += 1' in the init() function of each object, completely overrides the unique values which m1.members and m2.members refer to and redirects their pointers to the MemberCounter.members value making all the three pointers point at the same value
==> m1.members = m2.members = MemberCounter.members.
Moreover, I have tried defining the class in an opossite way (Increasing self.members instead of MemberCounter.members):
class MemberCounter:
members = 0
def init(self):
self.members += 1
m1 = MemberCounter()
m1.init()
m2 = MemberCounter()
m2.init()
This definition yielded logical results (which got me curious about the above mentioned strange behaviour even more):
MemberCounter.members = 0
m1.members = 1
m2.members = 1
In short, I was curious about why the first class definition behaves in such a strange way? Why the mere 'MemberCounter.members += 1' statement completely erased 'm1.members' and 'm2.members' own unique value and made it equal to the MemberCounter.members value.
I hope I was able to clearly present my problem and I will be extremly happy to get an insight about this strange behaviour :)
That you can read a static attribute with instance.attribute notation as alternative to the more natural class.attribute notation, is an intended feature in Python.
From the documentation:
Both static data and static methods (in the sense of C++ or Java) are supported in Python.
For static data, simply define a class attribute. To assign a new
value to the attribute, you have to explicitly use the class name in
the assignment:
class C:
count = 0 # number of times C.__init__ called
def __init__(self):
C.count = C.count + 1
def getcount(self):
return C.count # or return self.count
c.count also refers to C.count for any c such that
isinstance(c, C) holds, unless overridden by c itself or by some
class on the base-class search path from c.__class__ back to C.
Caution: within a method of C, an assignment like self.count = 42
creates a new and unrelated instance named “count” in self’s own dict.
Rebinding of a class-static data name must always specify the class
whether inside a method or not:
C.count = 314
The paragraph just below the first code block explains your doubts. The "Caution" paragraph explains what you found logical.
In Python, I've been creating enums using the enum module. Usually with the int-version to allow conversion:
from enum import IntEnum
class Level(IntEnum):
DEFAULTS = 0
PROJECT = 1
MASTER = 2
COLLECT = 3
OBJECT = 4
I would like to provide some type of invalid or undefined value for variables of this type. For example, if an object is initialized and its level is currently unknown, I would like to create it by doing something like self.level = Level.UNKNOWN or perhaps Level.INVALID or Level.NONE. I usually set the internal value of these special values to -1.
The type of problems I keep running into is that adding any special values like this will break iteration and len() calls. Such as if I wanted to generate some list to hold each level type list = [x] * len(Level), it would add these extra values to the list length, unless I manually subtract 1 from it. Or if I iterated the level types for lvl in Level:, I would have to manually skip over these special values.
So I'm wondering if there is any clever way to fix this problem? Is it pointless to even create an invalid value like this in Python? Should I just be using something like the global None instead? Or is there some way to define the invalid representation of the enumerator so that it doesn't get included in iteration or length logic?
The answer to this problem is similar to the one for Adding NONE and ALL to Flag Enums (feel free to look there for an in-depth explanation; NB: that answer uses a class-type decorator, while the below is a function-type decorator).
def add_invalid(enumeration):
"""
add INVALID psuedo-member to enumeration with value of -1
"""
#
member = int.__new__(enumeration, -1)
member._name_ = 'INVALID'
member._value_ = -1
enumeration._member_map_['INVALID'] = member
enumeration._value2member_map_[-1] = member
return enumeration
Which would look like
#add_invalid
class Level(IntEnum):
DEFAULTS = 0
PROJECT = 1
MASTER = 2
COLLECT = 3
OBJECT = 4
and in use:
>>> list(Level)
[<Level.DEFAULTS: 0>, <Level.PROJECT: 1>, <Level.MASTER: 2>, <Level.COLLECT: 3>, <Level.OBJECT: 4>]
>>> type(Level.INVALID)
<enum 'Level'>
>>> Level.INVALID
<Level.INVALID: -1>
>>> Level(-1)
<Level.INVALID: -1>
>>> Level['INVALID']
<Level.INVALID: -1>
There are a couple caveats to this method:
it is using internal enum structures that may change in the future
INVALID, while not showing up normally, is otherwise an Enum member (so cannot be changed, deleted, etc.)
If you don't want to use internal structures, and/or you don't need INVALID to actually be an Enum member, you can instead use the Constant class found here:
class Constant:
def __init__(self, value):
self.value = value
def __get__(self, *args):
return self.value
def __repr__(self):
return '%s(%r)' % (self.__class__.__name__, self.value)
Which would look like
class Level(IntEnum):
#
DEFAULTS = 0
PROJECT = 1
MASTER = 2
COLLECT = 3
OBJECT = 4
#
INVALID = Constant(-1)
and in use:
>>> Level.INVALID
-1
>>> type(Level.INVALID)
<class 'int'>
>>> list(Level)
[<Level.DEFAULTS: 0>, <Level.PROJECT: 1>, <Level.MASTER: 2>, <Level.COLLECT: 3>, <Level.OBJECT: 4>]
The downside to using a custom descriptor is that it can be changed on the class; you can get around that by using aenum1 and its built-in constant class (NB: lower-case):
from aenum import IntEnum, constant
class Level(IntEnum):
#
DEFAULTS = 0
PROJECT = 1
MASTER = 2
COLLECT = 3
OBJECT = 4
#
INVALID = constant(-1)
and in use:
>>> Level.INVALID
-1
>>> Level.INVALID = None
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/home/ethan/.local/lib/python3.6/site-packages/aenum/__init__.py", line 2128, in __setattr__
'%s: cannot rebind constant %r' % (cls.__name__, name),
AttributeError: Level: cannot rebind constant 'INVALID'
1 Disclosure: I am the author of the Python stdlib Enum, the enum34 backport, and the Advanced Enumeration (aenum) library.
Idiomatically speaking, when you use an enumerator it is because you know without a doubt everything will fall into one of the enumerated categories. Having a catch-all "other" or "none" category is common.
If the level of an item isn't known at the time of creation, then you can instantiate all objects with the "unknown" level unless you supply it another level.
Is there a particular reason you are treating these internally with a -1 value? Are these levels erroneous, or are they having an "unknown" level valid?