Key Ideas
The key ideas for this part centre on making classes and objects.
Functions
An object is a variable with a coder-defined type. You create such a variable thus:
object_variable_name = ClassName()
The basic class declaration is:
class ClassName():
# stuff
Objects can have variables ("instance variables") and functions inside them. You access these using the dot operator:
object_variable_name.variable_inside = 10
object_variable_name.function_inside()
You set up instance variables in objects by assigning them within a method in the class. In general
this is the __init__
method. The variables also have to be assigned to self
,
which, in this case, will represent the object.
class ClassName():
def __init__(self):
self.variable_inside = 20
Functions within classes are set up like __init__
. In general, you can think of functions
within classes as fairly independent of either object or class (indeed, you can treat them as objects in
their own right and assign labels to them as we saw earlier in the course). Because of this, they need
something to tie them to the object they are associated with. This is done through the self
variable:
class ClassName():
def function_inside(self):
self.variable_inside = 20
self.some_other_function()
When a function is called inside an object, the function is found within the class, and
wrapped up in other code and run. Part of the wrapping up process involves passing the object into
the function called, where it is attached to the first variable in the function
declaration. Traditionally this is given the identifier self
. If you forget to
put this in the function declaration and call the function with nothing, Python will warn you that your declaration takes one less variable than is being passed as the object is being passed invisibly in.
The passing in of the object allows us to talk about variables and other functions associated with the object, as above.
Because of this wrapping process, we usually call functions inside objects "methods" to distinguish them. We call a method called from an object a "bound" method, and a method called from a class a "unbound" method.
object_variable_name.function_inside() # Bound
ClassName.function_inside() # Unbound
In general most methods are actually unbound, the binding only coming from the object itself being pass into the class version of the method.
Because we have added more blocks to the code, there are now a variety of positions variables can first be assigned, and therefore a variety of scopes. The following details which types of variables are defined where, and how we'd refer to them:
global_variable = "gv"
class ClassName():
class_attribute = "ca"
def __init__(self):
self.instance_variable = "iv"
method_local_variable = "mv"
global global_variable
print(GeoPoint.class_attribute)
print(self.instance_variable)
print(method_local_variable)
def another_method(self):
# No access to method_local_variable here.
The main things to note are that object instance variables need to be defined
as associated with self
and that if an instance variable doesn't exist
but you try to refer to it outside the object or class, and a class-level variable with
the same name exists, you get the class-level variable. If it is mutable and you change the
contents, it stays being the class level variable. If you change it, you get a new instance variable.
This is generally confusing, so avoid messing with variables like this and define everything as
instance variables.
We can invisibly inherit code from other classes by:
class SubClass(SuperClassToInherit):
From then on, you can use the functions and variables of the superclass
as if they were in the subclass. You can also write over ("override") the
superclass variables and functions by writing your own variables and functions
with the same name in the subclass (superclass versions can be
referred to with the super
keyword: super.variable
).
Both these things mean that if, for example,
a function requires a specific type of object, the subclass will do instead of the superclass.
In general, we try to avoid direct access to variables in objects. Object Oriented
Philosophy dictates that it is better to get at objects through methods that can
check that it is appropriate for you to adjust or read them. Such methods are
generally called "set_variable_name
" and
"get_variable_name
"; these are known as "mutator" and "accessor"
methods respectively. In Python these are generally attached to calls to
access variables directly, so such calls are redirected, using a
Property setup in the class.
class C():
def __init__(self):
self._x = None
def getx(self):
return self._x
def setx(self, value):
self._x = value
def delx(self):
del self._x
x = property(getx, setx, delx, "I'm the 'x' property.")