13
votes

Python noob here. How do I get hold of the 'inner' function within the 'fib' function?

from time import sleep

class Fibonacci(object):

    def __init__(self, a, b, limit=50):
        self.a = a
        self.b = b
        self.limit = limit

    def fib(self):

        while self.a < self.limit:
            c = self.a + self.b
            sleep(1)
            print self.a,
            self.b = self.a
            self.a = c

        def inner(self):
            print 'Damn it! Just print already!'


j = Fibonacci(0,1,2)
j.fib()

## This doesn't work. Gives an "AttibuteError: 'function' object has no attribute 'inner'"
j.fib.inner()
5
You could call inner() from within the definition of fib(), but not from outside of it. What are you trying to do? - Jordan
My Python is rusty, but I'm pretty sure you can't access inner from outside of fib, without changing how inner is defined. - Matt Ball
I figured since i can access fib inside Fibonacci(yeah, i know it's a class and not a function) with a dot operator, I should be able to access a function inside another function like that. Oh well - kassold
@seykom Nope. No more than it is possible to access inner after inner = "fubar". - user166390
seykom, You need to accept answers to more of your questions. Click the check mark next to the answer that helped you the most. You should do this for your old questions as well as for new ones. - agf

5 Answers

16
votes

You cannot, not unless fib returns inner somehow. inner is essentially a local variable inside the scope of fib and you can't access a function's locals from outside of it. (That wouldn't even make sense, since the locals don't exist except when the function is running. Think about it -- would it make sense to access fib's c variable from outside of the function?)

9
votes

Do not use the following.

[...]
>>> j = Fibonacci(0,1,2) 
>>> j.fib()
0 1 1
>>> # dark magic begins!
>>> import new
>>> new.function(j.fib.im_func.func_code.co_consts[2],{})(None)
Damn it! Just print already!

You can tell simply by looking at it that it's not really Python, and for that matter it isn't really calling the "inner" function itself, it's simply creating a new function like it. I also didn't bother setting the globals 'correctly', because this is a terrible thing to do in the first place..

[I should mention that the point of the above is to note that the idea that you can't access internals from outside isn't strictly true, though it's almost never a good idea. Exceptions include interpreter-level code inspections, etc.]

Unclean! Unclean!

5
votes
from time import sleep

class Fibonacci(object):

    def __init__(self, a, b, limit=50):
        self.a = a
        self.b = b
        self.limit = limit

    def fib(self):

        while self.a < self.limit:
            c = self.a + self.b
            sleep(1)
            print self.a,
            self.b = self.a
            self.a = c

        def inner(self):
            print 'Damn it! Just print already!'
        Fibonacci.fib.inner = inner

    fib.inner = None

This code snippet will allow you to use inner.

5
votes

The below seems to achieve what you want

from types import CodeType, FunctionType

def find_nested_func(parent, child_name):
    """ Return the function named <child_name> that is defined inside
        a <parent> function
        Returns None if nonexistent
    """
    consts = parent.func_code.co_consts
    for item in consts:
        if isinstance(item, CodeType) and item.co_name==child_name:
            return FunctionType(item, globals())
1
votes

As stated by some of the other readers, it's a problem of scope. FWIW, this works by returning the inner function:

from time import sleep

class Fibonacci(object):

    def __init__(self, a, b, limit=50):
        self.a = a
        self.b = b
        self.limit = limit

    def fib(self):

        while self.a < self.limit:
            c = self.a + self.b
            sleep(1)
            print self.a,
            self.b = self.a
            self.a = c

        def inner():
            print 'Damn it! Just print already!'

        return inner


j = Fibonacci(0,1,2)
j.fib()()

For reference, here's a good intro to python's scoping:

Short Description of the Scoping Rules?