why does not python3 for loop overwrite variables? - python-3.x

for beginner type question but can somebody tell me why does python 3 dont overwrite the below variable
in this line output = output + s1/output = output + s2
in short can anyone tell inner workings of this==>output = output + s2
is it getting assign ref variable? or storing the value in it?
s=input("enter your string: ") #A132BC
S1=''
S2=''
output=''
for x in s:
if x.isalpha():
S1=S1 + x
else:
S2=S2 + x
for s1 in sorted(S1):
output = output + s1 #output = '' + A==> output = 'A'
for s2 in sorted(S2):
output = output + s2
print(output)

In Python everything is an object that is treated as references.
S1 = "" creates the value "" and stores the reference into the name S1. If later you do S1 = "Hello, world!, the interpreter creates a new value an assigns this new reference to S1.
The same behaviour occurs for output, at first it stores a reference to "" and when you do output = output + s1 a new value that is the concatenation of output and s1 is created and the new reference is assigned to output.
Not that this can be written in a more concise way as output += s1.
This link contains useful stuff about Python types an assignment.
Note
As pointed out in the comments there is some subtleties with assignments, you can read [this](However, I'll update my answer concerning a missing note about mutable/immutable behaviour.) blog post for even more details.
An interesting and quite misleading behaviour for beginners pointed out is that there are differences in the use of operator +=.
For int, str or bool this operator effectively creates a new value from the old one, the original value is not mutated.
For sequences this operator behaves has a mutating operator.
For instance compare the output of those two functions:
def modify(int_value):
int_value += 1 # Does not add 1 in-place
def modify(array):
array += [1] # Mutates the array in-place

Related

How can i optimise my code and make it readable?

The task is:
User enters a number, you take 1 number from the left, one from the right and sum it. Then you take the rest of this number and sum every digit in it. then you get two answers. You have to sort them from biggest to lowest and make them into a one solid number. I solved it, but i don't like how it looks like. i mean the task is pretty simple but my code looks like trash. Maybe i should use some more built-in functions and libraries. If so, could you please advise me some? Thank you
a = int(input())
b = [int(i) for i in str(a)]
closesum = 0
d = []
e = ""
farsum = b[0] + b[-1]
print(farsum)
b.pop(0)
b.pop(-1)
print(b)
for i in b:
closesum += i
print(closesum)
d.append(int(closesum))
d.append(int(farsum))
print(d)
for i in sorted(d, reverse = True):
e += str(i)
print(int(e))
input()
You can use reduce
from functools import reduce
a = [0,1,2,3,4,5,6,7,8,9]
print(reduce(lambda x, y: x + y, a))
# 45
and you can just pass in a shortened list instead of poping elements: b[1:-1]
The first two lines:
str_input = input() # input will always read strings
num_list = [int(i) for i in str_input]
the for loop at the end is useless and there is no need to sort only 2 elements. You can just use a simple if..else condition to print what you want.
You don't need a loop to sum a slice of a list. You can also use join to concatenate a list of strings without looping. This implementation converts to string before sorting (the result would be the same). You could convert to string after sorting using map(str,...)
farsum = b[0] + b[-1]
closesum = sum(b[1:-2])
"".join(sorted((str(farsum),str(closesum)),reverse=True))

Strings in Python are immutable? [duplicate]

My understanding was that Python strings are immutable.
I tried the following code:
a = "Dog"
b = "eats"
c = "treats"
print a, b, c
# Dog eats treats
print a + " " + b + " " + c
# Dog eats treats
print a
# Dog
a = a + " " + b + " " + c
print a
# Dog eats treats
# !!!
Shouldn't Python have prevented the assignment? I am probably missing something.
Any idea?
First a pointed to the string "Dog". Then you changed the variable a to point at a new string "Dog eats treats". You didn't actually mutate the string "Dog". Strings are immutable, variables can point at whatever they want.
The string objects themselves are immutable.
The variable, a, which points to the string, is mutable.
Consider:
a = "Foo"
# a now points to "Foo"
b = a
# b points to the same "Foo" that a points to
a = a + a
# a points to the new string "FooFoo", but b still points to the old "Foo"
print a
print b
# Outputs:
# FooFoo
# Foo
# Observe that b hasn't changed, even though a has.
The variable a is pointing at the object "Dog". It's best to think of the variable in Python as a tag. You can move the tag to different objects which is what you did when you changed a = "dog" to a = "dog eats treats".
However, immutability refers to the object, not the tag.
If you tried a[1] = 'z' to make "dog" into "dzg", you would get the error:
TypeError: 'str' object does not support item assignment"
because strings don't support item assignment, thus they are immutable.
Something is mutable only when we are able to change the values held in the memory location without changing the memory location itself.
The trick is: If you find that the memory location before and after the change are the same, it is mutable.
For example, list is mutable. How?
>> a = ['hello']
>> id(a)
139767295067632
# Now let's modify
#1
>> a[0] = "hello new"
>> a
['hello new']
Now that we have changed "a", let's see the location of a
>> id(a)
139767295067632
so it is the same as before. So we mutated a. So list is mutable.
A string is immutable. How do we prove it?
> a = "hello"
> a[0]
'h'
# Now let's modify it
> a[0] = 'n'
----------------------------------------------------------------------
we get
TypeError: 'str' object does not support item assignment
So we failed mutating the string. It means a string is immutable.
In you reassigning, you change the variable to point to a new location itself. Here you have not mutated the string, but mutating the variable itself. The following is what you are doing.
>> a = "hello"
>> id(a)
139767308749440
>> a ="world"
>> id(a)
139767293625808
id before and after reassignment is different, so it this proves that you are actually not mutating, but pointing the variable to new location. Which is not mutating that string, but mutating that variable.
Consider:
>>> a='asdf'
>>> a.__repr__
<method-wrapper '__repr__' of str object at 0x1091aab90>
>>> a='asdf'
>>> a.__repr__
<method-wrapper '__repr__' of str object at 0x1091aab90>
>>> a='qwer'
>>> a.__repr__
<method-wrapper '__repr__' of str object at 0x109198490>
Notice that the hex memory location did not change when I stored the same value in the variable twice. It did change when I stored a different value. The string is immutable. Not because of zealotry, but because you pay the performance penalty of creating a new object in memory. The variable a is just a label pointing to that memory address. It can be altered to point to anything.
A variable is just a label pointing to an object. The object is immutable, but you can make the label point to a completely different object if you want to.
The statement a = a + " " + b + " " + c can be broken down based upon pointers.
a + " " says give me what a points to, which can't be changed, and add " " to my current working set.
memory:
working_set = "Dog "
a = "Dog"
b = "eats"
c = "treats"
+ b says give me what b points to, which can't be changed, and add it to current working set.
memory:
working_set = "Dog eats"
a = "Dog"
b = "eats"
c = "treats"
+ " " + c says add " " to the current set. Then give me what c points to, which can't be changed, and add it to current working set.
memory:
working_set = "Dog eats treats"
a = "Dog"
b = "eats"
c = "treats"
Finally, a = says set my pointer to point to the resulting set.
memory:
a = "Dog eats treats"
b = "eats"
c = "treats"
"Dog" is reclaimed, because no more pointers connect to it's chunk of memory. We never modified the memory section "Dog" resided in, which is what is meant by immutable. However, we can change which labels, if any, point to that section of memory.
There is a difference between data and the label it is associated with. For example when you do
a = "dog"
the data "dog" is created and put under the label a. The label can change but what is in the memory won't. The data "dog" will still exist in memory (until the garbage collector deletes it) after you do
a = "cat"
In your programm a now ^points to^ "cat" but the string "dog" hasn't changed.
l = [1,2,3]
print id(l)
l.append(4)
print id(l) #object l is the same
a = "dog"
print id(a)
a = "cat"
print id(a) #object a is a new object, previous one is deleted
Python strings are immutable. However, a is not a string: it is a variable with a string value. You can't mutate the string, but can change what value of the variable to a new string.
Python string objects are immutable.
Example:
>>> a = 'tanim'
>>> 'Address of a is:{}'.format(id(a))
'Address of a is:64281536'
>>> a = 'ahmed'
>>> 'Address of a is:{}'.format(id(a))
'Address of a is:64281600'
In this example we can see that when we assign different value in a it doesn't modify.A new object is created.
And it can't be modified.
Example:
>>> a[0] = 'c'
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
**TypeError**: 'str' object does not support item assignment
A error occurs.
Variables can point to anywhere they want..
An error will be thrown if you do the following:
a = "dog"
print a #dog
a[1] = "g" #ERROR!!!!!! STRINGS ARE IMMUTABLE
'mutable' means that we can change the content of the string,
'immutable' means that we can't add an extra string.
a = 'dog'
address = id(a)
print(id(a))
a = a + 'cat'
print(id(a)) #Address changes
import ctypes
ctypes.cast(address, ctypes.py_object).value #value at old address is intact
>>> a = 'dogs'
>>> a.replace('dogs', 'dogs eat treats')
'dogs eat treats'
>>> print a
'dogs'
Immutable, isn't it?!
The variable change part has already been discussed.
Consider this addition to your example
a = "Dog"
b = "eats"
c = "treats"
print (a,b,c)
#Dog eats treats
d = a + " " + b + " " + c
print (a)
#Dog
print (d)
#Dog eats treats
One of the more precise explanations I found in a blog is:
In Python, (almost) everything is an object. What we commonly refer to as "variables" in Python are more properly called names. Likewise, "assignment" is really the binding of a name to an object. Each binding has a scope that defines its visibility, usually the block in which the name originates.
Eg:
some_guy = 'Fred'
# ...
some_guy = 'George'
When we later say some_guy = 'George', the string object containing 'Fred' is unaffected. We've just changed the binding of the name some_guy. We haven't, however, changed either the 'Fred' or 'George' string objects. As far as we're concerned, they may live on indefinitely.
Link to blog: https://jeffknupp.com/blog/2012/11/13/is-python-callbyvalue-or-callbyreference-neither/
Adding a bit more to above-mentioned answers.
id of a variable changes upon reassignment.
>>> a = 'initial_string'
>>> id(a)
139982120425648
>>> a = 'new_string'
>>> id(a)
139982120425776
Which means that we have mutated the variable a to point to a new string. Now there exist two string(str) objects:
'initial_string' with id = 139982120425648
and
'new_string' with id = 139982120425776
Consider the below code:
>>> b = 'intitial_string'
>>> id(b)
139982120425648
Now, b points to the 'initial_string' and has the same id as a had before reassignment.
Thus, the 'intial_string' has not been mutated.
The built-in function id() returns the identity of an object as an integer. This integer usually corresponds to the object’s location in memory.
\>>a='dog'
\>>print(id(a))
139831803293008
\>>a=a+'cat'
\>>print(id(a))
139831803293120
Initially, 'a' is stored in 139831803293008 memory location, as the string object is immutable in python if you try to modify and reassign the reference will be removed and will be a pointer to a new memory location(139831803293120).
Summarizing:
a = 3
b = a
a = 3+2
print b
# 5
Not immutable:
a = 'OOP'
b = a
a = 'p'+a
print b
# OOP
Immutable:
a = [1,2,3]
b = range(len(a))
for i in range(len(a)):
b[i] = a[i]+1
This is an error in Python 3 because it is immutable. And not an error in Python 2 because clearly it is not immutable.
We r just concatenate the two string values. We never change the value of (a). Just now (a) represent another memory block that has "dogdog" value. Because in the backend, one variable never represent two memory blocks at same time. The value of (a) before concatenation was "dog". But after that (a) represent the "dogdog", because now (a) in backend rep. the block that has "dogdog" value. And "dog" is rep. by (b) and "dog" isn't counted as garbage value until (b) represent the "dog".
The confusion is we represent the memory blocks(that contain data or info.) in backend with same variable name.
You can make a numpy array immutable and use the first element:
numpyarrayname[0] = "write once"
then:
numpyarrayname.setflags(write=False)
or
numpyarrayname.flags.writeable = False
This image gives the answer. Please read it.

How to edit lists using input function

I am trying to edit and fill the lists in A, B and C using the input function.
A = ['']
B = ['']
C = ['']
key = input()
key[0] = 'X'
But i get this error.
TypeError: 'str' object does not support item assignment
How can i use the input function to edit my list. Or you might have better way to do this?
Thank You!
The strings per se in Python are immutable (you can't modify its content) however what you can do is to store them in a char list and a list in python is a mutable object, hence you can modify the char list as you see fit.
key = input() # "hello"
new_key = list(key) # ['h','e','l','l','o']
new_key[0] = 'X' # ['X','e','l','l','o']
Python strings are immutable objects which means you cannot change an existing string. The best you can do is create a new string that is a variation on the original. For e.g :
newstring = 'newchar' + oldstring[1:]
In your case it would be something like:
key = input()
newkey = 'X' +key[1:]
Solved. instead of using lists, better use dictionary to fill the A, B or C.
var = {'A':' ', 'B':' ', 'C':' '}
var[input()] = 'X'
Thanks for all of your participation!

Switching positions of two strings within a list

I have another question that I'd like input on, of course no direct answers just something to point me in the right direction!
I have a string of numbers ex. 1234567890 and I want 1 & 0 to change places (0 and 9) and for '2345' & '6789' to change places. For a final result of '0678923451'.
First things I did was convert the string into a list with:
ex. original = '1234567890'
original = list(original)
original = ['0', '1', '2' etc....]
Now, I get you need to pull the first and last out, so I assigned
x = original[0]
and
y = original[9]
So: x, y = y, x (which gets me the result I'm looking for)
But how do I input that back into the original list?
Thanks!
The fact that you 'pulled' the data from the list in variables x and y doesn't help at all, since those variables have no connection anymore with the items from the list. But why don't you swap them directly:
original[0], original[9] = original[9], original[0]
You can use the slicing operator in a similar manner to swap the inner parts of the list.
But, there is no need to create a list from the original string. Instead, you can use the slicing operator to achieve the result you want. Note that you cannot swap the string elements as you did with lists, since in Python strings are immutable. However, you can do the following:
>>> a = "1234567890"
>>> a[9] + a[5:9] + a[1:5] + a[0]
'0678923451'
>>>

How do you modify a variable that's a value in a dictionary when calling that variable by its key?

n = 3
d = {'x':n}
d['x'] += 1
print(n)
When I run it, I get
3
How do I make n = 4?
You can't do this, at least, not in any simple way.
The issue is very similar when you're just dealing with two variables bound to the same object. If you rebind one of them with an assignment, you will not see the new value through the other variable:
a = 3
b = a
a += 1 # binds a to a new integer, 4, since integers are immutable
print(b) # prints 3, not 4
One exception is if you are not binding a new value to the variable, but instead modifying a mutable object in-place. For instance, if instead of 1 you has a one-element list [1], you could replace the single value without creating a new list:
a = [3]
b = a
a[0] += 1 # doesn't rebind a, just mutates the list it points to
print(b[0]) # prints 4, since b still points to the same list as a
So, for your dictionary example you could take a similar approach and have n and your dictionary value be a list or other container object that you modify in-place.
Alternatively, you could store the variable name "n" in your dictionary and then rather than replacing it in your other code, you could use for a lookup in the globals dict:
n = 3
d = {"x": "n"} # note, the dictionary value is the string "n", not the variable n's value
globals()[d["x"]] += 1
print(n) # this actually does print 4, as you wanted
This is very awkward, of course, and only works when n is a global variable (you can't use the nominally equivalent call to locals in a function, as modifying the dictionary returned by locals doesn't change the local variables). I would not recommend this approach, but I wanted to show it can be done, if only badly.
You could use a class to contain the data values to enable additions. Basically you are creating a mutable object which acts as an integer.
It is a work around, but lets you accomplish what you want.
Note, that you probably need to override a few more Python operators to get full coverage:
class MyInt(object):
val = 0
def __init__(self,val):
self.val = val
def __iadd__(self,val):
self.val = self.val + val
def __repr__(self):
return repr(self.val)
n = MyInt(3)
print(n)
d = {'x':n}
d['x'] += 1
print(n)

Resources