#1
  1. Contributing User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Jun 2000
    Location
    Southern California
    Posts
    73
    Rep Power
    15

    calling function by symbolic reference


    In perl, I can call an object method by using a variable, like this:

    Code:
    $obj = new MyObject;
    $f = 'foo';
    
    $obj->$f($args);
    How can I do this in python? I know that I can do this, with a simple function:

    Code:
    def foo():
        print "OK"
    
    ref = foo
    ref()  # prints "OK"
    But what about an instance method?
  2. #2
  3. No Profile Picture
    Contributing User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Dec 2001
    Location
    Houston, TX
    Posts
    383
    Rep Power
    13

    Re: calling function by symbolic reference


    Originally posted by vpopper

    But what about an instance method?
    Not sure what you mean here.

    But, if you want to be able to get a reference to the function via the name, simply use the globals() dict.

    Code:
    >>> def foo():
    ...     print "Found it!"
    ...
    >>> globals()['foo']()
    Found it!
    >>> globals()
    {'__builtins__': <module '__builtin__' (built-in)>, '__name__': '__main__', 'foo': <function foo at 0x81612cc>, '__doc__': None}
    >>> globals()['foo']
    <function foo at 0x81612cc>
    Debian - because life's too short for worrying.
    Best. (Python.) IRC bot. ever.
  4. #3
  5. Contributing User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Jun 2000
    Location
    Southern California
    Posts
    73
    Rep Power
    15

    Re: Re: calling function by symbolic reference


    Originally posted by Strike
    Not sure what you mean here.
    I mean an instance method:

    Code:
    class Foo:
        def __init__(self):
            pass
        def ok(self):
            print "OK"
        def notok(self):
            print "nope"
    
    obj = Foo()
    
    # say I have a list of strings that correspond to 
    # methods in class Foo. The string (method) to 
    # call is decided at runtime:
    
    strings = ['ok', 'notok']
    
    for s in strings:
        # I want to be able to call a method using 
        # the variable 's' as the name of the method
        obj.s()   # this won't work, no method 's'
  6. #4
  7. Contributing User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Jan 2001
    Location
    St. George, Utah
    Posts
    63
    Rep Power
    37
    There are a couple of ways to do this. One is very messy:

    Code:
    class Foo:
        def __init__(self):
            pass
        def ok(self):
            print "OK"
        def notok(self):
            print "nope"
    
    obj = Foo()
    
    strings = ['ok', 'notok']
    
    for s in strings:
        obj.__class__.__dict__[s](obj)
    Some explaination here - class instances like obj have several built in attributes in python. One useful one is __class__ which refers to the class it is an instance of, and another is __dict__ which is a dictionary of the class instance's attributes. These attributes are actually used by Python if you have an expression like this:

    Code:
    obj.ok()
    In that case, Python looks in obj's __dict__ for an attribute called "ok" It won't find it there, as ok is not an attribute of obj, but rather of it's class, Foo. So next, it looks at obj's __class__ attribute's __dict__. It finds ok there and calls it, passing obj as the first argument. So, what we are doing above is handling all that ourselves. Messy.

    Here's a more correct way:

    Code:
    class Foo:
        def __init__(self):
            pass
        def ok(self):
            print "OK"
        def notok(self):
            print "nope"
    
    obj = Foo()
    
    strings = ['ok', 'notok']
    
    for string in strings:
        eval("obj.%s()" % string)
    I don't even need to explain that one, do I?
    Lucas Marshall
  8. #5
  9. No Profile Picture
    Contributing User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Dec 2001
    Location
    Houston, TX
    Posts
    383
    Rep Power
    13
    I'd say the first way is more correct, not to mention safer. Because I'm assuming those strings aren't hardcoded anyway. So that means they depend somewhat on user interaction. And using eval() on strings that a user passes in is extremely dangerous (for example, what if I put in 'os.system("rm -rf /")' as the string somehow?) and should always be avoided. The dict method is the most appropriate.
    Debian - because life's too short for worrying.
    Best. (Python.) IRC bot. ever.
  10. #6
  11. Contributing User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Jan 2001
    Location
    St. George, Utah
    Posts
    63
    Rep Power
    37
    I did mean "more correct in this instance." Obviously if you are dealing with user-input data, you wouldn't use eval. I should have clarified that.

    Using eval is more "pythonish," from a readability standpoint, anyway.
    Lucas Marshall
  12. #7
  13. Contributing User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Jun 2000
    Location
    Southern California
    Posts
    73
    Rep Power
    15
    Originally posted by ZeUs
    Using eval is more "pythonish," from a readability standpoint, anyway.
    Thank you very much for your help!

IMN logo majestic logo threadwatch logo seochat tools logo