What is id?

Rami Louhibi
12 min readJun 30, 2021

--

id()

Id is a built-in function in Python. It gives us the ability to check the unique identifier of an object. Let’s take a look at how this works.

>>> a = 1
>>> a
1

We see that a is an object that contains the value 1. Now let’s check the id, the unique identifier, of that object.

>>> id(a)
10105088

The unique identifier is pointing to a location in memory, which is an object. Let’s try another one.

>>> b = 2
>>> b
2
>>> id(b)
10105120

You can see that id(a) is different from id(b) but we can also run a test.

>>> a = 1
>>> b = 2
>>> id(a) == id(b)
False

Let’s assign another variable to 2 and check the id.

>>> a = 1
>>> b = 2
>>> c = 2
>>> c
2
>>> id(c)
10105120

Hmm, id(c) looks the same as id(b). Why is that? Let’s run a test.

>>> id(c) == id(b)
True

They have the same unique identifier because c is referencing an object that contains the value 2, and b is also referencing the same object that contains the value 2. Okay, let that sink in for a moment. Wow, right?

They’re both pointing to the same object that contains a value 2.

>>> id(c)
10105120
>>> id(b)
10105120

The object, 10105120, is the unique identifier. It’s the location in memory. Objects can have multiple variables. If you’re debugging a program, this might be a useful function. Let’s now try this with strings.

>>> a = "strawberry"
>>> b = "strawberry"
>>> id(a) == id(b)
True
>>> a = "strawberry"
>>> b = "Strawberry"
>>> id(a) == id(b)
False

What happened here? In the second example above, we used a capital S in variable b’s object. That’s why it was False.

Let’s look at more examples.

>>> a = 256
>>> b = 256
>>> id(a) == id(b)
True
>>> a = 260
>>> b = 260
>>> id(a) == id(b)
False

Why is it True for 256 but False for 260?

The reason is that Python keeps an array of integer objects for all integers between -5 and 256. When you create an integer in that range, you get back a reference to the already existing object.

It does this with two macros, NSMALLNEGINTS and NSMALLPOSINTS. If the value ival satisfies the condition of being between -5 and 256, the function get_small_int is called.

#ifndef NSMALLPOSINTS
#define NSMALLPOSINTS 257
#endif
#ifndef NSMALLNEGINTS
#define NSMALLNEGINTS 5
#endif#define CHECK_SMALL_INT(ival)
do if (-NSMALLNEGINTS <= ival && ival < NSMALLPOSINTS) {
return get_small_int((sdigit)ival);
} while(0)

Note: You can also use “is” to check if two variables have the same object id.

>>> list1 = [1, 2, 3]
>>> list2 = list1
>>> list1 is list2
True

Let’s introduce some more terminology.

The same list above has two different names, list1 and list2, and we can say that it is aliased. Variables refer to objects and if we assign one variable to another, both variables refer to the same object. That is what aliasing means.

Let’s also talk about cloning. If we want to modify a list and also keep a copy of the original, we need to make a copy of the list. This process is called cloning. Taking any slice of a list creates a new list.

>>> list1 = [1, 2, 3]
>>> list2 = list1[:]
>>> list1
[1, 2, 3]
>>> list2
[1, 2, 3]
>>> list1 is list2
False
>>> id(list1) == id(list2)
False

What is type?

type()

In Python, all data is stored in the form of an object. An object has three things: id, type, and value.

The type function will provide the type of the object that’s provided as its argument.

>>> type(2)
<class 'int'>
>>> type(-6.25)
<class 'float'>
>>> type(2,)
<class 'tuple'>
>>> type("hello")
<class 'str'>
>>> type('A')
<class 'str'>
>>> type('346.789')
<class 'str'>
>>> type([2, 3, 4])
<class 'list'>
>>> type({'category': 'produce', 'count': 200})
<class 'dict'>
>>> type(print)
<class 'builtin_function_or_method'>
>>> type(type)
<class 'type'>

Like id(), type() is also useful for debugging.

type() method returns class type of the argument(object) passed as parameter. type() function is mostly used for debugging purposes. Two different types of arguments can be passed to type() function, single and three argument. If single argument type(obj) is passed, it returns the type of given object

What are mutable objects?

PythonMutable objects:
list, dict, set

A program stores data in variables that represent the storage locations in the computer’s memory. The contents of the memory locations, at any given point in the program’s execution, is called the program’s state.

Some objects in Python are mutable, and some are immutable. First, we’ll discuss mutable objects. A mutable object is a changeable object and its state can be modified after it is created.

>>> my_list = ['cat', 'dog', 'bunny']
>>> my_list
['cat', 'dog', 'bunny']
>>> print('Address of my_list is: {}'.format(id(my_list)))
Address of my_list is: 139929780579208

If we want to change the first value in our list and print it out, we can see that the list changed, but the memory address of the list is the same. It changed the value in place. That’s what mutable means.

>>> my_list[0] = 'sugar glider'
>>> my_list
['sugar glider', 'dog', 'bunny']
>>> print('Address of my_list is: {}'.format(id(my_list)))
Address of my_list is: 139929780579208

Let’s look now at the memory addresses of values of the list and see what happens before and after we change the value of the first element of the list.

>>> my_list
['sugar glider', 'dog, 'bunny']
>>> id(my_list)
139929780579208
>>> my_list[0]
'sugar glider'
>>> id(my_list[0])
139905997708792
>>> my_list[0] = 'rabbit'
>>> id(my_list[0])
139905997708400
>>> id(my_list)
139929780579208

The id of my_list[0] is 139905997708792 when the value of the first element is ‘sugar glider’. The id of my_list[0] is 139905997708400 after we change the value to ‘rabbit.’ Notice they are two different ids.

When we modify a list and change its values in place, the list keeps the same address. However, the address of the value that you changed will have a different address.

The id of my_list still remained the same at 139929780579208.

What are immutable objects?

PythonImmutable objects:
integer, float, string, tuple, bool, frozenset

An immutable object is an object that is not changeable and its state cannot be modified after it is created.

In Python, a string is immutable. You cannot overwrite the values of immutable objects.

However, you can assign the variable again.

>>> phrase = 'how you like me now'
>>> phrase
how you like me now
>>> phrase = 'do you feel lucky'
>>> phrase
do you feel lucky

It’s not modifying the string object; it’s creating a new string object.

To see this in more detail, we can utilize the id function that we learned earlier. Recall that id() function prints out the memory address.

>>> phrase = 'how you like me now'
>>> print('Address of phrase is: {}'.format(id(phrase)))
Address of phrase is: 139929793080104
>>> phrase = 'do you feel lucky'
>>> print('Address of phrase is: {}'.format(id(phrase)))
Address of phrase is: 139929792606832

Since a string is immutable, it created a new string object. The memory addresses do not match.

Let’s try to change a single character of the phrase.

>>> phrase[0] = 'D'
...
TypeError: 'str' object does not support item assignment

We get a TypeError because strings are immutable. We can’t change the string object.

Let’s also talk about tuples.

Immutability on tuples is only partly true. The tuple itself cannot be modified, but objects referenced by the tuple can be modified. If the tuple has an immutable field like a string, then the tuple cannot be modified and it is sometimes called “non-transitive immutability.” But a mutable field like a list can be edited, even if it’s embedded in the “immutable” tuple.

differently does Mutable vs Immutable Objects in Python

Everything in Python is an object. And what every newcomer to Python should quickly learn is that all objects in Python can be either mutable or immutable.

Lets dive deeper into the details of it… Since everything in Python is an Object, every variable holds an object instance. When an object is initiated, it is assigned a unique object id. Its type is defined at runtime and once set can never change, however its state can be changed if it is mutable. Simple put, a mutable object can be changed after it is created, and an immutable object can’t.

Objects of built-in types like (int, float, bool, str, tuple, unicode) are immutable. Objects of built-in types like (list, set, dict) are mutable. Custom classes are generally mutable. To simulate immutability in a class, one should override attribute setting and deletion to raise exceptions.

Now comes the question, how do we find out if our variable is a mutable or immutable object. For this we should understand what ‘ID’ and ‘TYPE’ functions are for.

ID and TYPE

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, although this is specific to the Python implementation and the platform being used. The is operator compares the identity of two objects.

The built-in function type() returns the type of an object. Lets look at a simple example

''' Example 1 '''
>>> x = "Holberton"
>>> y = "Holberton"
>>> id(x)
140135852055856
>>> id(y)
140135852055856
>>> print(x is y) '''comparing the types'''
True''' Example 2 '''
>>> a = 50
>>> type(a)
<class: ‘int’>
>>> b = "Holberton"
>>> type(b)
<class: 'string'>

We have now seen how to compare two simple string variables to find out the types and id’s .So using these two functions, we can check to see how different types of objects are associated with variables and how objects can be changed .

Mutable and Immutable Objects

So as we discussed earlier, a mutable object can change its state or contents and immutable objects cannot.

Mutable objects:

list, dict, set, byte array

Immutable objects:

int, float, complex, string, tuple, frozen set [note: immutable version of set], bytes

A practical example to find out the mutability of object types

x = 10x = y

We are creating an object of type int. identifiers x and y points to the same object.

id(x) == id(y)id(y) == id(10)

if we do a simple operation.

x = x + 1

Now

id(x) != id(y)id(x) != id(10)

The object in which x was tagged is changed. object 10 was never modified. Immutable objects doesn’t allow modification after creation

In the case of mutable objects

m = list([1, 2, 3])n = m

We are creating an object of type list. identifiers m and m tagged to the same list object, which is a collection of 3 immutable int objects.

id(m) == id(n)

Now poping an item from list object does change the object,

m.pop()

object id will not be changed

id(m) == id(n)

m and n will be pointing to the same list object after the modification. The list object will now contain [1, 2].

So what have we seen so far from the above examples?

  • Python handles mutable and immutable objects differently.
  • Immutable are quicker to access than mutable objects.
  • Mutable objects are great to use when you need to change the size of the object, example list, dict etc.. Immutables are used when you need to ensure that the object you made will always stay the same.
  • Immutable objects are fundamentally expensive to “change”, because doing so involves creating a copy. Changing mutable objects is cheap.

Exceptions in immutability..

Not all of the immutable objects are actually immutable. Confused? Let me explain.

As discussed earlier, Python containers liked tuples are immutable. That means value of a tuple can't be changed after it is created. But the "value" of a tuple is infact a sequence of names with unchangeable bindings to objects. The key thing to note is that the bindings are unchangeable, not the objects they are bound to.

Let us consider a tuple t = (‘holberton’, [1, 2, 3])

The above tuple t contains elements of different data types, the first one is an immutable string and the second one is a mutable list.The tuple itself isn’t mutable . i.e. it doesn’t have any methods for changing its contents. Likewise, the string is immutable because strings don’t have any mutating methods. But the list object does have mutating methods, so it can be changed. This is a subtle point, but nonetheless important: the “value” of an immutable object can’t change, but it’s constituent objects can.

How objects are passed to Functions

Its important for us to know difference between mutable and immutable types and how they are treated when passed onto functions .Memory efficiency is highly affected when the proper objects are used.

For example if a mutable object is called by reference in a function, it can change the original variable itself. Hence to avoid this, the original variable needs to be copied to another variable. Immutable objects can be called by reference because its value cannot be changed anyways.

def updateList(list1):
list1 += [10]n = [5, 6]
print(id(n)) # 140312184155336updateList(n)
print(n) # [5, 6, 10]
print(id(n)) # 140312184155336

As we can see from the above example, we have called the list via call by reference, so the changes are made to the original list itself.

Lets take a look at another example:

def updateNumber(n):
print(id(n))
n += 10b = 5
print(id(b)) # 10055680
updateNumber(b) # 10055680
print(b) # 5

In the above example the same object is passed to the function, but the variables value doesn’t change even though the object is identical. This is called pass by value. So what is exactly happening here? When the value is called by the function, only the value of the variable is passed, not the object itself. So the variable referencing the object is not changed, but the object itself is being changed but within the function scope only. Hence the change is not reflected.

how arguments are passed to functions and what does that imply for mutable and immutable objects?

Compiling function arguments in Python changed based on the function arguments type ( Mutable or immutable).

when mutable objects are being called by reference in a function the variable itself might change however this can be avoided by creating a copy. On the other hand when immutable objects are called being called by reference in a function their values remain the same and it cannot be changed

Passing Immutable Objects:

>>> def increment(n):
... n += 1>>> a = 3
>>> increment(a)
>>> print(a)
a = 3 # a is still referring to the same object

Let’s decompose this:

the variable is refers to the object with the value 3 (a = 3)

passing a to increment(n), the function refers the local variable n to the same object

let’s pass to the n += 1 par. We previously mentioned that integers are immutable objects that makes them impossible to modify so we can’t change the 3 to a 4 with this being said we created a new object with the value 4.

And that’s why the value of a didn’t change even after using increment().

Passing Mutable Objects:

>>> def increment(n):
... n.append([4])>>> L = [1, 2, 3]
>>> increment(L)
>>> print(L)
L = [1, 2, 3, 4] # a changed!

Let’s decompose this:

As mentioned before lists are mutable L = [1, 2, 3] refer to a list object that refer to “3" integers 1, 2, and 3.

when passsing L to increment(), the function makes the local variable n refers to the same object as L

using the .append() method will modify the list. When L is printed we get a midified listsince no object was being created and the changed occurred in place of the object.

And with this being said that’s the diffrance between mutable and immutable objects and how arguments are passed to functions.

--

--

Rami Louhibi
Rami Louhibi

No responses yet