What's this syntax in python mean? - python

I have the following code in python:
a = "xxx" # a is a string
b = "yyy" # b is another string
for s in a, b:
t = s[:]
...
I dont understand the meaning of for line. I know a, b returns a tuple. But what about looping through a, b? And why you need t = s[:]. I know s[:] creates a copy of list. But if s is a string, why don't you write t = s to make a copy of the string s into t?
Thank you.

The meaning of the for loop is to iterate over the tuple (a, b). So the loop body will run twice, once with s equal to a and again equal to b.
t = s[:]
On the face of it, this creates a copy of the string s, and makes t a reference to that new string.
However strings are immutable, so for most purposes the original is as good as a copy. As an optimization, Python implementations are allowed to just re-use the original string. So the line is likely to be equivalent to:
t = s
That is to say, it will not make a copy. It will just make t refer to the same object s refers to.

Related

Using range function in Python

I am new to learning Python and have a question about using the range function to iterate a string.
Let's say I need to capitalize everything in the following string:
string = 'a b c d e f g'
Can I just write the following code?
for i in string:
i = i.upper()
return string
Or should I use the range function to iterate every element in the string?
Finally, a more general question is whenever I need to iterate all elements in a string/list, when should I use the range function and when can I just use the "for" loop?
Strings are a bad example, because strings in Python cannot be changed. You have to build a new one:
new = ''
for i in string:
new += i.upper()
For the sake of example, we're all going to ignore the fact that new = string.upper() would do this in one statement.
In general, when you iterate through an object, you are handed references to the members. You can't change the container object, but if the inner object is mutable, you can change it. Consider this silly example:
a = [[1],[2],[3],[4]]
for element in a:
element[0] *= 2
This will result in a being [[2],[4],[6],[8]], because we are allowed to modify those inner lists. We can't change the outer list.
AS A GENERAL RULE, if you find yourself writing for i in range(len(xxx)):, then there is a better way to do it. It isn't ALWAYS true, but it's a key Python learning point.

Why is python treating lists like this when defining a function?

I was making a code, and variables started to behave strangely and get assigned to things which I thought they shouldn't. So, I decided to reduce the situation to minimal complexity in order to solve my doubts, and this is what happened:
The following code:
a = [2]
def changeA(c):
d = c
d[0] = 10
return True
changeA(a)
print(a)
prints '[10]'. This doesn't make sense to me, since I never assigned the list "a" to be anything after the first assignment. Inside the function changeA, the local variable d is assigned to be the input of the function, and it seems to me that this assignment is happening both ways, and even changing the "outside". If so, why? If not, why is this happening?
I've also noticed that the code
a = [2]
def changeA(c):
d = list(c)
d[0] = 10
return True
changeA(a)
print(a)
behaves normally (i.e., as I would expect).
EDIT: This question is being considered a duplicate of this one. I don't think this is true, since it is also relevant here that the locality character of procedures inside a function is being violated.
Python variables are references to objects, and some objects are mutable. Numbers are not, neither are strings nor tuples, but lists, sets and dicts are.
Let us look at the following Python code
a = [2] # ok a is a reference to a mutable list
b = a # b is a reference to the exact same list
b[0] = 12 # changes the value of first element of the unique list
print(a) # will display [12]
In the first example, you simply pass the reference of c(which is a) to d.
So whatever you do to d will happen on a.
In the second example, you copy the value of c(which is a) and give it to a new variable d.
So the d now has the same value as c(which is a) but different reference.
Note: you can see the reference or id of a variable using the id() function.
a = [2]
print id(a)
def changeA(c):
d = c
pirnt id(d)
d[0] = 10
return True
changeA(a)
print(a)
a = [2]
print id(a)
def changeA(c):
d = list(c)
print id(d)
d[0] = 10
return True
changeA(a)
print(a)
Its because when you do:
d = list(c)
that creates a new object. But when you do
d = c
You are making a reference to that object.
if you did
d.append(5)
to the first example you would get
[10,5]
Same operation to the second one and the list isn't modified.
Deeper explanation in the following link: http://henry.precheur.org/python/copy_list
In Python, names refer to values, so you have 2 names pointing to the same value.
In version 1 you create a list a, pass it in to the function under the pseudonym c, create another pseudonym d for the very same list, and then change the first value in that list. a, c, and d all refer to the same list.
In version 2, you're using list(c) which, to Python, means "take the contents of this iterable thing named c and make a new, different, list from it named d". Now, there are two copies of your list floating around. One is referred to as a or c, and the other is d. Therefore, when you update d[0] you're operating a second copy of the list. a remains unchanged.
'd=c' means reference copy as stated before. What it means, is that now d will reference the same object as c. As you are doing a direct manipulation on the referenced object, the value of the object a was referencing to is changed as well.
When you do 'd = list(c)' what it means that a new list object is created, with the baked of c. However, d is not referencing the same object as a anymore. Hence, the changes within the function doesn't impact a.

Unclarity on variable cloning behavior

Along with a book I was provided with a Python program, into which I am digging deep now.
The program uses a global data structure named globdat, in a specific routine a numpy array inside globdat is assigned to a local variable:
a = globdat.array
Then in a following while loop the variable a is updated every iteration according to:
a[:] += da[:]
The result of this operation is that globdat.array is updated, which is used in subsequent operations.
Is the usage of [:] required here, or is it merely used to indicate that it also clones into globdat.array? Can anyone clarify this coding style?
The second [:], in the right-hand-side, is redundant. It just copies da before using it in the concatenation, which is pointless.
We're left with:
a[:] += da
First, let's understand what a += da does. It maps to:
a = a.__iadd__(da)
The call to __iadd__ extends the original list a, and returns self, i.e. a reference to the list. The assignment which happens after, then, has no effect in this case (same as a=a).
This achieves the original goal, i.e. to extend the global array.
Now, What does a[:] += da do? It maps to:
a[:] = a[:].__iadd__(da)
Or more tediously to:
a.__setitem__(slice(None), a.__getitem__(slice(None)).__iadd__(da))
For readability, let's write it as (not a valid python syntax):
a.__setitem__(:, a.__getitem__(:).__iadd__(da))
So a[:].__iadd__(da):
creates a copy of a (call is a2)
concatenate da to a2 in place
return a2
Then the assignment a[:] = ...:
replaces all values in a with all values in a2 in place.
So that too, achieves the original goal, but is wayyyyyy less efficient.
There are some interesting details about this stuff in the answers to this question.
This statement is rather nasty:
a[:] += da[:]
It translates into this:
a.__setitem__(slice(None),
a.__getitem__(slice(None)).__iadd__(da.__getitem__(slice(None))))
This makes unnecessary copies of both lists.
Assuming a and da are lists, you could much more reasonably use the extend() method:
a.extend(da)
If you want to modify a list in place, rather than replace it with a new list, you need to use the slicing syntax.
a[:] = da[:]
In this case though, += will always modify the list in place, so the slicing is redundant.
This might be a perfect example of cargo cult programming.

Shared References and Equality

Using Python 3.4 and working through examples in a book by O'Reily.
The example shows:
A = ['spam']
B = A
B[0] = 'shrubbery'
Result after running print A:
'shrubbery'
Now my thought process is thatA was defined but never changed.
This example yields a different result
A = 'string'
B = A
B = 'dog'
This is the result after running print A:
'string'
Can someone explain?
In the first example, you are modifying the list referenced by B.
Doing:
B[0] = 'shrubbery'
tells Python to set the first item in the list referenced by B to the value of 'shrubbery'. Moreover, this list happens to be the same list that is referenced by A. This is because doing:
B = A
causes B and A to each refer to the same list:
>>> A = ['spam']
>>> B = A
>>> A is B
True
>>>
So, any changes to the list referenced by B will also affect the list referenced by A (and vice-versa) because they are the same object.
The second example however does not modify anything. Instead, it simply reassigns the name B to a new value.
Once this line is executed:
B = 'dog'
B no longer references the string 'string' but rather the new string 'dog'. The value of A meanwhile is left unchanged.
As is the case in most modern dynamic languages, variables in python are actually references which are sort of like C pointers. This means that when you do something like A = B (where A and B are both variables), you simply make A point to the same location in memory as B.
In the first example you are mutating (modifying) an existing object in place -- this is what the variable_name[index/key] = value syntax does. Both A and B continue to point at the same thing, but this things first entry is now 'shrubbery', instead of 'spam'.
In the second example, you make B point at a different (new at this point) object when you say B = 'dog'.
I hope you could understand it with this way :-)
As you see in first method, both of them refers to same list, second one different.So in second way changes not effects on another one.
Mutable objects are Lists while Strings are immutable that's why you can change the memory address and the lists itself but not the string.
We are talking here about shared references and mutable / immutable objects . When you do B = A, both variables points to same memory address ( shared reference) .
First case , list is a mutable object ( it's state can be change ) but object memory address remains the same . So if you change it's state , then the other variable will see those changes as it points to same memory address .( A and B have same value as they point to same object in memory )
Second case , string is immutable ( you cannot change it ) .By doing
B = 'dog' , basically you create another object and now B points to another object ( another memory address ) . In this case A still points to same old memory reference ( A and B have different values )
Here are the differences between the two:
Here's a step by step analysis:
A = ['spam']
"A points to a list whose first element, or A[0], is 'spam'."
B = A
"B points to what A points to, which is the same list."
B[0] = 'shrubbery'
"When we set B[0] to 'shrubbery', the result can be observed in the diagram.
A[0] is set to 'shrubbery' as well."
print (A):
A = 'string'
"A points to 'string'."
B = A
"B points to what A points to, which is 'string'."
B = 'dog'
"Oh look! B points to another string, 'dog', now.
So does what A points to change? No."
The result can be observed in the diagram.
print (A):

How does Python referencing work?

I am confused with Python referencing. Consider the following example:
My task : To edit each element in the list
d = { 'm': [1,2,3] }
m = d['m']
m = m[1:] # m changes its reference to the new sliced list, edits m but not d (I wanted to change d)
Similarly:
d = { 'm': [1,2,3] }
m = d['m']
m = m[0] # As per python referencing, m should be pointing to d['m'] and should have edited d
In python everything goes by reference, then when is a new object created?
Do we always need copy and deepcopy from copy module to make object copies?
Please clarify.
In Python a variable is not a box that holds things, it is a name that points to an object. In your code:
d = { 'm': [1,2,3] } --> binds the name d to a dictionary
m = d['m'] --> binds the name m to a list
m = m[1:] --> binds the name m to another list
Your third line is not changing m itself, but what m is pointing to.
To edit the elements in the list what you can do is:
m = d['m']
for i, item in enumerate(m):
result = do_something_with(item)
m[i] = result
Ethan Furman did an excellent job of explaining how Python internals work, I won't repeat it.
Since m really does represent the list inside the dictionary, you can modify it. You just can't reassign it to something new, which is what happens when you use = to equate it to a new slice.
To slice off the first element of the list for example:
>>> m[0:1] = []
>>> d
{'m': [2, 3]}
In python everything goes by reference
In Python, everything is a reference, and the references get passed around by value.
If you want to use those terms. But those terms make things harder to understand.
Much simpler: in Python, a variable is a name for an object. = is used to change what object a name refers to. The left-hand side can refer to part of an existing object, in which case the whole object is changed by replacing that part. This is because the object, in turn, doesn't really contain its parts, but instead contains more names, which can be caused to start referring to different things.
then when is a new object created ?
Objects are created when they are created (by using the class constructor, or in the case of built-in types that have a literal representation, by typing out a literal). I don't understand how this is relevant to the rest of your question.
m = m[1:] # m changes its reference to the new sliced list
Yes, of course. Now m refers to the result of evaluating m[1:].
edits m but not d (I wanted to change d)
Yes, of course. Why would it change d? It wasn't some kind of magic, it was simply the result of evaluating d['m']. Exactly the same thing happens on both lines.
Let's look at a simpler example.
m = 1
m = 2
Does this cause 1 to become 2? No, of course not. Integers are immutable. But the same thing is happening: m is caused to name one thing, and then to name another thing.
Or, another way: if "references" were to work the way you expect, then the line m = m[1:] would be recursive. You're expecting it to mean "anywhere that you see m, treat it as if it meant m[1:]". Well, in that case, m[1:] would actually mean m[1:][1:], which would then mean m[1:][1:][1:], etc.

Categories