9. Classes¶
9.1 Class¶
A class is the basis of all data in python.
Everything is an object in python, and a class is how an object is defined.
class Duck:
sound = 'Quack quack.'
movement = 'Walks like a duck.'
def quack(self):
print(self.sound)
def move(self):
print(self.movement)
The first argument is
self
which is a reference to the object (not the class).When an object is created from a class,
self
references that object.Everything that references anything defined in the class is dereferenced off of
self
using a.
9.2 Constructor¶
class Animal:
def __init__(self, **kwargs):
self._type = kwargs['type']
self._name = kwargs['name']
self._sound = kwargs['sound']
def type(self):
return self._type
def name(self):
return self._name
def sound(self):
return self._sound
__init__
special name for a class function which operates as an initializer or constructor.First argument is always
self
which is what makes it amethod
.self._type
is an object variable. Never initialized until after object is defined. Usually have an underscore (tradition) to discourage users from accessing these variables directly.You should typically use an accessor or getter to access the variables. For example:
type
orname
in the above.
9.3 Methods¶
A function that is associated with a class is a method.
Such functions always have the first argument as
self
. That’s what makes them a method, and not just any plain function.The argument can be named something other than
self
as well, but traditional to name itself
.Consider:
class Animal:
def __init__(self, **kwargs):
self._type = kwargs['type'] if 'type' in kwargs else 'kitten'
self._name = kwargs['name'] if 'name' in kwargs else 'fluffy'
self._sound = kwargs['sound'] if 'sound' in kwargs else 'meow'
def type(self, t = None):
if t: self._type = t
return self._type
def name(self, n = None):
if n: self._name = n
return self._name
def sound(self, s = None):
if s: self._sound = s
return self._sound
def __str__(self):
return f'The {self.type()} is named "{self.name()}" and says "{self.sound()}".'
type
,name
,sound
are sometimes referred to as setter-getter functions.For example:
a0 = Animal(type = 'kitten', name = 'fluffy', sound = 'rwar')
a1 = Animal(type = 'duck', name = 'donald', sound = 'quack')
a0.sound('bark')
can be used to set the sound of
a0
and get it.__str__
is a special method name. More info in the documentationIn this case,
__str__
can be used to print the object asprint(a0)
.
9.4 Data¶
Data may be associated with a class or an object.
In the above piece of code, the variabes
self._var_name
is an object variable. They don’t exist in the class, they’re bound to the object and not the class itself.It’s not good practice to set the name of
a0
asa0._name = 'Joe'
(that’s why it starts with an underscore). Indeed, it only changes the_name
variable ina0
, not ina1
.You could also define a variable in the class (not in any method), and that would be a class variable. It only exists in the class. The object just draws it from the class.
If a class variable is changed in one object, it’s changed in the other object as well (defined based on the same class).
Class variables are not encapsulated.
In general, class variables shouldn’t be mutable.
9.5 Class inheritance¶
class Animal:
def __init__(self, **kwargs):
if 'type' in kwargs: self._type = kwargs['type']
if 'name' in kwargs: self._name = kwargs['name']
if 'sound' in kwargs: self._sound = kwargs['sound']
def type(self, t = None):
if t: self._type = t
try: return self._type
except AttributeError: return None
def name(self, n = None):
if n: self._name = n
try: return self._name
except AttributeError: return None
def sound(self, s = None):
if s: self._sound = s
try: return self._sound
except AttributeError: return None
class Kitten(Animal):
def __init__(self, **kwargs):
self._type = 'kitten'
if 'type' in kwargs: del kwargs['type']
super().__init__(**kwargs)
<>
def kill(self, s):
print(f'{self.name()} will now kill all {s}!')
Animal
is a base class.Kitten
is a subclass that inheritsAnimal
.super
is a function used to override__init__
in the subclass and inherit the parent class.
9.6 Iterator¶
class inclusive_range:
def __init__(self, *args):
numargs = len(args)
self._start = 0
self._step = 1
if numargs < 1:
raise TypeError(f'expected at least 1 argument, got {numargs}')
elif numargs == 1:
self._stop = args[0]
elif numargs == 2:
(self._start, self._stop) = args
elif numargs == 3:
(self._start, self._stop, self._step) = args
else: raise TypeError(f'expected at most 3 arguments, got {numargs}')
<>
self._next = self._start
<>
def __iter__(self):
return self
<>
def __next__(self):
if self._next > self._stop:
raise StopIteration
else:
_r = self._next
self._next += self._step
return _r
A class that provides a sequence of items.
The constructor
__init__
sets up all the variables based on the number of variables.__iter__
is a special method that identifies the object as an iterator object.__next__
: A construct such as thefor
loop looks for this method to treat the class as an iterator.Generator functions are a simpler way to do the above.
yield
was implemented after iterator functions.