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.
Related
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
def isBetween(a, b, c):
crossproduct = (c[1] - a[1]) * (b[0] - a[0]) - (c[0] - a[0]) * (b[1] - a[1])
if abs(crossproduct) != 0 : return False
dotproduct = (c[0] - a[0]) * (b[0] - a[0]) + (c[1] - a[1])*(b[1] - a[1])
if dotproduct < 0 : return False
squaredlengthba = (b[0] - a[0])*(b[0] - a[0]) + (b[1] - a[1])*(b[1] - a[1])
if dotproduct > squaredlengthba: return False
return True
class HouseLocation(object):
def __init__(self,street_name,x,y):
self.street_name=street_name
self.x=x
self.y=y
def print_address(self):
print '{}: {},{}'.format(self.street_name,self.x,self.y)
class Street(HouseLocation):
def __init__(self,street_name,map_symbol,street_ends):
if len(map_symbol)>1:
raise ValueError('The map_symbol should be only one letter!')
if map_symbol==str.upper(map_symbol):
raise ValueError('Make sure that the map_symbol is a small letter!')
house_locations={}
global x1,x2,y1,y2
x1=street_ends[0][0]
x2=street_ends[1][0]
y1=street_ends[0][1]
y2=street_ends[1][1]
def valid_house_to_add(self, house):
HouseLocation.__init__(house,house.street_name,house.x,house.y)
ava=isBetween((x1,y1),(x2,y2),(house.x,house.y))
return ava
def add_house(self,house):
HouseLocation.__init__(house,house.street_name,house.x,house.y)
if Street.valid_house_to_add(house)==True:
house_locations.update(house,house.x,house.y)
else:
raise ValueError("Can not add {} to map").format(house)
The code is not yet complete, but so far I have a problem in the last function add_house
When I run the following commands in the shell:
mockingbird = Street("mockingbird lane", "m", ((5,36),(30,36)))
frat_house = HouseLocation("mockingbird lane", 11, 36)
bad_house = HouseLocation("mockingbird lane", 31, 36)
mockingbird.add_house(my_house)
print mockingbird.valid_house_to_add(frat_house)
I get the following error:
if Street.valid_house_to_add(house)==True:
TypeError: unbound method valid_house_to_add() must be called with Street instance as first argument (got HouseLocation instance instead)
I am still new to classes, so if anyone can guide me or tell me how to fix the problem that'd be really appreciated
You are accessing the method straight on the class:
if Street.valid_house_to_add(house)==True:
The expression Street.valid_house_to_add retrieves the unbound method from the class, meaning it is not associated with an instance.
Access the method on self instead, so that it is bound to self:
if self.valid_house_to_add(house):
Note that you do not need to add == True, if already tests for the truth value.
You also should not be calling HouseLocation.__init__ from each method. You probably want to call that only from Street.__init__. In fact, I'd say Street should be a container, holding HouseLocation instances, instead of it being a subclass. Only use subclassing if Street is a specialised HouseLocation.
I've been implementing a model with spark via a python class. I had some headaches calling class methods on a RDD defined in the class (see this question for details), but finally have made some progress. Here is an example of a class method I'm working with:
#staticmethod
def alpha_sampler(model):
# all the variables in this block are numpy arrays or floats
var_alpha = model.params.var_alpha
var_rating = model.params.var_rating
b = model.params.b
beta = model.params.beta
S = model.params.S
Z = model.params.Z
x_user_g0_inner_over_var = model.x_user_g0_inner_over_var
def _alpha_sampler(row):
feature_arr = row[2]
var_alpha_given_rest = 1/((1/var_alpha) + feature_arr.shape[0]*(1/var_rating))
i = row[0]
items = row[1]
O = row[3] - np.inner(feature_arr,b) - beta[items] - np.inner(S[i],Z[items])
E_alpha_given_rest = var_alpha_given_rest * (x_user_g0_inner_over_var[i] + O.sum()/var_rating)
return np.random.normal(E_alpha_given_rest,np.sqrt(var_alpha_given_rest))
return _alpha_sampler
As you can see, to avoid serialization errors, I define a static method that returns a function that is in turn applied to each row of an RDD (model is the parent class here, and this is called from within another method of model):
# self.grp_user is the RDD
self.params.alpha = np.array(self.grp_user.map(model.alpha_sampler(self)).collect())
Now, this all works fine, but is not leveraging Spark's broadcast variables at all. Ideally, all the variables I'm passing in this function (var_alpha, beta, S, etc.) could first be broadcast to the workers, so that I wasn't redundantly passing them as part of the map. But I'm not sure how to do this.
My question, then, is the following: How/where should I make these into broadcast variables such that they are available to the alpha_sampler function that I map to grp_user? One thing I believe will work would be to make them globals, e.g.
global var_alpha
var_alpha = sc.broadcast(model.params.var_alpha)
# and similarly for the other variables...
Then the alpha_sampler could be much simplified:
#staticmethod
def _alpha_sampler(row):
feature_arr = row[2]
var_alpha_given_rest = 1/((1/var_alpha.value) + feature_arr.shape[0]*(1/var_rating.value))
i = row[0]
items = row[1]
O = row[3] - np.inner(feature_arr,b.value) - beta.value[items] - np.inner(S.value[i],Z.value[items])
E_alpha_given_rest = var_alpha_given_rest * (x_user_g0_inner_over_var.value[i] + O.sum()/var_rating.value)
return np.random.normal(E_alpha_given_rest,np.sqrt(var_alpha_given_rest))
But of course this is really dangerous use of globals that I would like to avoid. Is there a better way that lets me leverage broadcast variables?
Assuming that variables you use here are simply scalars there is probably nothing to gain here from a performance perspective and using broadcast variables will make you code less readable but you can either pass a broadcast variable as an argument to the static method:
class model(object):
#staticmethod
def foobar(a_model, mu):
y = a_model.y
def _foobar(x):
return x - mu.value + y
return _foobar
def __init__(self, sc):
self.sc = sc
self.y = -1
self.rdd = self.sc.parallelize([1, 2, 3])
def get_mean(self):
return self.rdd.mean()
def run_foobar(self):
mu = self.sc.broadcast(self.get_mean())
self.data = self.rdd.map(model.foobar(self, mu))
or initialize it there:
class model(object):
#staticmethod
def foobar(a_model):
mu = a_model.sc.broadcast(a_model.get_mean())
y = a_model.y
def _foobar(x):
return x - mu.value + y
return _foobar
def __init__(self, sc):
self.sc = sc
self.y = -1
self.rdd = self.sc.parallelize([1, 2, 3])
def get_mean(self):
return self.rdd.mean()
def run_foobar(self):
self.data = self.rdd.map(model.foobar(self))
How do I mock a class that has unbound methods? For example, this class has a #classmethod and a #staticmethod:
class Calculator(object):
def __init__(self, multiplier):
self._multiplier = multiplier
def multiply(self, n):
return self._multiplier * n
#classmethod
def increment(cls, n):
return n + 1
#staticmethod
def decrement(n):
return n - 1
calculator = Calculator(2)
assert calculator.multiply(3) == 6
assert calculator.increment(3) == 4
assert calculator.decrement(3) == 2
assert Calculator.increment(3) == 4
assert Calculator.decrement(3) == 2
The above pretty much describes my question. The following is a working example that demonstrates the things I have tried.
Class Machine contains an instance of Calculator. I will be testing Machine with a mock of Calculator. To demonstrate my issue, Machine calls the unbound methods via an instance of Calculator and via the Calculator class:
class Machine(object):
def __init__(self, calculator):
self._calculator = calculator
def mult(self, n):
return self._calculator.multiply(n)
def incr_bound(self, n):
return self._calculator.increment(n)
def decr_bound(self, n):
return self._calculator.decrement(n)
def incr_unbound(self, n):
return Calculator.increment(n)
def decr_unbound(self, n):
return Calculator.decrement(n)
machine = Machine(Calculator(3))
assert machine.mult(3) == 9
assert machine.incr_bound(3) == 4
assert machine.incr_unbound(3) == 4
assert machine.decr_bound(3) == 2
assert machine.decr_unbound(3) == 2
All the functional code above works fine. Next is the part that does not work.
I create a mock of Calculator to use in testing Machine:
from mock import Mock
def MockCalculator(multiplier):
mock = Mock(spec=Calculator, name='MockCalculator')
def multiply_proxy(n):
'''Multiply by 2*multiplier instead so we can see the difference'''
return 2 * multiplier * n
mock.multiply = multiply_proxy
def increment_proxy(n):
'''Increment by 2 instead of 1 so we can see the difference'''
return n + 2
mock.increment = increment_proxy
def decrement_proxy(n):
'''Decrement by 2 instead of 1 so we can see the difference'''
return n - 2
mock.decrement = decrement_proxy
return mock
In the unit test below, the bound methods use MockCalculator as I had hoped. However, the calls to Calculator.increment() and Calculator.decrement() still use Calculator:
import unittest
class TestMachine(unittest.TestCase):
def test_bound(self):
'''The bound methods of Calculator are replaced with MockCalculator'''
machine = Machine(MockCalculator(3))
self.assertEqual(machine.mult(3), 18)
self.assertEqual(machine.incr_bound(3), 5)
self.assertEqual(machine.decr_bound(3), 1)
def test_unbound(self):
'''Machine.incr_unbound() and Machine.decr_unbound() are still using
Calculator.increment() and Calculator.decrement(n), which is wrong.
'''
machine = Machine(MockCalculator(3))
self.assertEqual(machine.incr_unbound(3), 4) # I wish this was 5
self.assertEqual(machine.decr_unbound(3), 2) # I wish this was 1
So I try to patch Calculator.increment() and Calculator.decrement():
def MockCalculatorImproved(multiplier):
mock = Mock(spec=Calculator, name='MockCalculatorImproved')
def multiply_proxy(n):
'''Multiply by 2*multiplier instead of multiplier so we can see the difference'''
return 2 * multiplier * n
mock.multiply = multiply_proxy
return mock
def increment_proxy(n):
'''Increment by 2 instead of 1 so we can see the difference'''
return n + 2
def decrement_proxy(n):
'''Decrement by 2 instead of 1 so we can see the difference'''
return n - 2
from mock import patch
#patch.object(Calculator, 'increment', increment_proxy)
#patch.object(Calculator, 'decrement', decrement_proxy)
class TestMachineImproved(unittest.TestCase):
def test_bound(self):
'''The bound methods of Calculator are replaced with MockCalculator'''
machine = Machine(MockCalculatorImproved(3))
self.assertEqual(machine.mult(3), 18)
self.assertEqual(machine.incr_bound(3), 5)
self.assertEqual(machine.decr_bound(3), 1)
def test_unbound(self):
'''machine.incr_unbound() and Machine.decr_unbound() should use
increment_proxy() and decrement_proxy(n).
'''
machine = Machine(MockCalculatorImproved(3))
self.assertEqual(machine.incr_unbound(3), 5)
self.assertEqual(machine.decr_unbound(3), 1)
Even after patching, the unbound methods want an instance of Calculator as an argument:
TypeError: unbound method increment_proxy() must be called with Calculator instance as first argument (got int instance instead)
How do I mock out class method Calculator.increment() and static method Calculator.decrement()?
You were patching the wrong object. You must patch the Calculator from the Machine class, not the general Calculator class. Read about it here.
from mock import patch
import unittest
from calculator import Calculator
from machine import Machine
class TestMachine(unittest.TestCase):
def my_mocked_mult(self, multiplier):
return 2 * multiplier * 3
def test_bound(self):
'''The bound methods of Calculator are replaced with MockCalculator'''
machine = Machine(Calculator(3))
with patch.object(machine, "mult") as mocked_mult:
mocked_mult.side_effect = self.my_mocked_mult
self.assertEqual(machine.mult(3), 18)
self.assertEqual(machine.incr_bound(3), 5)
self.assertEqual(machine.decr_bound(3), 1)
def test_unbound(self):
'''Machine.incr_unbound() and Machine.decr_unbound() are still using
Calculator.increment() and Calculator.decrement(n), which is wrong.
'''
machine = Machine(Calculator(3))
self.assertEqual(machine.incr_unbound(3), 4) # I wish this was 5
self.assertEqual(machine.decr_unbound(3), 2) # I wish this was 1
One way to do it is
def test_increment(mocker):
mocker.patch.object(Calculator, attribute='increment', return_value=10)
...actual test code...
I just did something that could be translated to your case like this:
class Calculator_Mock(object):
def __init__(self, multiplier):
... # add whatever you need here
def multiply(self, n):
... # add whatever you need here
#classmethod
def increment(self, n):
... # add whatever you need here
Then, in your test, something as simple as this:
class TestCalculator(TestCase):
def test_increment_or_whatever(self):
with patch.object(Calculator,
"increment",
return_value=Calculator_Mock.increment()) as increment_mock:
... # call whatever your calls Calculator.increment, the mock should run instead the Calculator.increment
C#, Java and C++ programmers tend to overuse class and static methods in Python. The Pythonic approach is to use module functions.
So first, here is the refactored software under test, with methods increment() and decrement() as module functions. The interface does change, but the functionality is the same:
# Module machines
class Calculator(object):
def __init__(self, multiplier):
self._multiplier = multiplier
def multiply(self, n):
return self._multiplier * n
def increment(n):
return n + 1
def decrement(n):
return n - 1
calculator = Calculator(2)
assert calculator.multiply(3) == 6
assert increment(3) == 4
assert decrement(3) == 2
class Machine(object):
'''A larger machine that has a calculator.'''
def __init__(self, calculator):
self._calculator = calculator
def mult(self, n):
return self._calculator.multiply(n)
def incr(self, n):
return increment(n)
def decr(self, n):
return decrement(n)
machine = Machine(Calculator(3))
assert machine.mult(3) == 9
assert machine.incr(3) == 4
assert machine.decr(3) == 2
Add functions increment_mock() and decrement_mock() to mock increment() and decrement():
from mock import Mock
import machines
def MockCalculator(multiplier):
mock = Mock(spec=machines.Calculator, name='MockCalculator')
def multiply_proxy(n):
'''Multiply by 2*multiplier instead of multiplier so we can see the
difference.
'''
return 2 * multiplier * n
mock.multiply = multiply_proxy
return mock
def increment_mock(n):
'''Increment by 2 instead of 1 so we can see the difference.'''
return n + 2
def decrement_mock(n):
'''Decrement by 2 instead of 1 so we can see the difference.'''
return n - 2
And now for the good part. Patch increment() and decrement() to replace them with their mocks:
import unittest
from mock import patch
import machines
#patch('machines.increment', increment_mock)
#patch('machines.decrement', decrement_mock)
class TestMachine(unittest.TestCase):
def test_mult(self):
'''The bound method of Calculator is replaced with MockCalculator'''
machine = machines.Machine(MockCalculator(3))
self.assertEqual(machine.mult(3), 18)
def test_incr(self):
'''increment() is replaced with increment_mock()'''
machine = machines.Machine(MockCalculator(3))
self.assertEqual(machine.incr(3), 5)
def test_decr(self):
'''decrement() is replaced with decrement_mock()'''
machine = machines.Machine(MockCalculator(3))
self.assertEqual(machine.decr(3), 1)
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.