I'm trying to find an elegant solution to this basic problem. I'm working on a webhook, I receive data in form of a json.
I have to handle the data based on one of the values.
import json
data = json.loads(raw_json)
data = {
'type': 'A', # Type can be A, B or C
'SomeAttribute' : 'SomeValue',
'SomeOtherAttribute' : 'SomeOtherValue',
}
Based on the type I have to process the whole dictionary and perform some actions based one the type. Of course I could simply define some functions and make an infinite stack of ifs, but that doesn't look elegant.
if data['type'] == 'A':
handle_type_A(data)
elif data['type'] == 'B':
handle_type_B(data)
elif data['type'] == 'C':
handle_type_C(data)
elif data['type'] == 'D':
handle_type_D(data)
elif data['type'] == 'E':
handle_type_E(data)
Strategy pattern seems the way ? I'm a beginner.
CodePudding user response:
I think it should be possible as following:
data = {
'type': 'A', # Type can be A or B
'SomeAttribute' : 'SomeValue',
'SomeOtherAttribute' : 'SomeOtherValue',
}
def str2function(string:str):
module = __import__('foo')
func = getattr(module, 'bar')
return func
func = str2function("handle_type_" data["type"])
func(data)
Not absolutely shure about this method tho. Resource: https://stackoverflow.com/a/4605/20443541
or
data = {
'type': 'A', # Type can be A or B
'SomeAttribute' : 'SomeValue',
'SomeOtherAttribute' : 'SomeOtherValue',
}
rule = {"A":hande_type_A, "B":handly_type_B}
rule[data["type"]](data)
You might adapt the code for other letters
CodePudding user response:
Welcome to Python! You haven't stated what "elegance" mean in this context, but my guess is that you don't like the if
statements, mostly because you'd have to code, say, 25 branches for 25 types, in addition to your 25 different implementations of handle
.
If you are new to classes in Python, and you are certain that every datum in your dataset is different only by its type, you may want to look at something like the following code:
import abc
class Datum(metaclass=abc.ABCMeta):
def __init__(self, some_attr, some_other_attr):
self._some_attr = some_attr
self._some_other_attr = some_other_attr
@abc.abstractmethod
def tag():
pass
@abc.abstractmethod
def handle(self):
pass
class TypeA(Datum):
@staticmethod
def tag():
return "A"
def __init__(self, some_attr, some_other_attr):
super().__init__(some_attr, some_other_attr)
def handle(self):
print("Type-A stuff.")
class TypeB(Datum):
@staticmethod
def tag():
return "B"
def __init__(self, some_attr, some_other_attr):
super().__init__(some_attr, some_other_attr)
def handle(self):
print("Type-B stuff.")
class TypeC(Datum):
@staticmethod
def tag():
return "C"
def __init__(self, some_attr, some_other_attr):
super().__init__(some_attr, some_other_attr)
def handle(self):
print("Type-C stuff.")
types = {datatype.tag() : datatype for datatype in [TypeA, TypeB, TypeC]}
Here, Datum
is an abstract parent class, from which A, B, C, and possibly more derive or inherit. Every derived class will have its own implementation of handle
, while still retaining its meaning as a datum, including its own other attributes. The final line defining types
provides a way to select the correct type given a tag, as explained in the next code:
# your json file.
dataset = [{"Type" : "A", "SomeAttr" : "Thor", "SomeOtherAttr" : "Blue"},
{"Type" : "B", "SomeAttr" : "Hulk", "SomeOtherAttr" : "Green"},
{"Type" : "C", "SomeAttr" : "IronMan", "SomeOtherAttr" : "Red"}]
data = []
for entry in dataset:
# `entry["Type"]` will be "A", "B", "C", ...
# `types[entry["Type"]]` will be TypeA, TypeB, TypeC, ...
# `types[entry["Type"]](entry["SomeAttr"], entry["SomeOtherAttr"])` will
# construct Data object with appropriate handle function
data.append(types[entry["Type"]](entry["SomeAttr"], entry["SomeOtherAttr"]))
# This will process every datum with appropriate handle function.
for d in data:
d.handle()