How can I get all instances of a enum class that fulfill a criterion on a given, but unknown field?
I have the following three classes deriving from Enum, two simple ones (VariableType and VariableCategory) and one, in which the instances actually store content in fields (Variable).
class VariableCategory(Enum):
SHORT = auto()
MEDIUM = auto()
LONG = auto()
class VariableType(Enum):
ONE = auto()
TWO = auto()
class Variable(Enum):
A = ('Variable A',
VariableCategory.SHORT,
VariableType.ONE,
1)
B = ('Variable B',
VariableCategory.MEDIUM,
VariableType.TWO,
2)
C = ('Variable V',
VariableCategory.SHORT,
VariableType.TWO,
3)
def __init__(self, name: str, variable_category: VariableCategory, variable_type: VariableType,
number:int) -> None:
self.name = name
self.variable_category = variable_category
self.variable_type = variable_type
self.number = number
Now, I would like to create a class function, that can take an arbitrary parameter of types VariableType or VariableCategory and return all corresponding instances of Variable:
#classmethod
def by_x(cls, x: Union[VariableType, VariableCategory]):
# something like return [cls[member] for member in cls.__members__ if x in cls[member]]
For example, given a VariableType the according instances of Variable, as in:
>>> Variable.by_x(VariableType.ONE)
[Variable.A]
>>> Variable.by_x(VariableType.TWO)
[Variable.B, Variable.C]
>>> Variable.by_x(VariableCategory.SHORT)
[Variable.A, Variable.C]
>>> Variable.by_x(VariableCategory.MEDIUM)
[Variable.B]
>>> Variable.by_x(VariableCategory.LONG)
[]
I also know, that I can use more specific functions for one of the fields of the Variable enum:
#classmethod
def by_variable_type(cls, variable_type: VariableType) -> List['Variable']:
return [cls[member] for member in cls.__members__ if cls[member].variable_type == variable_type]
However, I do not known, how to make just one, generic function instead of many specific ones (that is, without checking the type of the parameter and calling the specific solution accordingly).
#classmethod
def by_x(cls, criterion):
return [
m
for m in cls
if m.variable_category == criterion
or m.variable_type == criterion
]
As an aside, you cannot assign to self.name -- it raises an AttributeError.
This solution does what you want.
The get_by_values class method can receive a list, or list of lists with any combination of values you want satisfied. It guarantees to only return members that fully satisfied one of the combinations you passed in. It returns a set of Enum members, so guaranteeing there aren't any repetitions. If you used a list in the return, you would get ordered results corresponding to every argument you passed in.
Variable members already have Variable.name you declare them with. In case you want something more expressive I'd go with __str__ or __init__, perhaps combining with VariableCategory.name and VariableType.name.
from enum import Enum, auto
class VariableCategory(Enum):
SHORT = auto()
MEDIUM = auto()
LONG = auto()
class VariableType(Enum):
ONE = auto()
TWO = auto()
class Variable(Enum):
A = VariableCategory.SHORT, VariableType.ONE
B = VariableCategory.MEDIUM, VariableType.TWO
C = VariableCategory.SHORT, VariableType.TWO
def __init__(self, variable_category: VariableCategory, variable_type: VariableType):
self.variable_category = variable_category
self.variable_type = variable_type
def in_value(self, argument):
if set(argument).issubset(set(self.value)):
return self
#classmethod
def get_by_values(cls, *args):
return {member for member in cls
if member in {member.in_value(arg) for arg in args}}
first = [VariableCategory.SHORT, VariableType.ONE]
second = [VariableCategory.SHORT]
print(*Variable.get_by_values(first, second))
# Variable.A Variable.C
print(*Variable.get_by_values([VariableCategory.MEDIUM]))
# Variable.B
print(*Variable.get_by_values([VariableType.TWO]))
# Variable.B Variable.C
EDIT: Replaced for's with set comprehensions. The expanded for's section is below:
def in_value(self, argument):
for element in argument:
if element not in self.value:
return False
return True
#classmethod
def get_by_values(cls, *args):
result = list()
for one_member in Variable:
for argument in args:
if one_member.in_value(argument):
if one_member not in result:
result.append(one_member)
return result
Related
Given a class with class methods that contain only self input:
class ABC():
def __init__(self, input_dict)
self.variable_0 = input_dict['variable_0']
self.variable_1 = input_dict['variable_1']
self.variable_2 = input_dict['variable_2']
self.variable_3 = input_dict['variable_3']
def some_operation_0(self):
return self.variable_0 + self.variable_1
def some_operation_1(self):
return self.variable_2 + self.variable_3
First question: Is this very bad practice? Should I just refactor some_operation_0(self) to explicitly take the necessary inputs, some_operation_0(self, variable_0, variable_1)? If so, the testing is very straightforward.
Second question: What is the correct way to setup my unit test on the method some_operation_0(self)?
Should I setup a fixture in which I initialize input_dict, and then instantiate the class with a mock object?
#pytest.fixture
def generator_inputs():
f = open('inputs.txt', 'r')
input_dict = eval(f.read())
f.close()
mock_obj = ABC(input_dict)
def test_some_operation_0():
assert mock_obj.some_operation_0() == some_value
(I am new to both python and general unit testing...)
Those methods do take an argument: self. There is no need to mock anything. Instead, you can simply create an instance, and verify that the methods return the expected value when invoked.
For your example:
def test_abc():
a = ABC({'variable_0':0, 'variable_1':1, 'variable_2':2, 'variable_3':3))
assert a.some_operation_0() == 1
assert a.some_operation_1() == 5
If constructing an instance is very difficult, you might want to change your code so that the class can be instantiated from standard in-memory data structures (e.g. a dictionary). In that case, you could create a separate function that reads/parses data from a file and uses the "data-structure-based" __init__ method, e.g. make_abc() or a class method.
If this approach does not generalize to your real problem, you could imagine providing programmatic access to the key names or other metadata that ABC recognizes or cares about. Then, you could programmatically construct a "defaulted" instance, e.g. an instance where every value in the input dict is a default-constructed value (such as 0 for int):
class ABC():
PROPERTY_NAMES = ['variable_0', 'variable_1', 'variable_2', 'variable_3']
def __init__(self, input_dict):
# implementation omitted for brevity
pass
def some_operation_0(self):
return self.variable_0 + self.variable_1
def some_operation_1(self):
return self.variable_2 + self.variable_3
def test_abc():
a = ABC({name: 0 for name in ABC.PROPERTY_NAMES})
assert a.some_operation_0() == 0
assert a.some_operation_1() == 0
I am adapting some code provided by a company (for controlling an instrument). I am not a python expert myself so I would like to understand if there is any particular advantage of doing something one way or the other.
Their code is basically
def enum(**enums):
return type('Enum', (), enums)
Inst = enum(
CONTINUE = 0,
STOP = 1,
LOOP = 2,
END_LOOP = 3,
JSR = 4,
RTS = 5,
BRANCH = 6,
LONG_DELAY = 7,
WAIT = 8,
RTI = 9
)
and they use it to access the values of the above "entries" like:
Inst.RTS
% 5
From my understanding, Inst is a type object.
For the purposes of storing strings and their associated values, I could also define a dict:
Inst = {'CONTINUE':0, 'STOP':1, 'LOOP':2, 'END_LOOP':3, 'JSR':4, 'RTS':5}
and then basically getting the same result:
Inst['RTS']
% 5
Question
What is the use / what are the advantages of a type object over a normal dict? Or is the above example just a bad example?
With:
def enum(**enums):
return type('Enum', (), enums)
You are actually creating a class with the value of the argument enums as the __dict__ attribute, which is a dict itself, so it is not technically any "better" than a dict, but its usage is syntactically simpler than a dict, since:
Inst.RTS
is arguably cleaner to read and easier to type than
Inst['RTS']
Using the three-argument form of type is similar to defining a class:
class type(name, bases, dict)
With three arguments, return a new type object. This is essentially a
dynamic form of the class statement.
Your Inst expression is a needlessly convoluted but practically equivalent way to explicitly define this class:
class Enum:
CONTINUE = 0
STOP = 1
LOOP = 2
END_LOOP = 3
JSR = 4
RTS = 5
BRANCH = 6
LONG_DELAY = 7
WAIT = 8
RTI = 9
Inst = Enum
The advantage of a class is that attributes are well-defined by the class. For example, you can use it to type-hint, type-check or reference the exact enum set. In contrast, a dict only has some keys and some values -- the exact members are coincidental.
Note that Python has the enum module since Python 3.4. It allows to define proper enums that replicate the features known from enums in other languages:
from enum import Enum, auto
class Inst(int, Enum):
CONTINUE = auto()
STOP = auto()
LOOP = auto()
END_LOOP = auto()
JSR = auto()
RTS = auto()
BRANCH = auto()
LONG_DELAY = auto()
WAIT = auto()
RTI = auto()
I've got a file like this:
class Level(Enum):
prerequisite_level: Optional["Level"]
dependent_level: Optional["Level"]
lower_priority_levels: List["Level"]
greater_priority_levels: List["Level"]
DATA_CHECK = "data check"
DESIGN_CHECK = "design check"
ALERT = "alert"
The enum values are in a specific order, and based on each of those levels I need to be able to get the previous one, the next one, and all the previous and next ones. I believe I need to be able to index the levels numerically to get these values, so I've added a constant to be able to do this:
INCREASING_PRIORITY_LEVELS: List[Level] = list(Level)
for priority_level_index, threshold_level in enumerate(Level):
if priority_level_index > 0:
threshold_level.prerequisite_level = Level[priority_level_index - 1]
else:
threshold_level.prerequisite_level = None
if priority_level_index < len(Level) - 1:
threshold_level.dependent_level = Level[priority_level_index + 1]
else:
threshold_level.dependent_level = None
threshold_level.lower_priority_levels = Level[:priority_level_index]
threshold_level.greater_priority_levels = Level[priority_level_index + 1:]
This is clunky, and I'd like to get rid of this constant. Do I need to implement __getitem__ or something to make this possible?
You can subclass EnumMeta to override the __getitem__ method with additional conditions to return a list of Enum values or a specific Enum value based on the given index, and create a subclass of Enum with the aforementioned subclass of EnumMeta as the metaclass, so that any subclass of this new subclass of Enum can be indexed as desired:
from itertools import islice
from enum import Enum, EnumMeta
class IndexableEnumMeta(EnumMeta):
def __getitem__(cls, index):
if isinstance(index, slice):
return [cls._member_map_[i] for i in islice(cls._member_map_, index.start, index.stop, index.step)]
if isinstance(index, int):
return cls._member_map_[next(islice(cls._member_map_, index, index + 1))]
return cls._member_map_[index]
class IndexableEnum(Enum, metaclass=IndexableEnumMeta):
pass
class Level(IndexableEnum):
DATA_CHECK = "data check"
DESIGN_CHECK = "design check"
ALERT = "alert"
so that Level[1:3] returns:
[<Level.DESIGN_CHECK: 'design check'>, <Level.ALERT: 'alert'>]
and Level[1] returns:
Level.DESIGN_CHECK
(Credit goes to #EthanFurman for pointing out the viability of subclassing EnumMeta.)
class Level(Enum):
prerequisite_level: Optional["Level"]
dependent_level: Optional["Level"]
lower_priority_levels: List["Level"]
greater_priority_levels: List["Level"]
DATA_CHECK = "data check"
DESIGN_CHECK = "design check"
ALERT = "alert"
I'm having a hard time understanding the above: ... [comments clarified that the first four should be attributes, and prequisite and dependent are the previous and following members, respectively].
The solution is to modify previous members as the current member is being initialized (the trick being that the current member isn't added to the parent Enum until after the member's creation and initialization). Here is the solution using the stdlib's Enum1 (Python 3.6 and later):
from enum import Enum, auto
class Level(str, Enum):
#
def __init__(self, name):
# create priority level lists
self.lower_priority_levels = list(self.__class__._member_map_.values())
self.greater_priority_levels = []
# update previous members' greater priority list
for member in self.lower_priority_levels:
member.greater_priority_levels.append(self)
# and link prereq and dependent
self.prerequisite = None
self.dependent = None
if self.lower_priority_levels:
self.prerequisite = self.lower_priority_levels[-1]
self.prerequisite.dependent = self
#
def _generate_next_value_(name, start, count, last_values, *args, **kwds):
return (name.lower().replace('_',' '), ) + args
#
DATA_CHECK = auto()
DESIGN_CHECK = auto()
ALERT = auto()
and in use:
>>> list(Level)
[<Level.DATA_CHECK: 'data check'>, <Level.DESIGN_CHECK: 'design check'>, <Level.ALERT: 'alert'>]
>>> Level.DATA_CHECK.prerequisite
None
>>> Level.DATA_CHECK.dependent
<Level.DESIGN_CHECK: 'design check'>
>>> Level.DESIGN_CHECK.prerequisite
<Level.DATA_CHECK: 'data check'>
>>> Level.DESIGN_CHECK.dependent
<Level.ALERT: 'alert'>
>>> Level.ALERT.prerequisite
<Level.DESIGN_CHECK: 'design check'>
>>> Level.ALERT.dependent
None
Note: If you don't want to see the name twice, a custom __repr__ can show just the enum and member names:
def __repr__(self):
return '<%s.%s>' % (self.__class__.__name__, self.name)
then you'll see:
>>> Level.DESIGN_CHECK
<Level.DESIGN_CHECK>
1If using Python 3.5 or older you need to use aenum2.
2 Disclosure: I am the author of the Python stdlib Enum, the enum34 backport, and the Advanced Enumeration (aenum) library.
A possible alternative to achieve the same result in terms of usage would be to use collections.namedtuple instead:
from collections import namedtuple
LevelSequence = namedtuple('Level', ('DATA_CHECK', 'DESIGN_CHECK', 'ALERT'))
Level = LevelSequence('data check', 'design check', 'alert')
So that:
Level.DESIGN_CHECK and Level[1] both return 'design check', and
Level[1:3] returns ('design check', 'alert')
I am new to python and I would like to pass an enum as an argument to a constructor, within a function.
EDIT: I am working on a program with a class that has to organize different types of data, but most of these data types can be treated the same way. This data won't be all be added at the same time or in a foreseeable order. I would therefore like to keep the same functions, and just change the way the constructor stores the data. Let's consider this simpler example:
Say I have an enum
from enum import Enum, auto
class HouseThing(Enum):
people = auto()
pets = auto()
furniture = auto()
And I have a class House that can contain some or all of those things
class House():
def __init__(self, address, people = None, pets = None,
furniture = None):
self.address = address,
if self.people is not None:
self.people = people
etc....
And now I want to have a function that makes new furbished houses, but I want to use a function that could be used for any house:
house_things = HouseThing.furniture
def make_house_with_some_house_things(neighborhood, house_things):
neighborhood.append(House(house_things.name = house_things.name))
Is there a way to do this without first testing what kind of HouseThing house_things is first? house_things.name passes a string, but I would like it to be able to use it as a keyword.
I'm not sure exactly what you are trying to achieve here, but for the sake of solving the puzzle:
First, change House to determine what it has been passed:
class House():
def __init__(self, address, *house_things):
self.address = address
for ht in house_things:
if ht is HouseThings.people:
self.people = ht
elif ht is HouseThings.pets:
self.pets = ht
elif ht is HouseThings.furniture:
self.furniture = ht
else:
raise ValueError('unknown house thing: %r' % (ht, ))
Then, change make_house_with_some_house_things to just pass the house things it was given:
def make_house_with_some_house_things(neighborhood, house_things):
neighborhood.append(House(house_things))
I am creating a class to make some calculations. The class would have 3 arguments to get started. I have done like this in a simplified representation:
class TheCalcs:
def __init__(self, pk_from_db, cat_score_list, final_score):
self.pk_from_db = pk_from_db
self.cat_score_list = cat_score_list
self.final_score = final_score
def calculate_cat_score(self):
#Do some calcs with the data of the pk_from_db and return that!
a_list_of_scores = [] # create a list of scores
return a_list_of_scores
def final_score(self): # The argument for this function would be the return of the calculate_cat_score function!
# Again do some calcs and return the final score
the_final_score = int()
return the_final_score
def score_grade(self): # the argument this this function again the return but now from the final_score function
# Do some cals and return the grade
the_grade = ("a string", "an integer")
return the_grade
When I call the class I would have to present the arguments --> However as you can see I just do now the value of the first argument. The second and the third being calculated throughout the class. When I call the class just with one argument I will of course have an error of failing arguments. Anyone has an idea on that?
If those values are calculated, simply don't make them arguments. You could instead call those calculation methods to compute the values:
class TheCalcs:
def __init__(self, pk_from_db):
self.pk_from_db = pk_from_db
self.cat_score_list = self.calculate_cat_score()
self.final_score = self.calculate_final_score()
# ...
or postpone calculations until you need them.