Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python closures and replacing surrounding scope

I know when using Groovy closures, I can change the delegate on the closure so function calls made within the closure could be defined externally.

Can I do something similar in Python?

Specifically, if you take the following code:

def configure():
  build()

def wrap(function):
  def build():
    print 'build'

  function()

wrap(configure)

I'd like it to print 'build' (only making changes to wrap()).

Some notes:

I don't want to pass functions into configure() since there may be a large number of functions that can be called by configure().

I also don't want to define those globally, because, once again, there may be a large number of functions that can be called by configure() and I don't want to pollute the global namespace.

like image 280
Reverend Gonzo Avatar asked Jul 14 '26 05:07

Reverend Gonzo


2 Answers

Whether or not is a good way to do this is debatable, but here's a solution that doesn't modify the global namespace.

def configure():
  build()

def wrap(f):
  import new
  def build():
    print 'build'

  new.function(f.func_code, locals(), f.func_name, f.func_defaults, f.func_closure)()

wrap(configure)

I found it at How to modify the local namespace in python

like image 99
Reverend Gonzo Avatar answered Jul 15 '26 19:07

Reverend Gonzo


This is doable without metaprogramming. Have configure take the build function as a parameter:

def default_build():
    print "default build"

def configure(build_func=None):
    build_func = build_func or default_build
    build_func()

def wrap(func):
    def build():
        print "wrap build"

    func(build)

wrap(configure)

This way makes it explicit that the behaviour of the configure function can change.

You can fiddle with the namespaces configure sees as well to do something more like what I understand Groovy does:

def build():
    print "default build"

def configure():
    build()

def wrap(func):
    def _build():
        print "wrap build"

    old_build = func.func_globals['build']
    func.func_globals['build'] = _build
    func()
    func.func_globals['build'] = old_build
like image 25
millimoose Avatar answered Jul 15 '26 17:07

millimoose



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!