Making things happen¶
To analyze a sample in the real world you would put the sample in a special device (like e.g. an Ion Proton Sequencer). While we should include something in our model to reflect this process, we will keep it a bit simpler.
Start by doing Task 5
One for all¶
We notice that the possible bases from which the analysis results are made up actually are the same, independent of which sequencer we use.
This circumstance can be expressed with a class attribute.
A class attribute is shared by all instances of a class.
These class attributes are accessed via the class name and the
from random import choices class DnaSequencer: BASES = ["A", "C", "G", "T"] # If it is written at the indentation level # directly below the class header, it is a class attribute # This time in upper case because we want this to be treated # as a constant, which are written this way by convention def __init__(self, …): … # As before def clean(…): … def analyze_sample(self, sample): if not self.is_clean: print("Sequencer", self.serial_number, "is still dirty - abort") return sequence_length = 2000 dna_sequence = "".join( choices( DnaSequencer.BASES, # Note how it is accessed k=sequence_length ) ) sample.take_analysis_result(dna_sequence) self.is_clean = False
Note that the
analyze_sample-method triggers a modification of the object that was given for the
sample parameter, even though it was not returned.
This kind of “hidden” change of state is called a side-effect.
Those can be convenient but are notoriously hard to debug!
A side-effect-free variant would be to construct a copy of the
sample-parameter, modify the copy and then return this modified copy.
Finish by doing Task 06
Note: Accessibility of Class Attributes¶
In Python you can access class attributes via
ClassName.class_attribute_name. However, you can access class attributes also via
Python’s class attributes and object attributes are stored separately. When you reference the attribute of an object, Python first tries to look it up in the object. If it does not find it there, it checks the class. This is especially important when assigning values to class attributes:
class Car: color = "red" my_car = Car() print(my_car.color) # "red" my_car.color = "green" # change this only for my_car -> becomes an instance attribute print(my_car.color) # "green" print(Car.color) # still "red" Car.color = "blue" # change this fo all cars print(my_car.color) # still "green", since instance attribute is present print(type(my_car).color) # "blue", since we first obtain the type/class and derive the color from that del my_car.color # remove the instance attribute print(my_car.color) # "blue" since it falls back to the class attribute again