🚀 Alguni Start learning

Unit 14: Keeping Things Safe

Sharing, hiding and guarding.

Unit 14 of 31 in Python for kids. Its 6 lessons are Shared by Everyone, Methods on the Class, Hands Off, Properties, Classes Inside Classes and Safekeeping Master — below is everything each one explains, and a question or two from it to try.

Every sample on this page was run through real Python before it shipped, and prints exactly what it says it prints.

This unit opens with a fortnight’s trial of everything — no card needed — or with a family plan, bought in the iPhone app. The first two units of every track are free for ever. Try it in the app.

🏫 Shared by Everyone

Two kinds of information

Something set in __init__ on self belongs to that one object. Something written straight inside the class belongs to all of them.

Python

class Cat:
    legs = 4

    def __init__(self, name):
        self.name = name

a = Cat("Tig")
b = Cat("Bo")
print(a.name, a.legs)
print(b.name, b.legs)

It prints

Tig 4
Bo 4

Counting how many you made

A class attribute is a good place to keep a running total, because every object shares the same one.

Python

class Player:
    total = 0

    def __init__(self):
        Player.total = Player.total + 1

Player()
Player()
Player()
print(Player.total)

It prints

3

Try it yourself

Why put legs = 4 on the class rather than in __init__?

  • It is faster
  • Every cat has 4 legs, so there is no reason to store it once per cat
  • It cannot go in __init__
  • It makes legs impossible to change

What does this print?

Python

class Dog:
    sound = "Woof"

a = Dog()
b = Dog()
Dog.sound = "Yip"
print(a.sound, b.sound)

Answer them in the app

🏭 Methods on the Class

A method about the class itself

A @classmethod gets cls — the class — instead of one object. Use it when the question is about all of them at once.

Python

class Player:
    total = 0

    def __init__(self):
        Player.total = Player.total + 1

    @classmethod
    def how_many(cls):
        return cls.total

Player()
Player()
print(Player.how_many())

It prints

2

A method that needs nothing at all

A @staticmethod takes neither self nor cls. It is a plain helper that simply belongs with the class because that is where it makes sense.

Python

class Maths:
    @staticmethod
    def double(n):
        return n * 2

print(Maths.double(7))

It prints

14

Building one a different way

A classmethod can hand you back a new object, which is handy when there is more than one sensible way to make one.

Python

class Pet:
    def __init__(self, name):
        self.name = name

    @classmethod
    def stray(cls):
        return cls("Nobody")

print(Pet.stray().name)

It prints

Nobody

Try it yourself

What does a classmethod get as its first argument?

  • The object, called self
  • The class, usually called cls
  • Nothing
  • The parent class

What does this print?

Python

class Temp:
    @staticmethod
    def freezing(c):
        return c <= 0

print(Temp.freezing(-3))
print(Temp.freezing(10))

Answer them in the app

🔒 Hands Off

Marking something as "not for you"

A name starting with _ means "this is the inside of the class, please leave it alone". Python does not stop you — it is a note to other people, and Python trusts you to read it.

Python

class Bank:
    def __init__(self):
        self._balance = 10

    def show(self):
        print(self._balance)

Bank().show()

It prints

10

Two underscores hide it properly

Two underscores make Python quietly rename it behind the scenes, so reaching for it from outside genuinely fails. This is called encapsulation — keeping the insides of a thing to itself.

Python

class Safe:
    def __init__(self):
        self.__code = 1234

    def open_it(self):
        print(self.__code)

s = Safe()
s.open_it()
try:
    print(s.__code)
except AttributeError:
    print("cannot reach it from out here")

It prints

1234
cannot reach it from out here

Why bother hiding anything?

So the class can guarantee something. If anyone could set health to -50, no method could promise it never goes below zero. Hide the value, and let a method be the only way in.

Python

class Player:
    def __init__(self):
        self.__health = 100

    def hurt(self, amount):
        self.__health = max(0, self.__health - amount)

    def health(self):
        return self.__health

p = Player()
p.hurt(150)
print(p.health())

It prints

0

Try it yourself

What does one underscore actually do?

  • Locks the value so nobody can read it
  • Nothing to Python — it is a message to other programmers
  • Makes it faster
  • Deletes it when the method ends

Answer it in the app

🎚️ Properties

A method that looks like a value

@property lets you work something out on the spot, while anyone using it just reads it like an ordinary attribute — no brackets.

Python

class Temp:
    def __init__(self, celsius):
        self.celsius = celsius

    @property
    def fahrenheit(self):
        return self.celsius * 9 / 5 + 32

t = Temp(100)
print(t.fahrenheit)

It prints

212.0

It always stays correct

Because it works itself out every time, it can never drift out of step with what it was built from.

Python

class Box:
    def __init__(self, side):
        self.side = side

    @property
    def area(self):
        return self.side * self.side

b = Box(3)
print(b.area)
b.side = 5
print(b.area)

It prints

9
25

Guarding what goes in

A setter runs when someone assigns to it, so the class can quietly refuse silly values. Here the volume can never leave 0 to 100.

Python

class Radio:
    def __init__(self):
        self._volume = 5

    @property
    def volume(self):
        return self._volume

    @volume.setter
    def volume(self, n):
        self._volume = max(0, min(100, n))

r = Radio()
r.volume = 500
print(r.volume)
r.volume = -20
print(r.volume)

It prints

100
0

Try it yourself

Why is there no () after t.fahrenheit?

  • A mistake
  • @property makes it read like a value even though it runs a method
  • Properties are variables
  • Brackets are optional in Python

What does this print?

Python

class Circle:
    def __init__(self, r):
        self.r = r

    @property
    def diameter(self):
        return self.r * 2

c = Circle(4)
print(c.diameter)

Answer them in the app

🪆 Classes Inside Classes

A class that belongs to another

A class written inside another is an inner class. It says "this little thing only makes sense as part of that bigger thing".

Python

class Computer:
    class Screen:
        def __init__(self, size):
            self.size = size

    def __init__(self):
        self.screen = Computer.Screen(15)

c = Computer()
print(c.screen.size)

It prints

15

Reaching it from outside

It is still a real class — you get at it through the outer one, with a dot.

Python

class House:
    class Door:
        def __init__(self, colour):
            self.colour = colour

d = House.Door("red")
print(d.colour)

It prints

red

Try it yourself

Why write a class inside another?

  • It runs faster
  • To show it is only meant to be used as part of the outer one
  • Python requires it
  • To hide it from Python

What does this print?

Python

class Zoo:
    class Cage:
        def __init__(self, animal):
            self.animal = animal

    def __init__(self):
        self.cages = [Zoo.Cage("lion"), Zoo.Cage("bear")]

z = Zoo()
print(len(z.cages))
print(z.cages[0].animal)

Answer them in the app

🏆 Safekeeping Master

Try it yourself

What does this print?

Python

class Counter:
    made = 0

    def __init__(self):
        Counter.made = Counter.made + 1

    @classmethod
    def report(cls):
        return cls.made

Counter()
Counter()
print(Counter.report())

Answer it in the app