99

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 have myDict["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.

jonrsharpe
  • 115,751
  • 26
  • 228
  • 437
JohnnyDH
  • 2,015
  • 2
  • 17
  • 12

12 Answers12

201

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)

cs95
  • 379,657
  • 97
  • 704
  • 746
S.Lott
  • 384,516
  • 81
  • 508
  • 779
  • 1
    I 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. – JohnnyDH Feb 06 '12 at 22:41
  • 25
    The user can always change anything they want at run-time. It's Python. They have the source. – S.Lott Feb 06 '12 at 22:41
  • 1
    "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. – S.Lott Feb 06 '12 at 23:05
  • I 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 of `x++` we must deliver... – JohnnyDH Feb 06 '12 at 23:43
  • "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. – S.Lott Feb 06 '12 at 23:48
  • I 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. – JohnnyDH Feb 07 '12 at 00:01
  • "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. – S.Lott Feb 07 '12 at 00:05
  • I 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 of `x++` 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... – JohnnyDH Feb 07 '12 at 00:16
  • 13
    @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 – wim Feb 07 '12 at 00:33
  • @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 Feb 07 '12 at 00:48
  • We 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. – JohnnyDH Feb 07 '12 at 00:54
  • 2
    @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**. – S.Lott Feb 07 '12 at 10:51
  • I 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 Feb 07 '12 at 19:40
  • This answer repeats every function name 3 times in the source. More concise solutions exist, see below. – Vladimir Panteleev Dec 14 '18 at 15:58
  • This 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 the `p1` function. – alex May 07 '21 at 18:16
  • 1
    @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. – Faraaz Kurawle Sep 30 '21 at 16:07
32

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]()
Joe
  • 16,328
  • 12
  • 61
  • 75
  • 14
    That's not DRY, that's obfuscated. – Aran-Fey Jun 09 '18 at 19:27
  • 7
    I think referring to advanced language features like lambdas and custom decorators as obfuscation is misleading, to say the least. – Vladimir Panteleev Dec 14 '18 at 16:00
  • 2
    Sure. 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). – Joe Dec 15 '18 at 19:43
  • 1
    This is elegant and DRY, but definitely not simplified. – John Lunzer Feb 05 '20 at 15:59
29

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()
Ohad
  • 2,752
  • 17
  • 15
  • 1
    Works, 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. – Ohad Feb 06 '12 at 23:14
  • See https://stackoverflow.com/a/9168387/4909087 for how to _actually and correctly_ do this – cs95 May 18 '18 at 18:21
10
# 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
jwc3119
  • 109
  • 1
  • 2
5

You can just use

myDict = {
    "P1": (lambda x: function1()),
    "P2": (lambda x: function2()),
    ...,
    "Pn": (lambda x: functionn())}
myItems = ["P1", "P2", ..., "Pn"]

for item in myItems:
    myDict[item]()
Jonas De Schouwer
  • 755
  • 1
  • 9
  • 15
4

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
akD
  • 1,137
  • 1
  • 10
  • 15
2

Often classes are used to enclose methods and following is the extension for answers above with default method in case the method is not found.

class P:

     def p1(self):
         print('Start')

     def p2(self):
         print('Help')

     def ps(self):
         print('Settings')

     def d(self):
         print('Default function')

     myDict = {
         "start": p1,
         "help": p2,
         "settings": ps
     }

     def call_it(self):
         name = 'start'
         f = lambda self, x : self.myDict.get(x, lambda x : self.d())(self)
         f(self, name)


 p = P()
 p.call_it()
Milind Deore
  • 2,887
  • 5
  • 25
  • 40
2
class CallByName():
    
    def method1(self):
        pass

    def method2(self):
        pass

    def method3(self):
        pass

    def get_method(self, method_name):
        method = getattr(self, method_name)
        return method()


callbyname = CallByName()
method1 = callbyname.get_method(method_name)

```
Adebayo
  • 53
  • 6
2
#!/usr/bin/python

def thing_a(arg=None):
    print 'thing_a', arg

def thing_b(arg=None):
    print 'thing_b', arg

ghetto_switch_statement = {
    'do_thing_a': thing_a,
    'do_thing_b': thing_b
}

ghetto_switch_statement['do_thing_a']("It's lovely being an A")
ghetto_switch_statement['do_thing_b']("Being a B isn't too shabby either")

print "Available methods are: ", ghetto_switch_statement.keys()
synthesizerpatel
  • 27,321
  • 5
  • 74
  • 91
  • I 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 – JohnnyDH Feb 06 '12 at 22:43
  • Then 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 http://fightingquaker.com/pyanno/ for some inspiration.. – synthesizerpatel Feb 06 '12 at 22:49
0
def p1( ):
    print("in p1")

def p2():
    print("in p2")

myDict={
    "P1": p1,
    "P2": p2

}

name=input("enter P1 or P2")

myDictname

mrsrinivas
  • 34,112
  • 13
  • 125
  • 125
Gokhale
  • 9
  • 1
0
# Define your functions here (replace with actual function definitions)

# Function that simulates calling FunctionA with parameters
def FunctionA(param1, param2):
    print(f"Calling FunctionA with params: {param1}, {param2}")

# Function that simulates calling FunctionB with parameters
def FunctionB(img_ch, output_ch, lamda, start_n):
    print(f"Calling FunctionB with params: {img_ch}, {output_ch}, {lamda}, {start_n}")

# Function that simulates calling FunctionC
def FunctionC():
    print("Calling FunctionC")

# Function that simulates calling FunctionD with parameters
def FunctionD(param1, param2):
    print(f"Calling FunctionD with params: {param1}, {param2}")

# Your dictionary
a = {
'FunctionA': 'FunctionA(9, 5)',
'FunctionB': 'FunctionB(img_ch=3,output_ch=2,lamda=0.1,start_n=16)',
'FunctionC': 'FunctionC()',
'FunctionD': 'FunctionD(3,2)'
}


# Loop through the dictionary
for key, value in a.items():
    try:
        # Evaluate the value as a function call
        eval(value)
    except Exception as e:
        print(f"Error calling {key}: {e}")

Output:

Calling FunctionA with params: 9, 5
Calling FunctionB with params: 3, 2, 0.1, 16
Calling FunctionA with params: 9, 3
Calling FunctionC
Calling FunctionD with params: 3, 2
Keval
  • 557
  • 10
  • 15
-2

You are wasting your time:

  1. You are about to write a lot of useless code and introduce new bugs.
  2. To execute the function, your user will need to know the P1 name anyway.
  3. Etc., etc., etc.

Just put all your functions in the .py file:

# my_module.py

def f1():
    pass

def f2():
    pass

def f3():
    pass

And use them like this:

import my_module

my_module.f1()
my_module.f2()
my_module.f3()

or:

from my_module import f1
from my_module import f2
from my_module import f3

f1()
f2()
f3()

This should be enough for starters.

Misha Akovantsev
  • 1,817
  • 13
  • 14
  • You 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... – JohnnyDH Feb 06 '12 at 23:00
  • @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. – Misha Akovantsev Feb 06 '12 at 23:07
  • The 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 Feb 06 '12 at 23:34
  • @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. – Ben Feb 07 '12 at 01:21
  • @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. – JohnnyDH Feb 07 '12 at 19:59