Python - external variable being changed by function - python

I am trying to write a function that squares each value in a list, returning a new list with individual values squared. I pass a list to it and it changes the original list. I'd like it to not make any changes to the original list, so that I can use it in other functions.
def squareset(c):
d=c
count = len(c)
print(c)
for i in range(0,(count),1):
d[i]=d[i]**2
return d
test = [1,2,3]
print(squareset(test))
print(test)
I don't have this problem with functions operating on simple variables of type int or float.
I added the d=c line trying to prevent the change to the list test, but it makes no difference. print(test) is producing the result [1,4,9] instead of [1,2,3]. Why is this happening and how can I fix this?

Doing d=c simply makes the parameter d point to the same object that c is pointing to. Hence, every change made to d is made to the same object that c points to.
If you want to avoid changing c you'll have to either send a copy of the object, or make a copy of the object inside the function and use this copy.
For example, do:
d = [i for i in c]
or:
d = c[:]
instead of:
d = c

Assigning the list to another variable doesn't copy the list. To copy, just
def squareset(c):
d=c[:]
...

While the other answers provided are correct I would suggest using list comprehension to square your list.
In [4]:
test = [1,2,3]
results = [elm**2 for elm in test]
print test
print results
[1, 2, 3]
[1, 4, 9]
If you wanted a function:
def squareList(lst):
return [elm**2 for elm in lst]

Try this:
def square(var):
return [x*x for x in var]
x = [1,2,3]
z = square(x)

Related

Why can't a list be constructed and modified in the same line?

For example, why is a not equal to b?
a = [1]
a.append(2)
print(a) # [1, 2]
b = [1].append(2)
print(b) # None
The syntax for b doesn't look wrong to me, but it is. I want to write one-liners to define a list (e.g. using a generator expression) and then append elements, but all I get is None.
It's because:
append, extend, sort and more list function are all "in-place".
What does "in-place" mean? it means it modifies the original variable directly, some things you would need:
l = sorted(l)
To modify the list, but append already does that, so:
l.append(3)
Will modify l already, don't need:
l = l.append(3)
If you do:
l = [1].append(2)
Yes it will modify the list of [1], but it would be lost in memory somewhere inaccessible, whereas l will become None as we discovered above.
To make it not "in-place", without using append either do:
l = l + [2]
Or:
l = [*l, 2]
The one-liner for b does these steps:
Defines a list [1]
Appends 2 to the list in-place
Append has no return, so b = None
The same is true for all list methods that alter the list in-place without a return. These are all None:
c = [1].extend([2])
d = [2, 1].sort()
e = [1].insert(1, 2)
...
If you wanted a one-liner that is similar to your define and extend, you could do
c2 = [1, *[2]]
which you could use to combine two generator expressions.
All built-in methods under class 'List' in Python are just modifying the list 'in situ'. They only change the original list and return nothing.
The advantage is, you don't need to pass the object to the original variable every time you modify it. Meanwhile, you can't accumulatively call its methods in one line of code such as what is used in Javascript. Because Javascript always turns its objects into DOM, but Python not.

How to update list with function (with return value) in python

I am implementing few list methods manually like append(), insert(), etc. I was trying to add element at the end of list (like append method). This the working code i am using:
arr = [4,5,6]
def push(x, item):
x += [item]
return x
push(arr,7)
print(arr) #Output: [4,5,6,7]
But when I am implementing same code with little difference. I am getting different output.
arr = [4,5,6]
def push(x, item):
x = x + [item]
return x
push(arr,7)
print(arr) #Output: [4,5,6]
And I am facing same for insert method. Here is code for insert method:
arr = [4,5,7,8]
def insert(x, index, item):
x = x[:index] + [item] + x[index:]
return x
insert(arr,2,6)
print(arr) #Output: [4,5,7,8]
I know I can store return value to the list by arr=insert(arr,2,6) but I want an alternative solution, that list automatically gets update after calling function like in my first code sample.
Edit 1:
I think x[index:index] = [item] is better solution for the problem.
x += [item] and x = x + [item] are not a little difference. In the first case, you are asking to make a change to the list referenced by x; this is why the result reflects the change. In the second, you are asking to have x reference a new list, the one made by combining x's original value and [item]. Note that this does not change x, which is why your result is unchanged.
Also note that your return statements are irrelevant, since the values being returned are ignored.
In your first example you mutated(a.k.a changed) the list object referred to by x. When Python sees x += [item] it translates it to:
x.__iadd__([item])
As you can see, we are mutating the list object referred to by x by calling it's overloaded in-place operator function __iadd__. As already said, __iadd__() mutates the existing list object:
>>> lst = [1, 2]
>>> lst.__iadd__([3])
[1, 2, 3]
>>> lst
[1, 2, 3]
>>>
In your second example, you asked Python to assign x to a new reference. The referenced now referrer to a new list object made by combining (not mutating) the x and [item] lists. Thus, x was never changed.
When Python sees x = x + [item] it can be translated to:
x = x.__add__([item])
The __add__ function of lists does not mutate the existing list object. Rather, it returns a new-list made by combing the value of the existing list and the argument passed into __add__():
>>> lst = [1, 2]
>>> lst.__add__([3]) # lst is not changed. A new list is returned.
[1, 2, 3]
>>>
You need to return the the result of the version of push to the arr list. The same goes for insert.
You can assign to a slice of the list to implement your insert w/o using list.insert:
def insert(x, index, item):
x[:] = x[:index] + [item] + x[index:]
this replaces the contents of the object referenced by x with the new list. No need to then return it since it is performed in-place.
The problem is that you haven't captured the result you return. Some operations (such as +=) modify the original list in place; others (such as x = x + item) evaluate a new list and reassign the local variable x.
In particular, note that x is not bound to arr; x is merely a local variable. To get the returned value into arr, you have to assign it:
arr = push(arr, 7)
or
arr = insert(arr, 2, 6)
class DerivedList(list):
def insertAtLastLocation(self,obj):
self.__iadd__([obj])
parameter=[1,1,1]
lst=DerivedList(parameter)
print(lst) #output[1,1,1]
lst.insertAtLastLocation(5)
print(lst) #output[1,1,1,5]
lst.insertAtLastLocation(6)
print(lst) #output[1,1,1,5,6]
You can use this code to add one element at last position of list
class DerivedList(list):
def insertAtLastLocation(self,*obj):
self.__iadd__([*obj])
parameter=[1,1,1]
lst=DerivedList(parameter)
print(lst) #output[1,1,1]
lst.insertAtLastLocation(5)
print(lst) #output[1,1,1,5]
lst.insertAtLastLocation(6,7)
print(lst) #output[1,1,1,5,6,7]
lst.insertAtLastLocation(6,7,8,9,10)
print(lst) #output[1,1,1,5,6,7,8,9,10]
This code can add multiple items at last location

How to update a list of variables in python?

This is an aggravating issue that I've come into whilst programming in python. The user can append various variables to a list however, this list when accessed later on will still display the same results as when first assigned. e.g below
a=1
b=2
list = [a,b] #user defined order
print list # 1,2
a=2
print list # prints 1,2
I need list to print out 2,2. However i cannot find out a way to DYNAMICALLY update the list to accommodate ANY order of the variables (many ways are hard coding which i've seen online such as assigning list = [a,b] when needed to update however i dont know if its b,a or a,b)
Help would be much appreciated. Thank you
Edit : My question is different due to being about varaibles that need to be dynamically updated in a list rather than simply changing a list item.
you need to update the list and not the variable:
a = 1
b = 2
lst = [a, b] # the list now stores the references a and b point to
# changing a later on has no effect on this!
lst[0] = 2
and please do not use list as variable name! this is a keyword built-in type in python and overwriting it is a very bad idea!
if you only know the value of the element in the list (and this value is unique) you could do this:
old_val = 2
new_val = 3
lst[lst.index(old_val)] = new_val
It's not possible if you store immutable items in a list, which Python integers are, so add a level of indirection and use mutable lists:
a,b,c,d = [1],[2],[3],[4]
L = [d,b,a,c] # user-defined order
print L
a[0] = 5
print L
Output:
[[4], [2], [1], [3]]
[[4], [2], [5], [3]]
This has the feel of an X-Y Problem, however. Describing the problem you are solving with this solution may elicit better solutions.
Why don't you use dictionary??
With dictionary:
_dict = {}
_dict["a"] = 1
_dict["b"] = 2
print _dict["a"] # prints 1
Now if you want to set and get value of variable "a":
_dict["a"] = 2
print _dict["a"] # prints 2
At the assignment 'a=1' an integer object is created with the value 1. That object is added to your list. At the second assignment (a=2) a new object is created, but the old one still is in the list, its value unchanged. That is what you see.
To change this behaviour, the value of the object must be changed. Ints are immutable in Python, meaning that exactly this, i.e. changing the value, is not possible. You'll have to define your own and then change its value. For example:
class MyInt(object):
def __init__(self, value):
self.setValue(value)
def setValue(self, value):
self.value = value
def __repr__(self):
return str(self.value)
a=MyInt(1)
print(dir(a))
b=2
list = [a,b] #user defined order
print (list) # 1,2
a.setValue(2)
print (list) # prints 2,2
It is not clean but a work around trick , you can assign the value of a is a single element list
a = [1]
b = 2
So when you assign them to a list:
lst = [a,b]
and change the value of a by calling its 0th element:
a[0] = 2
it will also change the referenced list that is a
print(lst)
#[[2],2]
likewise if you place the value in the list:
lst[0][0] = 2
print(a[0])
#2
You can define a function:\
a, b = 1, 2
def list_():
return [a, b]
list_() # [1, 2]
a = 2
list_() # [2, 2]

Copy values from one list to another without altering the reference in python

In python objects such as lists are passed by reference. Assignment with the = operator assigns by reference. So this function:
def modify_list(A):
A = [1,2,3,4]
Takes a reference to list and labels it A, but then sets the local variable A to a new reference; the list passed by the calling scope is not modified.
test = []
modify_list(test)
print(test)
prints []
However I could do this:
def modify_list(A):
A += [1,2,3,4]
test = []
modify_list(test)
print(test)
Prints [1,2,3,4]
How can I assign a list passed by reference to contain the values of another list? What I am looking for is something functionally equivelant to the following, but simpler:
def modify_list(A):
list_values = [1,2,3,4]
for i in range(min(len(A), len(list_values))):
A[i] = list_values[i]
for i in range(len(list_values), len(A)):
del A[i]
for i in range(len(A), len(list_values)):
A += [list_values[i]]
And yes, I know that this is not a good way to do <whatever I want to do>, I am just asking out of curiosity not necessity.
You can do a slice assignment:
>>> def mod_list(A, new_A):
... A[:]=new_A
...
>>> liA=[1,2,3]
>>> new=[3,4,5,6,7]
>>> mod_list(liA, new)
>>> liA
[3, 4, 5, 6, 7]
The simplest solution is to use:
def modify_list(A):
A[::] = [1, 2, 3, 4]
To overwrite the contents of a list with another list (or an arbitrary iterable), you can use the slice-assignment syntax:
A = B = [1,2,3]
A[:] = [4,5,6,7]
print(A) # [4,5,6,7]
print(A is B) # True
Slice assignment is implemented on most of the mutable built-in types. The above assignment is essentially the same the following:
A.__setitem__(slice(None, None, None), [4,5,6,7])
So the same magic function (__setitem__) is called when a regular item assignment happens, only that the item index is now a slice object, which represents the item range to be overwritten. Based on this example you can even support slice assignment in your own types.

Explanation regarding "generator object"

Could some one please explain why this code:
A = [1,2,3,4]
B = ((element) for element in A)
print(B)
produces: <generator object <genexpr> at 0x0319B490>
while this code:
A = [1,2,3,4]
for element in A:
print(A)
produces:
1
2
3
4
They seem to be the same to me but they are obviously different. I can't figure out the difference between them.
Thanks.
The first code is a generator expression, hence it will create a generator object at a certain memory address. If you want to use list comprehension then use [] as per:
A = [1,2,3,4]
B = [element for element in A]
print(B)
# [1, 2, 3, 4]
This list comprehension is equivalent to:
A = [1,2,3,4]
B = []
for element in A:
B.append(element)
The first is not a loop but a generator expresion so will printing B it shows us the object ref.
The second one Is a loop, it iterates over the elements and print them all.
Try doing this, you can iterate over a generator:
A = [1,2,3,4]
B = ((element) for element in A)
for e in B:
print(e)
This will result in the same as your second expresion:
for e in A:
print(e)
Notice that you can only iterate once until the generator is exausted.
The fundamental difference between the two is that a generator expression defines an object that will generate values as you loop. In other words, the values will be generated on each iteration and consumed on demand. With a list comprehension, the values are created up-front and will consume as much memory as is required to hold all the values in memory at once.
It's easy to look at these two constructs as being the exact same thing but in the case of the generator, you are consuming the values on demand in a lazy way. This is very useful because you don't have pay the cost of memory to hold all of the data up-front.

Categories