# Classes¶

All objects in Python are in fact implemented as classes, but you can program with objects without knowing about classes. Nevertheless, the class concept is a powerful tool and some basic knowledge will make it easier to understand much useful Python information that is floating around.

## A very simple class¶

A class packs together a set of variables and a set of functions. All functions can access all variables. [1] The idea is to encapsulate variables and functions in logical units such that a larger program can be composed by combing such units (classes).

[1] | In classes, the functions are called
methods and the variables are called attributes. |

Here is a trivial class that has one variable `a`

and one
function `dump`

that writes the contents of `a`

:

```
class Trivial:
def __init__(self, a):
self.a = a
def dump(self):
print self.a
```

How can we use this class? First, we must make an *instance* (object)
of the class:

```
t = Trivial(a=4)
```

The syntax `Trivial(a=4)`

implies a call to the `__init__`

method (function)
with `4`

as the value of the argument `a`

. Inside `__init__`

,
we store the value of `a`

as an attribute in the class: `self.a`

.
If there were several methods in the class, all of them could then
access `self.a`

(as if `a`

were some global variable in the class).
The `__init__`

function is called a *constructor* since it is used
to construct an instance (object) of the class.

Having an instance `t`

of class `Trivial`

, we can call the `dump`

method
as follows:

```
t.dump()
```

Even though both `__init__`

and `dump`

have `self`

as first argument,
this argument *is not used in a call*.

The `self`

argument

It takes time and experience to understand the `self`

argument in
class methods.

`self`

must always be the first argument.`self`

is never used in calls.`self`

is used to access attributes and methods inside methods.

We refer to a more comprehensive text on classes
for better explanation of `self`

.

## A class for representing a mathematical function¶

The Python implementation of the mathematical function

can benefit from being implemented as a class. The reason is that \(s\) is a function of one variable, \(t\), so it should be called as \(s(t)\), but the function also contains two parameters, \(v_0\) and \(a\). Using a class, we can pack \(v_0\) and \(a\) together with a function computing \(s(t)\) and that can be called with one argument.

### The class code¶

```
class Distance:
def __init__(self, v0, a):
self.v0 = v0
self.a = a
def __call__(self, t):
v0, a = self.v0, self.a # make local variables
return v0*t + 0.5*a*t**2
```

### Dissection¶

The class has two methods (functions). The name of a method can
be freely chosen by the programmer, say `dump`

as we used above,
but here we have used three
special names, starting and
ending with double underscores, which allows us to use special
attractive syntax in the calls (such methods are actually known as
*special methods*).

The constructor `__init__`

has one purpose: storing data in
class attributes, here `self.v0`

and `self.a`

. We can then access
these data in class methods.

The `__call__`

method is used to evaluate \(s(t)\). It has one
argument (`self`

does not count since it is never used in calls).
This special methods allows us to view a class instance as if
were a function. Let us illustrate by some code:

```
s = Distance(v0=2, a=0.5) # create instance
v = s(t=0.2) # runs s.__call__(t=0.2)
```

The last line has some magic: `s`

is a class instance, not a function,
but still we can write `s(t=0.2)`

or `s(0.2)`

as if `s`

were
a function. This is the purpose of the special method `__call__`

:
to allow such syntax. Actually, `s(0.2)`

means
`s.__call__(0.2)`

. The nice result is that `s`

looks like a
function, it takes one argument `t`

, as the mathematical function \(s(t)\),
but it also contains values of the two parameters \(v_0\) and \(a\).

In general, for a function \(f(x,y,z; p_1,p_2,\ldots,p_n)\), here with three independent variables and \(n\) parameters \(p_1,p_2,\ldots,p_n\), we can pack the function and the parameters together in a class:

```
class F:
def __init__(self, p1, p2, ...):
self.p1 = p1
self.p2 = p2
...
def __call__(self, x, y, z):
# return formula involving x, y, z and self.p1, self.p2 ...
```

The \(f\) function is initialized by

```
f = F(p1=..., p2=..., ...)
```

and later called as `f(x, y, z)`

.