4

I know how to override methods:

class Foo(object):
    def bar(self):
        return True


def damn_monkeys(self):
    return False

Foo.bar = damn_monkeys

Is it possible to achieve something similar with functions?

def foo(arg1):
    def bar(arg2):
        return 1
    return bar(arg1)

foo.bar = monkeys  # doesn't work as bar obviously isn't an attribute

I have found some hooks (func_code), but those seem to be solely for the inspector and it seems rather dubious to fiddle with them.

I also noticed functions have an __init__ and __call__ reference, but those seem to point to the __builtin__.__function object. I haven't inspected further but it seems functions are objects too (which makes sense reading everything in python is an object). I suppose functions are already instances of the __function object, and thats why foo.__init__ doesn't raise an error, however its never touched by the debugger.

Is there any way to override an inner function without copying or patching the code?

edit duplicate with answer: Can you patch *just* a nested function with closure, or must the whole outer function be repeated?

Community
  • 1
  • 1
Bea Trix
  • 75
  • 1
  • 7
  • I think you should read more about namespaces in general. It does not make much of a difference if the oject has a type or another. In order to reach it python has to look it up in a namespace. – Pynchia Jul 08 '15 at 13:58
  • 1
    If you need to *"override"* it, why don't you restructure it to not be an inner function? Note that you actually *can* give a function arbitrary attributes, so you could make `bar` into `foo.bar`. – jonrsharpe Jul 08 '15 at 13:58
  • @jonrsharpe background info the code is actually much longer and in a 3rd party package, I cannot change it like that – Bea Trix Jul 08 '15 at 14:00
  • 2
    Could you provide a less abstract example, then? What are you really trying to achieve? The whole point of a nested function is that it's required only within the function, as an implementation detail than no other code should care about, so they're pretty difficult to get at. – jonrsharpe Jul 08 '15 at 14:01
  • @jonrsharpe the example seems sufficient, the problem is that in my case my argument to the outer function triggers a bug inside the inner function, so actually the 3rd party package should be fixed.. I suppose I'll have to cry and shout and get them to do it >:-) – Bea Trix Jul 08 '15 at 14:04
  • 1
    The inner function is actually a local variable of the outer function. So your question is equivalent to _"Can I change a function local variable from outside the function?". AFAIK, the answer is: no, unless you are willing to modify the bytecode of the function itself. – rodrigo Jul 08 '15 at 14:07

2 Answers2

0

I don't believe it's possible.

Even if you do something like this, it won't work:

def test_func():
    def inner():
        print("inner")
    test_func.inner = inner
    test_func.inner()

test_func()    # inner
test_func.inner()    # inner

def outer(): 
    print("outer")

test_func.inner = outer
test_func.inner()    # outer
# AH HA!! SUCCESS?
test_func()    # inner
# NOPE!! 

The reason for this result is that even though functions are objects and can even have attributes, the contents of the function object (that is, its local namespace) are evaluated ONCE. You can't change it after the fact (that is, without wrapping it in another function, which isn't what you are asking about), because it never gets reevaluated - unless you redefine it. Have a look at this famous question , as well as the last section of this article, for some additional clarification of what it means for functions objects to be first-class objects.

This is actually the case with any object. Consider:

class test_class(): 
    print("test_class evaluation")
    class_attribute = None
    def inner(self):
        print("inner")

# test_class evaluation
test_class
test_class()

The evaluation message gets printed only once because the namespace is evaluated ONCE, just as it is with any other object.

Now, as you said in your question, you can override the inner method of the test_class class using test_class.inner = my_func. However, the reason you can do this in the first place is the class object test_class is an instance of type (as are all classes), and type objects automatically have local objects added to the parent object as attributes, such as for test_class.inner and test_class.class_attribute in the above example.

This is not the case for function objects, which are NOT instances of type. You have to explicitly add attributes to function objects. However, even when you do that, the namespace of the function has already been evaluated, so updating the function object attribute has no effect on the evaluation of the function, as shown in the test_func example.

Community
  • 1
  • 1
Rick
  • 43,029
  • 15
  • 76
  • 119
0

You inner function does not even exist unless you outer one is called. And only during the call. Look at this question about having a static variable in a function, then modify the answers from a counter to you inner function.

What is the Python equivalent of static variables inside a function?

You could also modify the outer function with a different defined inner function.

Community
  • 1
  • 1
Robert Jacobs
  • 3,266
  • 1
  • 20
  • 30