0%

Representing and Comparing Objects · practice

Identity and Equality Are Different

Two questions with exactly the same prompt, answer, and points. Are they the same question?

Try it

False. Two that look identical, printed identically by their own repr, and Python says they are not equal.

What == does by default

Without being told otherwise, == on your own objects asks a much narrower question than you probably meant. It asks: are these the same object?

Try it

first is second is False, because two separate objects were constructed. third is first is True, because third = first stored a reference to the one that already existed. This is the two-names-for-one-object idea from Chapter 4, and by default == gives exactly the same answer as is.

Two different ideas are hiding here, and Python keeps them apart:

  • Identity asks whether two names refer to one object in memory. is answers it, and it is never something you define.

  • Equality asks whether two objects should count as the same . == answers it, and it is a decision belonging to the class.

Two separately constructed Question objects have equal prompt, answer, and points but distinct identities; an alias points to the first object and shares its identity.

In this chapter, we want to recognize duplicate question content. For that job, questions with matching prompts, answers, and points should compare equal. A different quiz system could give each authored question its own identity and deliberately keep them distinct.

a = Question("Q", "A", 1) and b = a, then c = Question("Q", "A", 1). Which is true with no __eq__ defined?

Where the default bites

The default is not merely surprising. It quietly breaks things you have already written:

Try it

in reports False, so it does not recognize a freshly built question as a content duplicate. such as .remove() would instead raise when they cannot find a match. in, .index(), .remove(), .count(), and list1 == list2 are all built on ==, so all of them inherit whatever the class decided.

When the default is right

Before rushing to change it, notice that the default is correct more often than it looks.

For a QuizAttempt, sameness by content would be wrong. Two learners who both scored 4 out of 10 on Tuesday did not make the same attempt, and if one is later corrected the other must not change with it. An attempt is an event: it has an identity of its own, and two of them are the same only if they are literally the same one.

The distinction that decides it:

KindSame whenExamples
Value objectTheir contents matchQuestion, a point, an amount of money, a date
EntityThey are the same oneQuizAttempt, a learner, an order, a session

Value equality treats matching contents as interchangeable for comparison. It does not make two objects share later changes. We will use read-only question state in the next exercise to make this value model easier to preserve. Entities are not: attempt_a and attempt_b are two things that happened, however similar they look.

The next lesson defines equality for the value objects. This lesson’s job is making sure you can tell which is which, because giving an entity value equality can silently merge events that should remain distinct.

Task

Before changing how anything compares, find out what the current behavior actually is.

Complete the three , using only what Python does today. None of these classes defines __eq__ yet.

  • same_object(a, b) reports whether two names refer to one .

  • default_equal(a, b) reports what == currently says about them.

  • found_in(bank, wanted) reports whether a question equal to wanted is in the , using in.

The -object/entity distinction is a design decision, not a -mapping trick, so it stays in the worked discussion. The exercise concentrates on observing the three Python operations that make the distinction visible.