I have a large function in my script that contains the bulk of the logic of my program.
At one point, it used to span ~100 lines which I then tried to refactor into multiple smaller functions. However, I had many local variables that were eventually being modified in the smaller functions, and I needed some way to keep track of them in the scope of the larger function.
For instance, it looked like
def large_func():
x = 5
... 100 lines ...
to
def large_func():
x = 6
small_func_that_will_increment_x()
small_func()
....
What is a pythonic way to handle this?
The two approaches I can think of are:
1) global variables --- will probably get messy as I have many variables
2) using a dict to keep track of them like
tracker = {
'field1' : 5
'field2' : 4
}
and make modifications on the dict instead.
Is there a different way to do this that I might have overlooked?
Without more information, it's hard to know whether this is appropriate or not, but…
An object is a namespace. In particular, you can turn each of those local variables into attributes on an object. For example:
class LargeThing(object):
def __init__(self):
self.x = 6
def large_func(self):
self.small_func_that_will_increment_x()
self.small_func()
# ...
def small_func_that_will_increment_x(self):
self.x += 1
Whether the self.x = 6 belongs in __init__ or at the start of large_func, or whether this is even a good idea, depends on what all those variables actually mean, and how they fit together.
Closures will work here:
def large_func()
x = 6
def func_that_uses_x():
print x
def func_that_modifies_x():
nonlocal x # python3 only
x += 1
func_that_uses_x()
func_that_modifies_x()
Another tip - make use of Python's ability to return multiple values. If you have a function that modifies two variables, do something like this:
def modifies_two_vars(a, b, c, d):
return a+b, c+d
x, y = modifies_two_vars(x, y, z, w)
One alternative could be:
def small_func_that_will_return_new_x(old_x):
return old_x + 1
def large_func():
x = small_func_that_will_return_new_x(6)
instead of:
def large_func():
x = 6
small_func_that_will_increment_x()
Object composition. Create small objects that hold state, and then feed them as initializers an object that manages them. See Global State and Singletons
"Build the door knob, which you use to build the door, which you use to construct the house. Not the other way around"
Related
Is there a clean way to reuse code blocks in python classes to avoid having to retype the same code? For example lets say at the beginning of 2 different functions there is the same lines of code required to set up the function (where it is desired to keep the set up variables locally in the function).
def func1(path, x, y):
img = load_img('path')
img.normalize()
foo = Foo(x)
bar = Bar(y)
**rest of the function (using img, foo and bar)
def func2(path, x, y):
img = load_img('path')
img.normalize()
foo = Foo(x)
bar = Bar(y)
**different code here to func 1
Is there a standard best practice to set up the local variables to avoid code repetition? Like a function which can set local variables (maybe using nonlocal)? I know I could technically do the entire set up in a single function, however with a lot of input and output variables it might get a bit messy. I just wanted to see if there was a more elegant way to approach this, as ideally I'd just be able to call some arbitrary function which sets up the entire local space.
*Also just noting, I am aware I can wrap all the code in a class, turn the functions into methods and have shared attributes, however I don't think that's quite the right solution for this situation.
Put the common code in another function that returns the values.
def common(path, x, y):
img = load_img(path)
img.normalize
return img, Foo(x), Bar(y)
def func1(path, x, y):
img, foo, bar = common(path, x, y)
# different code
def func2(path, x, y):
img, foo, bar = common(path, x, y)
# different code
You might also consider using a decorator.
Goal
I am trying to write a function where one or more of the input parameters is a global variable that is updated by the function, without having to return values from within the function. I am aware I could just return a tuple or two separate values from the function, but I think updating the global variables from within the function would be another interesting method if it is possible.
Reason to do this
Updating global variables with a function is easy when the global variable is known (ie. defined previously in the python script). However, I want to define the function in a separate .py file to easily use the function within other python scripts. Therefore, I need to be able to support different variable names to update.
While this is not at all necessary, I am just interested if this is even possible.
Example Pseudocode
I'm thinking something like this:
def math_function(input_val, squared_result, cubed_result):
squared_result = input_val**2 #update the var input as the squared_result parameter
cubed_result = input_val**3 #update the var input as the cubed_result parameter
where you would input a number for input_val and then global variables for squared_result and cubed_result that the function updates with the result. It would then theoretically work like:
#Declare global variables
b = 0
c = 0
#then somewhere in the code, call the function
math_function(2, b, c)
#check the new values
print(b) #Output: b = 4
print(c) #Output: c = 8
This would allow me to use the function in different python scripts without having to worry about what order the results are returned in.
First: I am in no way advocating this.
You could use the globals builtin function to access a global variable by name:
def gtest(name,value):
globals()[name] = value
gtest('new_global','new_value')
print(new_global)
I am not advocating this too, I would like to hear everyone's thoughts, but this can be done -
my_global = [0, 0]
def my_math_func(x, g):
g[0] = x ** 2
g[1] = x ** 3
my_math_func(3, my_global)
How can I pass an integer by reference in Python?
I want to modify the value of a variable that I am passing to the function. I have read that everything in Python is pass by value, but there has to be an easy trick. For example, in Java you could pass the reference types of Integer, Long, etc.
How can I pass an integer into a function by reference?
What are the best practices?
It doesn't quite work that way in Python. Python passes references to objects. Inside your function you have an object -- You're free to mutate that object (if possible). However, integers are immutable. One workaround is to pass the integer in a container which can be mutated:
def change(x):
x[0] = 3
x = [1]
change(x)
print x
This is ugly/clumsy at best, but you're not going to do any better in Python. The reason is because in Python, assignment (=) takes whatever object is the result of the right hand side and binds it to whatever is on the left hand side *(or passes it to the appropriate function).
Understanding this, we can see why there is no way to change the value of an immutable object inside a function -- you can't change any of its attributes because it's immutable, and you can't just assign the "variable" a new value because then you're actually creating a new object (which is distinct from the old one) and giving it the name that the old object had in the local namespace.
Usually the workaround is to simply return the object that you want:
def multiply_by_2(x):
return 2*x
x = 1
x = multiply_by_2(x)
*In the first example case above, 3 actually gets passed to x.__setitem__.
Most cases where you would need to pass by reference are where you need to return more than one value back to the caller. A "best practice" is to use multiple return values, which is much easier to do in Python than in languages like Java.
Here's a simple example:
def RectToPolar(x, y):
r = (x ** 2 + y ** 2) ** 0.5
theta = math.atan2(y, x)
return r, theta # return 2 things at once
r, theta = RectToPolar(3, 4) # assign 2 things at once
Not exactly passing a value directly, but using it as if it was passed.
x = 7
def my_method():
nonlocal x
x += 1
my_method()
print(x) # 8
Caveats:
nonlocal was introduced in python 3
If the enclosing scope is the global one, use global instead of nonlocal.
Maybe it's not pythonic way, but you can do this
import ctypes
def incr(a):
a += 1
x = ctypes.c_int(1) # create c-var
incr(ctypes.ctypes.byref(x)) # passing by ref
Really, the best practice is to step back and ask whether you really need to do this. Why do you want to modify the value of a variable that you're passing in to the function?
If you need to do it for a quick hack, the quickest way is to pass a list holding the integer, and stick a [0] around every use of it, as mgilson's answer demonstrates.
If you need to do it for something more significant, write a class that has an int as an attribute, so you can just set it. Of course this forces you to come up with a good name for the class, and for the attribute—if you can't think of anything, go back and read the sentence again a few times, and then use the list.
More generally, if you're trying to port some Java idiom directly to Python, you're doing it wrong. Even when there is something directly corresponding (as with static/#staticmethod), you still don't want to use it in most Python programs just because you'd use it in Java.
Maybe slightly more self-documenting than the list-of-length-1 trick is the old empty type trick:
def inc_i(v):
v.i += 1
x = type('', (), {})()
x.i = 7
inc_i(x)
print(x.i)
A numpy single-element array is mutable and yet for most purposes, it can be evaluated as if it was a numerical python variable. Therefore, it's a more convenient by-reference number container than a single-element list.
import numpy as np
def triple_var_by_ref(x):
x[0]=x[0]*3
a=np.array([2])
triple_var_by_ref(a)
print(a+1)
output:
7
The correct answer, is to use a class and put the value inside the class, this lets you pass by reference exactly as you desire.
class Thing:
def __init__(self,a):
self.a = a
def dosomething(ref)
ref.a += 1
t = Thing(3)
dosomething(t)
print("T is now",t.a)
In Python, every value is a reference (a pointer to an object), just like non-primitives in Java. Also, like Java, Python only has pass by value. So, semantically, they are pretty much the same.
Since you mention Java in your question, I would like to see how you achieve what you want in Java. If you can show it in Java, I can show you how to do it exactly equivalently in Python.
class PassByReference:
def Change(self, var):
self.a = var
print(self.a)
s=PassByReference()
s.Change(5)
class Obj:
def __init__(self,a):
self.value = a
def sum(self, a):
self.value += a
a = Obj(1)
b = a
a.sum(1)
print(a.value, b.value)// 2 2
In Python, everything is passed by value, but if you want to modify some state, you can change the value of an integer inside a list or object that's passed to a method.
integers are immutable in python and once they are created we cannot change their value by using assignment operator to a variable we are making it to point to some other address not the previous address.
In python a function can return multiple values we can make use of it:
def swap(a,b):
return b,a
a,b=22,55
a,b=swap(a,b)
print(a,b)
To change the reference a variable is pointing to we can wrap immutable data types(int, long, float, complex, str, bytes, truple, frozenset) inside of mutable data types (bytearray, list, set, dict).
#var is an instance of dictionary type
def change(var,key,new_value):
var[key]=new_value
var =dict()
var['a']=33
change(var,'a',2625)
print(var['a'])
Beginner with Python, need some help to understand how to manage list of objects.
I built a list of simple objects representing a coordinate
class Point:
def __init__(self, x, y):
self.x=0
self.y=0
I create a empty list to store different points :
combs = []
point = Point(0, 0)
then I build different points using point and ever ytime appending to the list combs
For instance:
point.x=2
point.y=2
combs.append(point)
point.x=4
point.y=4
combs.append(point)
I expect that combs is something like [.. 2,2 4,4] on the contrary it's [....4,4 4,4].
It means that every time I change the instance of a point, I change all the points stored in the list with the latest value.
How can I do this?
The thing is when you're trying to change the value of x and y , you're expecting to have a new object (like a new x and y with different values) but you aren't. What happens is I think is whenever you set point.x =4 and point.y = 4 is you're just changing the attribute x and y in your class
take a look at this link. This helped me a lot, I encountered that kind of problem or should I say similar of yours
I suggest using the copy package
https://www.programiz.com/python-programming/shallow-deep-copy
You are appending the same variable to the combine. You need to create a new Point object and initialize it with the new values.
combine = []
p = Point(2,2)
combine.append(p)
p = Point(4,4)
combine.append(p)
This is because Python is using reference count for the garbage collection.
In your example you create point which increments the ref count.
Then you pass it to the list which increment the count. Then change the value and pass the same variable. Which increments the count once again.
Think of it more like passing a reference or memory pointer of the point variable to the list. You gave the list twice the same pointer.
So you need to create different variables or make a deep copies https://docs.python.org/3.8/library/copy.html
Custom classes, unless they are built on an immutable type, are mutable. This means that you have appended a reference to your list, and that changing the value of the references will change every instance of the reference. Consider this:
class Test():
def __init__(self, a):
self.a = a
>>> t1 = Test(1)
>>> t1.a
1
>>> t2 = t1
>>> t2.a
1
>>> t2.a = 2
>>> t2.a
2
>>> t1.a
2
See how changing t2 also changed t1?
So you have a few options. You could create new points instead of reusing old ones, you could use a copy() method, or you could write a method into your Point class that exports something immutable, like a tuple of the (x, y) values and append that to your list, instead of appending the entire object to your list.
You are working with only a single Point. Construct a second one. See commented line below.
point = Point(0, 0)
point.x=2
point.y=2
combs.append(point)
point = Point(0, 0) # add this
point.x=4
point.y=4
combs.append(point)
By the way, your __init__ ignores its parameters -- throws them away. A better version is below. We assign self.x=x to make use of the parameter. (Likewise y).
def __init__(self, x, y):
self.x=x
self.y=x
You need to pass one value into point
How to add an item to your list
combs = []
point = 1 # For example
combs.append(point)
Use command lines to study
Try to use BASH or CMD... Use command lines... They will have instant feedback of your code
Good place to find basic stuff
Try to see the examples on w3scholl. It is a great place. Here is the link for W3Scholl - Python - List
Understand basics first
Before you jump into classes, try to understand lists very well! You will learn more and build a solid knowledge if you take a step by step growing! Keep pushing!!!
I've been trying to make a soccer game using Python. Most of the problems I've run into I've been able to find a way around. However, I'm getting stuck on the error "global name '---' not defined", referring to a method name. From what I've seen, the problem deals with trying to call a method from a method. Since pasting my code would take too much time explaining what was going on, I wrote a very basic example of the problem:
class example():
def doMath(x,y):
z = x + y
z = square(z)
return z
def square(z):
z = z * z
return z
print doMath(5,4)
This is in no way meant to be a program worth writing, nor is it the smart way of doing that calculation... It just imitates the problem I'm having. This code will not work, because the method "doMath" doesn't know the method "square" exists. I've seen this fixed by making the square method a submethod (I don't know what it's called, it's just indented under the primary method). However, that is not viable in my soccer code since I'd be having multiple methods calling it. I've seen similar questions to this, but the answers still don't fit my code. A global function seems like it would be what I'm looking for, but it typically leads to an error of something not existing. I could just add a bunch of instructions to the main, but that's alot of work and lines of code that I'd prefer not to have to add - it would make it pretty ugly.
So the main question is... how can I get the doMath method to call the square method without having to combine them.
While we're at it... I've been calling these methods rather than functions... am I correct on that?
As others have noted, you need to use self, and you need to call your methods correctly, with an instance, for example:
#!/usr/bin/python
class example():
def doMath(self, x, y):
z = x + y
z = self.square(z)
return z
def square(self, z):
z = z * z
return z
p = example()
print p.doMath(5,4)
outputs:
paul#local:~/src/python$ ./square.py
81
paul#local:~/src/python$
Clearly, in this particular case there's no advantage to these methods being in a class at all, and you could more easily do:
#!/usr/bin/python
def square(z):
return z * z
def doMath(x, y):
return square(x + y)
print doMath(5,4)
While we're at it... I've been calling these methods rather than functions... am I correct on that?
method -> routine that is a member of a class.
function -> routine that returns a result (compare with mathematical function)
procedure -> routine that does not return a result
member -> part of a class or struct (either a member variable or a member function etc)
Procedures are odd in python because even though a procedure does not return anything you can still assign its result to a variable.
The result in this case is None see here: Python procedure return values
If doMath and square are part of a class, they both should have another parameter called self. Methods calls take place on this self parameter. For example:
def doMath(self, x, y):
z = x + y
z = self.square(z)
return z
class example():
def doMath(self,x,y):
z = x + y
z = self.square(z)
return z
def square(self,z):
z = z * z
return z
def p(self):
print self.doMath(5,4)
e=example()
e.p()