OOP II.
Let's write a python class Person
which stores a name and a title (Mr, Ms, Dr ...).
Let's also write a __repr__
method which prints the name nicely.
After that we make a Knight
class inheritted from the Person
. The knights are just like the persons but their title is "Sir"
.
See how the child class calles the parent's constructor (super
).
class Person(object):
def __init__(self, name, title):
self.name = name
self.title = title
def __repr__(self):
return self.title + " " + self.name
class Knight(Person):
def __init__(self, name):
super(Knight, self).__init__(name, 'Sir')
smith = Person('Smith', 'Mr')
launcelot = Knight('Launcelot')
print smith
print launcelot
You can also define members in the child class, but they are only present in the child, not in the parent.
For example a Knight can have an optional epithet!
class Person(object):
def __init__(self, name, title):
self.name = name
self.title = title
def __repr__(self):
return self.title + " " + self.name
class Knight(Person):
def __init__(self, name, epithet=""):
super(Knight, self).__init__(name, 'Sir')
self.epithet = epithet
launcelot = Knight('Launcelot', 'the brave')
launcelot
Now we override the inherited __repr__
method with a new one, a same method but in the child class.
If you don't write a new method, the parent (inherited) method is used instead.
class Person(object):
def __init__(self, name, title):
self.name = name
self.title = title
def __repr__(self):
return self.title + " " + self.name
class Knight(Person):
def __init__(self, name, epithet=""):
super(Knight, self).__init__(name, 'Sir')
self.epithet = epithet
def __repr__(self):
if len(self.epithet) > 0:
return self.title + " " + self.name + ", " + self.epithet
else:
return super(Knight, self).__repr__()
launcelot = Knight('Launcelot', 'the brave')
black = Knight('Black')
robin = Knight('Robin', 'the Not-quite-so-brave-as-Sir-Launcelot')
print launcelot
print black
print robin
One can define a variable in a class but outside a method. This is a class member, it is the same for all of the instances.
For example the "Sir"
title of knights is the same for all knights.
Let's call this special_title
, not to interfere with the Person
s title
member.
The Knight.special_title
will be the same as any instance's .special_title
.
class Person(object):
def __init__(self, name, title):
self.name = name
self.title = title
def __repr__(self):
return self.title + " " + self.name
class Knight(Person):
special_title = 'Sir'
def __init__(self, name, epithet=""):
super(Knight, self).__init__(name, "")
self.epithet = epithet
def __repr__(self):
return Knight.special_title + " " + self.name + ", " + self.epithet
launcelot = Knight('Launcelot', 'the brave')
robin = Knight('Robin', 'the Not-quite-so-brave-as-Sir-Launcelot')
print launcelot
print robin
print robin.special_title, launcelot.special_title, Knight.special_title
The child class can be a parent of a third class, the inheritence structure can be deeper and more complicated.
There are cases when your code does something wrong or encounters some invalid value.
In this case an object of type Exception
is raised (or thrown).
What happens:
Unless
try:
...
except ... :
...
An exception can be handled at any place with this block. If it is not handled, then the exception can break out from any functions.
For example let's raise an exception:
try:
raise Exception('spam', 'eggs')
except Exception as inst:
print type(inst)
print inst.args
print inst
x, y = inst.args
print 'x =', x
print 'y =', y
Now handle an other exception:
try:
str(5) + 5
except TypeError as inst:
print type(inst)
print inst.args
print inst
It is a natural way to handle exceptions in order to correct mistakes.
For example read an integer from the user, but prepare that the user might not enter a proper integer.
while True:
try:
x = int(raw_input("Please enter a number: "))
break
except ValueError:
print "Oops! That was no valid number. Try again..."
print 2*x
Look where the exception happens and where is it handled.
def f(x):
return x + 5 # not good if x is a string
def g(x):
return x + x # good for integers and strings too
x = "5"
y = g(x)
z = f(y)
You can write your own exception class. You can use it to mark that the error happened in one of your own code.
All you have to do is to inherit from the built-in Exception
class.
Optionally, you can have other functions or members in the exception class.
class KnightException(Exception):
pass
lancelot = Person("Lancelot", "Mr")
x = repr(lancelot)
if x[:3] != "Sir":
raise KnightException("a", "b")
As we have seen, the for loop can work on several data types:
for i in L:
L
can be list, tuple, dict.
What type of objects can follow for ... in
?
What happens in a for loop?
for
calls the iter()
function, which returns an iterable object.next()
function which returns the next elements.StopIteration
exception if it ran out of elements.An object is iterable if it has a next
method.
Any object with an __iter__ method can stand after for
if it returns an iterable object.
These are special methods!
r = range(3)
it = iter(r)
next(it)
print next(it)
print next(it)
next(it)
You can write your own iterable and tell python how to iterate over it.
First you need an __iter__
method, the returned object can be the self
or a built-in iterable (list, set, tuple
).
The iterable object have to define a next()
method which returns the elements one after the other.
Raise StopIteration when you want to stop.
class Group(object):
def __init__(self, name, persons):
self.persons = persons
self.name = name
def __iter__(self):
self.index = 0
return self
def next(self):
if self.index >= len(self.persons):
raise StopIteration # tell to stop
self.index += 1
return self.persons[self.index - 1]
kotrt = Group('Knights of The Round Table',
[Knight('Launcelot', 'the brave'),
Knight('Galahad', 'the pure'),
Knight('Bedevere', 'the wise'),
Knight('Robin', 'the Not-quite-so-brave-as-Sir-Launcelot')])
for knight in kotrt:
print knight
Mind that you have to re-start the index in the __iter__
otherwise you could iterate only once over the object.
In this case you could solve this easier, since a list
is already iterable:
class Group:
def __init__(self, name, persons):
self.persons = persons
self.name = name
def __iter__(self):
return iter(self.persons)
kotrt = Group('Knights of The Round Table',
[Knight('Launcelot', 'the brave'),
Knight('Galahad', 'the pure'),
Knight('Bedevere', 'the wise'),
Knight('Robin', 'the Not-quite-so-brave-as-Sir-Launcelot')])
for knight in kotrt:
print knight