Way to call method depending on variable? - python

I already have a working, but in my oppinion not beautiful solution for a part of a long script.
My script uses several similar methods, that differ too much to combine. However I came to a point where I want to call one of those methods depending on a given variable.
The names of the methods are build up like this:
def read_A():
#doing sth
def read_B():
#doing sth else
def read_C():
etc.
Now I would like to call those methods in a pythonic way, when the letter ('A', 'B', 'C', ...) is given as a variable.
A non-pythonic solution would be:
if var == "A":
read_A()
if var == "B":
read_B() .....
And I hope to find a more pythonic solution that allows me to call those methods simply like this:
var = "A"
read_var() #This would call the method 'read_A()'
Please mind that the code above is only an image of what I hope to do, it is not a working example!

I dont see an issue with just using
if var == 'A':
read_a()
but if you'd like to make it more 'pythonic' you could map your variables to the methods using a dictionary and execute it based on the result of what's stored in your dictionary:
def read_a():
print('Running method read_a')
def read_b():
print('Running method read_b')
switch = {'A': read_a, 'B': read_b}
case = 'A'
switch.get(case)()
>> 'Running method read_a'
case = 'B'
switch.get(case)()
>> 'Running method read_b'

Stick the functions in a dictionary, and use the dictionary to dispatch to the chosen one:
read = {'A': read_a, 'B': read_b, 'C': read_c}
choice = 'A'
read[choice]()
On that last line, you lookup the function that matches your choice in the dictionary, then you immediately call it.

you may use next construction:
def execute_func(x):
return {
'0':read_A(),
'1':read_B()
}[x]
Set your variables instead '0' and '1' or more and pass your params to execute_func().

You can do it in this way if you have many functions named read_a, read_b...etc, instead of writing huge dictionary.
def read_a():
print('Running method read_a')
def read_b():
print('Running method read_b')
def read_c():
print("running method read_c")
def read_d():
print("running method read_d")
............
............
def read_z():
print("running method read_z")
def _read_var(var):
method = "read_{}".format(var.lower())
try:
eval(method)()
except NameError:
raise NotImplementedError
var = "A"
_read_var(var)# will invoke read_a method

"""
modified from
https://stackoverflow.com/questions/65163600/how-to-call-a-class-method-given-its-name
"""
class MyClass(object):
def __init__(self):
pass
def call_method_by_string(self, method_name):
getattr(self, method_name)() # call local method based on string
def get_year(self):
print("here")
if __name__ == "__main__":
mc = MyClass()
mc.call_method_by_string(method_name="get_year")

Related

How to save a list of functions as an object in Python3

I'm working on a filter. For each case, I have a list of functions to process the input data, which differs from case to case.
I want to serialize and save such list of function, so that I can load and use functions for each case. I tried to use pickle to dump the list as a pkl file, but it cannot be load if I delete the definition of functions.
To deliver it more explicitly, it runs like this
def a1(obj):
pass
def a2(obj):
pass
def b1(obj):
pass
def b2(obj):
pass
a_func = [a1, a2]
b_func = [b1, b2]
if obj.flag == 1:
for fun in a_func:
fun(obj)
elif obj.flag == 2:
for fun in b_func:
fun(obj)
and I want to save such a_func and b_func as pkl file or so.
I don't know how to save them as py. I need to deal with more than 100 cases, and each case may need about 10 functions, most of which are in common. I don't want type them manually.
Here you can try like this, using exec() and store string names of the functions in lists:
class obj:
def __init__(self):
self.flag = 1
def a1(obj):
pass
def a2(obj):
pass
def b1(obj):
pass
def b2(obj):
pass
a_func = ['a1', 'a2']
b_func = ['b1', 'b2']
objg = obj()
if objg.flag == 1:
for fun in a_func:
exec('{x}(objg)'.format(x=fun))
elif objg.flag == 2:
for fun in b_func:
exec('{x}(objg)'.format(x=fun))
You can define functions that use functions such as:
def a1(obj):
result = obj+1
return result
def a2(obj):
result = obj*3
return result
def a1_a2(obj):
result1 = a1(obj)
result2 = a2(result1)
return result2
then calling a1_a2 will do a1 first and apply a2 to the result of a1.
If you save the code to say filters.py you can do in any other programm in the same directory:
from filters import a1_a2

Pythonic way of using enum to select and run a particular sub method?

I have a function that will be called by an end user, and this function accepts an int argument from 1 to 3 inclusive. Depending on the int, I want the function to run a seperate sub method. I would usually do this with if statements as below, but I am looking for a cleaner / more pythonic way to do this using Enum.
def user_function(user_arg: int) -> float:
if user_arg == 1:
return sub_method_1()
elif user_arg == 2:
return sub_method_2()
elif user_arg == 3:
return sub_mthod_3()
print(f"Error: user_arg must be in [1,2,3]. Supplied argument: {user_arg}")
Ideally the function would look something like
from enum import Enum
class UserMethod(Enum):
FIRST_METHOD = 1
SECOND_METHOD = 2
THIRD_METHOD = 3
def choose_method(self):
#... use enum to choose from the 3 sub methods ...
# This function is now much cleaner
def user_function(user_arg: int) -> float:
um = UserMethod(user_arg)
return um.choose_method()
I am confused on how to use Enum on how to do this, or if this is even the best way?
The cleanest way to do this at the moment is to use the aenum library1:
from aenum import Enum
class UserMethod(Enum):
#
_init_ = 'value method'
#
def __call__(self, *args, **kwds):
return self.method(*args, **kwds)
#
def first(blah):
return blah
#
def second(spam=2):
return spam
#
def third(this, that):
return this, that
#
FIRST_METHOD = 1, first
SECOND_METHOD = 2, second
THIRD_METHOD = 3, third
Each method is stored on the enum member itself, and calling the member passes the call to the appropriate function.
--> list(UserMethod)
[<UserMethod.FIRST_METHOD: 1>, <UserMethod.SECOND_METHOD: 2>,
<UserMethod.THIRD_METHOD: 3>]
--> UserMethod.THIRD_METHOD("hello", "world")
('hello', 'world')
and
# This function is now much cleaner
def user_function(user_arg: int) -> float:
return UserMethod(user_arg).method
1 Disclosure: I am the author of the Python stdlib Enum, the enum34 backport, and the Advanced Enumeration (aenum) library.
You can keep it even simpler (that is, without relying on Python Enums):
def user_function(user_arg: int) -> float:
assert user_arg in [1,2,3], \
f"Error: user_arg must be in [1,2,3]. Supplied argument: {user_arg}"
return [sum_method_1,
sub_method_2,
sub_method_3][user_arg-1]()
On your method choose_method you can get the selected Enum using self like this:
def choose_method(self):
print(self) # Will print the Enum atributte
With this you can create a dict pointing to another methods;
def choose_method(self):
methods = {
UserMethod.FIRST_METHOD: self.sub_method_one,
UserMethod.SECOND_METHOD: self.sub_method_two,
UserMethod.THIRD_METHOD: self.sub_method_three,
}
return methods.get(self)()
def sub_method_one(self):
print("Method 1")
def sub_method_two(self):
print("Method 2")
def sub_method_three(self):
print("Method 3")
Using enums would not be my first impulse - I would argue that it wouldn't be pythonic to use one in this scenario, and I don't think you can justify using an enum. You'll probably just want to map the user's input to a function using a dictionary:
def first_function():
print("In first!")
def second_function():
print("In second!")
def third_function():
print("In third!")
functions = {
1: first_function,
2: second_function,
3: third_function
}
functions[1]()
Output:
In first!
>>>
In this case, the keys are integers, but they could be strings, too - or anything. I also just hardcoded the 1 in functions[1](), but you get the idea.

How do I run two or more methods in a class like a chain?

I'm trying to learn OOP but I'm getting very confused with how I'm supposed to run the methods or return values. In the following code I want to run read_chapters() first, then sendData() with some string content that comes from read_chapters(). Some of the solutions I found did not use __init__ but I want to use it (just to see/learn how i can use them).
How do I run them? Without using __init__, why do you only return 'self'?
import datetime
class PrinceMail:
def __init__(self):
self.date2 = datetime.date(2020, 2, 6)
self.date1 = datetime.date.today()
self.days = (self.date1 - self.date2).days
self.file = 'The_Name.txt'
self.chapter = '' # Not sure if it would be better if i initialize chapter here-
# or if i can just use a normal variable later
def read_chapters(self):
with open(self.file, 'r') as book:
content = book.readlines()
indexes = [x for x in range(len(content)) if 'CHAPTER' in content[x]]
indexes = indexes[self.days:]
heading = content[indexes[0]]
try:
for i in (content[indexes[0]:indexes[1]]):
self.chapter += i # can i use normal var and return that instead?
print(self.chapter)
except IndexError:
for i in (content[indexes[0]:]):
self.chapter += i
print(self.chapter)
return self????? # what am i supposed to return? i want to return chapter
# The print works here but returns nothing.
# sendData has to run after readChapters automatically
def sendData(self):
pass
#i want to get the chapter into this and do something with it
def run(self):
self.read_chapters().sendData()
# I tried this method but it doesn't work for sendData
# Is there anyother way to run the two methods?
obj = PrinceMail()
print(obj.run())
#This is kinda confusing as well
Chaining methods is just a way to shorten this code:
temp = self.read_chapters()
temp.sendData()
So, whatever is returned by read_chapters has to have the method sendData. You should put whatever you want to return in read_chapters in a field of the object itself (aka self) in order to use it after chaining.
First of all, __init__ has nothing to do with what you want to achieve here. You can consider it as a constructor for other languages, this is the first function that is called when you create an object of the class.
Now to answer your question, if I am correct you just want to use the output of read_chapters in sendData. One of the way you can do that is by making the read_chapters a private method (that is if you don't want it to use through the object) using __ in the starting of the name like __read_chapters then make a call to the function inside the sendData function.
Another point to consider here is, when you are using self and don't intend to use the function through the object you don't need to return anything. self assigns the value to the attribute of the current instance. So, you can leave the function read_chapters at self.chapter = i and access the same in sendData.
Ex -
def sendData(self):
print(self.chapter)
I'm not an expert but, the reason to return self is because it is the instance of the class you're working with and that's what allows you to chain methods.
For what you're trying to do, method chaining doesn't seem to be the best approach. You want to sendData() for each iteration of the loop in read_chapters()? (you have self.chapter = i which is always overwritten)
Instead, you can store the chapters in a list and send it after all the processing.
Also, and I don't know if this is a good practice but, you can have a getter to return the data if you want to do something different with (return self.chapter instead of self)
I'd change your code for:
import datetime
class PrinceMail:
def __init__(self):
self.date2 = datetime.date(2020, 2, 6)
self.date1 = datetime.date.today()
self.days = (self.date1 - self.date2).days
self.file = 'The_Name.txt'
self.chapter = []
def read_chapters(self):
with open(self.file, 'r') as book:
content = book.readlines()
indexes = [x for x in range(len(content)) if 'CHAPTER' in content[x]]
indexes = indexes[self.days:]
heading = content[indexes[0]]
try:
for i in (content[indexes[0]:indexes[1]]):
self.chapter.append(i)
except IndexError:
#not shure what you want to do here
for i in (content[indexes[0]:]):
self.chapter.append(i)
return self
# sendData has to run after readChapters automatically
def sendData(self):
pass
#do what ever with self.chapter
def get_raw_chapters(self):
return self.chapter
Also, check PEP 8 Style Guide for naming conventions (https://www.python.org/dev/peps/pep-0008/#function-and-variable-names)
More reading in
Method chaining - why is it a good practice, or not?
What __init__ and self do on Python?

Python unit testing on class methods with no input arguments

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

How do I use dictionaries to make functions easily accessable

I declared 3 functions earlier, this is just a goofy text based cookie clicker-esque game.
dostuff={"" : turn() , "help" : helpf() , "invest" : invest() }
while done != True:<br>
do = input("What do you want to do? ")
do = do.lower()
if do == "" or do == "help" or do == "invest":
dostuff[do]
elif do == "quit":
done = True
So when I use dostuff["turn"] it does nothing (the function is supposed to print some things). I have the same problem with the other options.
Your parentheses must be omitted in the dict, and then put at the end of the dict call. You define a function, which becomes a python object. You reference the object with the dict, and then you call the function with the object reference followed by parentheses:
def one():
print("one")
def two():
print("two")
do_stuff = {
"one": one,
"two": two
}
do_stuff["one"]()
prints:
"one"
You can take this concept of executing calls with string inputs a lot farther by familiarizing yourself with the builtin functions of python.
https://docs.python.org/2/library/functions.html
For example, you can create a class and call its methods or properties using text based input with the getattr method:
class do_stuff():
def __init__(self):
pass
def one(self):
print("one")
def two(self):
print("two")
doer = do_stuff()
inp = "one"
getattr(doer, inp)()
prints->
"one"

Categories

Resources