Sharing global variables between classes in Python - python

I'm relatively new to thinking of python in terms of object-oriented programming, and it's coming relatively slowly to me.
Is it possible to pass global variables between classes with multiple functions in them? I've read extensively about them here and in other sources, but I'm still a bit confused.
My ultimate goal is to define a variable globally, assign it a value in a function of one class, and then USE it in another class's function. Is that possible? I'm building a pythonaddin for ArcMap, which requires a class for each button. I want to have a button to assign a value to a variable, and then use that variable in another class in the script.
(Before I get the flak, I know it's relatively bad form to use global variables in the first place, I'm trying to learn)
For instance, as a highly simplified example:
x = []
class A():
def func_1(self):
#populate the x variable
global x
x = 1
class B():
def func_2(self):
global x
#do stuff with x
Is this acceptable (even if not pythonic)?

Yes, it can work. Here is the modified code:
x = []
class A:
def func_1(self):
#populate the x variable
global x
x.append(1)
class B:
def func_2(self):
global x
print x
a = A()
a.func_1()
b = B()
b.func_2()
It can print the list x correctly. When it's possible, you should try to avoid global variables. You can use many other ways to pass variables between classes more safely and efficiently.
PS: Notice the parameters in the functions.

A more Pythonic solution would be to have the button objects (in their method that handles the "press" event) set an attribute or call a method of whatever object needs the information.

Yes, it is possible. Your code is almost right, just missing a couple of parenthesis in the function definitions. Other than that, it works.
Now, is that acceptable? In 99% of cases, no, it's not. There're many ways of passing variables and sharing data between classes, and using global variables is one of the worse ones, if not the worst.

Related

How should I modify global variables from within a function?

I've been trying to clean up my code for a neural network evolution simulator I'm working on, because it's grown to over 1000 lines and it's very difficult to read. Part of that process involves moving blocks of code from the main loop into their own functions (event handling, drawing to the screen, etc) or their own modules. However, I'm having trouble figuring out how to deal with the global variables.
As an example, I have a variable called "selected" that keeps track of a particular neural network creature selected by the user. I want to change the value of this variable in the event handling function when the user clicks on a new creature; I also want to change its value when the creature dies, which happens in a different block of code in its own function.
I understand that I can use the "global" keyword to do this, but my impression is that it's generally poor practice to do so. I also know that I can store these variables in a separate module and import them, but when I'm dealing with 30+ variables, typing "module_name.variable_name" every time I want to modify them seems unnecessarily tedious. I was also considering storing all of these values in a global dictionary so I can modify them from within functions, but again, typing "dict_name['var_name']" each time seems unnecessary.
So essentially my question is, what would be the best practice in this situation? Is it one of these solutions, or is there a cleaner approach? I'd like to make sure I'm on the right track before I spend hours overhauling my code. Thank you for your help!
30 global variables is just a big no-no. At some point you'll forget the global statement, and you'll create a local variable and will spend hours to debug your code.
you could build a singleton object instead
instead of:
a = 12
b = 33
c = "foo"
def run():
global a
if a == 34:
# do something
a += 1
you could create a big wrapping class with all variables as members:
class Program:
def __init__(self):
self.a = 12
self.b = 33
self.c = "foo"
now you can access all your variables with the self prefix. It may be tedious but at least it's short, and most good IDEs (pycharm, pyscripter, eclipse) propose completion (much better than a dictionary, where you cannot have completion)
# let's say it's the main method
def run(self):
if self.a == 34:
# do something
self.a += 1
now:
o = Program()
o.run()
print(o.a) # without __ prefix, your data is visible from the outside
So using an object not for inheritance, polymorphism or such, but just to define the "global" context and work inside the instance, with self. prefix, and say goodbye to global variables.

Using a class as a container object for local variables as an alternative to 'nonlocal' in Python

Question
In Python, if I have a function with local variables and nested functions, I can assign these variables in the nested functions with the help of nonlocal:
def f():
x = 0
def _g():
nonlocal x
x = 1
def _h():
x = 2
_g()
_h()
print(x)
f() # 1
My issue with this solution is that if I have many local variables in f(), the use of nonlocal is verbose, and more disturbingly, it is very easy to forget nonlocal for a variable, and to create local variables in the nested functions without noticing it (what did I really mean in the h() function above, for instance?).
I have seen and used an alternative:
def f():
class state:
x = 0
def _g():
state.x = 1
def _h():
x = 2
_g()
_h()
print(state.x)
f() # 1
If uses the fact that a class in Python is actually an object too. Using 'class' that way is actually the least verbose way of creating a mutable container for local values (I believe).
I think there are two questionable aspects in this pattern:
That particular use of the 'class'-keyword might be considered as a hack.
Since the container is somewhat artificial, it is sometimes difficult to find a good name for it (I went so far as testing the use of the name 'self' for it, but that seemed even more like a hack).
Is this a good pattern? What alternatives do you usually use?
If StackOverflow is not the right forum for this question, please advise on a different forum that you think is better suited (I did read the FAQs, but what the correct forum is for this question not obvious to me).
Thanks in advance.
P.S.
For the sake of completeness, there is at least one more alternative, which feels even more like a hack:
class f:
def __init__(self):
self.x = 0
self._g()
self._h()
print(self.x)
def _g(self):
self.x = 1
def _h(self):
x = 2
f() # 1
This works because in Python, class instantiation has the same syntax as a function call.
Answer
See the accepted answer below. For a discussion about a solution when the function needs to return a value see there.
Your "last resort" alernative is of course cleaner - if you can do it in flattened methods that share an state, that is more readable code than nested functions to do the same job. "Flat is better than nested".
Other than that, you are creating a class to have a namespace. You could simply create an object to work as namespace, and it would work as a namespace - that is more usual. The only thing there is that if you simply create an instance of object itself, it can't work as namespace, because it have no __dict__, so you can't freely attribute objects for this.
That is why buried in the types module in the stdlib there is a class named SimpleNamespace exactly for this use case.
Just do:
from types import SimpleNamespace
def f():
state = SimpleNamespace()
state.x = 0
...
(but only if you don't change your mind and go for the class-based solution which is cleaner anyway).
Why not just use a dict instead of the class. This removes some of the "hackyness" and is Python 2 compatible:
def f():
state=dict(x=0)
def _g():
state["x"] = 1
def _h():
x = 2
_g()
_h()
print(state["x"])
f() # 1

Making all variables global

I have a function in my Python script where multiple variables are created, and I want to use them in other functions. I thought of using global for this but I thought it would be the incorrect way to do so.
So can anyone tell me what would be the best way to create variables in a function for other functions?
Organize the variables into a class. Instantiate the class in one function and then pass the instance to wherever you need it.
Rule of thumb: If making something global seems like a good solution at some point, don't do it. There is always a better way.
You could create a "namespace" object -- an object which functions as a namespace for the purpose of keeping your global namespace clear:
class namespace():
pass
global_data=namespace()
def add_attributes(obj):
obj.foo="bar"
add_attributes(global_data)
print (global_data.foo) #prints "bar"
However, this is only marginally better than using the global keyword. You really do want a class here as mentioned by Paul.
Declare the variables as attributes of the function.
def f1():
f1.a=100
f2.b=200
def f2():
print(f1.a,f2.b)
f1()
f2()
output:
100 200
This might be a good place to implement a class. This has lots of advantages. See Classes in the tutorials.

How do I use the same variable in one function with another function, without making that variable Global?

How can I take one variable from one function and use it in another function without having to make that variable global?
You have basically two choices.
One is to pass it to the second function as a parameter. (If you want the first function to see changes to the value, it needs to be a reference type (e.g. a dict/list) and you have to not overwrite the object, only modify it (e.g. a.append(b) rather than a = a + [b]).
The second is to define a class that can be used as a singleton. Technically, this is still defining something 'globally', but it lets you keep things grouped:
class FooSingleton(object):
class_var = "foo"
def func1():
FooSingleton.class_var = "bar"
def func2():
print(FooSingleton.class_var)
(You could also do this with a dict instead of a class; matter of preference.)
have the function take a parameter, and pass that parameter in.
def one_function():
one_variable = ''
return one_variable
def another_function(a_param):
# do something to a_param
return
Technically possible and may be used, e.g. for memoisation (but then it is usually hidden behind a decorator and only implemented by people who are sure they do the right thing even though they might still feel a bit bad about it):
def g():
if not hasattr(g, "x"):
g.x = 0
return g.x
g()
# 0
g.x = 100
g()
# 100
You can use closure to handle this, or more natural is to define both functions as methods of a class and the "global" variable as member of the class.

Making all variables accessible to namespace

Say I have a simple function:
def myfunc():
a = 4.2
b = 5.5
... many similar variables ...
I use this function one time only and I am wondering what is the easiest way to make all the variables inside the function accessible to my main name-space.
Do I have to declare global for each item? or any other suggested methods?
Thanks.
Best way, in my biased opinion, is to wrap the dictionary into a nice object where the "variables" are accessed as attributes -- the pattern I named Bunch when I introduced it many years ago, and a great example of the last item in the Zen of Python (if you don't know what that is, import this at an interpreter interactive prompt). To wit...:
class Bunch(object):
def __init__(self, d=None):
if d is not None: self.__dict__.update(d)
def myfunc():
a = 4.2
b = 5.5
...
return Bunch(locals())
x = myfunc()
print x.a, x.b
Using qualified names like x.a and x.b, rather than barenames such as a and b, is the crucial idea here: qualified names let you separate namespaces and treat them right, while barenames would make one big soup of everything and violate the Zen of Python;-). And of course there's no need to use an unwrapped dict and unsightly x['a'], x['b'] accesses!-)
If what you want is separation of your constants then put them in another module:
consts.py:
foo = 42
bar = 'quux'
main.py:
import consts
print consts.foo
print consts.bar[::-1]
I can't think of any good reason for doing this, but if you REALLY want to for some strange reason:
def myfunc():
x = 5.6
y = 7.3
# many other variables
globals().update( locals() )
This works because the globals function returns a dictionary of the global variables in your module's namespace, which you can dynamically update with the dictionary of all local variables returned by the locals function.
This is very dangerous because it will clobber everything in your global namespace, so if you already had an x or y variable, then they'll get overwritten. And as I said, I really can't think of a good reason to ever do this, so definitely think twice before resorting to this kind of trickery!
It sounds like what you want to do isn't natural because it isn't something you should generally do much.
Perhaps what you really want is to make a class and set all these things on an instance of it.
class MyState(object):
def __init__(self):
self.a = 4.2
self.b = 5.5
... many similar variables ...
Storing state on an instance of a class you create is usually very strongly preferred to mutating global state. This method also does not require any magic.

Categories

Resources