Python List assignment seems like it gets carried away [duplicate] - python

This question already has answers here:
How do I clone a list so that it doesn't change unexpectedly after assignment?
(24 answers)
Closed 3 years ago.
a=[1] # here is a comment to the right
print("as expected, a=", a)
b=a
print("as expected, b=", b)
a[0]=2
print("as expected, now a=", a)
print("NOT EXPECTED TO CHANGE now b=", b)

lists are mutable ... both a and b point to the same list, so changing a will also change b
if you do not want this behaviour assign a copy of a to b
b = a[:]
if you had used an immutable datatype (like a string or number) when you change it it assigns a completely new variable to b and works ... but mutable datatypes wont work like this

In python (as in many languages, with the notable exception of C/C++), names are references to values. When you write a = b, you make a refer to the same value as b, you don't copy its contents.
Use b = list(a) to create a copy (or see deepcopy for more complex objects)

Related

*= multiplier question between arrays and integers [duplicate]

This question already has answers here:
Why does += behave unexpectedly on lists?
(9 answers)
Closed 1 year ago.
I'm trying to learn coding through some tutorials and encountered something curious as I'm going over operators. The basic ones I get, but some others (like *= here) throw me off.
a = 1
b = a
a *= 2
print(a)
print(b)
Output
2
1
But when a is an array, this happens:
a = np.array([1, 2, 3, 3, 5, 5])
b = a
a *= 2
print(a)
print(b)
Output
[2 4 6 6 10 10]
[2 4 6 6 10 10]
Any insight as to why this happens? I can't really find much about it. The only thing I came across was "coercion rules"; does the second instance happen because a is being assigned to an array but then to an integer? Or is it a matter of the print statement order?
It's probably trivial but I'm just curious, thanks!
The Simple answer to your question is,
There are two kinds of objects in Python: Mutable objects and Immutable objects. The value of a mutable object can be modified in place after it’s creation, while the value of an immutable object cannot be changed.
Immutable(Not Modifiable) Object: int, float, long, complex, string tuple, bool
Mutable(Modifiable) Object: list, dict, set, byte array, user-defined classes.
So,here in your case firstly a and b are immutable objects as it belongs to class int in python,"b=a" means b is pointing towards a address,and as you update value of a to a*=2, the value is store on new memory location, but b is still pointing towards older address, thats why b is not showing changed value of a.
For more understanding of memory management in python, please read this blog, thankyou :)
https://medium.com/#tyastropheus/tricky-python-i-memory-management-for-mutable-immutable-objects-21507d1e5b95

Using list comprehension for repeated function calls in Python [duplicate]

This question already has answers here:
Is it Pythonic to use list comprehensions for just side effects?
(7 answers)
Closed 6 years ago.
In my current codebase, I have the following line where to_remove is a set
[to_remove.update(b) for b in some_set if all(a <= b for a in some_dict)]
Although it works, it bothers me a little bit since it creates a list of None which is not used. Is it considered not standard? Is there a better way to do it?
Update:
Since it has been pointed out that list comprehension solely for side effect is not good, I have changed my code to
to_remove.update(itertools.chain.from_iterable(
b for b in some_set if all(a <= b for a in some_dict))
It's not standard or recommended to use a list comprehension if its output is not assigned to a variable. A static analyzer such as pylint will even flag it.
Use a conventional loop instead:
for b in some_set:
if all(a <= b for a in some_dict):
to_remove.update(b)
In your specific case, since to_remove is a set, the following may or may not work correctly:
to_remove.update(b for b in some_set if all(a <= b for a in some_dict))

Pass-by-object-reference and mutability [duplicate]

This question already has answers here:
How do I pass a variable by reference?
(39 answers)
Closed 6 years ago.
Much has been written about python pass-by-object-reference. But it is confusing to read threads like this or this.
In those threads, some say mutability is important, while others say that mutable or immutable objects are handled the same way.
I have a simple question, why are the contents of a not modified in the first snippet of code and modified in the second? According to this explanation of pass-by-object-reference, shouldn't the contents of the reference be modified in both cases?
def fn(b):
b += 1
a = 2
fn(a)
print(a)
def fn(b):
b += [4]
a = [2]
fn(a)
print(a)
shouldn't the contents of the reference be modified in both cases?
No, since int objects are immutable.
You don't even need functions to demonstrate this.
Consider the following:
a = 1
print(id(a))
a += 1
print(id(a))
>> 496418832
496418848
We are obviously getting a new object.
Compare to:
a = [1]
print(id(a))
a += [2]
print(id(a))
>> 36641608
36641608
Note a is still referencing the same object in this case.

Python slice without copy? [duplicate]

This question already has answers here:
Can I create a "view" on a Python list?
(10 answers)
Closed 8 years ago.
Is there a way to create a "slice view" of a sequence in Python 3 that behaves like a regular slice but does not create a copy of the sliced part of the sequence? When the original sequence is updated, the "slice view" should reflect the update.
>>> l = list(range(100))
>>> s = Slice(l, 1, 50, 3) # Should behave like l[1:50:3]
>>> s[1]
4
>>> l[4] = 'foo'
>>> s[1] # Should reflect the updated value
'foo'
I can write my own Slice class that does this but I wanted to find out if there was a built-in way.
Use islice from itertools library
EDIT:
I see where I misunderstood the question.
Well, there is no such thing. If you want to create your class, you'll have to:
Keep a reference to the original list in you Slice class
Define, __iter__, __getitem__ and __setitem__ methods to work on the original list with index conversion

How to get a copy from a list? [duplicate]

This question already has answers here:
How do I clone a list so that it doesn't change unexpectedly after assignment?
(24 answers)
Closed 9 years ago.
I am trying to get an element from list and make some change on this element (which is also a list). Weirdly, the change applied on the previous list. Here is my code:
>>>sentences[0]
['<s>/<s>',
'I/PRP',
'need/VBP',
'to/TO',
'have/VB',
'dinner/NN',
'served/VBN',
'</s>/</s>']
>>>sentence = sentences[0]
>>>sentence.insert(0,startc); sentence.append(endc)
>>>sentences[0]
['<s>/<s>',
'<s>/<s>',
'I/PRP',
'need/VBP',
'to/TO',
'have/VB',
'dinner/NN',
'served/VBN',
'</s>/</s>'
'</s>/</s>']
It is like I just got a pointer to that element, not a copy
You do get a "pointer", in fact. Lists (and any mutable value type!) are passed around as reference in Python.
You can make a copy of a list by passing it to the list() object constructor, or by making a full slice using [:].
a = [1,2,3]
b = a
c = list(a)
d = a[:]
a[1] = 4 # changes the list referenced by both 'a' and 'b', but not 'c' or 'd'
You're exactly right! In Python, when you pass a list as an argument to a function, or you assign a list to another variable, you're actually passing a pointer to it.
This is for efficiency reasons; if you made a separate copy of a 1000-item list every time you did one of the aforementioned things, the program would consume way too much memory and time.
To overcome this in Python, you can duplicate a one-dimensional list using = originalList[:] or = list(originalList):
sentence = sentences[0][:] # or sentence = list(sentences[0])
sentence.insert(0,startc)
sentence.append(endc)
print(sentence) # modified
print(sentences[0]) # not modified
Consider using list comprehension if you need to duplicate a 2D list.

Categories