'Python Switch/Case Statement Adaptation
Ok, at the risk of being ridiculed for not 'trying harder', I have a scenario that I've been attempting to adapt to a pythonic switch case statement. I know python has the new match
method in 3.10 but I'm limited to 3.8.10 in my AWS use case. I've been reading up on switch cases in other languages and I want to find a pythonic way to convert the following jumbled mess of if/elif/else
statements to a clean switch case. I am wondering what others would do in this scenario
OBJECTIVE: I have a filename that will be passed into this sequence of code and I need to return the first three items (i.e. transaction_recipient_verification
, transaction_account_tokenization
, etc). Occasionally the code will receive a filename containing "field_results" or "issuers" and I need to make sure that the trimmed return string contains the respective case.
import random
sampleKeys = [
'transaction_recipient_notification_status_sent/transaction_recipient_notification_status_sent_2021_10_29_12_02_14.snappy',
'transaction_recipient_payment_status_success/transaction_recipient_payment_status_success_2021_10_29_12_02_14.snappy',
'transaction_recipient_verification_rvdm_failure/transaction_recipient_verification_rvdm_failure_2021_10_29_12_02_14.snappy',
'transaction_recipient_verification_rvdm_failure_field_results/transaction_recipient_verification_rvdm_failure_2021_10_29_12_02_14.snappy',
'transaction_recipient_authentication_status_success/transaction_recipient_authentication_status_success_2021_10_29_12_02_14.snappy',
'transaction_recipient_authentication_status_success_field_results/transaction_recipient_authentication_status_success_2021_10_29_12_02_14.snappy',
'transaction_account_tokenization_success/transaction_account_tokenization_success_2021_10_29_12_02_14.snappy',
'transaction_account_tokenization_success_issuers/transaction_account_tokenization_success_2021_10_29_12_02_14.snappy',
'transaction_recipient_payment_status_terminated/transaction_recipient_payment_status_terminated_2021_10_29_12_02_14.snappy',
'transaction_recipient_verification_rvdm_success/transaction_recipient_verification_rvdm_success_2021_10_29_12_02_14.snappy',
'transaction_recipient_verification_rvdm_success_field_results/transaction_recipient_verification_rvdm_success_2021_10_29_12_02_14.snappy',
'transaction_recipient_notification_status_received/transaction_recipient_notification_status_received_2021_10_29_12_02_14.snappy',
'transaction_recipient_authentication_status_success/transaction_recipient_authentication_status_success_2021_10_29_11_17_45.snappy'
]
key = random.choice(sampleKeys)
array_data = any(substring in key for substring in ['_issuers', '_field_results'])
if not array_data:
if 'transaction_recipient_notification' in key:
keySubject = 'transaction_recipient_notification'
elif 'transaction_recipient_authentication' in key:
keySubject = 'transaction_recipient_authentication'
elif 'transaction_recipient_verification' in key:
keySubject = 'transaction_recipient_verification'
elif 'transaction_account_verification' in key:
keySubject = 'transaction_account_verification'
elif 'transaction_account_tokenization' in key:
keySubject = 'transaction_account_tokenization'
elif 'transaction_recipient_payment' in key:
keySubject = 'transaction_recipient_payment'
else:
if '_issuers' in key:
if 'transaction_recipient_notification' in key:
keySubject = 'transaction_recipient_notification_issuers'
elif 'transaction_recipient_authentication' in key:
keySubject = 'transaction_recipient_authentication_issuers'
elif 'transaction_recipient_verification' in key:
keySubject = 'transaction_recipient_verification_issuers'
elif 'transaction_account_verification' in key:
keySubject = 'transaction_account_verification_issuers'
elif 'transaction_account_tokenization' in key:
keySubject = 'transaction_account_tokenization_issuers'
elif 'transaction_recipient_payment' in key:
keySubject = 'transaction_recipient_payment_issuers'
elif '_field_results' in key:
if 'transaction_recipient_notification' in key:
keySubject = 'transaction_recipient_notification_field_results'
elif 'transaction_recipient_authentication' in key:
keySubject = 'transaction_recipient_authentication_field_results'
elif 'transaction_recipient_verification' in key:
keySubject = 'transaction_recipient_verification_field_results'
elif 'transaction_account_verification' in key:
keySubject = 'transaction_account_verification_field_results'
elif 'transaction_account_tokenization' in key:
keySubject = 'transaction_account_tokenization_field_results'
elif 'transaction_recipient_payment' in key:
keySubject = 'transaction_recipient_payment_field_results'
print(f'BEFORE ===> {key}')
print(f'AFTER ===> {keySubject}')
Possible Direction:
import re
class MainKeyHandleSwitch:
ARRAY_OPTIONS = ['_issuers', '_field_results']
def __init__(self,key):
self._original_key = key
self._array_data = any(substring in key for substring in self.ARRAY_OPTIONS)
self._trimmed_dict = self.trimmed_dict()
@property
def get_trimmed_dict(self):
return self._trimmed_dict
@property
def get_trimmed_key(self):
return self.__get_key_subject__()
def trimmed_dict(self):
trim_dict = dict()
trim_dict['case_one'] = re.search('transaction_recipient_notification+', self._original_key)
trim_dict['case_two'] = re.search('transaction_recipient_authentication+', self._original_key)
trim_dict['case_three'] = re.search('transaction_recipient_verification+', self._original_key)
trim_dict['case_four'] = re.search('transaction_account_verification+', self._original_key)
trim_dict['case_five'] = re.search('transaction_account_tokenization+', self._original_key)
trim_dict['case_six'] = re.search('transaction_recipient_payment+', self._original_key)
return trim_dict
def __get_key_subject__(self):
obj = next(item for item in list(self._trimmed_dict.values()) if item is not None)
if not self._array_data:
return obj.group(0)
else:
if '_issuers' in self._original_key:
return f'{obj.group(0)}_issuers'
elif '_field_results' in self._original_key:
return f'{obj.group(0)}_field_results'
And the code to test the class:
import random
key = random.choice(sampleKeys)
print(f'before ===> {key}')
a = MainKeyHandleSwitch(key)
trimmed_key = a.get_trimmed_key
print(f'after ===> {trimmed_key}')
Solution 1:[1]
I see a lot of repetition in your code, so the first thing I'll think of would be: "can I use a loop to simply this code?" and the answer is yes!
Since your code repeatedly used the six subjectTypes
and the keySubject
depends on the subject type, creating a list of the six types then use next()
with a generator expression should simplify the over abundance of if
's (If there weren't any correlations, a dictionary would work instead). Also, instead of array_data
, you can use an if-elif-else
clause to prevent an extra block level.
sampleKeys = [...]
key = random.choice(sampleKeys)
subjectTypes = ['transaction_recipient_notification', 'transaction_recipient_authentication',
'transaction_recipient_verification', 'transaction_account_verification',
'transaction_account_tokenization', 'transaction_recipient_payment']
if '_issuers' in key:
keySubject = next(t + '_issuers' for t in subjectTypes if t in key)
elif '_field_results' in key:
keySubject = next(t + '_field_results' for t in subjectTypes if t in key)
else:
keySubject = next(t for t in subjectTypes if t in key)
print(f'BEFORE ===> {key}')
print(f'AFTER ===> {keySubject}')
Solution 2:[2]
If you're looking for an approach to implement a switch statement, here's how I tackled it:
First create a helper function for the switch like this (I placed mine in a "myTools.py" module that I use in all my projects):
def switch(v): yield lambda *c:v in c
The way it works is by returning a function (lambda) that captured the switch value and returns True if the value is in any of its argument. The single yield makes switch() a generator usable in a for-loop. The for-loop will only perform one iteration but, given that it is a loop nonetheless, it will support break statements and the else clause at the end (when no breaks have been executed).
This allows a one iteration for-loop to be used in a form very similar to the switch statement in other languages (such as C++):
value = 5
for case in switch(value): # case is actually a function
if case(0): print("None"); break
if case(2,4,6): print("Even"); break
if case(3,5,7): print("Odd"); break
else:
print("invalid value")
With this approach you can make your switch function perform any kind of pattern matching:
def switchAllIn(v): yield lambda *c:all(s in v for s in c)
This version of the switch function checks if all case arguments are in the switch value:
value = 'The quick brown fox, jumped over the lazy dogs'
for case in switchAllIn(value):
if case('quick','lazy'): print('oxymoron'); break
if case('quick','fast'): print('redundant'); break
else:
print('nothing special')
You can even make it use regular expressions:
def switchMatch(v): yield lambda *c:any(re.match(p,v) for p in c)
value = 'The quick brown fox, jumped over the lazy dogs'
for case in switchMatch(value):
if case(r'\bdog\b',r'\bfox\b') and not case('lazy'):
print('lively canine')
break
if case(r'\bquick\b.+\bfox\b'):
print('veloce vulpe')
break
There is a lot of flexibility in this solution.
you don't actually have to use break, so you can pass through multiple cases or even perform some logic between the
if case(...):
blocksyou can combine cases with and/or operators such as
if case(1) or case(3):
when using breaks, you can nest the
for case in switch
without having to use a different name for thecase
function
for example:
for case in switch(letter):
if case('a','b'):
for case in switch(number):
if case(1,2,3): print('vitamin'); break
...
break
if case('c','d'):
...
break
- You can combine multiple switch calls with zip()
For example:
for caseL,caseN in zip(switch(L),switch(N)):
if caseL('a','b') and caseN(1,2,3):
print('vitamin')
break
...
- You can apply the switch to every element of a list using map()
In which case the for-loop will run more than once and you must use continue instead of break:
L = [1,2,3,4,5]
for case in map(switch,L):
if case(1,2,3):
print('low')
continue # using continue instead of break
if case(4,5,6):
print('medium')
continue
print('high')
For your specific scenario however, a switch statement is not necessarily the best solution. There seems to be a combinatory pattern of prefixes and suffixes with a limited set of keywords in between. You could use a loop on regular expressions composed of the prefixes, keywords and suffixes to get the keySubject:
import re
prefixes = ('transaction_recipient_','transaction_account_')
suffixes = ('_issuers','_field_results','') # in priority order
keywords = r'notification|authentication|verification|tokenization|payment'
for suffix in suffixes:
for prefix in prefixes:
pattern = r'\b('+prefix+keywords+suffix+')\b'
m = re.match(pattern,key)
if not m: continue
keySubject = m.group()
break
else: continue; break
print(f'AFTER ===> {keySubject}')
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | Taku |
Solution 2 |