Start learning free

LESSON 555 OF 569 · OBJECT-ORIENTED PROGRAMMING - CHALLENGES

Ticket Numbers in Python: Class Attributes and Static Methods

Practice O(1) Read the full explanation

A ticket machine gives every new ticket the next number: the first ticket gets 1, the next one 2, and so on. The counter belongs to the class, not to any one ticket: it is a static member of Ticket, one copy shared by all the tickets. Each ticket keeps its own number, copied from the counter when the ticket is created.

Implement the class Ticket:

  • Ticket(name) creates a ticket for name. It adds 1 to the class-level counter and gives the new ticket that value as its number. The ticket keeps its name and its number in two fields, name and number.
  • The static method issued() returns how many tickets were created since the counter was last reset.
  • The static method resetCounter() sets the counter back to 0, so the next ticket gets number 1 again.

For each test case, the test code calls resetCounter() through the class name, then creates one ticket per name, in order. Once all the tickets of the case exist, it prints each ticket as its name, a space, # and its number, one ticket per line, and then the value of issued() on a line of its own.

The starter code spells these names the way your language does: reset_counter in Python, Issued and ResetCounter in C#. In Python the counter is a class attribute and the two methods are marked @staticmethod; in Kotlin and Scala the counter and the two methods live in the companion object, as in the Static Members lesson.

 

Example 1:

Input:
    Ticket("Ann")
    Ticket("Bob")
    Ticket("Cy")
Output:
Ann #1
Bob #2
Cy #3
3
Explanation: resetCounter() set the counter to 0. Each new ticket adds 1 to it and keeps the new value,
so the tickets get 1, 2 and 3, and issued() returns 3.

Example 2:

Input:
    Ticket("Mia")
    Ticket("Mia")
Output:
Mia #1
Mia #2
2
Explanation: The number comes from the shared counter, not from the name, so the two tickets for Mia
get different numbers. The numbers start at 1 again because resetCounter() ran before this case.

Example 3:

Input:
    (no tickets)
Output:
0
Explanation: resetCounter() set the counter to 0 and no ticket was created, so issued() returns 0.

 

Constraints:

  • A test case creates from 0 to 1000 tickets.
  • A name is one word of 1 to 20 English letters. Two tickets can have the same name.
  • The tickets are printed only after all of them were created, so every ticket must store its own number: reading the shared counter when printing would give every ticket the last number.
STEP 1 OF 6
    Ctrl + Enter to check

    PYTHON · OBJECT-ORIENTED PROGRAMMING - CHALLENGES

    Ticket Numbers in Python, explained

    Problem overview

    A ticket machine gives every new ticket the next number: 1, 2, 3 and so on. The counter belongs to the class, not to any one ticket: there is one copy, shared by all tickets. Each ticket also keeps its own number, copied from the counter when the ticket is created.

    The class Ticket has a constructor Ticket(name) that stores name and number, a static method issued() that returns how many tickets were created since the last reset, and a static method reset_counter() that sets the counter back to 0. After a reset, creating tickets for Ann, Bob and Cy gives them the numbers 1, 2 and 3, and issued() returns 3. Two tickets for the same name still get different numbers, because the number comes from the counter, not the name.

    Approach: one shared counter, one copy per ticket

    Two kinds of data live here. The counter is shared, so it is a class attribute: a variable written directly in the class body, outside any method, with no self.. The name and the number differ from ticket to ticket, so they are instance attributes, set through self in __init__.

    When a ticket is created, it adds 1 to the shared counter and then copies the counter's value into its own number. The copy matters. The counter keeps moving as more tickets are made, but a number that was copied stays fixed. If a ticket read the counter every time it was asked for its number, all the tickets would show the latest value.

    Step by step

    1. Create the class attribute count = 0 at the top of the class, at the same indent as def and without self.. Every ticket sees the same Ticket.count.
    2. Store the name in self.name inside __init__. This belongs to the one ticket.
    3. Add 1 to Ticket.count. Write the class name, not self.
    4. Set self.number to Ticket.count: this ticket's own copy of the counter's current value.
    5. issued returns Ticket.count. The @staticmethod decorator marks a method that needs no ticket: it has no self and is called on the class, Ticket.issued().
    6. reset_counter assigns 0 to Ticket.count, so the next ticket gets number 1 again.

    The solution in Python

    class Ticket:
        count = 0
        def __init__(self, name):
            self.name = name
            Ticket.count += 1
            self.number = Ticket.count
        @staticmethod
        def issued():
            return Ticket.count
        @staticmethod
        def reset_counter():
            Ticket.count = 0
    

    Using it:

    Ticket.reset_counter()
    tickets = [Ticket(n) for n in ("Ann", "Bob", "Cy")]
    for t in tickets:
        print(t.name, "#" + str(t.number))
    print(Ticket.issued())
    Ticket.reset_counter()
    print(Ticket.issued())
    
    Ann #1
    Bob #2
    Cy #3
    3
    0
    

    Why Ticket.count and not self.count

    This is the classic trap. In Python, self.count += 1 reads the class attribute, adds 1, and then stores the result as a new attribute on that one object. The shared counter is not touched. Compare:

    class Bad:
        count = 0
        def __init__(self):
            self.count += 1
    
    a = Bad()
    b = Bad()
    print(Bad.count, a.count, b.count)
    
    0 1 1
    

    The class counter is still 0, and each object has its own private count of 1. With Ticket.count += 1, the count really is shared.

    Edge cases and common mistakes

    • No tickets. After reset_counter(), issued() returns 0.
    • Not copying the number. If a ticket returned Ticket.count instead of storing self.number, printing the tickets after creating all of them would show the last number on every ticket.
    • Forgetting the reset. Without it, a second round of tickets would continue from where the first one stopped. After reset_counter(), two tickets for Mia get 1 and 2.
    • Same names. Names don't have to be unique: the number is what tells tickets apart.
    • Complexity. Creating a ticket and both static methods are O(1).
    Keep going with Python500+ step-by-step lessons, from your first line of code to coding interviews. Free to start.
    Start learning free