We saw early on the importance of algorithms, but what about structuring our code?
What's the best way to structure a larger project made of several files?
Structure we've seen
Clauses
Blocks (Functions; Classes)
Exception handling
Modules
Packages
How do we judge where code should go?
Coupling and Cohesion
Good OO design calls for LOOSE coupling and HIGH cohesion.
Coupling is the degree to which two classes 'know about each other'.
Just uses unobfuscated methods: low coupling.
Uses internal code, e.g. internal variables: high coupling.
Cohesion is the purpose of the class. Is it well focused?
We can talk about accessing a class through its methods that haven't been mangled: its "interface".
Coupling
Close coupling causes issues for code maintenance.
Enhancement or bug fixes to a class should be possible without impacting the interface of methods it presents.
Assumption is that changes will not impact anywhere else, why should it if the interface has not changed.
A close coupled class means if part of the code changed, the alteration has an undetected impact.
Close coupling limits code reuse because you can't just separate off one class.
Cohesion
Classes and packages with high cohesion have a well defined purpose.
They are:
Easier to maintain.
Less likely to require frequent changes.
Easier to reuse in multiple projects / applications.
Tend to be simpler to follow for other developers.
Generally have a well defined interface.
High Cohesion Loose Coupling
Remember when designing a class or package:
Define the purpose and don't let that creep.
Keep the inner variables of the class protected.
Think about how you want other classes to interact with this class and define its interface accordingly.
Ask yourself, how easy would this class or package be to reuse in another project?
Main script
Ideally this just wants to be class method calls, or, at worse, function calls. These should describe the story of the code, for example:
io = IO()
processor = Processor()
data = io.read_data("data.txt")
new_data = processor.process(data)
io.write_data(new_data, "new_data.txt")
This is "Self-documenting code"; it needs no comments to tell the story.
Structure we've seen
Clauses
Blocks (Functions; Classes)
Exception handling
Modules
Packages
GUIs
Others we haven't: threads/concurrency, parallelisation, other programs.
Getting processor time
Usually a program will take over the processor once and run through the code a line at a time in one go.
However, you can force the processor to split up and run several copies of the same bit of code.
Each runs in its own processor "thread" of execution.
For example, you might want a web server to have one thread for each person accessing files so you can send the right file back to the right person, even though lots of requests are being made simultaneously.
In many languages, threading a program will allow chunks of it to run on separate processor units in the computer.
However, the standard CPython implementation allows only one thread (the thread holding the Global Interpreter Lock or GIL) at a time to access objects.
This means multi-threaded programs aren't generally more efficient, unless they are slowed by elements outside the GIL system, such as I/O or processing done in some libraries like numpy.
program = subprocess.Popen("program", stdin=subprocess.PIPE, universal_newlines=True)
program.stdin.write("Hello World") # Not on Windows unless program reads stdin.
Structure we've seen
Clauses
Blocks (Functions; Classes)
Exception handling
Modules
Packages
Threads / Parallelisation
Other programs
GUIs
Where do we get advice?
Design Patterns
"Design Patterns" or, more usually just "Patterns", are standard ways of solving a software problem that people come across again and again. They aren't specific to Python, or any other language.
They save us having to reinvent solutions.
The most influential book in the field was Design Patterns - Elements of Reusable Software by the "Gang of Four" Gamma, Helm, Johnson and Vlissides (1995).
The Gang of Four Patterns
They gave 23 patterns with C examples.
Creational Patterns - classes to make stuff.
The Factory, Abstract Factory, Singleton, Builder, and Prototype.
Structural Patterns - software architecture.
The Adapter, Bridge, Composite, Decorator, Facade, Flyweight and Proxy.
Behavioural Patterns - which classes do what.
The Chain of Responsibility, Command, Interpreter, Iterator, Mediator, Memento, Observer, State, Strategy, Template, and Visitor.
Patterns we've seen
Iterator
Decorators (wrappers, not Python decorators)
Factory (class that quietly produces the right subclass of something)
Why Use Patterns?
Industry standard methods of doing something, therefore other programmers will understand your code.
They encapsulate solutions so you can get on with more tricky things.
They'll give you a deeper understanding of why Python is the way it is.
They're the advice of top experts with years of experience - they'll teach you a lot about good Object Orientated software design.
Book
"Head First Design Patterns":
This book will certainly help you think like an OOProgrammer. However, the examples are written in Java (shouldn't be too bad to convert).
A good starting point for Python is Andrei Boyanov's post "Python Design Patterns: For Sleek And Fashionable Code" which gives a realistic overview of which patterns are needed in Python:
https://www.toptal.com/python/python-design-patterns
Summary
Good Object Orientated code structure encourages robust and reusable software.
Read up on patterns: they'll teach you about good program style, and help solve major issues.
Unit Testing
A way to ensure that methods have the desired 'behaviour'
Decide on behaviour
Write tests
Write code to satisfy tests
Time consuming
Produces robust code
Future maintenance is easier
Other tests
Stress testing
Alpha/Beta releases
Usability
Regression testing
Software testing
Summary
Good code structure centres on building to interfaces: are the inputs and outputs to methods and classes reliable, and do they respond well if things go wrong.
Get this right, and you'll minimise bugs.
Unit testing formalises this.
However, also test usability and go through alpha and beta test releases.