I might be overthinking this, but I'm currently facing this code:
result = None
if 'word_a' in my_string.lower():
result = do_action_A()
elif 'word_b' in my_string.lower():
result = do_action_B()
elif 'word_c' in my_string.lower():
result = do_action_C()
etc.
I'm trying to think of a nicer / more pythonic /readable way of doing this, but my brain seems empty for solutions. It is somehow related to this question I asked years ago, but it's not exactly the same, since it's not simply a string comparison which can be substituted with a dictionary lookup.
Does anybody know a more Pythonic way of doing this?
To be clear; I'm not specifically looking for more performance, but rather for elegant/readable code.
CodePudding user response:
Use a dictionary (could be a list of 2-tuples too for this purpose), a for loop since you can't just directly access it with the substring match, and for:else:
to figure out if you didn't get a match.
actions = {
"word_a": do_action_A,
"word_b": do_action_B,
"word_c": do_action_C,
}
result = None
my_string_lower = my_string.lower()
for word, action in actions.items():
if word in my_string_lower:
result = action()
break
else:
print("oh no, no match")
CodePudding user response:
something like the below
from typing import NamedTuple,Callable,List
class Pair(NamedTuple):
word: str
func: Callable
def do_action_A():
pass
def do_action_B():
pass
def do_action_C():
pass
result = None
my_string = 'Somthing'
my_string = my_string.lower()
pairs: List[Pair] = [Pair('word_a', do_action_A), Pair('word_b', do_action_B), Pair('word_c', do_action_C)]
for pair in pairs:
if pair.word in my_string:
result = pair.func()
break
CodePudding user response:
With regex this could be really much shorter:
import re
actions = {
"word_a": do_action_A,
"word_b": do_action_B,
"word_c": do_action_C,
}
result = actions.get(next(iter(re.findall(f'(?:{"|".join(actions)})', s)), None))()
CodePudding user response:
Here's a one-liner to really get your readers confused :)
actions = {
'word_a': do_action_A,
'word_b': do_action_B,
'word_c': do_action_C,
}
my_string = 'has_word_a_yea'
result = next(func for action,func in actions.items() if action in my_string.lower())()
Will raise StopIteration
if no match.
CodePudding user response:
Customizable code for easy changes and future demands. 2 Different attitudes.
You can subclass it, change regex parser, act functions and etc..
import re
class Executor:
def __init__(self, action_dict):
self.action_dict = action_dict
self.keys = list(action_dict.keys())
def regex_parser(self, regex):
return regex
def string_parser(self, string):
return string.lower()
def match(self, string):
for key in self.keys:
regex = re.compile(self.regex_parser(key))
if regex.search(self.string_parser(string)) is not None:
return self.act(key, string)
return None
def act(self, key, string):
func = self.action_dict[key]
return func(string)
executor = Executor(
{
"test": print,
"some": lambda s: print(s " Hello matcher"),
"end$": lambda s: print("End"),
}
)
executor.match("testing")
executor.match("something")
executor.match("LegenD")
>>> testing
>>> something Hello matcher
>>> End
Second attitude is more verbose, but benefit is that each Matcher class can have its own set of rules and evaluation.
import re
class DefaultMatcher:
regex = "default"
def regex_parser(self, regex):
return regex
def string_parser(self, string):
return string.lower()
def function(self, string, *args, **kwargs):
"""This function is the action."""
return args, kwargs
def match(self, string, *args, **kwargs):
# Returns something or None
return self.compiled_regex.search(self.string_parser(string))
def __init__(self, *args, **kwargs):
self.compiled_regex = re.compile(self.regex_parser(self.regex))
def __call__(self, string, *args, **kwargs):
parsed_string = self.string_parser(string)
if self.match(string):
return self.function(string, *args, **kwargs)
else:
return None
class Matcher1(DefaultMatcher):
regex = "test1"
def function(self, *args, **kwargs):
return "Matcher_1"
class Matcher2(DefaultMatcher):
regex = "test2"
def function(self, *args, **kwargs):
return "Matcher_2"
class Matcher3(DefaultMatcher):
regex = "o"
def regex_parser(self, regex):
super_regex = super().regex_parser(regex)
return super_regex "$"
def function(self, *args, **kwargs):
return "Matcher_3"
class DefaultExecutor:
def __init__(self, list_of_matcher, *args, **kwargs):
self.list_of_matcher = [matcher(*args, **kwargs) for matcher in list_of_matcher]
def __call__(self, string, *args, **kwargs):
for matcher in self.list_of_matcher:
result = matcher(string, *args, **kwargs)
if result is not None:
return result
executor = DefaultExecutor([Matcher1, Matcher2, Matcher3])
print(executor("test1"))
print(executor("Otto"))
print(executor("test2"))