Create a library that can be used global

edited May 2017 in Python Mode

Is it possible and if so, what is the proper way to create a global library?

If I have (my_lib.py):

foo = 1

def init():
    global foo
    foo += 5
    print foo # prints 6

And then in another file (main.py):

from my_lib import *

if __name__ == "__main__":

    init()
    print foo # prints 1 instead of 6!!

It prints 1 because it refers to the foo created with the import. The one that is 6 refers to sys.modules['my_lib'].frame_count instead.

This is because I use the import *. But without I have to use my_lib. as a prefix all the time. And I can do import my_lib as l to shorten that prefix but I don't want that.

So is there any way to make things global without this annoying side effect?

Answers

  • edited May 2017

    How interesting and somehow unexpected to me: >-)

    • Ironically, even global variables in Python are local to their own module! @-)
    • And when we import them, they arrive as clones of the original global variables.
    • Therefore we can reassign them to something else and that won't spread to the other modules! =P~
    • They are effectively local variables w/ a global scope within their own module. :P

    And another curious fact: Java doesn't have actual global variables, but fields & methods. ~O)
    However, when they're public, we have direct access to them. They're not clones as in Python. \m/

    But unfortunately, in order to access a public member of some class, we need to rely on the dot . operator for it. Not as clean & direct as local variables though. :(

    However, there's a hackish way to make fields to behave as local variables in Java: :ar!
    We declare them as static. And then import its class w/ import static:

    Let's say we compile a class called MutableInt and use it as a library:

    MutableInt.java:

    package org.mutable.numbers;
    
    public abstract class MutableInt {
      static public int num;
    }
    

    Now in Processing's IDE (PDE), we can directly access its field num as if it was a global variable like this:

    GlobalVariable.pde:

    import static org.mutable.numbers.MutableInt.num;
    
    println(num); // 0
    num += 5;
    println(num); // 5
    exit();
    

    Even though num has not been declared as a variable anywhere within the sketch, it behaves as such.

    And for any other ".java" files which happen to import static field num as well, they're gonna get the actual num, w/ the most current modified value. In this case 5 if it's executed after the sketch. :)>-

  • edited May 2017
    • Of course you're looking for a solution in Python instead. :P
    • I've made it! And it's similar to Java's. O:-)
    • That is, I've created a wrapper class called MutableNumber just for storing 1 mutable number value in it.
    • Plus override __add__() in order to overload the add + operator for the class: *-:)
      http://ThePythonGuru.com/python-operator-overloading/
    • Obviously you're gonna need to implement the rest of the operators, so they're overloaded as well. #:-S
    • The way it works is by mutating the MutableNumber's internal field n when using the add + operator; then return self.
    • So as for any variable holding the reference to an instance of it, it won't lose it when reassigning it w/ +=. $-)
    • On the other hand, the original class numbers.Number is immutable.
    • And every time we use =, even its composite += form, another Number object is created and reassigned to it. :-SS
    • And since imported global variables are merely clones of the original module, they won't point to the same object anymore after any = operator is performed. :-B
    • By making our own wrapper mutable class and overloading all the corresponding operators, overrides that standard reassignment behavior. >:)

    my_lib.py:

    from numbers import Number
    
    class MutableNumber(Number):
        def __init__(self, num): self.n = num
    
        def __add__(self, num):
            self.n += num.n if isinstance(num, MutableNumber) else num
            return self
    
        def __repr__(self): return `self.n`
    
        __str__ = __repr__
    
    
    foo = MutableNumber(1)
    
    def init():
        global foo
        foo += 5.3
        print foo
    

    Mutable_Global_Number.pyde:

    """
    # Mutable Global Number (v1.2)
    # GoToLoop (2017-May-16)
    #
    # https://forum.Processing.org/two/discussion/22610/
    # create-a-library-that-can-be-used-global#Item_2
    """
    
    from my_lib import *
    
    if __name__ == '__main__':
        init()
        print foo # 6.3
    
        foo + -2 + 1.5 + foo
        print foo # 11.6
    
        init()
        print foo # 16.9
    
    exit()
    
  • Answer ✓

    Thanks a lot. I think I just go with extending a class with the things I need. I don't really like it but out of all the solutions with their drawbacks it's still the one I like the most.

Sign In or Register to comment.