Using a dictionary to select function to execute
Solution 1
Not proud of it, but:
def myMain(key):
def ExecP1():
pass
def ExecP2():
pass
def ExecP3():
pass
def ExecPn():
pass
locals()['Exec' + key]()
I do however recommend that you put those in a module/class whatever, this is truly horrible.
If you are willing to add a decorator for each function, you can define a decorator which adds each function to a dictionary:
def myMain(key):
tasks = {}
def task(task_fn):
tasks[task_fn.__name__] = task_fn
@task
def ExecP1():
print(1)
@task
def ExecP2():
print(2)
@task
def ExecP3():
print(3)
@task
def ExecPn():
print(4)
tasks['Exec' + key]()
Another option is to place all the functions under a class (or in a different module) and use getattr
:
def myMain(key):
class Tasks:
def ExecP1():
print(1)
def ExecP2():
print(2)
def ExecP3():
print(3)
def ExecPn():
print(4)
task = getattr(Tasks, 'Exec' + key)
task()
Solution 2
Simplify, simplify, simplify:
def p1(args):
whatever
def p2(more args):
whatever
myDict = {
"P1": p1,
"P2": p2,
...
"Pn": pn
}
def myMain(name):
myDict[name]()
That's all you need.
You might consider the use of dict.get
with a callable default if name
refers to an invalid function—
def myMain(name):
myDict.get(name, lambda: 'Invalid')()
(Picked this neat trick up from Martijn Pieters)
Solution 3
Simplify, simplify, simplify + DRY:
tasks = {}
task = lambda f: tasks.setdefault(f.__name__, f)
@task
def p1():
whatever
@task
def p2():
whatever
def my_main(key):
tasks[key]()
Solution 4
# index dictionary by list of key names
def fn1():
print "One"
def fn2():
print "Two"
def fn3():
print "Three"
fndict = {"A": fn1, "B": fn2, "C": fn3}
keynames = ["A", "B", "C"]
fndict[keynames[1]]()
# keynames[1] = "B", so output of this code is
# Two
Solution 5
This will call methods from dictionary
This is python switch statement with function calling
Create few modules as per the your requirement. If want to pass arguments then pass.
Create a dictionary, which will call these modules as per requirement.
def function_1(arg):
print("In function_1")
def function_2(arg):
print("In function_2")
def function_3(fileName):
print("In function_3")
f_title,f_course1,f_course2 = fileName.split('_')
return(f_title,f_course1,f_course2)
def createDictionary():
dict = {
1 : function_1,
2 : function_2,
3 : function_3,
}
return dict
dictionary = createDictionary()
dictionary[3](Argument)#pass any key value to call the method
JohnnyDH
Updated on July 05, 2022Comments
-
JohnnyDH almost 2 years
I am trying to use functional programming to create a dictionary containing a key and a function to execute:
myDict={} myItems=("P1","P2","P3",...."Pn") def myMain(key): def ExecP1(): pass def ExecP2(): pass def ExecP3(): pass ... def ExecPn(): pass
Now, I have seen a code used to find the defined functions in a module, and I need to do something like this:
for myitem in myItems: myDict[myitem] = ??? #to dynamically find the corresponding function
So my question is, How do I make a list of all the
Exec
functions and then assign them to the desired item using the a dictionary? so at the end I will havemyDict["P1"]() #this will call ExecP1()
My real problem is that I have tons of those items and I making a library that will handle them so the final user only needs to call
myMain("P1")
I think using the inspect module, but I am not so sure how to do it.
My reason to avoid:
def ExecPn(): pass myDict["Pn"]=ExecPn
is that I have to protect code as I am using it to provide a scripting feature within my application.
-
JohnnyDH about 12 yearsI know, it was my first choice, but I want the final user to have a limited access, so the user can't change the contents in the dictionary in run-time.
-
user1066101 about 12 yearsThe user can always change anything they want at run-time. It's Python. They have the source.
-
JohnnyDH about 12 yearsI know it is easy to do it that way, I really do, but then my dictionary has no protection, I really need to prevent the end user to modify that dictioanry
-
synthesizerpatel about 12 yearsThen make it a class guarded by (and I use this term loosely) write-only enforcement decorators and use __ prefixed variables in a (lets be honest, vain attempt) to hide the implementation details from the user. Take a look at fightingquaker.com/pyanno for some inspiration..
-
JohnnyDH about 12 yearsYou are right the end user will know the name and I really appreciate your tip, I am a starter, but just in python, I am very well aware of some programming principles but I am not implementing this in a personal project, is work related and customer asks for a robust application and the truth is that this software will be used by a bunch of monkeys and they want to see everything but a simple and plain application...
-
user1066101 about 12 years"It depends of the user"? What does that mean? Not every user can tweak the code? If that's what you mean then you should spend even less time worrying about "limited access". Since -- effectively -- all users can tweak the code, it's hardly worth trying to add code to create "limited access". Go for simple in all cases.
-
Misha Akovantsev about 12 years@JohnnyDH, describe at least few different scenarios with those monkeys taking over your application. This might get you some answers more suitable for your situation.
-
Ohad about 12 yearsWorks, but I'd create a filtered version of locals (e.g,
filter(lambda x: x.startswith('Exec'), locals())
). As far as safety goes,locals
will always contain functions defined in local scope. -
JohnnyDH about 12 yearsThe application is GUI interface for a device, we offer "scripting" features within our application, but we do not offer "full" support for python since this is our main goal, limit the access. The final users are sellers, people that only need to follow a manual to write basic "scripts", they did not accept a proprietary "language", they wanted a python based one, so yeah, the user can modify the dictionary in a single buggy line and the product won't work, so yes, they just need to restart but the costumer will complain.
-
JohnnyDH about 12 yearsI know, but as I posted below:"The application is GUI interface for a device, we offer "scripting" features within our application... ...The final users are sellers, people that only need to follow a manual to write basic "scripts", they did not accept a proprietary "language", they wanted a python based one, so yeah, the user can modify the dictionary in a single buggy line.." & "and they want to see everything but a simple and plain application..." The costumer knows nothing about programming but they want what they want and if they want to see
x = x+y/y
instead ofx++
we must deliver... -
user1066101 about 12 years"as I posted below"? Please update the question to contain all the facts. There's no "above" or "below" since the order of the answers changes. "the user can modify the dictionary in a single buggy line" doesn't mean anything. They can also reformat their hard drive in a single buggy line that calls
subprocess.Popen
. There's no good reason for extra complexity. It doesn't prevent the user from causing havoc with "single buggy line". Indeed, the more complexity you layer on, the less transparency and the more mysteries to debug. -
JohnnyDH about 12 yearsI have to disagree, as I said, we do not support all python capabilities, we have a filter and we only provide access to some modules, I do not care what they do with their hard drives of if they know something about python, I only care the guy writing the script can't modify the modules, just read them, it is a basic scripting feature, sadly Python does not provide much security in cases like this. So, I need to make the modules safer, and it does not matter if code has more complexity, the costumer knows the employees and so they request for such "safety" measures.
-
user1066101 about 12 years"I need to make the modules safer". How? You can't. "they request for such "safety" measures". Good for them. Nothing you do can actually have any impact except to make things more complex. But. You keep repeating yourself that it's essential that you create useless, no-value complexity. I guess that's it. Cheers.
-
JohnnyDH about 12 yearsI guess you missed the line I posted "The costumer knows nothing about programming but they want what they want and if they want to see
x = x+y/y
instead ofx++
we must deliver... " I hope you do not work providing software as I do, tell your costumer "Good for you" and you will find yourself searching for a job. I keep repeating my self because if you were able to understand that I could not use the simple way, then everything would be easier... -
wim about 12 years@JohnnyDH This is a crazy argument. If you have a real requirement to make the code more "secure" then you must use a compiled language - python is not the right tool for the job and it's your responsibility to explain that to the customer. Adding lines of crap to your code to try and workaround python's inherent openness is not a safety measure - it can't be done properly, and attempting to do so will only make your code more ugly and fragile
-
JohnnyDH about 12 years@wim I know :(, I have never used python precisely because it's too flexible and open, sadly I am not the leader engineer, this costumer is new and the company wants to hold them, so I am with you people, but I can't do much, too late to start again and the reason we did not want to use Python is the same reason they think they need it... Sad, but money is the king...
-
JohnnyDH about 12 yearsWe always use C (for embedded), C#, Java and ASP. However, the former provider for this costumer was using Python for other device's tools, so that's why they think they need to keep using it.
-
Ben about 12 years@JonnyDH Your arguments don't really make sense. "With a single buggy line" they can clobber your main function just as easily as they could modify a dictionary. In truth it will about as unlikely that either will happen, because neither resembles a legitimate normal operation. But this answer is suggesting that you don't even use a dictionary, you just give them a single public module that only contains the things the end user is supposed to call. There is nothing you can do if you're worried about them importing other modules and messing with their internals.
-
user1066101 about 12 years@JohnnyDH: I've only been providing software for 30 years, so I have seen a few examples of useless complexity before. This is another. There's a difference between complexity and security. The "if they want to see..." line is a really, really bad example, because Python has no "++" operator like C does. And. The "Scripting Features" explanation must be folded into your question so that your question makes any sense at all. Repeating requirements in comments is bad.
-
JohnnyDH about 12 yearsI am sure you understood my x++ example. Well, my bad if I did not say we are providing scripting feature, I did not realize that people needed that much information
-
JohnnyDH about 12 years@Ben Believe me, I am very aware of that, I have 8 years providing software solutions, this is the first time I use Python and I wish I could use many security features from other languages, however I can not control it, I need to make the module somewhat "Read-Only", the costumer asks us to give them "proofs" that the scripting feature is as safe as possible, we offered a proprietary language so we had full control of the scripting process but they refused, and we can't control all without compromising flexibility for the feature.
-
cs95 almost 6 yearsSee stackoverflow.com/a/9168387/4909087 for how to actually and correctly do this
-
Aran-Fey almost 6 yearsThat's not DRY, that's obfuscated.
-
Vladimir Panteleev over 5 yearsThis answer repeats every function name 3 times in the source. More concise solutions exist, see below.
-
Vladimir Panteleev over 5 yearsI think referring to advanced language features like lambdas and custom decorators as obfuscation is misleading, to say the least.
-
Joe over 5 yearsSure. The lambda here is only for brevity. Whether you stick with that or rewrite to
def task(f):\n tasks[f.__name__] = f
, the principle remains the same. Being DRY removes your responsibility to keep the dictionary keys in sync with function names. It also makes it easier to spot mistakes, such as a missing@task
(vs a missing dictionary item). -
John Lunzer over 4 yearsThis is elegant and DRY, but definitely not simplified.
-
Alex Povel about 3 yearsWith integers as keys, a list would also do instead.
-
alex about 3 yearsThis isn't working for me.
p1()
has args. In the dictionary,"P1": p1,
is called without args. When I specify args like"P1": p1(arg1, arg2),
- the dictionary automatically calls thep1
function. -
alex about 3 yearsWhat if
p1()
andp2()
have parameters? -
Faraaz Kurawle over 2 years@S.Lott , I don't agree as we can convert Python files to exe using
pyinstaller
, by which the user don't get the source code.