Make all variables in a Python function global - python

Is there a simple way to make all variables in a function global?
I have 20 odd variables in a function and naming them global one by one doesn't make nice code... to me anyway :)

Warning: Don't try this at home, you might burn it down.
There is no legitimate reason to do the following in the course of normal day-to-day programming. Please review the other answers to this question for more realistic alternatives.
I can barely imagine why you would want to do this, but here is a way to do it:
def f(a, b, c):
d = 123
e = 'crazy, but possible'
globals().update(locals())
def g():
print a, b, c, d ,e
>>> globals()
{'g': <function g at 0x875230>, 'f': <function f at 0x8751b8>, '__builtins__': <module '__builtin__' (built-in)>, '__package__': None, '__name__': '__main__', '__doc__': None}
>>> g()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 2, in g
NameError: global name 'a' is not defined
>>> f(10, 20, 'blah')
>>> g()
10 20 blah 123 crazy, but possible
>>> globals()
{'a': 10, 'c': 'blah', 'b': 20, 'e': 'crazy, but possible', 'd': 123, 'g': <function g at 0x875230>, 'f': <function f at 0x8751b8>, '__builtins__': <module '__builtin__' (built-in)>, '__package__': None, '__name__': '__main__', '__doc__': None}

The pythonic way to do this is either to keep the variables in local scope (i.e. define them within each function) and pass them between the functions as arguments / return values; or to keep your variables as attributes of an object or class making your "functions" methods in that class. Either way is OK, but the global keyword is designed specifically to put you off using it in the way you describe. Global variables are not just "bad style" but they make your code very difficult to maintain, as any invariants that your variables need to stick to need to be checked in every function.
Here is an example of good style (with functions):
def quads(a, b, c):
x1 = (-1.0 * b + math.sqrt(b * b - 4.0 * a * c)) / (2.0 * a)
x2 = (-1.0 * b - math.sqrt(b * b - 4.0 * a * c)) / (2.0 * a)
return x1, x2
def pretty(a, b, c, x1, x2):
eqn = "%fx^2 + %fx + %c" % (a, b, c)
print "The first solution to the equation %s is: %f" % (eqn, x1)
print "The second solution to the equation %s is: %f" % (eqn, x2)
return
def main():
a = 100
b = 200
c = 300
x1, x2 = quads(a, b, c)
pretty(a, b, c, x1, x2)
return
if __name__ == '__main__':
main()
Here is an example of good style (with OOP):
class Quadratic(object):
def __init__(self, a, b, c):
self.a = a
self.b = b
self.c = c
self.x1 = None
self.x2 = None
self.solve() # Set x1 and x2 to correct values
# To maintain the invariant between a, b, c and x1, x1
# we should override __setattr__ or use descriptors or
# properties so that self.solve() is called every time
# a, b, or c are updated.
return
def solve(self):
self.x1 = (-1.0 * self.b +
math.sqrt(self.b * self.b - 4.0 * self.a * self.c)) / (2.0 * self.a)
self.x2 = (-1.0 * self.b -
math.sqrt(self.b * self.b - 4.0 * self.a * self.c)) / 2.0 * self.a
return
def pretty(self):
eqn = "%fx^2 + %fx + %c" % (self.a, self.b, self.c)
print "The first solution to the equation %s is: %f" % (eqn, self.x1)
print "The second solution to the equation %s is: %f" % (eqn, self.x2)
return
def main():
quad = Quadratic(100, 200, 300)
quad.pretty()
return
if __name__ == '__main__':
main()

There's no way to declare them all as global, and you really don't want to. Those 20 variables probably should be turned into an object with 20 attributes instead.

The simplest solution is to have only a single global — or, better yet, to figure out how to pass it in to the function. Using it as a global would look like this (again, I am showing the simplest possible case, not necessarily the best use of Python):
class Info(object): # or whatever you want to name the container
"""Holder for global information."""
info = Info() # single instance we will use
def my_function():
print "Here is some info:"
print info.a, info.b, info.c
info.a = 3
info.b = 8
info.c = []
if __name__ == '__main__':
my_function()
Again, I would probably pass info to the function instead. But since your question was about a global, it's shown here as a global.

A niche example where I wanted to do this: use a function for importing.*
def temp():
a = "stays local value"
old_locs = locals().copy()
b = "is global value"
import math
new_locs = locals()
new_vars = {k: new_locs[k] for k in set(new_locs) - set(old_locs)
if k != 'old_locs'}
globals().update(new_vars)
temp()
print(b)
print(math.sqrt(3))
print(a)
gives
is global value
1.7320508075688772
NameError: name 'a' is not defined
This way only the specific 20 or so variables would get update the global namespace and intermediate variable names in the function wouldn't.
*I needed to import from a .ipynb file, and the process for doing so depends if called from google Collaboratory, a desktop .ipynb, or a desktop .py file; this involved the use of magics which are treated as invalid syntax in situation which wouldn't call those branches, so by importing my import function I can escape that issue.

Related

how to i get my function to recognise a dataframe?

This is my function
def PredictCorner(Home_Team, Away_Team):
Home_Average = Table_Current_Normalised.loc["Average", "Att_Home"]
Away_Average = Table_Current_Normalised.loc["Average", "Att_Away"]
AH = Table_Current_Normalised.loc[Home_Team, "Att_Home"] / Home_Average
DH = Table_Current_Normalised.loc[Home_Team, "Def_Home"] / Away_Average
AA = Table_Current_Normalised.loc[Away_Team, "Att_Away"] / Away_Average
DA = Table_Current_Normalised.loc[Away_Team, "Def_Away"] / Home_Average
Rate_Home = AH * DA * Home_Average
Rate_Away = AA * DH * Away_Average
return [Rate_Home,Rate_Away]
and the df in question is Table_Current_Normalised.
This is the error I get
NameError: name 'Table_Current_Normalised' is not defined
Thanks in advance, am new to python.
As the error suggests, the function can't find any object with the name Table_Current_Normalised. Because this name isn't a parameter or assigned within your function, it's assumed to be a globally defined variable. IE it is defined or imported somewhere in the body of the module, not inside a function.
More than likely, the problem you have is either:
(1) you have called this function before the global df exists
(2) You have defined the df in a local scope (within another function)
(3) there is a typo in the name.
For instance, here, the function do_something is called before the name d is defined:
def do_something():
d['foo'] = 'bar'
do_something() # NameError
d = {}
Or if you define d in the local scope of another function, you'll have a similar problem.
def do_something():
d['foo'] = 'bar'
def main():
d = {} # d is _local_ to the function ``main``
do_something() # NameError
This results in both cases is the error NameError: name 'd' is not defined
To address, we make sure that d is defined globally before calling the function:
def do_something():
d['foo'] = 'bar'
d = {}
def main():
do_something()
print(d)
main()
This works as expected.
Further, you should consider rewriting this function to accept the dataframe as an argument. That way, it won't rely on some globally-defined variable.
def PredictCorner(Home_Team, Away_Team, table_current):
Home_Average = table_current.loc["Average", "Att_Home"]
Away_Average = table_current.loc["Average", "Att_Away"]
AH = table_current.loc[Home_Team, "Att_Home"] / Home_Average
DH = table_current.loc[Home_Team, "Def_Home"] / Away_Average
AA = table_current.loc[Away_Team, "Att_Away"] / Away_Average
DA = table_current.loc[Away_Team, "Def_Away"] / Home_Average
Rate_Home = AH * DA * Home_Average
Rate_Away = AA * DH * Away_Average
return [Rate_Home,Rate_Away]
Then call it like so:
PredictCorner(home_team, away_team, Table_Current_Normalised)

Name Error: Global name 'r' is not defined

Ive been working on this sample code for a while and cant seem to wrap my head around this seemingly simple error.
The code is as follows:
class area :
r=5
l=2
b=3
def __init__(self,r,l,b):
print "parent constructor"
self.r=r
self.l=l
self.b=b
def __del__(self):
print "parent deconstructor"
def circle(self):
circle_area= 3.14 * r * r
print "area of circle is :",circle_area
def rectangle(self):
rect_area=l*b
print "area of rectangle :",rect_area
obj=area(4,5,4)
obj2=area(2,5,4)
obj.circle()
The error message says :
File "yaa.py", line 18, in circle
circle_area= 3.14 * r * r
NameError: global name 'r' is not defined.
You need to use the self for refering class attributes:
def circle(self):
circle_area= 3.14 * self.r * self.r
print "area of circle is :",circle_area
In case you want to use the r within the class, not the instance you have to use the name of the class then:
def circle(self):
circle_area= 3.14 * area.r * area.r
print "area of circle is :",circle_area
You probably need to change your method circle(self) from
circle_area= 3.14 * r * r
to
circle_area= 3.14 * self.r * self.r
because r is an attribute of the class, not a global variable.
The same goes for your method rectangle(self):
rect_area = self.l * self.b

Sharing a piece of code with methods inside a class in Python

I started making a draft for one of the classes that are supposed to be used in my programm and I first wrote this piece of code:
import math
import numpy as np
R = 6.371e6
phi_src, theta_src = 10, 40
phi_det,theta_det = -21, 10
depth_src, depth_det = 0,0 # both on the surface
l = 0
class Trajectory:
def __init__(self,
phi_src,
theta_src,
phi_det,
theta_det,
depth_src,
depth_det,
l):
self.phi_src = phi_src
self.theta_src = theta_src
self.phi_det = phi_det
self.theta_det = theta_det
self.depth_src = depth_src
self.depth_det = depth_det
self.l = l
#property
def r(self):
r_src = R - self.depth_src
r_det = R - self.depth_det
x_src = r_src * math.cos(self.phi_src) * math.cos(self.theta_src)
y_src = r_src * math.cos(self.phi_src) * math.sin(self.theta_src)
z_src = r_src * math.sin(self.phi_src)
x_det = r_det * math.cos(self.phi_det) * math.cos(self.theta_det)
y_det = r_det * math.cos(self.phi_det) * math.sin(self.theta_det)
z_det = r_det * math.sin(self.phi_det)
coord_src = np.array((x_src, y_src, z_src))
coord_det = np.array((x_det, y_det, z_det))
L = np.linalg.norm(coord_src - coord_det)
return math.sqrt(r_src**2 + self.l * (1.0 - L - (r_src - r_det) * (r_src + r_det)/L))
def phi(r):
pass
trajectory = Trajectory(phi_src,theta_src,phi_det,theta_det,depth_src,depth_det,l)
print(trajectory.r)
But then realized that the
r_src = R - self.depth_src
r_det = R - self.depth_det
x_src = r_src * math.cos(self.phi_src) * math.cos(self.theta_src)
y_src = r_src * math.cos(self.phi_src) * math.sin(self.theta_src)
z_src = r_src * math.sin(self.phi_src)
x_det = r_det * math.cos(self.phi_det) * math.cos(self.theta_det)
y_det = r_det * math.cos(self.phi_det) * math.sin(self.theta_det)
z_det = r_det * math.sin(self.phi_det)
coord_src = np.array((x_src, y_src, z_src))
coord_det = np.array((x_det, y_det, z_det))
L = np.linalg.norm(coord_src - coord_det)
part is common for all the methods of the class and hence there's no point in calculating it numerous times in every method, this piece should be shared with all the methods.
What would be the best way to do that? Do I have to put it into the __init__ method? I've heard it's not a good practice to make any calculations inside the __init__ method.
The common way of declaring a function in a class that does not depend on the state of the object itself is to use the #staticmethod decorator, followed by the function definition. You only pass the function parameters.
If you need to use class level parameters, use #classmethod instead and note that you pass cls instead of self to the function (one could use any variable, so really it doesn't matter. The point is that you are now accessing class attributes and methods instead of those of the object).
class Trajectory:
c = 10 # <<< Class level property.
def __init__(self):
self.c = 5 # <<< Object level property.
#staticmethod
def foo(a, b):
return a * b
#classmethod
def bar(cls, a, b):
return cls.foo(a, b) * cls.c # <<< References class level method and property.
def baz(self, a, b):
return self.foo(a, b) * self.c # <<< References object level method and property.
t = Trajectory()
>>> t.foo(3, 5)
15
>>> t.bar(3, 5)
150
>>> t.baz(3, 5)
75
Hmmm, not totally sure if I get what you want, but quoting you a bit...
def r(self):
r_src = R - self.depth_src
r_det = R - self.depth_det
....
L = np.linalg.norm(coord_src - coord_det)
This is common, you say because methods like def r(self) always some of these variables, like r_src, L:
def r(self):
return math.sqrt(r_src**2 + self.l * (1.0 - L - (r_src - r_det) * (r_src + r_det)/L))
This, imho, tells me that, if you want to reuse those computations then they should be part of __init__ (or called from __init__). But mostly, you need to set all those variables to self.
...whereever you compute them in a common location...
self.r_src = R - self.depth_src
self.r_det = R - self.depth_det
....
self.L = np.linalg.norm(coord_src - coord_det)
Note that as you depend on instance variables such as self.depth_src in the above, this method can't be a class method, it needs to be an instance method.
Now, change your other methods to point to those precomputed attributes.
def r(self):
return math.sqrt(self.r_src**2 + self.l * (1.0 - self.L ....
Now, you could get fancy and only compute those attributes on demand, via properties. But if you are asking a fairly basic Python question, which I think you are, then worry about optimization later and do the easiest for now. I.e. compute them all in the __init__ or from a method called from there.
Now, there are perfectly good reasons to break them out of init, but it mostly has to do with code clarity and modularity. If that chunk of code has some specific math/business domain meaning, then create a method that is named appropriately and call it from main.
On the other hand, some IDEs and code analyzers are better at figuring instance variables when they see them assigned in __init__ and with Python being as dynamic as it is the poor things need all the help they can get.

Python: Class instance "has no attribute" while passed to function in another module

I have a problem with passing class instance to function in another module.
Here's the code:
(in module "base"):
from physics import *
...
class CellsArray(blist):
def getCollidingCells(self, cell):
diff = ((0,1), (1,0), (1,1), (0,-1), (-1,0), (-1,-1), (-1, 1), (1, -1))
result = []
for x, y in diff:
c = self.getCellByCoords(cell.x + x, cell.y + y)
if c:
result.append(c)
if result:
return result
else:
return None
def getCellByCoords(self, x, y):
for c in self:
if c.x == x and c.y == y:
return c
return None
def getCellById(self, uid):
for c in self:
if id(c) == uid:
return c
return None
class World():
def __init__(self, ...):
self.cells = CellsArray([])
...
self.procedures = [freeFallAccel, airEffects, specialProcedures,
moveCells, collisionsTest]
...
def step(self):
if not self.paused:
for f in self.procedures:
f(self.cells) #here the collisionsTest function is being called
(in module "physics"):
import base
...
def collisionsTest(cells):
for c in cells:
cells.getCollidingCells(cells.getCellByCoords(c.x, c.y))
When I load the "base" module into interactive interpreter (or create CellsArray instance inside the "base" module), everything seems fine:
(interactive interpreter example):
>>> a = CellsArray([])
>>> a.append(Cell())
>>> a.getCollidingCells(a[0])
#returns None
>>> a.getCollidingCells
<bound method CellsArray.getCollidingCells of blist([<__main__.Cell instance at 0x7f847e8fa170>])>
But when physics.collisionsTest is executed somewhere inside the "base" module, something wrong happens:
cells.getCollidingCells(cells.getCellByCoords(c.x, c.y))
AttributeError: 'list' object has no attribute 'getCollidingCells'
However, if I add print type(cells); print cells.getCollidingCells to the collisionsTest function (before the for c in cells ...), it returns :
<class 'base.CellsArray'>
<bound method CellsArray.getCollidingCells of blist([<base.Cell instance at 0x7f2e4a7d1638>, ...])>
I feel that something is wrong with importing "physics" module, but I've tried almost every available methods of importing that module and I still get that error.
Module "physics" uses some variables and classes from module "base", so I can't simply remove "import base".
Is there any way to resolve that problem, or I'll have to move all functions and constants from "physics" back to "base"?
cells is a list of CellsArray, but you are expecting it to be an individual CellsArray.
Perhaps collisionsTest should be:
def collisionsTest(cells):
for c in cells:
c.getCollidingCells(c.getCellByCoords(c.x, c.y))

Python pickle instance variables

I am doing some calculations on an instance variable, and after that is done I want to pickle the class instance, such that I don't have to do the calculations again. Here an example:
import cPickle as pickle
class Test(object):
def __init__(self, a, b):
self.a = a
self.b = b
self.c = None
def compute(self, x):
print 'calculating c...'
self.c = x * 2
test = Test(10, 'hello')
test.compute(6)
# I have computed c and I want to store it, so I don't have to recompute it again:
pickle.dump(test, open('test_file.pkl', 'wb'))
After test.compute(6) I can check to see what test.__dict__ is:
>>> test.__dict__
{'a': 10, 'c': 12, 'b': 'hello'}
I thought that is what going to get pickled; however,
When I go to load the class instance:
import cPickle as pickle
from pickle_class_object import Test
t2 = pickle.load(open('test_file.pkl', 'rb'))
I see this in the shell:
calculating c...
Which means that I did not pickle c and I am computing it over again.
Is there a way to pickle test how I want to? So I don't have to compute c over again. I see that I could just pickle test.__dict__, but I am wondering if there is a better solutions. Also, my understanding about what is going on here is weak, so any comment about what is going would be great. I've read about __getstate__ and __setstate__, but I don't see how to apply them here.
You are importing the pickle_class_object module again, and Python runs all code in that module.
Your top-level module code includes a call to .compute(), that is what is being called.
You may want to move the code that creates the pickle out of the module, or move it to a if __name__ == '__main__': guarded section:
if __name__ == '__main__':
test = Test(10, 'hello')
test.compute(6)
pickle.dump(test, open('test_file.pkl', 'wb'))
Only when running a python file as the main script is __name__ set to __main__; when imported as a module __name__ is set to the module name instead and the if branch will not run.
Pickling works as you expect it to work. The problem here is when you run the new script, you import the module that contains the class Test. That entire module is run including the bit where you create test.
The typical way to handle this sort of thing would be to protect the stuff in a if __name__ == "__main__: block.
class Test(object):
def __init__(self, a, b):
self.a = a
self.b = b
self.c = None
def compute(self, x):
print 'calculating c...'
self.c = x * 2
if __name__ == "__main__":
import cPickle as pickle
test = Test(10, 'hello')
test.compute(6)
# I have computed c and I want to store it, so I don't have to recompute it again:
pickle.dump(test, open('test_file.pkl', 'wb'))
That isn't what's happening. You import a python module that has code in it at the top level, which executes when you import the module. You can see that your code works as you intended:
import cPickle as pickle
class Test(object):
def __init__(self, a, b):
self.a = a
self.b = b
self.c = None
def compute(self, x):
print 'calculating c...'
self.c = x * 2
test = Test(10, 'hello')
test.compute(6)
pickle.dump(test, open('test_file.pkl', 'wb'))
t2 = pickle.load(open('test_file.pkl', 'rb'))
print t2.c
--output:--
calculating c...
12
If your code worked as you describe, then you would see "calculating c..." twice.

Categories

Resources