Dictionary to switch between methods with different arguments - python-3.x

A common workaround for the lack of a case/switch statement in python is the use of a dictionary. I am trying to use this to switch between methods as shown below, but the methods have different argument sets and it's unclear how I can accommodate that.
def method_A():
pass
def method_B():
pass
def method_C():
pass
def method_D():
pass
def my_function(arg = 1):
switch = {
1: method_A,
2: method_B,
3: method_C,
4: method_D
}
option = switch.get(arg)
return option()
my_function(input) #input would be read from file or command line
If I understand correctly, the dictionary keys become associated with the different methods, so calling my_function subsequently calls the method which corresponds to the key I gave as input. But that leaves no opportunity to pass any arguments to those subsequent methods. I can use default values, but that really isn't the point. The alternative is nested if-else statements to choose, which doesn't have this problem but arguably less readable and less elegant.
Thanks in advance for your help.

The trick is to pass *args, **kwargs into my_function and the **kwargs onto to your choosen function and evaluate it there.
def method_A(w):
print(w.get("what")) # uses the value of key "what"
def method_B(w):
print(w.get("whatnot","Not provided")) # uses another keys value
def my_function(args,kwargs):
arg = kwargs.get("arg",1) # get the arg value or default to 1
switch = {
1: method_A,
2: method_B,
}
option = switch.get(arg)
return option(kwargs)
my_function(None, {"arg":1, "what":"hello"} ) # could provide 1 or 2 as 1st param
my_function(None, {"arg":2, "what":"hello"} )
Output:
hello
Not provided
See Use of *args and **kwargs for more on it.

Related

when we need to specify the data type in exec() function?

I have a piece of code that has used python exec() function. data is saved as an array in a file and when exec() is used, data type is specified as dictionary. I can't quit understand what's the output
style = dict()
# test.py includes one 10 x 10 array
with open('test.py')as output:
exec(output.read(), style)
Since you are passing empty dict() as globals argument for exec() output will not be defined when output.read() is executed. If you need to print the result of output.read() then you need to pass either globals() or locals() as second argument to exec. They return a dictionary containing objects available in global and local scope respectively. The new code may be:
style = dict()
with open('test.py') as output:
exec("print(output.read())", globals())
or
style = dict()
with open('test.py') as output:
exec("print(output.read())", locals())
Return value from exec statement is None so you need to use print to see the output of output.read()

Python get #property.setter decorated method in a class

In Python there is no switch/case. It is suggested to use dictionaries: What is the Python equivalent for a case/switch statement?
in Python it is good practise to use #property to implement getter/setter: What's the pythonic way to use getters and setters?
So, if I want to build a class with a list of properties to switch so I can get or update values, I can use something like:
class Obj():
"""property demo"""
#property
def uno(self):
return self._uno
#uno.setter
def uno(self, val):
self._uno = val*10
#property
def options(self):
return dict(vars(self))
But calling
o=Obj()
o.uno=10 # o.uno is now 100
o.options
I obtain {'_uno': 100} and not {'uno': 100}.
Am I missing something?
vars is really a tool for introspection, and gives you the local variables of the current space, or in a given object - it is not a good way to get attributes and variables ready for final consumption.
So, your options code must be a bit more sophisticated - one way to go
is to search the class for any properties, and then using getattr to get
the values of those properties, but using the getter code, and
introspect the instance variables, to get any methods attributed directly,
but discard the ones starting with _:
#property
def options(self):
results = {}
# search in all class attributes for properties, including superclasses:
for name in dir(self.__class__):
# obtain the object taht is associated with this name in the class
attr = getattr(self.__class__, name)
if isinstance(attr, property):
# ^ if you want to also retrieve other "property like"
# attributes, it is better to check if it as the `__get__` method and is not callable:
# "if hasattr(attr, '__get__') and not callable(attr):"
# retrieves the attribute - ensuring the getter code is run:
value = getattr(self, name)
results[name] = value
# check for the attributes assigned directly to the instance:
for name, value in self.__dict__.items():
# ^ here, vars(self) could have been used instead of self.__dict__
if not name.startswith("_"):
results[name] = value
return results
about switch..case
On a side note to your question, regarding the "switch...case" construction: please disregard all content you read saying "in Python one should use dictionaries instead of switch/case". This is incorrect.
The correct construct to replace "switch...case" in Python is the "if..elif..else". You can have all the expressiveness one does have with a C-like "switch" with a plain "if-else" tree in Python, and actually, go much beyond that, as the testing expression in if...elif can be arbitrary, and not just a matching value.
option = get_some_user_option()
if option == "A":
...
elif option == "B":
...
elif option in ("C", "D", "E"):
# common code for C, D, E
...
if option == "E":
# specialized code for "E",
else:
# option does not exist.
...
While it is possible to use a dictionary as a call table, and having functions to perform actions in the dictionary values, this construct is obviously not a "drop in" replacement for a plain switch case - starting from the point that the "case" functions can't be written inline in the dictionary, unless they can be written as a lambda function, and mainly
the point that they won't have direct access to the variables on the function calling them.

How to return a variable from a python function with a single parameter

I have the following function:
def test(crew):
crew1 = crew_data['CrewEquipType1']
crew2 = crew_data['CrewEquipType2']
crew3 = crew_data['CrewEquipType3']
return
test('crew1')
I would like to be able to use any one of the 3 variables as an argument and return the output accordingly to use as a reference later in my code. FYI, each of the variables above is a Pandas series from a DataFrame.
I can create functions without a parameter, but for reason I can't quite get the concept of how to use parameters effectively such as that above, instead I find myself writing individual functions rather then writing a single one and adding a parameter.
If someone could provide a solution to the above that would be greatly appreciated.
Assumption: You problem seems to be that you want to return the corresponding variable crew1, crew2 or crew3 based on your input to the function test.
Some test cases based on my understanding of your problem
test('crew1') should return crew_data['CrewEquipType1']
test('crew2') should return crew_data['CrewEquipType2']
test('crew3') should return crew_data['CrewEquipType3']
To accomplish this you can implement a function like this
def test(crew):
if crew=='crew1':
return crew_data['CrewEquipType1']
elif crew=='crew2':
return crew_data['CrewEquipType2']
elif crew=='crew3':
return crew_data['CrewEquipType3']
...
... # add as many cases you would like
...
else:
# You could handle incorrect value for `crew` parameter here
Hope this helps!
Drop a comment if not

How to modify the signature of a function dynamically

I am writing a framework in Python. When a user declares a function, they do:
def foo(row, fetch=stuff, query=otherStuff)
def bar(row, query=stuff)
def bar2(row)
When the backend sees query= value, it executes the function with the query argument depending on value. This way the function has access to the result of something done by the backend in its scope.
Currently I build my arguments each time by checking whether query, fetch and the other items are None, and launching it with a set of args that exactly matches what the user asked for. Otherwise I got the "got an unexpected keyword argument" error. This is the code in the backend:
#fetch and query is something computed by the backend
if fetch= None and query==None:
userfunction(row)
elif fetch==None:
userunction (row, query=query)
elif query == None:
userfunction (row, fetch=fetch)
else:
userfunction (row,fetch=fetch,query=query)
This is not good; for each additional "service" the backend offers, I need to write all the combinations with the previous ones.
Instead of that I would like to primarily take the function and manually add a named parameter, before executing it, removing all the unnecessary code that does these checks. Then the user would just use the stuff it really wanted.
I don't want the user to have to modify the function by adding stuff it doesn't want (nor do I want them to specify a kwarg every time).
So I would like an example of this if this is doable, a function addNamedVar(name, function) that adds the variable name to the function function.
I want to do that that way because the users functions are called a lot of times, meaning that it would trigger me to, for example, create a dict of the named var of the function (with inspect) and then using **dict. I would really like to just modify the function once to avoid any kind of overhead.
This is indeed doable in AST and that's what I am gonna do because this solution will suit better for my use case . However you could do what I asked more simply by having a function cloning approach like the code snippet I show. Note that this code return the same functions with different defaults values. You can use this code as example to do whatever you want.
This works for python3
def copyTransform(f, name, **args):
signature=inspect.signature(f)
params= list(signature.parameters)
numberOfParam= len(params)
numberOfDefault= len(f.__defaults__)
listTuple= list(f.__defaults__)
for key,val in args.items():
toChangeIndex = params.index(key, numberOfDefault)
if toChangeIndex:
listTuple[toChangeIndex- numberOfDefault]=val
newTuple= tuple(listTuple)
oldCode=f.__code__
newCode= types.CodeType(
oldCode.co_argcount, # integer
oldCode.co_kwonlyargcount, # integer
oldCode.co_nlocals, # integer
oldCode.co_stacksize, # integer
oldCode.co_flags, # integer
oldCode.co_code, # bytes
oldCode.co_consts, # tuple
oldCode.co_names, # tuple
oldCode.co_varnames, # tuple
oldCode.co_filename, # string
name, # string
oldCode.co_firstlineno, # integer
oldCode.co_lnotab, # bytes
oldCode.co_freevars, # tuple
oldCode.co_cellvars # tuple
)
newFunction=types.FunctionType(newCode, f.__globals__, name, newTuple, f.__closure__)
newFunction.__qualname__=name #also needed for serialization
You need to do that weird stuff with the names if you want to Pickle your clone function.

Pass in two variables to **kwargs

Say I have an entry form for a GUI window that asks for a new setting and a value for that setting, which it then passes to another object that manages settings and expects **kwargs as input:
class SettingsForm(someFormClass):
def onSubmit(self):
new_setting = self.content['new setting'].get()
new_value = self.content['value'].get()
settings_instance.add_setting(new_setting=new_value)
This sets new_value to the literal string 'new_setting'. I've tried various ways to get around this, such as using a dictionary:
class SettingsForm(someFormClass):
def onSubmit(self):
new_setting = self.content['new setting'].get()
new_value = self.content['value'].get()
mydict = {}
mydict[new_setting] = new_value
settings_instance.add_setting(**mydict)
This works, but doesn't make much sense for a single pair of values... is there an obvious way that I'm missing?

Resources