OOP II.
5 is an instance of int.
__init___)
self, it is defined inside a method. It is the property of an instance, different instances can have different values.
self.
Let's write a python class Person which stores a name and a title (Mr, Ms, Dr ...).
Let's also write a __str__ 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 __str__(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 __str__(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')
print launcelot
Now we override the inherited __str__ 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 __str__(self):
return self.title + " " + self.name
class Knight(Person):
def __init__(self, name, epithet=""):
super(Knight, self).__init__(name, 'Sir')
self.epithet = epithet
def __str__(self):
if len(self.epithet) > 0:
return self.title + " " + self.name + ", " + self.epithet
else:
return super(Knight, self).__str__()
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 Persons 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 __str__(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 __str__(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 = float(raw_input("Please enter a real number: "))
break
except ValueError:
print "Oops! That was not a real 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)
def read(n):
maximum = float("-inf")
for i in range(n):
x = float(raw_input())
if x > maximum:
maximum = x
return maximum
try:
y = read(3)
except ValueError as e:
print e
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 = str(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