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