Class and object attributes
Object classes serve to bring together information and class functions that act on that
information. To hold this information, Python classes have simple attributes; plain
variables tied to the object. A very simple example would be to associate a variable called
description to the Molecule class so that for a given instance called molecule, we can do
the following to get its value:
print(molecule.description)
There are two ways in which such variables can be associated with a class. They can be
an object attribute and belong to a specific object, or a class attribute and belong to the
class as a whole. Class attributes are defined outside all function blocks and will have the
same value for all objects. Object attributes are defined inside class functions and must be
set explicitly for each object.
For example, the AminoAcid class might have a dictionary containing the molecular
masses of amino acids, with the key being the one-letter code of the amino acid.
4
The
weight of amino acids is independent of which particular protein molecule we may be
considering,
5
so in this case we could define a dictionary as a class attribute.
class AminoAcid:
massDict = {'A': 71.07, 'R': 156.18, … }
Such class attributes can be accessed either via an object instance or via the class name.
As with functions, attribute access is via the ‘dot’ syntax, but note that there are no
parentheses when you access an attribute; that is how you can tell the difference between a
function call and an attribute access. Here, if you have an object lysine made from the
AminoAcid class, then you can get to the dictionary attribute via:
massDict = lysine.massDict
However, because it is a class attribute you could also access it via the name of the
class:
massDict = AminoAcid.massDict
Class attributes are often used for variables which do not change, because they are
defined for the class as a whole and their values are made available to all instances of that
kind of object.
The AminoAcid class might have another class attribute called acceptableCodes, which
lists the codes that are deemed to be valid for an aminoAcid. In fact, here these could just
be the keys from the massDict dictionary, so we could do:
class AminoAcid:
massDict = {'A': 71.07, 'R': 156.18, … }
acceptableCodes = set(massDict.keys())
In Python 2, the above massDict.keys() call gives back a list of keys for mostly historic
reasons, because lists and dictionaries were introduced into Python long before sets.
Although we could leave it as a list, we turn it into a set for efficiency reasons;
determining whether something is in a set is faster than determining whether it is in a list.
In Python 3 massDict.keys() returns a view onto the keys, and the set() converts this
explicitly into a set.
To access the massDict class variable inside a class function we write ‘self.massDict’.
But it is notable that outside a class function, in the context of the main code block for a
class, there is no self so above we would not write ‘self.massDict.keys()’. Also, when the
Python interpreter sees this usage of massDict it is not yet finished with reading the class
definition, so technically the class does not yet exist. Thus, the syntax
‘AminoAcid.massDict.keys()’ is also not allowed inside the class code block. Instead, a
class attribute that refers to a previous class attribute just uses the name directly, as
illustrated here.
Interestingly, bare function names, without the parentheses, are also class attributes, so
you can get hold of them and then call them later. For example, with the function
getSequence in the Protein class you could do:
getSequenceFunc = Protein.getSequence
getSequenceFunc(protein) # same as protein.getSequence()
The AminoAcid class might have an attribute for the one-letter code of the amino acid.
The code letter depends on the particular amino acid in question, and not on the class as a
whole. Thus, this provides an example where it is more natural to have an object attribute,
rather than a class attribute. Accordingly, we might have a function setCode() where this
attribute, which we imaginatively call code, can be set (based upon the input value) by
using the ‘dot’ syntax to tie the variable to self:
class AminoAcid:
def setCode(self, code):
self.code = code
Then for the object lysine, built from this class, you can get at its code, once it has been
set:
lysine = AminoAcid()
lysine.setCode('Lys')
code = lysine.code
It is relatively easy to see the particular object instance called lysine becomes the self in
the function definition. Taken together, you might have the following implementation of
the getSequence() function to fetch residue codes from a list of AminoAcid objects:
class Protein(Molecule):
def getSequence(self):
return [aminoAcid.code for aminoAcid in self.aminoAcids]
This assumes that the code has indeed been set for all the amino acids making up the
protein; and that the attribute self.aminoAcids is defined somehow for the Protein class.
As it happens, in the situation described here we would probably not introduce the
function setCode() at all. Instead, we would set the important code attribute when we
create the object in what is called the constructor, a special function called whenever a
new object is made, as discussed in the next section. Nonetheless other, less fundamental
attributes might use setter functions.
Lastly we must come clean and admit that Python actually has another way to set
attributes, namely:
aminoAcid.code = 'A'
This syntax allows you to set any attribute to any value, without having to write setter
functions, or even having to define the attribute elsewhere. Because it is so simple, this
syntax is very popular among Python programmers. However, we have chosen to
introduce the more formal way first, because classes depend on knowing which attributes
are present, what the values of the attributes are and possibly how those values relate. For
example, in setCode() we can easily introduce a check to ensure that an amino acid code is
valid. When designing a new class, setting all kinds of attributes in an unregulated way is
not the best way to start. However, as we will go on to discuss in the Properties section at
the end of
Chapter 8
, there is actually a way to get the best of both worlds so that you can
use the above assignment syntax while under-the-hood it will really be using a special
setter function.
Do'stlaringiz bilan baham: |