4

Sample code file:

MyCode.py

def DoSomething(data):
    # data is a string
    # find a function in the same scope as this one that has the name contained
    # in "data"
    try:
        func = getattr(self,data) # this is wrong
    except AttributeError:
        print "Couldn't find function %s." % data
        return

    # execute the function
    func()

def AFunction():
    print "You found a function!"

def Add():
    print "1 + 1 = %d." % ( (1+1) )

def test():
    # unit test
    DoSomething("AFunction")

--------

test.py

import MyCode

# Try executing some functions
MyCode.DoSomething("AFunction") # should print You found a function!
MyCode.DoSomething("Add") # should print 1+1=2.
MyCode.DoSomething("IDoNotExist") # should run the exception handler

# Try executing a function from inside the module
MyCode.test() # should print You found a function!

If I were dealing with a class object, that getattr statement would end up retrieving a reference to the function within the class that matches the name provided. Then, as shown, I could execute that function directly from its variable name.

However, since these functions are NOT in a class, rather they are at the module/file level, using getattr on self won't work since we don't have a self reference into a class instance.

My question is: is it actually necessary to wrap this function along with all its supporting functions in a class and instantiate that class just to have this capability? Or, is there an alternative way to use getattr so that I can access the functions defined at the file level.

Notice both use cases: Within the file itself, the "test" function needs to call these functions, but also from outside as imported the function that runs arbitrary other functions might need to run.

Advice appreciated.

Thanks!

2 Answers 2

8
import sys
current_module = sys.modules[__name__]
getattr(current_module, 'AFunction')

Wrapping everything with a class will be more secure though.

Sign up to request clarification or add additional context in comments.

10 Comments

more secure in what way?
Anything involving messing with sys.modules raises red flags about the architectural design of your solution. So more secure in the sense that it's easier to understand if it's in a class, and you don't have a risk of having another side effect while you're messing with sys.modules
@ErlVolton What side effect? What red flags? It's not "messing" with anything, it's simple "read" operation. I don't see anything inappropriate in it.
I've never said it's hacky. It's a completely legit yet insecure code. You guys keep using this word convincing me that you simply don't know much about Python. And you are afraid of using well documented stuff which is not widely used. Well that's your problem.
@brunodesthuilliers No, but saying that this solution is a ugly/clever hack make you and ignorant and/or afraid. Because the only argument you have against it is "it is ugly" whatever it means. Even though it is a stable and well documented feature.
|
1

getattr works on any object, and in Python mostly everything is an object, including classes and modules. So from a purely technical POV you can either store your functions in a class (probably as staticmethods) and use getattr on that class, or even use the hack posted by freakish to directly retrieve them from the module.

BUT...

But it's not the RightWay(tm) to solve your problem.

In it's simplest form, an object (class, instance, module, whatever) is mostly the combination of a dict, a class and the attribute resolution operator - which quite often ends up doing a simple lookup on one dict or another (ie the instance's dict, the class's dict, then the parents classes etc). In your above example, all you need is a simple dict lookup, and the obvious solution is, well, to use a plain old dict - remember, in Python everything is an object, including functions:

MyCode.py

FUNCTIONS = {}

def DoSomething(data):
    # data is a string
    # find a function in the same scope as this one that has the name contained
    # in "data"
    func = FUNCTIONS.get(data)
    if func is None:
        print "Couldn't find function %s." % data
        return

    # execute the function
    func()

def AFunction():
    print "You found a function!"

FUNCTIONS["AFunction"] = AFunction

def Add():
    print "1 + 1 = %d." % ( (1+1) )

FUNCTIONS["Add"] = Add

def test():
    # unit test
    DoSomething("AFunction")

Now you may find the FUNCTIONS["whatever"] = whatever pattern a bit boring - but hopefully we can make it a bit more sexy thanks to the decorator syntax:

FUNCTIONS = {}

def register(func):
    FUNCTIONS[func.__name__] = func      
    return func

def DoSomething(data):
    # data is a string
    # find a function in the same scope as this one that has the name contained
    # in "data"
    func = FUNCTIONS.get(data)
    if func is None:
        print "Couldn't find function %s." % data
        return

    # execute the function
    func()

@register    
def AFunction():
    print "You found a function!"

@register            
def Add():
    print "1 + 1 = %d." % ( (1+1) )

As an added benefit: you have complete control AND some automatic documentation of which functions are supposed to be accessible thru DoSomething()

5 Comments

Right, right, and FUNCTIONS[func.__name__] = func is suddenly not an ugly hack. You have a very weird sense of ugliness, seriously. Plus you've added lots of redundant code, per function actually.
a non-obvious shortcut that technically solves the problem but can break in unpredictable ways In what unpredictable ways? You are talking nonsense, proving again that you simply don't understand most basic Python libraries. doesn't make the code easier to understand and maintain Really? You have a problem in understanding and maintaining two lines of code, one of which is an import? The code is self explanatory my friend. Unlike yours. can have unwanted side effects <facepalm> What unwanted side effects? Enlighten me.
BTW: even use the **ugly** hack posted by freakish cause it seems you've forgotten. Yes, I do read your posts carefuly. Now if that's not personal then I don't know what is.
Well, sorry to hear that. After all you've accused my code of potential side effects and unpredicatable behaviour. That's a serious stuff you know. You should take responsibility for your words. BTW: you might call me egotic and I agree I totally am. That's completely irrelevant though. Plus I don't know why you apologize for one personal offense when you do another one in the next sentence.
All comments and "ugly" word removed - I'm not going to argue with you Sir, it's just a waste of time, storage and bandwidth. Understand it anyway you want, I honestly don't care.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.