22

I have a method that i have broken into smaller nested functions to break up the code base:

def foo(x,y):
    def do_this(x,y):
        pass
    def do_that(x,y):
        pass
    do_this(x,y)
    do_that(x,y)
    return

Is there a way to run one of the nested functions by itself. eg:

foo.do_this(x,y)

EDIT:

I am trying to setup caching on a web server i have built using pyramid_breaker

def getThis(request):
    def invalidate_data(getData,'long_term',search_term):
         region_invalidate(getData,'long_term',search_term)
    @cached_region('long_term')
    def getData(search_term):
         return response
    search_term = request.matchdict['searchterm']
    return getData(search_term)

This is my understanding may not be accurate:

Now the reason i have this is that the namespace used by the decorator to create the cache key is genereated from the function and the arguements. You can't therefore just put the decorator on getThis as the request variable is unique-ish and the cache is useless. So i created the inner function which has repeatable args (search_term).

However to invalidate the cache (ie refresh), the invalidation function requires scope to know of the 'getData' function so also needs to be nested. Therefore i need to call the nested function. You wonderful people have made it clear its not possible so is someone able to explain how i might do it with a different structure?

user1474424
  • 657
  • 1
  • 5
  • 18
  • your code `foo.do_this` will try to access do_this as a member function of `foo`, which will give you an attribute error, instead make foo as a class – avasal Jun 22 '12 at 10:44
  • "break up the code base" is what module namespaces are good for. If you really want to *encapsulate* the `do_` functions then use a class as @lazyr shows. – msw Jun 22 '12 at 10:55
  • Nested functions is not the way to structure code in Python (neither are classes). Take a look at [modules](http://docs.python.org/tutorial/modules.html). – georg Jun 22 '12 at 11:04
  • See my answer in this one: http://stackoverflow.com/questions/7054228/accessing-a-function-within-a-functionnested-function – lai Jul 19 '15 at 15:40

7 Answers7

31

I assume do_this and do_that are actually dependent on some argument of foo, since otherwise you could just move them out of foo and call them directly.

I suggest reworking the whole thing as a class. Something like this:

class Foo(object):
    def __init__(self, x, y):
        self.x = x
        self.y = y

    def do_this(self):
        pass

    def do_that(self):
        pass

    def __call__(self):
        self.do_this()
        self.do_that()

foo = Foo(x, y)
foo()
foo.do_this()
Lauritz V. Thaulow
  • 49,139
  • 12
  • 73
  • 92
  • Very helpful answer and feels like my entire structure maybe wrong. Might be more helpful if i explain the bigger picture to see if you can help (don't worry if its too difficult). – user1474424 Jun 22 '12 at 10:54
  • 2
    @user1474424 I think you should put this in a new question. – Lauritz V. Thaulow Jun 22 '12 at 10:59
  • @user1474424 Having looked at the question I feel it's perhaps better to post it as a new question. I'm unfamiliar with this library you're using. Remove the update and post it as new question. – Lauritz V. Thaulow Jun 22 '12 at 11:20
  • I tried new queston - no one view/answered... thanks for your help any way. – user1474424 Jun 22 '12 at 11:50
  • @user1474424 You forgot the all-important "python" keyword, which is why no-one answered. And you can drop all the other keywords you added. I suggest you delete that question and repost it with the correct keywords. You should make a better title -- those are IMPORTANT if you want people to look at your question -- like e.g. "nested function problem" or "how to restructure nested function code". Your explanation could be improved as well. Include a link to the module you're using, perhaps, and this question. – Lauritz V. Thaulow Jun 22 '12 at 12:13
  • Downvoted. This does not answer the question. See @Jens Timmerman's answer for an answer and solution. – Moberg Dec 17 '20 at 11:08
20

These previous answers, telling you that you can not do this, are of course wrong. This is python, you can do almost anything you want using some magic code magic.

We can take the first constant out of foo's function code, this will be the do_this function. We can then use this code to create a new function with it.

see https://docs.python.org/2/library/new.html for more info on new and https://docs.python.org/2/library/inspect.html for more info on how to get to internal code.

Warning: it's not because you CAN do this that you SHOULD do this, rethinking the way you have your functions structured is the way to go, but if you want a quick and dirty hack that will probably break in the future, here you go:

import new
myfoo = new.function(foo.func_code.co_consts[1],{}) 
myfoo(x,y) # hooray we have a new function that does what I want

UPDATE: in python3 you can use the types module with foo.__code__:

import types
myfoo = types.FunctionType(foo.__code__.co_consts[1], {})
myfoo()  # behaves like it is do_this()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: do_this() missing 2 required positional arguments: 'x' and 'y'
Jens Timmerman
  • 9,316
  • 1
  • 42
  • 48
  • In Python 3 I can use `types`? – Vitaly Zdanevich Sep 16 '16 at 13:37
  • @VitalyZdanevich Yes, according to the [documentation](https://docs.python.org/2/library/new.html): `Deprecated since version 2.6: The new module has been removed in Python 3. Use the types module’s classes instead.` – Pedro May 01 '17 at 15:22
  • It breaks, at least with `enumerate` in the inner func :p – Er... Aug 19 '20 at 11:52
  • This seems to be the only answer that actually answers the question. It should be the accepted answer. – Moberg Dec 17 '20 at 11:06
4

There is, you have to make them as an attribute of the function object. But this will work only after the first call of foo.

def foo(x,y):
    def do_this(x,y):
        pass
    def do_that(x,y):
        pass
    do_this(x,y)
    do_that(x,y)
    foo.do_this = do_this
    foo.do_that = do_that
    return

>>> foo.do_this(1, 2)
AttributeError: 'function' object has no attribute 'do_this'
>>> foo(1, 2)
>>> foo.do_this(1, 2)
>>>
mutantacule
  • 6,913
  • 1
  • 25
  • 39
3

No (apart from poking around in closure objects, which is complete overkill here). If you need that, use a class.

class foo(object):
    def do_this(self, x, y):
       ...
    def do_that(self, x, y):
       ...
    def do_other_stuff(self, x, y):
       # or __call__, possibly

Or just put those functions in the outer scope, since you're passing everything as arguments anyway:

def foo(x, y):
    do_this(x, y)
    do_that(x, y)

def do_this(x, y):
    ...

def do_that(x, y):
    ...
Cat Plus Plus
  • 125,936
  • 27
  • 200
  • 224
2

No, there is not. Since you may access variables in an outer scope from within a nested function:

def foo(x,y):
    def do_this(z):
        print(x,y,z)
    # ...

there is no way to call do_this while providing a binding for x and y.

If you must call do_this from elsewhere, simply make it a top level function at the same level as foo.

Greg Hewgill
  • 951,095
  • 183
  • 1,149
  • 1,285
  • yes you can, see my answer: `myfoo = types.FunctionType(foo.__code__.co_consts[1], {'x': 'value for x here', 'y': 'value for y here'})` – Jens Timmerman Aug 20 '20 at 11:36
1

You can try this way:

def a(x, y):
    name = 'Michael'
    a.name = name

    a.z = z = x * y
    #a.z = z

def b():
    def give_me_price(f,g):
        price = f * g
        return price

    def two(j,k):
        surname = 'Jordan' # without return surname give None

    # two = two('arg1', 'arg2')
    # b.blabla = two

    one = give_me_price(5, 10)
    b.halabala = one

    print(a.name) # ;)

x = 20
y = 30

a(x,y) # IMPORTANT! first you must run function
print(a.z)
print(a.name * 5)

print('-'*12)
b() # IMPORTANT! first you must run function
print('price is: ' + str(b.give_me_price(5, 25)))
# print(b.blabla)
1

This is how I did it.

CODE

def getMessage(a="", b="", c=""):
    def getErrorMessage(aa, bb):
        return "Error Message with/without params: {}{}".format(aa, bb)

    def getSuccessMessage(bb, cc):
        return "Success Message with/without params:  {}{}".format(bb, cc)

    def getWarningMessage(aa, cc):
        return "Warning Message with/without params:  {}{}".format(aa, cc)

    return {
        "getErrorMessage": getErrorMessage(a, b),
        "getSuccessMessage": getSuccessMessage(b, c),
        "getWarningMessage": getWarningMessage(a, c),
    }


a = "hello"
b = " World"
c = "!"
print(getMessage(a, b)["getErrorMessage"])
print(getMessage(b=b, c=c)["getSuccessMessage"])
print(getMessage(a=a, c=c)["getWarningMessage"])
print(getMessage(c=c)["getWarningMessage"])

OUTPUT

Error Message with/without params: hello World
Success Message with/without params:   World!
Warning Message with/without params:  hello!
Warning Message with/without params:  !
Anurag P
  • 337
  • 3
  • 11