Inner Workings of Python, Part-II

Introduction

This is the second part of a blog series where I explain the inner workings of Python. To get started, let's look at an example.

This article is inspired by

x = ["fruit"]
y = ["fruit"]
z = x

x is z  # True
x is y  # False
x == y  # True

I came across this example in a Python lecture, but the teacher skipped over it, saying, "It is what it is." To be precise, his/her exact words in Hindi were "Python banane wale ne aayse hi banaya tha." However, by the end of this article, you will understand why this is the result.

Mutable and Immutable

By definition, an immutable object is an object whose state can’t be changed after you’ve created it. In contrast, a mutable object allows you to modify its internal state after its creation. This seems easy enough. Now let's see what is mutable and what isn't.

Here is a small list.

MutableImmutable
ListInteger
SetBoolean
DictionaryString
BytearrayFloat
ArrayTuple, etc...

But we know that is possible in Python.

name = "Hitesh"
name = "Rushabh"
# We changed the value in name form Hitesh to Rushabh

I think this is where the confusion ray hits, isn't it? What's going on?

For us to understand this, we need to know what is going on under the hood.

So when you run,name = "Hitesh" a memory reference is created, and actually, this memory reference is "immutable." Therefore, when you run name = "Rushabh" the memory reference, to "Hitesh" doesn't change; just the pointer is moved to the desired location. And since Python has automatic garbage collection, if it is found that no variable is referencing to "Hitesh" , it gets deleted by the garbage collector.

Lets take another example

x = 10
y = x
print(x) # 10
print(y) # 10
x = 15
print(x) # 15
print(y) # 10

This is what happened under the hood: it x = 10 created a memory reference to10, then y = x simply means, let y point where x is pointing, i.e., to the same memory reference. but when we set x = 15 another memory reference, to 15 was created since integers are "immutable" in Python, and y was unchanged.

Reference Counter

As we briefly discussed earlier, Python has a garbage collector that deletes all the orphans in memory. But for the garbage collector to know if the object in memory has any references or not, every object has a reference counter associated with it. But the thing is, I can't prove this by printing the reference count since Python doesn't provide any mechanism to give a precise ref_count of any object. You just have to take my word for this, or you can dive into the Python documentation and read all about it.

Know your Data Types

Well, in Python, the information about the datatype is in memory and not in the variable itself, i.e., a variable has no datatypes but the memory reference does.

name = "chai"

Therefore we can say that name is not of type string but chai inside name is of type string.

Q n A

Does Python have no datatypes?

Yes, Python has no datatypes, we never assign variables any datatype.

Does the memory reference also don't have any datatypes?

No, objects in memory do have datatypes, and they are assigned in memory itself.

Quirks of Numbers and Strings

Since Python is a language that is heavily focused on numbers and is used in many scientific works, numbers in Python behave a bit differently than expected. This type of behavior is also seen in strings.

Let's take an example of the following code:.

a = 3
a = "chai"

What do you think happens here?

From what we have learned so far, we might assume that when we assign a = "chai" the garbage collector deletes 3 from the memory immediately. But that's not the case, the collection of numbers and strings does not happen immediately for the purpose of optimization.

Revisiting Mutable and Immutable

list1 = [1,2,3]
list2 = lsit1
list2[0] = 100
print(list1) # 100, 2, 3
print(list2) # 100, 2, 3

From the above table, we know that lists are mutable; therefore, no new list object is created in memory, but the existing one is just changed.

a = 10
b = 10
list1 = [1,2,3]
list2 = [1,2,3]
list3 = list1

"Immutable" objects do not create copies in memory, while "mutable" objects may or may not, depending upon how they are created. The == operator will return True when a and b are compared, or list1, list2 or list3 are compared since it only compares the value stored in the variable. To check the memory reference we can make use of is operator.

a = 10
b = 10
a is b # True
# Since both a and b 
# points to the same object in memory

list1 = [1,2,3]
list2 = [1,2,3]
list1 is list2 # False
# Since list1 and list2 does not
# points to the same object in memory

list3 = list1
list1 is list3 # True
# Since list1 and list2
# points to the same object in memory