Strony

piątek, 4 listopada 2011

Dear Python, can I create global variables in functions or methods?

Yeeee... no. Global variables in python are global on module level only. So, the answer is noooo... yes, you can.

Having a problem of a sub-process that has to execute just a callable (function, lambda or class with __call__ defined), and following DRY, I've put common functionality in base class and then created a set of children. As this code is part of much bigger project, DIRAC, where have to follow conventions and use everywhere a set of of global variables (references to services, clients, some global functions, etc.). Of course I was going to import them in a base class, but then what?

Globals are visible at the module level, so if I put them to base class, I would be able to use them in base class module (all functions and methods over there). But what I really want, was to use them in inherited classes to.

No problem, you can always store them as a member of base... Yuck! But then instead of just calling "gGlobalVar.doSomethingForMe()" I have to call it by "self.gGlobalVar.doSomethingForMe()". Awkward! Ugly! Bad!

What come to my mind is a python sequence for looking up symbols. There is always __builtins__ module, right? And this one is first in a line of r lookup, right? So what about storing them over there?

So here is an example:


  • let's say this is our module storing "globals", say testGlobals.py:
    gX = 1
    def foo( ):
    print "function foo called"
    view raw testGlobals.py hosted with ❤ by GitHub
  • and here is a base class, say testBase.py:
    class baseClass( object ):
    def __init__( self ):
    from testGlobal import gX, foo
    try:
    __builtins__["gX"] = gX
    __builtins__["foo"] = foo
    except:
    setattr( __builtins__, "gX", gX )
    setattr( __builtins__, "foo", foo )
    def run( self ):
    print "in base class run"
    print "gX = ", gX
    foo()
    view raw testBase.py hosted with ❤ by GitHub
    Notice, we can't put there anything outside baseClass, as it wouldn't be visible in inherited classes at all. I'm using built-in setattr or __builtins__.__setattr__, cause in pure python __builtins__ is visible as module, but when you import baseClass elsewhere, it would be just a dictionary. If you don't believe just execute this one:
    class A:
    def __init__( self ):
    print type(__builtins__)
    if __name__ == "__main__":
    a = A()
    view raw a.py hosted with ❤ by GitHub
    once in interactive session:

    Fri, 04 Nov 14:29 E0][cibak@localhost:~/test]> python
    Python 2.7 (r27:82500, Sep 16 2010, 18:02:00)
    [GCC 4.5.1 20100907 (Red Hat 4.5.1-3)] on linux2
    Type "help", "copyright", "credits" or "license" for more information.
    >>> from a import A
    >>> a = A()
    <type 'dict'>
    view raw inte.py hosted with ❤ by GitHub
    and then in batch:
    [Fri, 04 Nov 14:32 E0][cibak@localhost:~/test]> python -m a
    <type 'module'>
    view raw a.sh hosted with ❤ by GitHub
  • and here it is, a testChild.py
    from testBase import baseClass
    class testChild( baseClass ):
    def __init__( self ):
    baseClass.__init__( self )
    print "in child, gX is there? ", "gX" in globals()
    print "in child, foo is there? ", "foo" in globals()
    def myRun( self ):
    print "in testChild.MyRun"
    foo()
    print "gX = ", gX
    view raw testChild.py hosted with ❤ by GitHub
  • at least some testing:

    if __name__ == "__main__":
    print "#"*5, "creating child"
    child = testChild()
    print "#"*5, "baseClass.run() execution"
    child.run()
    print "#"*5, "testChild.myRun() execution"
    child.myRun()
    view raw test.py hosted with ❤ by GitHub
  • and its output:
    [Fri, 04 Nov 14:17 E0][cibak@localhost:~/test]> python test.py
    ##### creating child
    in child, gX is there? False
    in child, foo is there? False
    ##### baseClass.run() execution
    in base class run
    gX = 1
    function foo called
    ##### testChild.myRun() execution
    in testChildMyRun
    function foo called
    gX = 1
    view raw b.txt hosted with ❤ by GitHub
Once you put your stuff to __builtins__, it is visible everywhere. So in fact it's a global symbol created on the fly. Of course, you can do the same with whatever object you like, i.e. os module. It would be imported in baseClass and then once you put it to the __builtins__, it would be visible in testChild.

Dear Python, if you knew how to cook and clean...