Reputation: 2281
I'm confused on what an immutable type is. I know the float
object is considered to be immutable, with this type of example from my book:
class RoundFloat(float):
def __new__(cls, val):
return float.__new__(cls, round(val, 2))
Is this considered to be immutable because of the class structure / hierarchy?, meaning float
is at the top of the class and is its own method call. Similar to this type of example (even though my book says dict
is mutable):
class SortedKeyDict(dict):
def __new__(cls, val):
return dict.__new__(cls, val.clear())
Whereas something mutable has methods inside the class, with this type of example:
class SortedKeyDict_a(dict):
def example(self):
return self.keys()
Also, for the last class(SortedKeyDict_a)
, if I pass this type of set to it:
d = (('zheng-cai', 67), ('hui-jun', 68),('xin-yi', 2))
without calling the example
method, it returns a dictionary. The SortedKeyDict
with __new__
flags it as an error. I tried passing integers to the RoundFloat
class with __new__
and it flagged no errors.
Upvotes: 211
Views: 291442
Reputation: 31
in python every datatype is a class (everything is object)
var1 = 5
var1
is the variable, a name tag pointing to an object(holds the object address, like a pointer in c)
id(var1) #returns the memory address storing int type object with value 5
5
is the object instance of class int stored on memory
if i perform some operations on var1
var1 += 3
int ,float, string, tuple, ... are immutable. they cant change value after creation. this will not change the object 5 stored on memory which var1 is currently pointing to, instead it just take the value 5 of this object, add 3 making 8, then create a new int object with value 8 stored at different location and redirect var1 to the memory location of the new object
var1 = 5
print(id(var1))
var1 += 3
print(id(var1))
this prints two different memory address storing int obj 5 and 8 respectively
for mutables types list, set, dictionary, ...
var1 = [1, 2, 3]
print(id(var1))
var1 += [4,5]
print(id(var1))
this prints same memory address, showing var1
is still pointing to the same memory address holding an object of list type [1, 2, 3]
at one point in time then holding same object with added elements [1, 2, 3, 4, 5]
at the last print
this way, it does not create new instance but the same obj instance was mutated to include the added elements.
immutable objects memory management
since this object can't change value after creation, changing anything means creating a new object. python avoid creating duplicate objects by Interning. it has a list of objects ,objects types and its values that are in use or in memory, if you define new variable to a new immutable object of a value,it will check its objects list in memory, if it has object of this type with same value in memory, it just point the new variable assigned, to the obj in memory with the same value instead of creating a new one with same value. multiple variable reuse same object in memory without any side effect to the variables referencing them, since the object value can't change.
var1 = 1006
print(id(var1))
var2 = 1006
print(id(var2))
it will print same address as both are pointing to same object even though you define the variable individually, not by aliasing var1 = var2 = 1006
and its of immutable type
it keeps track of this object and the variables that are pointing to it, if no variable is referencing it anymore, it is passed to the garbage collector for deletion
In Fact in the default implementation of Python CPython, small integers (usually within the range of -5 to 256) are cached and reused. This optimization is known as "integer interning" When Python starts up, it creates a pool of integer objects for small values (usually from -5 to 256). There is only one instance of each integer in the memory pool. When you create a variable and assign it a small integer value, Python checks if the value falls within the interned range. If it does, it reuses the existing object rather than creating a new one.
This behavior is an optimization strategy aimed at reducing memory consumption and improving performance for frequently used small integers.
Upvotes: 0
Reputation: 53165
Update: disregard my erroneous points in this answer. All variables are passed by reference. I'll fix this answer later. See this comment, and the comments below my answer: Immutable vs Mutable types
I'm choosing not to delete it while I fix it up so that I can get more feedback and corrections in the comments first.
Mutable types vs immutable types in Python
Mutable in plain English means changeable. Think: "mutation".
Immutable means unchangeable.
Python has no concept of constants. Immutable vs mutable does not mean constant vs not constant, respectively. Rather, it means immutable --> shared memory (a single underlying object in memory via dynamic memory allocation, for a given literal value assigned to variables) vs mutable --> not shared memory (multiple underlying objects in memory via dynamic memory allocation, for a given literal value assigned to variables), respectively. I have more on this below, as this is quite nuanced.
Everything in Python is an object. Even numbers, integers, float, etc. are objects. All variables are objects.
The mutable vs immutable objects types in Python are listed below.
Mutable types are passed by reference, and cause side effects.
If you do my_dict3 = my_dict2 = my_dict1 = {}
, then change my_dict3
, it does also change my_dict2
and my_dict1
. This is a side effect. It is because each variable points to the same underlying object (memory blob).
Again, if you chain-assign mutable types, each mutable variable points to the same underlying object in memory, since the value is passed from one variable to the next by reference:
my_dict1 = {"key": "value"}
# Copy **by reference**, so all variables point to the same
# underlying object.
my_dict2 = my_dict1
my_dict3 = my_dict2
Therefore, the following are all True
:
# Each of these is True because the underlying object is the same
# blob of memory.
print(my_dict3 is my_dict2) # True
print(my_dict2 is my_dict1) # True
print(my_dict3 is my_dict1) # True
# And each of these is True because all variables have the same value.
print(my_dict3 == my_dict2) # True
print(my_dict2 == my_dict1) # True
print(my_dict3 == my_dict1) # True
If you independently assign the same literal value to mutable types, however, each mutable variable points to its own underlying object in memory, since it is expected to be able to independently mutate the memory it points to:
# **Mutable type:** each variable has an **independent underlying
# object**, even though each of those underlying objects has the same
# value.
my_dict1 = {"key": "value"}
my_dict2 = {"key": "value"}
my_dict3 = {"key": "value"}
# Therefore, each of these is False because the underlying objects
# differ.
print(my_dict3 is my_dict2) # False
print(my_dict2 is my_dict1) # False
print(my_dict3 is my_dict1) # False
# But, each of these is True because all variables have the same value.
print(my_dict3 == my_dict2) # True
print(my_dict2 == my_dict1) # True
print(my_dict3 == my_dict1) # True
If you pass a mutable variable to a function, and that function modifies it, the modification will automatically be seen outside the function. This is a side effect:
def modify_dict(my_dict):
my_dict["new_key"] = "new_value"
my_dict1 = {"key": "value"}
modify_dict(my_dict1)
print(my_dict1) # prints: {"key": "value", "new_key": "new_value"}
To force a mutable type to be passed by value instead of by reference, you can call the .copy()
method to force a copy of the underlying object to be made.
my_dict1 = {"key": "value"}
# Force-copy **by value**, so each variable has its own underlying
# object. The `.copy()` method makes an entirely new copy of the
# underlying object.
my_dict2 = my_dict1.copy()
my_dict3 = my_dict2.copy()
# Therefore, each of these is False because the underlying objects
# differ.
print(my_dict3 is my_dict2) # False
print(my_dict2 is my_dict1) # False
print(my_dict3 is my_dict1) # False
# But, each of these is True because all variables have the same value.
print(my_dict3 == my_dict2) # True
print(my_dict2 == my_dict1) # True
print(my_dict3 == my_dict1) # True
Immutable types are passed by copy, and do not cause side effects.
If you do my_int3 = my_int2 = my_int1 = 1
, then change my_int3
, it does not change my_int2
or my_int1
, because that would be a side effect. It has no side effects.
However, if you assign the same value to multiple immutable variables, whether by chain assignment or independent literal assignment, the variables are both equal (var1 == var2
is True
) and they are the same (var1 is var2
is True
), as shown here:
## **Immutable types:** each variable apparently has the **same
# underlying object**, but side effects are not allowed
my_int1 = 7
my_int2 = 7
my_int3 = 7
# Therefore, each of these is True because the underlying objects are
# the same.
print(my_int3 is my_int2) # True
print(my_int2 is my_int1) # True
print(my_int3 is my_int1) # True
# And, each of these is also True because all variables have the
# same value.
print(my_int3 == my_int2) # True
print(my_int2 == my_int1) # True
print(my_int3 == my_int1) # True
# Try the test again, this time like this
my_int1 = 7
my_int2 = my_int1
my_int3 = my_int2
# Same as above: same underlying object, so each of these is True
print(my_int3 is my_int2) # True
print(my_int2 is my_int1) # True
print(my_int3 is my_int1) # True
# Same as above: same value, so each of these is True
print(my_int3 == my_int2) # True
print(my_int2 == my_int1) # True
print(my_int3 == my_int1) # True
print()
That's a bit confusing, but the immutability and lack of side effects holds true.
If you pass an immutable variable to a function, and that function modifies it, the modification will not be seen outside the function. There are no side effects. Rather, if you want to see the change outside the function, you must return the modified variable from the function, and reassign it outside the function.
def modify_int(my_int):
my_int += 1
return my_int
my_int1 = 7
# reassign the returned value to obtain the change from inside the
# function
my_int1 = modify_int(my_int1)
print(my_int1) # prints: 8
To force an immutable type to be passed by reference instead of by copy, you can simply wrap the immutable type inside of a mutable type, such as a list, and then pass the mutable wrapper to a function. This way, it gets passed by reference, and the side effect of modifying its contents is automatically visible outside the function:
def modify_immutable_type_hack(var_list):
var_list[0] += 1 # increment the number inside the first element
my_int = 10
my_int_list = [my_int]
print(my_int_list[0]) # 10
# Force an immutable type to act mutable by passing it inside a list,
# which is a mutable type, into a function. This way, the "side effect"
# of the change to the list being visible outside the function is still
# seen. This is because the list gets passed **by reference** instead
# of **by value.**
modify_immutable_type_hack(my_int_list)
print(my_int_list[0]) # 11
print(my_int) # 10
For single numbers, however, it's clearer to just use the return value method above, instead: my_int1 = modify_int(my_int1)
.
You can write a program to automatically identify whether a type is mutable or immutable by looking for and identifying side effects. I do this below.
Python is not an easy programming language. It has tons of nuances like this. It's just popular, different, and very high level.
All of the test code for the above learning is below.
Here's a list of the most common mutable and immutable objects in Python. This list can be obtained by 1) painstakingly searching Python's official "Built-in Types" reference page for the words "mutable" and "immutable", or by 2) asking GitHub Copilot (or BingAI or ChatGPT) for it. I did both. The latter is, of course, way faster, but requires verification. I verified and updated the list below based on my own findings, and added all quotes, mostly from the official documentation.
Mutable objects:
Immutable objects:
Stop here if you like. The above is the most important.
All variables can be reassigned in Python, whether they were previously assigned to mutable or immutable types. However, the behavior of reassignment is different for mutable and immutable types, and cannot be thought of purely in traditional C and C++-like memory terms and understanding. Python is Python, and Python is different.
Coming from C and C++ as my primary languages, the concept of "mutability" in Python is quite confusing. For this and other reasons, I do not consider Python to be an "easy" or "beginner" language. It is simply a very powerful "extra high-level" language, is all. It has a ton of very nuanced and confusing points. C is more straight-forward and concrete in this regard. (And C++ is just nuts).
In C and C++, my mental model of each variable is that it is a chunk of bytes in memory. When you do this:
int var = 0; // statically allocate bytes for `var`, and mutate them into a 0
var = 7; // mutate the bits in `var` into a 7 now instead of a 0
...you are changing the bytes in var
's chunk of memory from bits storing a 0
, to bits storing a 7
. You have "mutated" the memory allocated by that variable, meaning: the chunk of memory which is set apart for it. The type of the variable simply specifies the "lens" (think: looking through a magic glass lens that interprets bits into numbers, letters, etc.), or interpretation algorithm, through which you will interpret those bits (ex: float
, int
, char
, etc.).
In Python, however, that is not the mental model you should have for variables. In Python, think of variables as objects containing pointers to other objects, where everything is an object, and each object contains a bit specifying whether it is mutable or immutable, and mutable variables are passed by reference whereas immutable variables are passed by value. Note also that an object is a very sophisticated dynamically-allocated instance of a class, which manages its own state and chunk of memory, kind of like a std container in C++.
You can instantly see that types int
, bool
, float
, etc. are all classes (objects) in Python, not just trivial types like they are in C and C++, by doing this:
type(7) # returns <class 'int'>
type(True) # returns <class 'bool'>
type(1.0) # returns <class 'float'>
So, in Python, when you do this:
# dynamically allocate a pointer variable object named `var`, then
# dynamically allocate an integer object with a `0` in it, then point `var`
# to that int object which contains a `0`.
var = 0 # 0 is the value contained inside an immutable type `int` object
# dynamically allocate a new integer object with a `7` in it, then point `var`
# to that new, underlying int object which contains a `7`. Therefore,
# this simply re-assigns the variable `var` from pointing to the `0`
# object, to pointing to the `7` object; the `int` object with a `0`
# in it is now "orphaned" and I suspect will be garbage collected, but that
# level of understanding is beyond me.
var = 7
In the case of mutable objects, the reassignment modifies its bytes in-place rather than dynamically creating a new object and pointing the variable to it. For example:
my_dict = {} # dynamically allocate a pointer variable named `my_dict`,
# dynamically allocate a dict object, then point `my_dict` to
# that dict object
my_dict["some_key"] = "some_value" # mutate the dict object by adding a
# key-value pair
The details of how that "immutable" vs "mutable" characteristic is carried out aren't really important to the Python programmer like they would be to a low-level embedded systems C and C++ programmer like myself. Rather, it's sort of "hand-wavy". The Python programmer is supposed to just blindly accept it and move on. Some high-level C++ programmers are this way too. It's a mindset thing.
So, as long as you know the summary in section 1 above, and the list of mutable vs immutable objects in Python in section 2 above, you are good.
What a good Python programmer does need to know, however, is whether or not their particular variable type will be passed by reference or by copy when passed to a function. This is a very important distinction, as passing by reference causes side effects, which means that changes to the variable in one place will result to changes to that or other variables in other places, such as inside vs outside a function. This is a very important concept in Python, and to me is the main reason why the distinction between mutable and immutable types is important. I don't really care how they work under the hood otherwise.
Again:
is
vs ==
The operator ==
tests for equality of values stored within a variable. The operator is
, I think, is used to test if two variables refer to the same object in memory. Again, the nuances of how Python does this under-the-hood are beyond me. See my section 1 summary, however, for a bunch of nuanced cases of is
vs ==
.
The official Python documentation describes the is
and is not
operators under the "Identity comparisons" section of its documentation here: https://docs.python.org/3/reference/expressions.html#is-not:
The operators
is
andis not
test for an object’s identity:x is y
is true if and only ifx
andy
are the same object. An Object’s identity is determined using theid()
function.x is not y
yields the inverse truth value.
The id()
function says: https://docs.python.org/3/library/functions.html#id:
Return the “identity” of an object. This is an integer which is guaranteed to be unique and constant for this object during its lifetime. Two objects with non-overlapping lifetimes may have the same
id()
value.
So, I interpret this to mean that the id()
is a unique dynamic memory address, or index into a map to one, which is assigned to each object at object creation. When you do my_int3 = my_int2 = my_int1 = 7
, the id()
of all 4 of those parts is the same:
my_int3 = my_int2 = my_int1 = 7
print(id(my_int1)) # 140201501393328
print(id(my_int2)) # 140201501393328
print(id(my_int3)) # 140201501393328
print(id(7)) # 140201501393328
So, they all appear to be the same underlying object, or blob of memory.
Here's my test code. Nearly all of the behaviors in my test code were unpredictable to me, since Python is such a dramatically different language from C and C++, and I was unable guess the results of most of these tests prior to running them and doing this learning. I had just about everything wrong.
mutable_vs_immutable_types.py from my eRCaGuy_hello_world repo.
Note: the test code is too long to paste in this answer, as the Stack Overflow limit is 30000 chars. So, see it at the link above.
Sample run and output:
eRCaGuy_hello_world$ python/mutable_vs_immutable_types.py
var before modification: True
var is a bool
var after modification: False
var before modification: 1.0
var is a float
var after modification: 2.0
var before modification: 7
var is an int
var after modification: 8
var before modification: some words
var is a str (string)
var after modification: some words; some more words
var before modification: [7, 8, 9]
var is a list
var after modification: [7, 8, 9, 1]
var before modification: {'key1': 'value1', 'key2': 'value2'}
var is a dict
var after modification: {'key1': 'value1', 'key2': 'value2', 'new_key': 'new_value'}
var before modification: (7, 8, 9)
var is a tuple
var after modification: (1, 2, 3)
is_mutable(my_bool, True) --> immutable
is_mutable(my_float, 1.0) --> immutable
is_mutable(my_int, 7) --> immutable
is_mutable(my_str, "some words") --> immutable
is_mutable(my_list, [7, 8, 9]) --> mutable
is_mutable(my_dict, {"key1": "value1", "key2": "value2"}) --> mutable
is_mutable(my_tuple, (7, 8, 9)) --> immutable
int is immutable
list is mutable
MUTABLE TYPES
False
False
False
True
True
True
IMMUTABLE TYPES
True
True
True
True
True
True
integer types again
True
True
True
True
True
True
True
True
True
True
True
True
False
False
False
True
True
True
How to update immutable vs mutable variables in a function:
7
8
[7, 8, 9]
[7, 8, 9, 1]
10
11
10
Upvotes: -1
Reputation: 442
Every time we change value of a immutable variable it basically destroy the previous instance and create a new instance of variable class
var = 2 #Immutable data
print(id(var))
var += 4
print(id(var))
list_a = [1,2,3] #Mutable data
print(id(list_a))
list_a[0]= 4
print(id(list_a))
Output:
9789024
9789088
140010877705856
140010877705856
Note:Mutable variable memory_location is change when we change the value
Upvotes: 2
Reputation: 16186
Mutable object: Object that can be changed after creating it.
Immutable object: Object that cannot be changed after creating it.
In Python if you change the value of the immutable object it will create a new object.
Here are the objects in Python that are of mutable type:
list
Dictionary
Set
bytearray
user defined classes
Here are the objects in Python that are of immutable type:
int
float
decimal
complex
bool
string
tuple
range
frozenset
bytes
Question: Is string an immutable type?
Answer: yes it is, but can you explain this:
Proof 1:
a = "Hello"
a +=" World"
print a
Output
"Hello World"
In the above example the string got once created as "Hello" then changed to "Hello World". This implies that the string is of the mutable type. But it is not when we check its identity to see whether it is of a mutable type or not.
a = "Hello"
identity_a = id(a)
a += " World"
new_identity_a = id(a)
if identity_a != new_identity_a:
print "String is Immutable"
Output
String is Immutable
Proof 2:
a = "Hello World"
a[0] = "M"
Output
TypeError 'str' object does not support item assignment
Question: Is Tuple an immutable type?
Answer: yes, it is.
Proof 1:
tuple_a = (1,)
tuple_a[0] = (2,)
print a
Output
'tuple' object does not support item assignment
Upvotes: 31
Reputation: 4776
You have to understand that Python represents all its data as objects. Some of these objects like lists and dictionaries are mutable, meaning you can change their content without changing their identity. Other objects like integers, floats, strings and tuples are objects that can not be changed. An easy way to understand that is if you have a look at an objects ID.
Below you see a string that is immutable. You can not change its content. It will raise a TypeError
if you try to change it. Also, if we assign new content, a new object is created instead of the contents being modified.
>>> s = "abc"
>>> id(s)
4702124
>>> s[0]
'a'
>>> s[0] = "o"
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'str' object does not support item assignment
>>> s = "xyz"
>>> id(s)
4800100
>>> s += "uvw"
>>> id(s)
4800500
You can do that with a list and it will not change the objects identity
>>> i = [1,2,3]
>>> id(i)
2146718700
>>> i[0]
1
>>> i[0] = 7
>>> id(i)
2146718700
To read more about Python's data model you could have a look at the Python language reference:
Upvotes: 199
Reputation: 13622
Mutable means that it can change/mutate. Immutable the opposite.
Some Python data types are mutable, others not.
Let's find what are the types that fit in each category and see some examples.
Mutable
In Python there are various mutable types:
lists
dict
set
Let's see the following example for lists
.
list = [1, 2, 3, 4, 5]
If I do the following to change the first element
list[0] = '!'
#['!', '2', '3', '4', '5']
It works just fine, as lists are mutable.
If we consider that list, that was changed, and assign a variable to it
y = list
And if we change an element from the list such as
list[0] = 'Hello'
#['Hello', '2', '3', '4', '5']
And if one prints y
it will give
['Hello', '2', '3', '4', '5']
As both list
and y
are referring to the same list, and we have changed the list.
Immutable
In some programming languages one can define a constant such as the following
const a = 10
And if one calls, it would give an error
a = 20
However, that doesn't exist in Python.
In Python, however, there are various immutable types:
None
bool
int
float
str
tuple
Let's see the following example for strings
.
Taking the string a
a = 'abcd'
We can get the first element with
a[0]
#'a'
If one tries to assign a new value to the element in the first position
a[0] = '!'
It will give an error
'str' object does not support item assignment
When one says += to a string, such as
a += 'e'
#'abcde'
It doesn't give an error, because it is pointing a
to a different string.
It would be the same as the following
a = a + 'f'
And not changing the string.
Some Pros and Cons of being immutable
• The space in memory is known from the start. It would not require extra space.
• Usually, it makes things more efficiently. Finding, for example, the len()
of a string is much faster, as it is part of the string object.
Upvotes: 1
Reputation: 1
For immutable objects, assignment creates a new copy of values, for example.
x=7
y=x
print(x,y)
x=10 # so for immutable objects this creates a new copy so that it doesnot
#effect the value of y
print(x,y)
For mutable objects, the assignment doesn't create another copy of values. For example,
x=[1,2,3,4]
print(x)
y=x #for immutable objects assignment doesn't create new copy
x[2]=5
print(x,y) # both x&y holds the same list
Upvotes: -2
Reputation: 1
I haven't read all the answers, but the selected answer is not correct and I think the author has an idea that being able to reassign a variable means that whatever datatype is mutable. That is not the case. Mutability has to do with passing by reference rather than passing by value.
Lets say you created a List
a = [1,2]
If you were to say:
b = a
b[1] = 3
Even though you reassigned a value on B, it will also reassign the value on a. Its because when you assign "b = a". You are passing the "Reference" to the object rather than a copy of the value. This is not the case with strings, floats etc. This makes list, dictionaries and the likes mutable, but booleans, floats etc immutable.
Upvotes: -1
Reputation: 1625
The goal of this answer is to create a single place to find all the good ideas about how to tell if you are dealing with mutating/nonmutating (immutable/mutable), and where possible, what to do about it? There are times when mutation is undesirable and python's behavior in this regard can feel counter-intuitive to coders coming into it from other languages.
As per a useful post by @mina-gabriel:
Analyzing the above and combining w/ a post by @arrakëën:
What cannot change unexpectedly?
What can?
by "unexpectedly" I mean that programmers from other languages might not expect this behavior (with the exception or Ruby, and maybe a few other "Python like" languages).
Adding to this discussion:
This behavior is an advantage when it prevents you from accidentally populating your code with mutliple copies of memory-eating large data structures. But when this is undesirable, how do we get around it?
With lists, the simple solution is to build a new one like so:
list2 = list(list1)
with other structures ... the solution can be trickier. One way is to loop through the elements and add them to a new empty data structure (of the same type).
functions can mutate the original when you pass in mutable structures. How to tell?
Non-standard Approaches (in case helpful): Found this on github published under an MIT license:
For custom classes, @semicolon suggests checking if there is a __hash__
function because mutable objects should generally not have a __hash__()
function.
This is all I have amassed on this topic for now. Other ideas, corrections, etc. are welcome. Thanks.
Upvotes: 4
Reputation: 7401
The simplest answer:
A mutable variable is one whose value may change in place, whereas in an immutable variable change of value will not happen in place. Modifying an immutable variable will rebuild the same variable.
Example:
>>>x = 5
Will create a value 5 referenced by x
x -> 5
>>>y = x
This statement will make y refer to 5 of x
x -------------> 5 <-----------y
>>>x = x + y
As x being an integer (immutable type) has been rebuild.
In the statement, the expression on RHS will result into value 10 and when this is assigned to LHS (x), x will rebuild to 10. So now
x--------->10
y--------->5
Upvotes: 2
Reputation: 1
In Python, there's a easy way to know:
Immutable:
>>> s='asd'
>>> s is 'asd'
True
>>> s=None
>>> s is None
True
>>> s=123
>>> s is 123
True
Mutable:
>>> s={}
>>> s is {}
False
>>> {} is {}
Flase
>>> s=[1,2]
>>> s is [1,2]
False
>>> s=(1,2)
>>> s is (1,2)
False
And:
>>> s=abs
>>> s is abs
True
So I think built-in function is also immutable in Python.
But I really don't understand how float works:
>>> s=12.3
>>> s is 12.3
False
>>> 12.3 is 12.3
True
>>> s == 12.3
True
>>> id(12.3)
140241478380112
>>> id(s)
140241478380256
>>> s=12.3
>>> id(s)
140241478380112
>>> id(12.3)
140241478380256
>>> id(12.3)
140241478380256
It's so weird.
Upvotes: -2
Reputation: 51
It would seem to me that you are fighting with the question what mutable/immutable actually means. So here is a simple explenation:
First we need a foundation to base the explenation on.
So think of anything that you program as a virtual object, something that is saved in a computers memory as a sequence of binary numbers. (Don't try to imagine this too hard, though.^^) Now in most computer languages you will not work with these binary numbers directly, but rather more you use an interpretation of binary numbers.
E.g. you do not think about numbers like 0x110, 0xaf0278297319 or similar, but instead you think about numbers like 6 or Strings like "Hello, world". Never the less theses numbers or Strings are an interpretation of a binary number in the computers memory. The same is true for any value of a variable.
In short: We do not program with actual values but with interpretations of actual binary values.
Now we do have interpretations that must not be changed for the sake of logic and other "neat stuff" while there are interpretations that may well be changed. For example think of the simulation of a city, in other words a program where there are many virtual objects and some of these are houses. Now may these virtual objects (the houses) be changed and can they still be considered to be the same houses? Well of course they can. Thus they are mutable: They can be changed without becoming a "completely" different object.
Now think of integers: These also are virtual objects (sequences of binary numbers in a computers memory). So if we change one of them, like incrementing the value six by one, is it still a six? Well of course not. Thus any integer is immutable.
So: If any change in a virtual object means that it actually becomes another virtual object, then it is called immutable.
Final remarks:
(1) Never mix up your real-world experience of mutable and immutable with programming in a certain language:
Every programming language has a definition of its own on which objects may be muted and which ones may not.
So while you may now understand the difference in meaning, you still have to learn the actual implementation for each programming language. ... Indeed there might be a purpose of a language where a 6 may be muted to become a 7. Then again this would be quite some crazy or interesting stuff, like simulations of parallel universes.^^
(2) This explenation is certainly not scientific, it is meant to help you to grasp the difference between mutable and immutable.
Upvotes: 5
Reputation: 25180
A class is immutable if each object of that class has a fixed value upon instantiation that cannot SUBSEQUENTLY be changed
In another word change the entire value of that variable (name)
or leave it alone.
Example:
my_string = "Hello world"
my_string[0] = "h"
print my_string
you expected this to work and print hello world but this will throw the following error:
Traceback (most recent call last):
File "test.py", line 4, in <module>
my_string[0] = "h"
TypeError: 'str' object does not support item assignment
The interpreter is saying : i can't change the first character of this string
you will have to change the whole string
in order to make it works:
my_string = "Hello World"
my_string = "hello world"
print my_string #hello world
check this table:
Upvotes: 6
Reputation:
Common immutable type:
int()
, float()
, complex()
str()
, tuple()
, frozenset()
, bytes()
Common mutable type (almost everything else):
list()
, bytearray()
set()
dict()
One trick to quickly test if a type is mutable or not, is to use id()
built-in function.
Examples, using on integer,
>>> i = 1
>>> id(i)
***704
>>> i += 1
>>> i
2
>>> id(i)
***736 (different from ***704)
using on list,
>>> a = [1]
>>> id(a)
***416
>>> a.append(2)
>>> a
[1, 2]
>>> id(a)
***416 (same with the above id)
Upvotes: 119
Reputation: 366103
If you're coming to Python from another language (except one that's a lot like Python, like Ruby), and insist on understanding it in terms of that other language, here's where people usually get confused:
>>> a = 1
>>> a = 2 # I thought int was immutable, but I just changed it?!
In Python, assignment is not mutation in Python.
In C++, if you write a = 2
, you're calling a.operator=(2)
, which will mutate the object stored in a
. (And if there was no object stored in a
, that's an error.)
In Python, a = 2
does nothing to whatever was stored in a
; it just means that 2
is now stored in a
instead. (And if there was no object stored in a
, that's fine.)
Ultimately, this is part of an even deeper distinction.
A variable in a language like C++ is a typed location in memory. If a
is an int
, that means it's 4 bytes somewhere that the compiler knows is supposed to be interpreted as an int
. So, when you do a = 2
, it changes what's stored in those 4 bytes of memory from 0, 0, 0, 1
to 0, 0, 0, 2
. If there's another int variable somewhere else, it has its own 4 bytes.
A variable in a language like Python is a name for an object that has a life of its own. There's an object for the number 1
, and another object for the number 2
. And a
isn't 4 bytes of memory that are represented as an int
, it's just a name that points at the 1
object. It doesn't make sense for a = 2
to turn the number 1 into the number 2 (that would give any Python programmer way too much power to change the fundamental workings of the universe); what it does instead is just make a
forget the 1
object and point at the 2
object instead.
So, if assignment isn't a mutation, what is a mutation?
a.append(b)
. (Note that these methods almost always return None
). Immutable types do not have any such methods, mutable types usually do.a.spam = b
or a[0] = b
. Immutable types do not allow assignment to attributes or elements, mutable types usually allow one or the other.a += b
, sometimes not. Mutable types usually mutate the value; immutable types never do, and give you a copy instead (they calculate a + b
, then assign the result to a
).But if assignment isn't mutation, how is assigning to part of the object mutation? That's where it gets tricky. a[0] = b
does not mutate a[0]
(again, unlike C++), but it does mutate a
(unlike C++, except indirectly).
All of this is why it's probably better not to try to put Python's semantics in terms of a language you're used to, and instead learn Python's semantics on their own terms.
Upvotes: 30
Reputation: 2727
A mutable object has to have at least a method able to mutate the object. For example, the list
object has the append
method, which will actually mutate the object:
>>> a = [1,2,3]
>>> a.append('hello') # `a` has mutated but is still the same object
>>> a
[1, 2, 3, 'hello']
but the class float
has no method to mutate a float object. You can do:
>>> b = 5.0
>>> b = b + 0.1
>>> b
5.1
but the =
operand is not a method. It just make a bind between the variable and whatever is to the right of it, nothing else. It never changes or creates objects. It is a declaration of what the variable will point to, since now on.
When you do b = b + 0.1
the =
operand binds the variable to a new float, wich is created with te result of 5 + 0.1
.
When you assign a variable to an existent object, mutable or not, the =
operand binds the variable to that object. And nothing more happens
In either case, the =
just make the bind. It doesn't change or create objects.
When you do a = 1.0
, the =
operand is not wich create the float, but the 1.0
part of the line. Actually when you write 1.0
it is a shorthand for float(1.0)
a constructor call returning a float object. (That is the reason why if you type 1.0
and press enter you get the "echo" 1.0
printed below; that is the return value of the constructor function you called)
Now, if b
is a float and you assign a = b
, both variables are pointing to the same object, but actually the variables can't comunicate betweem themselves, because the object is inmutable, and if you do b += 1
, now b
point to a new object, and a
is still pointing to the oldone and cannot know what b
is pointing to.
but if c
is, let's say, a list
, and you assign a = c
, now a
and c
can "comunicate", because list
is mutable, and if you do c.append('msg')
, then just checking a
you get the message.
(By the way, every object has an unique id number asociated to, wich you can get with id(x)
. So you can check if an object is the same or not checking if its unique id has changed.)
Upvotes: 11
Reputation: 9162
What? Floats are immutable? But can't I do
x = 5.0
x += 7.0
print x # 12.0
Doesn't that "mut" x?
Well you agree strings are immutable right? But you can do the same thing.
s = 'foo'
s += 'bar'
print s # foobar
The value of the variable changes, but it changes by changing what the variable refers to. A mutable type can change that way, and it can also change "in place".
Here is the difference.
x = something # immutable type
print x
func(x)
print x # prints the same thing
x = something # mutable type
print x
func(x)
print x # might print something different
x = something # immutable type
y = x
print x
# some statement that operates on y
print x # prints the same thing
x = something # mutable type
y = x
print x
# some statement that operates on y
print x # might print something different
Concrete examples
x = 'foo'
y = x
print x # foo
y += 'bar'
print x # foo
x = [1, 2, 3]
y = x
print x # [1, 2, 3]
y += [3, 2, 1]
print x # [1, 2, 3, 3, 2, 1]
def func(val):
val += 'bar'
x = 'foo'
print x # foo
func(x)
print x # foo
def func(val):
val += [3, 2, 1]
x = [1, 2, 3]
print x # [1, 2, 3]
func(x)
print x # [1, 2, 3, 3, 2, 1]
Upvotes: 245
Reputation: 5167
One way of thinking of the difference:
Assignments to immutable objects in python can be thought of as deep copies, whereas assignments to mutable objects are shallow
Upvotes: 2
Reputation: 8721
Whether an object is mutable or not depends on its type. This doesn't depend on whether or not it has certain methods, nor on the structure of the class hierarchy.
User-defined types (i.e. classes) are generally mutable. There are some exceptions, such as simple sub-classes of an immutable type. Other immutable types include some built-in types such as int
, float
, tuple
and str
, as well as some Python classes implemented in C.
A general explanation from the "Data Model" chapter in the Python Language Reference":
The value of some objects can change. Objects whose value can change are said to be mutable; objects whose value is unchangeable once they are created are called immutable.
(The value of an immutable container object that contains a reference to a mutable object can change when the latter’s value is changed; however the container is still considered immutable, because the collection of objects it contains cannot be changed. So, immutability is not strictly the same as having an unchangeable value, it is more subtle.)
An object’s mutability is determined by its type; for instance, numbers, strings and tuples are immutable, while dictionaries and lists are mutable.
Upvotes: 18
Reputation: 176960
First of all, whether a class has methods or what it's class structure is has nothing to do with mutability.
int
s and float
s are immutable. If I do
a = 1
a += 5
It points the name a
at a 1
somewhere in memory on the first line. On the second line, it looks up that 1
, adds 5
, gets 6
, then points a
at that 6
in memory -- it didn't change the 1
to a 6
in any way. The same logic applies to the following examples, using other immutable types:
b = 'some string'
b += 'some other string'
c = ('some', 'tuple')
c += ('some', 'other', 'tuple')
For mutable types, I can do thing that actallly change the value where it's stored in memory. With:
d = [1, 2, 3]
I've created a list of the locations of 1
, 2
, and 3
in memory. If I then do
e = d
I just point e
to the same list
d
points at. I can then do:
e += [4, 5]
And the list that both e
and d
points at will be updated to also have the locations of 4
and 5
in memory.
If I go back to an immutable type and do that with a tuple
:
f = (1, 2, 3)
g = f
g += (4, 5)
Then f
still only points to the original tuple
-- you've pointed g
at an entirely new tuple
.
Now, with your example of
class SortedKeyDict(dict):
def __new__(cls, val):
return dict.__new__(cls, val.clear())
Where you pass
d = (('zheng-cai', 67), ('hui-jun', 68),('xin-yi', 2))
(which is a tuple
of tuples
) as val
, you're getting an error because tuple
s don't have a .clear()
method -- you'd have to pass dict(d)
as val
for it to work, in which case you'll get an empty SortedKeyDict
as a result.
Upvotes: 41