Aviral Srivastava
Aviral Srivastava

Reputation: 4582

Why are two class objects declared in the same line point to one object?

I declared two ListNode objects as head = curr = ListNode(0). Now, to my surprise, both of them point at the same address:

>>> head
<__main__.ListNode object at 0x10bf271d0>
>>> curr
<__main__.ListNode object at 0x10bf271d0>

Why is that? Why is it not like a = b = 4?

Because, if I make changes to b, a doesn't get affected. However, if I make changes to curr in the following manner, the head can keep a track of it:

>>> curr.next = ListNode(1)
>>> curr = curr.next
>>> head
<__main__.ListNode object at 0x10bf271d0>
>>> curr
<__main__.ListNode object at 0x10bf27190>
>>> head.next
<__main__.ListNode object at 0x10bf27190>

I understand that head and curr are declared by refercing to pointers(address) and a and b are made by referencing to value. Is that right?

If yes, how can I control what gets declared with ref to value and what gets declared with ref to pointers(address)?

The ListNode class used above is as follows:

>>> class ListNode(object):
...     def __init__(self, x):
...         self.val = x
...         self.next = None
...

EDIT: Explanation of how this question is different from Mutable v/s Immutable Immutable vs Mutable types.

This question addresses the binding and referencing of the variables whereas the other question strictly talks about what Mutable and Immutable objects are in Python and their differences. While the latter does tell about the referencing of variables in order to explain Mutable v/s Immutable, it does not address the doubt that is asked in this question and hence, for the community, those confused observers would know the referencing concept due to this question.

Upvotes: 1

Views: 58

Answers (2)

Konrad Rudolph
Konrad Rudolph

Reputation: 545923

Why is it not like a = b = 4?

On the contrary. It’s exactly like it:

a = b = 4
print(id(a))
# 4552081648
print(id(b))
# 4552081648

Because, if I make changes to b, a doesn't get affected. However, if I make changes to curr in the following manner, the head can keep a track of it:

You’re changing b by reassigning it, thus rebinding the name to a new value:

b = 5
print(id(b))
# 4552081680

Whereas, in your modification of curr, you’re not rebinding curr so it keeps referring to the same value.

Contrary to common misconception, this has nothing to do with immutability: you can of course rebind names to mutable values just fine:

head = curr = ListNode(0)
head = ListNode(0)
head
# <__main__.ListNode at 0x10bf27118>
curr
# <__main__.ListNode at 0x10bf271d0>

… as you can see, head and curr now refer to different values.

Upvotes: 1

Paul M.
Paul M.

Reputation: 10809

Integers in python are immutable. When you do:

a = b = 4
a = a + 1

a and b are independent in this case, because as soon as you try to mutate a, you are actually binding a new int object to a. You're never actually changing integers, just rebinding.

When you do:

head = curr = ListNode(0)

You create one ListNode object, and bind the references head and curr to it. They're both pointing to the same place in memory, just like the integers. However, since your class is mutable, there is no reason to rebind when a mutation occurs. This is the expected behavior.

EDIT - Just to make integer immutability and rebinding a bit clearer:

a = b = 4

print(f"ID of 'a': {id(a)}")
print(f"ID of 'b': {id(b)}")

a += 1

print("\nAfter adding +1 to 'a'...")
print(f"ID of 'a': {id(a)}")
print(f"ID of 'b': {id(b)}")

Output:

ID of 'a': 1853646048
ID of 'b': 1853646048

After adding +1 to 'a'...
ID of 'a': 1853646064
ID of 'b': 1853646048

You can see that attempting to mutate a doesn't change the underlying integer object - it simply rebinds a to a new integer object.

Upvotes: 1

Related Questions