views:

92

answers:

2

I want to be able to call a global function from an imported class, for example

In file PetStore.py

class AnimalSound(object):
   def __init__(self):
      if 'makenoise' in globals():
         self.makenoise = globals()['makenoise']
      else:
         self.makenoise = lambda: 'meow'

   def __str__(self):
      return self.makenoise()

Then when I test in the Python Interpreter

>>> def makenoise():
...    return 'bark'
...
>>> from PetStore import AnimalSound
>>> sound = AnimalSound()
>>> sound.makenoise()
'meow'

I get a 'meow' instead of 'bark'. I have tried using the solutions provided in python-how-to-make-a-cross-module-variable with no luck.

+2  A: 

"Global" scope in Python is Module scope.

import PetStore

PetStore.makenoise = makenoise
Ignacio Vazquez-Abrams
+3  A: 

The globals() call returns the globals of the module in which the call is lexically located; there is no intrinsic "dynamic scoping" in Python -- it's lexically scoped, like just about every modern language.

The solid, proper way to obtain the effect you desire is to explicitly pass to the initializer of AnimalSound the callable it's supposed to use to "make noise": i.e., the class should be

class AnimalSound(object):
   def __init__(self, makenoise=lambda: 'meow'):
       self.makenoise = makenoise

   def __str__(self):
       return self.makenoise()

and the call should be

sound = AnimalSound(makenoise)

There are practicable but less-sound solutions, such as the caller passing its own globals() (but that needlessly constrains the name of the callable!), or even (shudder) communicating via covert channels like the other answer advocates (that would be a potential disaster if you had two instantiations of AnimalSound built according to the same principle in two separate modules, etc, etc). But, "explicit is better than implicit", and clean, safe, overt communication leads to clean, safe, robust system architectures: I earnestly recommend you choose this route.

Alex Martelli
Thanks! I was a bit conflicted on who to give the accepted answer, but I think i'll go with your advice and explicitly pass in the function.
Evan
@Evan, sure -- I dislike @Ignacio's suggestion to communicate through covert channels but I _do_ understand it looks simpler (until the first few times you find yourself wasting hours or days debugging the disastrous effects of such "simple-looking" solutions, which may take years or decades of experience). Of course, if you accept the wrong solution but then implement the right one, you'll never get the painful experiences needed to really appreciate why the right solution _is_ right;-).
Alex Martelli