Register to get access to free programming courses with interactive exercises

References and variability Python: Lists

In the last lesson, we introduced the concept of references and mentioned that everything is always passed by reference in Python. Let us experiment with a list as our first known mutable object.

But first, we need to learn about tools for our experiments: the id function and the is operator.

The id and is methods

If you refer to the function description (help(id)), the documentation will tell you:

id(obj, /)
    Return the identity of an object.

    It is guaranteed to be unique among simultaneously existing objects.

The id function returns a unique identifier of an object passed to it as an argument and by reference.

The identifier is an ordinary number. But each object has a unique identifier.

It means that any two objects will always have different identifiers. Python does not preserve identifiers from one run to the next, so the object-identifier relationship is unbreakable within one run.

That is why identifiers help keep track of object references we pass between different code sections.

The object identifier will be the same regardless of what reference we use to access the object:

a = "some string"
b = a
id(a)  # 139739990935280
id(b)  # 139739990935280
print(a is b)  # => True

When we assign a value of one variable to another, we create a new named reference to the original value. Therefore id(a) and id(b) return the same result.

The is operator checks if the identifiers of its operands are equal. In this example, both variables refer to the same object, so checking a is b gives True.

Checking for equality of identifiers is very quick. And it is convenient to use when we deal with so-called single objects.

The most famous Python singles are:

  • True
  • False
  • None

So the None check is usually written like this:

...
if foo is None:
    ...

Lists, tuples, and references

Check out this example:

a = [1, 2, 3]
b = a
a.append(4)
print(b)  # => [1, 2, 3, 4]

Here we see that lists a and b have been changed. There are not two lists but two references to one list. We will continue:

a = []
l = [a, a]
a.append(1)
print(l)  # => [[1], [1]]

As you may have guessed, the list stores two references to the same object, which is also mutable. It is the subtlety of working with the references. When we get them from somewhere, we cannot be sure that the object will not change over time without our involvement.

Remember when we said that a tuple cannot change? Let us look at this example:

a = []
pair = (a, a)
pair[0].append(1)
pair[1].append(2)
print(pair)  # => ([1, 2], [1, 2])

As you can see, the value in the tuple has changed. It happened because the tuple contents are references to values. These references cannot change, but the objects connected to them may well change.

Let us observe lists and tuples created using the special syntax: multiplication of a list or tuple by a number.

If we multiply a list or tuple by n, we get a new collection of the corresponding type.

It consists of n repetitions of elements of the original collection. Here are a few examples:

print([1, 2, 3] * 3)  # => [1, 2, 3, 1, 2, 3, 1, 2, 3]
print(('foo', 'bar') * 2)  # => ('foo', 'bar', 'foo', 'bar')
print([[]] * 5)  # => [[], [], [], [], []]
print(((),) * 5)  # => ((), (), (), (), ())

Do not forget that collections are always collections of references. You can guess how these duplicated collections will behave when the items being modified change. Take a look:

t = ([], [], []) * 3
print(t)  # => ([], [], [], [], [], [], [], [], [])
t[0].append(42)
t[1].append(0)
print(t)  # => ([42], [0], [], [42], [0], [], [42], [0], [])

References and assignments

We saw that you can add more than one reference to a single object to a list. And that variables are the same as references, just named.

But what happens to variables and list items when you assign them? Let us have a look:

a = "foo"
id(a)  # 139739990954536
a += "bar"
print(a)  # => 'foobar'
id(a)  # 139739952783688

This example shows that the variable name is not strictly related to the reference to the value.

Assigning a variable (+= is a type of assignment) can change one reference to another. This property is also characteristic of list items:

a = "foo"
l = [a, a]
print(l[0] is l[1])  # => True
l[0] += "bar"
print(l)  # => ['foobar', 'foo']
print(l[0] is l[1])  # => False

Here the first two items in the list refer to the same value. However, when you assign a new value to the first element, you break the link between the original value and the element.


Are there any more questions? Ask them in the Discussion section.

The Hexlet support team or other students will answer you.

For full access to the course you need a professional subscription.

A professional subscription will give you full access to all Hexlet courses, projects and lifetime access to the theory of lessons learned. You can cancel your subscription at any time.

Get access
130
courses
1000
exercises
2000+
hours of theory
3200
tests

Sign up

Programming courses for beginners and experienced developers. Start training for free

  • 130 courses, 2000+ hours of theory
  • 1000 practical tasks in a browser
  • 360 000 students
By sending this form, you agree to our Personal Policy and Service Conditions

Our graduates work in companies:

Bookmate
Health Samurai
Dualboot
ABBYY
Suggested learning programs
profession
new
Developing web applications with Django
10 months
from scratch
under development
Start at any time

Use Hexlet to the fullest extent!

  • Ask questions about the lesson
  • Test your knowledge in quizzes
  • Practice in your browser
  • Track your progress

Sign up or sign in

By sending this form, you agree to our Personal Policy and Service Conditions
Toto Image

Ask questions if you want to discuss a theory or an exercise. Hexlet Support Team and experienced community members can help find answers and solve a problem.