16

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()
kassold
  • 469
  • 3
  • 8
  • 21
  • 1
    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 Aug 14 '11 at 00:44
  • 1
    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 Aug 14 '11 at 01:29
  • Why do you need to do this? Have you considered just not nesting the functions? Especially since the interface for `inner` looks like you want it to be a method of a `Fibonacci` object... it won't really work like that as an inner function... – Karl Knechtel Aug 14 '11 at 04:35
  • You could call `inner()` from within the definition of `fib()`, but not from outside of it. What are you trying to do? – Jordan Aug 14 '11 at 00:43
  • 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 Aug 14 '11 at 00:53
  • @seykom Nope. No more than it is possible to access `inner` after `inner = "fubar"`. –  Aug 14 '11 at 00:56
  • @pst, am not sure i understand you. Thank you all for helping. – kassold Aug 14 '11 at 01:34

5 Answers5

19

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?)

cdhowie
  • 158,093
  • 24
  • 286
  • 300
10

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!

DSM
  • 342,061
  • 65
  • 592
  • 494
6

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())
lai
  • 1,163
  • 10
  • 15
6
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.

Sunjay Varma
  • 5,007
  • 6
  • 34
  • 51
2

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?

Community
  • 1
  • 1
Rafael Ferreira
  • 1,260
  • 8
  • 11