
The most universal approach to installing Python is to visit the official Python download page at,
https://www.python.org/downloads/
Normally this page will detect your operating system from the user agent in your browser and select which install is appropriate for you.
There will be 64 and 32 bit versions for your operating system. At the time of writing this documentation, the option of downloading the 64bit version was the most common, and the version was 3.11.0.
The code in this documentation will be using a simplified generic style of Python that should work in all versions since Python version 3.
To test if you already have python on your system, depending on your operating system, whether Windows, Linux or Mac OSX, open a terminal/bash/cmd or PowerShell prompt.
and type
$ python -V
Note the capital V in the above command.
Sometimes python is named as python3
So you can also try
$ python3 -V
You are looking for a response that indicates you have Python 3 or above installed. Not an error, or Python 2.x
On my windows workstation, if I use PowerShell, the response is
PS> python -V
Python 3.11.0
I have Python3 already installed using the official python link from above.
If you are using a recent version of Linux or Mac OSX, then the command to check for the Python version on your system is most likely to be,
$ python3 -V
Remember to follow the official install instructions for your operating system at https://www.python.org/downloads/
Python Interactive Console Versus *.py Scripts
You can execute Python code by writing your scripts into text files and commonly using the .py extension. Text files on most operating systems will be UTF-8 encoded by default. Python also reads UTF-8 encoded text files by default.
Create a new text file called example.py and add the following text.
print("Hello World!")
and then you can execute it using python or python3 depending on your operating system and Python version.
PS> python ./example.py
Hello World!
You can also enter Python code directly into the Python Interactive Console by typing just python or python3 from the command line and then press Enter . You then get a prompt like below.
PS> python
Python 3.11.0 (main, Oct 24 2022, 18:26:48) [MSC v.1933 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>>
You can now enter python commands directly.
>>> print("Hello World!")
Hello World!
>>>
To exit the Python Interactive Console, you can usually type quit() or press Ctrl-Z then press Enter
This documentation will show examples of using both *.py scripts and the interactive console to execute Python. Look out for the >>> characters in the code blocks to indicate if I was using the Python Interactive Console or a *.py script.
PEP8
The code styling in this documentation is formatted using mostly PEP8 styling recommendations.
UPPER_CASE : Constants will be defined using UPPER_CASE naming style.
PascalCase : Class names will use PascalCase naming style
snake_case : For variables names, method names and method arguments.
Docstrings : Classes and methods contain extra documentation that is descriptive text enclosed in " or """ for multiline strings.
_leading_underscore : Use a leading underscore to indicate variables that should be considered as private class variables.
See PEP-0008 : https://www.python.org/dev/peps/pep-0008/
Pylint
I use the Pylint tool to check for code styling recommendations.
On most operating systems you would generally install Pylint by using the PIP or PIP3 installer.
PS> pip install pylint
If using VSCode, open the Command Palette (Ctrl+Shift+P), then set the
Python: Enable Linting to on
and
Python: Select Linter to Pylint
Unified Modeling Language (UML) Diagrams are used throughout this course to help describe the patterns.
Directed Association : A filled arrow with a line.
Extends/Inherits : An unfilled arrow, with a line pointing to the class that is being extended/inherited.
Implements : An unfilled arrow, with a dashed line pointing to the interface that is being implemented.
Aggregates : An unfilled diamond with a line and arrow head.
Composition : A filled diamond with a line and arrow head.
Pseudocode Annotation : A box with a dashed line and a circle placed near a class method.
When developing code, you may instantiate objects directly in methods or in classes. While this is quite normal, you may want to add an extra abstraction between the creation of the object and where it is used in your project.
You can use the Factory pattern to add that extra abstraction. The Factory pattern is one of the easiest patterns to understand and implement.
Adding an extra abstraction will also allow you to dynamically choose classes to instantiate based on some kind of logic.
Before the abstraction, your class or method would directly instantiate an object of a class. After adding the factory abstraction, the concrete product (object) is now created outside of the current class/method, and now in a subclass instead.
Imagine an application for designing houses and the house has a chair already added on the floor by default. By adding the factory pattern, you could give the option to the user to choose different chairs, and how many at runtime. Instead of the chair being hard coded into the project when it started, the user now has the option to choose.
Adding this extra abstraction also means that the complications of instantiating extra objects can now be hidden from the class or method that is using it.
This separation also makes your code easier to read and document.
The Factory pattern is really about adding that extra abstraction between the object creation and where it is used. This gives you extra options that you can more easily extend in the future.
Terminology
Concrete Creator: The client application, class or method that calls the Creator (Factory method).
Product Interface: The interface describing the attributes and methods that the Factory will require in order to create the final product/object.
Creator: The Factory class. Declares the Factory method that will return the object requested from it.
Concrete Product: The object returned from the Factory. The object implements the Product interface.
In this concept example, the client wants an object named b
Rather than creating b directly in the client, it asks the creator (factory) for the object instead.
The factory finds the relevant class using some kind of logic from the attributes of the request. It then asks the subclass to instantiate the new object that it then returns as a reference back to the client asking for it.
An example use case is a user interface where the user can select from a menu of items, such as chairs.
The user has been given a choice using some kind of navigation interface, and it is unknown what choice, or how many the user will make until the application is actually running and the user starts using it.
So, when the user selected the chair, the factory then takes some property involved with that selection, such as an ID, Type or other attribute and then decides which relevant subclass to instantiate in order to return the appropriate object.
ABCMeta classes are a development tool that help you to write classes that conform to a specified interface that you've designed.
ABCMeta refers to Abstract Base Classes.
The benefits of using ABCMeta classes to create abstract classes is that your IDE and Pylint will indicate to you at development time whether your inheriting classes conform to the class definition that you've asked them to.
Abstract interfaces are not instantiated directly in your scripts, but instead implemented by subclasses that will provide the implementation code for the abstract interface methods. E.g., you don't create IChair, but you create SmallChair that implements the methods described in the IChair interface.
An abstract interface method is a method that is declared, but contains no implementation. The implementation happens at the class that inherits the abstract class.
You don't need to use ABCMeta classes and interfaces that you have created in your final python code. You code will still work without them.
You can try it by removing the interfaces from all of the chair classes above, and you will see that your python program will still run.
eg, change
class BigChair(IChair):
to
class BigChair():
and it will still work.
While it is possible to ensure your classes are correct without using abstract classes, it is often easier to use abstract classes as a backup method of checking correctness, especially if your projects become very large and involve many developers.
Note that in all my code examples, the abstract classes are prefixed with a capital I, to indicate that they are abstract interfaces. They have no code in their methods. They do not require a self or cls argument due to the use of @staticmethod . The inheriting class will implement the code in each of the methods that the abstract class is describing. If subclasses are inheriting an abstract base class, and they do not implement the methods as described, there will be Pylint error or warning message (E0110).
The Abstract Factory Pattern adds an abstraction layer over multiple other creational pattern implementations.
To begin with, in simple terms, think if it as a Factory that can return Factories. Although you will find examples of it also begin used to return Builder, Prototypes, Singletons or other design pattern implementations.
Terminology
Client: The client application that calls the Abstract Factory. It's the same process as the Concrete Creator in the Factory design pattern.
Abstract Factory: A common interface over all of the sub factories.
Concrete Factory: The sub factory of the Abstract Factory and contains method(s) to allow creating the Concrete Product.
Abstract Product: The interface for the product that the sub factory returns.
Concrete Product: The object that is finally returned.
An example use case may be that you have a furniture shop front. You sell many different kinds of furniture. You sell chairs and tables. And they are manufactured at different factories using different unrelated processes that are not important for your concern. You only need the factory to deliver.
You can create an extra module called FurnitureFactory , to handle the chair and table factories, thus removing the implementation details from the client.
Your Python code may produce errors. It happens to everybody. It is hard to foresee all possible errors, but you can try to handle them in case anyway.
Use the Try , Except and optional finally keywords to manage error handling.
In the example code, if no chair or table is returned, an Exception error is raised and it includes a text string that can be read and written to the console.
Within your code you can use the raise keyword to trigger Python built in exceptions or even create your own.
See resources for code.
If 'WoodenTable' is requested from the factory, it will print No Factory Found
You don't need to always raise an exception to make one happen. In that case you can handle the possibility of any type of error using just try and except , with the optional finally if you need it.
The above code produces the message An Error Occurred because my_var is not defined.
The try/except allows the program to continue running, as can be verified by the line printed in the finally statement. So, this has given you the opportunity to manage any unforeseen errors any way you wish.
Alternatively, if your code didn't include the try/except and optional finally statements, the Python interpreter would return the error NameError: name 'my_var' is not defined and the program will crash at that line.
Also note how the default Python inbuilt error starts with NameError . You can handle this specific error explicitly using an extra except keyword.
See resources for code.
You can add exception handling for as many types of errors as you wish.
The Builder Pattern is a creational pattern whose intent is to separate the construction of a complex object from its representation so that you can use the same construction process to create different representations.
The Builder Pattern tries to solve,
How can a class create different representations of a complex object?
How can a class that includes creating a complex object be simplified?
The Builder and Factory patterns are very similar in the fact they both instantiate new objects at runtime. The difference is when the process of creating the object is more complex, so rather than the Factory returning a new instance of ObjectA, it calls the builders director constructor method ObjectA.construct() that goes through a more complex construction process involving several steps. Both return an Object/Product.
Terminology
Product: The Product being built.
Builder: Builds the concrete product. Implements the IBuilder interface.
Builder Interface: The Interface that the Concrete builder should implement.
Director: Has a construct() method that when called creates a customized product.
Using the Builder Pattern in the context of a House Builder.
There are multiple directors that can create their own complex objects.
Note that in the IglooDirector class, not all of the methods of the HouseBuilder were called.
The builder can construct complex objects in any order and include/exclude whichever parts it likes.
In the file ./builder/builder_concept.py
The [] is indicating a Python List.
The list can store multiple items, they can be changed, they can have items added and removed, can be re-ordered, can be pre-filled with items when instantiated and is also very flexible.
Lists are used in almost every code example in this documentation. You will see all the many ways they can be used.
In fact, a list was used in the Abstract Factory example. The code creates a list at runtime including the strings 'SmallChair', 'MediumChair' and 'BigChair'. If the value in furniture equals the same string as one of those items in the list, then the condition is true and the code within the if statement block will execute.
The Prototype design pattern is good for when creating new objects requires more resources than you want to use or have available. You can save resources by just creating a copy of any existing object that is already in memory.
E.g., A file you've downloaded from a server may be large, but since it is already in memory, you could just clone it, and work on the new copy independently of the original.
In the Prototype patterns interface, you create a static clone method that should be implemented by all classes that use the interface. How the clone method is implemented in the concrete class is up to you. You will need to decide whether a shallow or deep copy is required.
A shallow copy, copies and creates new references one level deep,
A deep copy, copies and creates new references for all levels.
In Python you have mutable objects such as Lists, Dictionaries, Sets and any custom Objects you may have created. A shallow copy, will create new copies of the objects with new references in memory, but the underlying data, e.g., the actual elements in a list, will point to the same memory location as the original list/object being copied. You will now have two lists, but the elements within the lists will point to the same memory location. So, changing any elements of a copied list will also affect the original list. Be sure to test your implementation that the copy method you use works as expected. Shallow copies are much faster to process than deep copies and deep copies are not always necessary if you are not going to benefit from using it.
Terminology
Prototype Interface: The interface that describes the clone() method.
Prototype: The Object/Product that implements the Prototype interface.
Client: The client application that uses and creates the ProtoType.
By default, it will shallow copy the object you've asked to be cloned. The object can be any type from number to string to dictionary to anything custom that you've created.
In my example, I have created a list of numbers. At first impressions, when this list is copied, it will appear that the list was fully cloned. But the inner items of the list were not. They will point to the same memory location as the original list; however, the memory identifier of the new list is new and different from the original.
In the MyClass.clone() method, there is a line self.field.copy() that is commented out. Uncomment out this line, and comment out the line before it to now be # self.field . Re execute the file, and now the list items will be copied as well. This however is still not a full deep copy. If the list items were actually other lists, dictionaries or other collections, then only the 1st level of that copy would have been cloned to new memory identifiers. I call this a 2-level copy.
For a full recursive copy, use the copy.deepcopy() method that is part of an extra dedicated copy import included with Python. I demonstrate this in the example use case further down.
Remember that full deep copies can potentially be much slower for very complicated object hierarchies.
In this example, an object called document is cloned using shallow, 2 level shallow, and full recursive deep methods.
The object contains a list of two lists. Four copies are created, and each time some part of the list is changed on the clone, and depending on the method used, it can affect the original object.
When cloning an object, it is good to understand the deep versus shallow concept of copying.
The Python id() function returns the memory address of an object.
All objects in Python will have a memory address.
You can test if an object is unique in Python by comparing its ID.
In the examples above, I can tell how deep the copies of the dictionaries and lists were, because the IDs of the inner items will be different. I.e., they point to different memory identifiers.
Note that every time you start a Python process, the IDs assigned at runtime will likely be different.
Also note that integers in Python also have their own IDs.
print(id(0))
print(id(1))
print(id(2))
Outputs
2032436013328
2032436013360
2032436013392
Sometimes you need an object in an application where there is only one instance.
You don't want there to be many versions, for example, you have a game with a score and you want to adjust it. You may have accidentally created several instances of the class holding the score object. Or, you may be opening a database connection, there is no need to create many, when you can use the existing one that is already in memory. You may want a logging component, and you want to ensure all classes use the same instance. So, every class could declare their own logger component, but behind the scenes, they all point to the same memory address (id).
By creating a class using the Singleton pattern, you can enforce that even if a second instance was created, it will still refer to the original.
The Singleton can be accessible globally, but it is not a global variable. It is a class that can be instanced at any time, but after it is first instanced, any new instances will point to the same instance as the first.
For a class to behave as a Singleton, it should not contain any references to self but use static variables, static methods and/or class methods.
In the example, there are three games created. They are all independent instances created from their own class, but they all share the same leaderboard. The leaderboard is a singleton.
It doesn't matter how the Games where created, or how they reference the leaderboard, it is always a singleton.
Each game independently adds a winner, and all games can read the altered leaderboard regardless of which game updated it.
In the file ./singleton/leaderboard.py,
The {} is indicating a Python Dictionary.
A Dictionary can be instantiated using the curly braces {} or dict()
The Dictionary is similar to a List, except that the items are key:value pairs.
The Dictionary can store multiple key:value pairs, they can be changed, can be added and removed, can be re-ordered, can be pre-filled with key:value pairs when instantiated and is very flexible.
Since Python 3.7, dictionaries are ordered in the same way that they are created.
The keys of the dictionary are unique.
You can refer to the dictionary items by key, which will return the value.
You can change the value at a key,
You can add new key:value pairs, and remove them by using the key.
You can order a dictionary alphabetically by key.
The decorator pattern is a structural pattern, that allows you to attach additional responsibilities to an object at runtime.
The decorator pattern is used in both the Object Oriented and Functional paradigms.
The decorator pattern is different than the Python language feature of Python Decorators in its syntax and complete purpose. It is a similar concept in the way that it is a wrapper, but it also can be applied at runtime dynamically.
The decorator pattern adds extensibility without modifying the original object.
The decorator forwards requests to the enclosed object and can perform extra actions.
You can nest decorators recursively.
Terminology
Component Interface: An interface for objects.
Component: The object that may be decorated.
Decorator: The class that applies the extra responsibilities to the component being decorated. It also implements the same component interface.
Let's create a custom class called Value that will hold a number.
Then add decorators that allow addition (Add) and subtraction (Sub) to a number (Value).
The Add and Sub decorators can accept integers directly, a custom Value object or other Add and Sub decorators.
Add, Sub and Value all implement the IValue interface and can be used recursively.
Summary
Use the decorator when you want to add responsibilities to objects dynamically without affecting the inner object.
You want the option to later remove the decorator from an object in case you no longer need it.
It is an alternative method to creating multiple combinations of subclasses. I.e., Instead of creating a subclass with all combinations of objects A, B, C in any order, and including/excluding objects, you could create 3 objects that can decorate each other in any order you want. E.g., (D(A(C))) or (B(C)) or (A(B(A(C)))
The decorator, compared to using static inheritance to extend, is more flexible since you can easily add/remove the decorators at runtime. E.g., use in a recursive function.
A decorator supports recursive composition. E.g., halve(halve(number))
A decorator shouldn't modify the internal objects data or references. This allows the original object to stay intact if the decorator is later removed.
When you print() an object, it will print out the objects type and memory location in hex.
You can change this default output by implementing the __str__ dunder method in your class. Dunder is short for saying double underscore.
Dunder methods are predefined methods in python that you can override with your own implementations.
Syntax: getattr(object, attribute, default)
Sometimes classes have been written and you don't have the option of modifying their interface to suit your needs. This happens if the method you are calling is on a different system across a network, a library that you may import or generally something that is not viable to modify directly for your particular needs.
The Adapter design pattern solves these problems:
How can a class be reused that does not have an interface that a client requires?
How can classes that have incompatible interfaces work together?
How can an alternative interface be provided for a class?
You may have two classes that are similar, but they have different method signatures, so you create an Adapter over top of one of the method signatures so that it is easier to implement and extend in the client.
An adapter is similar to the Decorator in the way that it also acts like a wrapper to an object. It is also used at runtime; however, it is not designed to be used recursively.
It is an alternative interface over an existing interface. It can also provide extra functionality that the interface being adapted may not already provide.
The adapter is similar to the Facade, but you are modifying the method signature, combining other methods and/or transforming data that is exchanged between the existing interface and the client.
The Adapter is used when you have an existing interface that doesn't directly map to an interface that the client requires. So, then you create the Adapter that has a similar functional role, but with a new compatible interface.
Terminology
Target: The domain specific interface or class that needs to be adapted.
Adapter Interface: The interface of the target that the adapter will need to implement.
Adapter: The concrete adapter class containing the adaption process.
Client: The client application that will use the Adapter.
The example client can manufacture a Cube using different tools. Each solution is invented by a different company. The client user interface manages the Cube product by indicating the width, height and depth. This is compatible with the company A that produces the Cube tool, but not the company B that produces their own version of the Cube tool that uses a different interface with different parameters.
In this example, the client will re-use the interface for company A's Cube and create a compatible Cube from company B.
An adapter will be needed so that the same method signature can be used by the client without the need to ask company B to modify their Cube tool for our specific domains use case.
My imaginary company needs to use both cube suppliers since there is a large demand for cubes and when one supplier is busy, I can then ask the other supplier.
Syntax: isinstance(object, type)
Returns: True or False
You can use the inbuilt function isinstance() to conditionally check the type of an object.
The time module provides time related functions which are outlined in more detail at https://docs.python.org/3/library/time.html
Sometimes you have a system that becomes quite complex over time as more features are added or modified. It may be useful to provide a simplified API over it. This is the Facade pattern.
The Facade pattern essentially is an alternative, reduced or simplified interface to a set of other interfaces, abstractions and implementations within a system that may be full of complexity and/or tightly coupled.
It can also be considered as a higher-level interface that shields the consumer from the unnecessary low-level complications of integrating into many subsystems.
This is an example of a game engine API. The facade layer is creating one streamlined interface consisting of several methods from several larger API backend systems.
The client could connect directly to each subsystems API and implement its authentication protocols, specific methods, etc. While it is possible, it would be quite a lot of consideration for each of the development teams, so the facade API unifies the common methods that becomes much less overwhelming for each new the client developer to integrate into.
The decimal module provides support for correctly rounded decimal floating-point arithmetic.
If representing money values in python, it is better to use the decimal type rather than float .
Floats will have rounding errors versus decimal.
Note that the Python runtime does not enforce the type hints and that they are optional. However, where they are beneficial is in the IDE of your choice or other third party tools such type checkers.
For type checking, you can install an extra module called mypy
$ pip install mypy
$ mypy eample.py
The Bridge pattern is similar to the Adapter pattern except in the intent that you developed it.
The Bridge is an approach to refactor already existing code, whereas the Adapter creates an interface on top of existing code through existing available means without refactoring any existing code or interfaces.
The motivation for converting your code to the Bridge pattern is that it may be tightly coupled. There is logic and abstraction close together that is limiting your choices in how you can extend your solution in the way that you need.
In this example, I draw a square and a circle. Both of these can be categorized as shapes.
The shape is set up as the abstraction interface. The refined abstractions, Square and Circle, implement the IShape interface.
When the Square and Circle objects are created, they are also assigned their appropriate implementers being SquareImplementer and CircleImplementer .
When each shape's draw method is called, the equivalent method within their implementer is called.
The Square and Circle are bridged and each implementer and abstraction can be worked on independently.
A Python Tuple is similar to a List. Except that the items in the Tuple are ordered, unchangeable and allow duplicates.
A Tuple can be instantiated using the round brackets () or tuple() , verses [] for a List and {} for a Set or Dictionary.
The *args argument takes all arguments that were sent to this method, and packs them into a Tuple.
It is useful when you don't know how many arguments, or what types, will be sent to a method, and you want the method to support any number of arguments or types being sent to it.
The Composite design pattern is a structural pattern useful for hierarchal management.
The Composite design pattern,
allows you to represent individual entities(leaves) and groups of leaves at the same.
is a structural design pattern that lets you compose objects into a changeable tree structure.
is great if you need the option of swapping hierarchal relationships around.
allows you to add/remove components to the hierarchy.
provides flexibility of structure
Examples of using the Composite Design Pattern can be seen in a filesystem directory structure where you can swap the hierarchy of files and folders, and also in a drawing program where you can group, un-group, transform objects and change multiple objects at the same time.
Demonstration of a simple in memory hierarchal file system.
A root object is created that is a composite.
Several files (leaves) are created and added to the root folder.
More folders (composites) are created, and more files are added, and then the hierarchy is reordered.
Conditional expressions an alternate form of if/else statement.
This conditional expression follows the format
value_if_true if condition else value_if_false
The Flyweight pattern, describes how you can share objects rather than creating thousands of almost repeated objects unnecessarily.
Instead of creating thousands of objects that share common attributes, and result in a situation where a large amount of memory or other resources are used, you can modify your classes to share multiple instances simultaneously by using some kind of reference to the shared object instead.
The best example to describe this is a document containing many words and sentences and made up of many letters. Rather than storing a new object for each individual letter describing its font, position, color, padding and many other potential things. You can store just a lookup id of a character in a collection of some sort and then dynamically create the object with its proper formatting etc., only as you need to.
This approach saves a lot of memory at the expense of using some extra CPU instead to create the object at presentation time.
In this example, I create a dynamic table with 3 rows and 3 columns each. The columns are then filled with some kind of text, and also chosen to be left, right or center aligned.
The letters are the flyweights and only a code indicating the letter is stored. The letters and numbers are shared many times.
The columns are the contexts and they pass the extrinsic vales describing the combination of letters, the justification left, right or center, and the width of the table column that is then used for the space padding.
In ./flyweight/column.py, there are commands center(), ljust() and rjust() .
These are special commands on strings that allow you to pad strings and align them left, right, center depending on total string length.
The Proxy design pattern is a class functioning as an interface to another class or object.
A Proxy could be for anything, such as a network connection, an object in memory, a file, or anything else you need to provide an abstraction between.
Types of proxies,
Virtual Proxy: An object that can cache parts of the real object, and then complete loading the full object when necessary.
Remote Proxy: Can relay messages to a real object that exists in a different address space.
Protection Proxy: Apply an authentication layer in front of the real object.
Smart Reference: An object whose internal attributes can be overridden or replaced.
Additional functionality can be provided at the proxy abstraction if required. E.g., caching, authorization, validation, lazy initialization, logging.
The proxy should implement the subject interface as much as practicable so that the proxy and subject appear identical to the client.
The Proxy Pattern can also be called Monkey Patching or Object Augmentation
In this example, I dynamically change the class of an object. So, I am essentially using an object as a proxy to other classes.
Every time the tell_me_the_future() method is called; it will randomly change the object to use a different class.
The object PROTEUS will then use the same static attributes and class methods of the new class instead.
You can change the class of an object by executing self.__class__ = SomeOtherClass
Note that doing this does not affect any attributes created during initialisation, eg self.instance_attribute = 'abc', since the object itself hasn't changed. Only the references to it's methods and static attributes have been replaced with the methods and static attributes of the new class.
To avoid circular import errors, you can import modules using the form.
import module
and when the import is actually needed in some method
OBJECT = module.ClassName
The Command pattern is a behavioral design pattern, in which an abstraction exists between an object that invokes a command, and the object that performs it.
E.g., a button will call the Invoker, that will call a pre-registered Command, that the Receiver will perform.
A Concrete Class will delegate a request to a command object, instead of implementing the request directly.
The command pattern is a good solution for implementing UNDO/REDO functionality into your application.
Uses:
GUI Buttons, menus
Macro recording
Multi-level undo/redo
Networking - send whole command objects across a network, even as a batch
Parallel processing or thread pools
Transactional behavior
Wizards
The single leading underscore _variable, on your class variables is a useful indicator to other developers that this property should be considered private.
Private, in C style languages, means that the variable/field/property is hidden and cannot be accessed outside of the class. It can only be used internally by its own class methods.
Python does not have a public/private accessor concept so the variable is not actually private and can still be used outside of the class in other modules.
It is just a useful construct that you will see developers use as a recommendation not to reference this variable directly outside of this class, but use a dedicated method or property instead.
Chain of Responsibility pattern is a behavioral pattern used to achieve loose coupling in software design.
In this pattern, an object is passed to a Successor, and depending on some kind of logic, will or won't be passed onto another successor and processed. There can be any number of different successors and successors can be re-processed recursively.
This process of passing objects through multiple successors is called a chain.
The object that is passed between each successor does not know about which successor will handle it. It is an independent object that may or may not be processed by a particular successor before being passed onto the next.
The chain that the object will pass through is normally dynamic at runtime, although you can hard code the order or start of the chain, so each successor will need to comply with a common interface that allows the object to be received and passed onto the next successor.
In the ATM example below, the chain is hard coded in the client first to dispense amounts of £50s, then £20s and then £10s in order.
This default chain order helps to ensure that the minimum number of notes will be dispensed. Otherwise, it might dispense 5 x £10 when it would have been better to dispense 1 x £50.
Each successor may be re-called recursively for each denomination depending on the value that was requested for withdrawal.
Normally division uses a single / character and will return a float even if the numbers are integers or exactly divisible with no remainder,
Python Version 3 also has an option to return an integer version (floor) of the number by using the double // characters instead.
The input command allows your script to accept user input from the command prompt.
Note that in Python 2.x, use the raw_input() command instead of input().
The Observer pattern is a software design pattern in which an object, called the Subject (Observable), manages a list of dependents, called Observers, and notifies them automatically of any internal state changes by calling one of their methods.
The Observer pattern follows the publish/subscribe concept. A subscriber, subscribes to a publisher. The publisher then notifies the subscribers when necessary.
The observer stores state that should be consistent with the subject. The observer only needs to store what is necessary for its own purposes.
This example mimics the MVC approach described earlier.
There is an external process called a DataController, and a client process that holds a DataModel and multiple DataViews that are a Pie graph, Bar graph and Table view.
Note that this example runs in a single process, but imagine that the DataController is actually an external process running on a different server.
The DataModel subscribes to the DataController and the DataViews subscribe to the DataModel.
The client sets up the various views with a subscription to the DataModel.
The hypothetical external DataController then updates the external data, and the data then propagates through the layers to the views.
Note that in reality this example would be much more complex if multiple servers are involved. I am keeping it brief to demonstrate one possible use case of the observer pattern.
Also note that in the DataController, the references to the observers are contained in a Set, while in the DataModel I have used a Dictionary instead, so that you can see an alternate approach.
A Python Set is similar to a List. Except that the items in the Set are guaranteed to be unique, even if you try to add a duplicate. A set is a good choice for keeping a collection of observables, since the problem of duplicate observables is automatically handled.
A Set can be instantiated pre-filled as {1, 2, 3} surrounded by curly braces or using the keyword set(), verses [] or list() for a List and () or tuple() for a Tuple. It is not the same as a Dictionary, that uses {}, since the dictionary items are created as key:value pairs. ie {"a": 1, "b": 2, "c": 3}
The Interpreter pattern helps to convert information from one language into another.
The language can be anything such as words in a sentence, numerical formulas or even software code.
The process is to convert the source information, into an Abstract Syntax Tree (AST) of Terminal and Non-Terminal expressions that all implement an interpret() method.
A Non-Terminal expression is a combination of other Non-Terminal and/or Terminal expressions.
Terminal means terminated, i.e., there is no further processing involved.
An AST root starts with a Non-Terminal expression and then resolves down each branch until all expressions terminate.
An example expression is A + B .
The A and B are Terminal expressions and the + is Non-Terminal because it depends on the two other Terminal expressions.
The example use case will expand on the concept example by dynamically creating the AST and converting roman numerals to integers as well as calculating the final result.
Sometimes you want part of a string. In the example code, when I am interpreting the roman numerals, I am comparing the first one or two characters in the context with IV or CM or many other roman numeral combinations. If the match is true then I continue with further commands.
The format is
string[start: end: step]
test = "MMMCMXCIX"
print(test[0: 3])
It is used in a very similar way to the __str__ dunder method. You can override it to produce a string representation of a class.
If you have a class that also overrides the __str__ dunder method already, then printing it will default to use the __str__ override instead.
You should prefer to use the __str__ method to print human readable versions of your classes instead. If you require something with more information that could alternatively be used for debugging, or for object recreation using eval() , and not intended for reading by users, then it is generally recommended to implement the __repr__ method instead of __str__ for these more programmatic purposes.
To specifically use a __repr__ output, where both __repr__ and __str__ are both implemented in the same class, then use object.__repr__() or repr(A).
The Iterator will commonly contain two methods that perform the following concepts.
next: returns the next object in the aggregate (collection, object).
has_next: returns a Boolean indicating if the Iterable is at the end of the iteration or not.
The benefits of using the Iterator pattern are that the client can traverse a collection of aggregates(objects) without needing to understand their internal representations and/or data structures.
One reason for not using the inbuilt Python data structures that implement iterators already, or using the iter function directly over an existing collection, is in the case when you want to create an object that can dynamically create iterated objects, you want a custom order of objects or an infinite iterator.
The iterator in this brief example will return the next number in the iterator multiplied by 2 modulus 11. It dynamically creates the returned object (number) at runtime.
It has no has_next() method since the result is modulated by 11, that will loop the results no matter how large the iterator index is. It will also appear to alternate between a series of even numbers and odd numbers.
Python Lists, Dictionaries, Sets and Tuples are already iterable, so if you want basic iteration for use in a for loop, then you only need to add your objects into one of those and it can be used right away.
also, you can instantiate an iterable from the List, Dictionary, Tuple or Set by using the Python iter() method, or its own __iter__() dunder method, and then iterate over that using the __next__() method.
The Python iter() method also can accept a sentinel parameter.
The sentinel parameter is useful for dynamically created objects that are returned from an iterator and indicates where the last item is in the iterator by raising a StopIteration exception.
Usage : iter(object, sentinel)
Objects communicate through the Mediator rather than directly with each other.
As a system evolves and becomes larger and supports more complex functionality and business rules, the problem of communicating between these components becomes more complicated to understand and manage. It may be beneficial to refactor your system to centralize some or all of its functionality via some kind of mediation process.
The mediator pattern is similar to implementing a Facade pattern between your objects and processes. Except that the structure of the Mediator allows multi directional communication between the objects or processes that would normally be interacting directly with each other.
While the Facade is a structural pattern, and the Mediator also implies structure in the way that it exists between two or more other objects or processes, it also allows changing the behavior of the interaction to make it more cooperative in some way. E.g., the centralization of application logic, managing the routing behavior, caching, logging, etc.
In this example use case, we will implement some behavior into the mediation process.
Before the mediation logic is added, consider that the below example is a series of components all subscribed to a central location being the subject. They all implement the Observer pattern.
Each component is updated independently by external forces, but when it has new information, it notifies the subject which in turn then notifies the other subscribed components.
During the synchronization of all the subscribed components, without the extra mediation, the component that provided the new information will receive back the same message that it just notified the subject of. In order to manage the unnecessary duplicate message, the notifications will be mediated to exclude to component where the original message originated from.
Throughout the lifecycle of an application, an objects state may change. You might want to store a copy of the current state in case of later retrieval. E.g., when writing a document, you may want to auto save the current state every 10 minutes. Or you have a game, and you want to save the current position of your player in the level, with its score and current inventory.
You can use the Memento pattern for saving a copy of state and for later retrieval if necessary.
The Memento pattern, like the Command pattern, is also commonly used for implementing UNDO/REDO functionality within your application.
The difference between the Command and the Memento patterns for UNDO/REDO, is that in the Command pattern, you re-execute commands in the same order that changed attributes of a state, and with the Memento, you completely replace the state by retrieving from a cache/store.
There is a game, and the character is progressing through the levels. It has acquired several new items in its inventory, the score is very good and you want to save your progress and continue later.
You then decide you made a mistake and need to go back to a previous save because you took a wrong turn.
Often when coding attributes in classes, you may want to provide methods to allow external functions to read or modify a classes internal attributes.
A common approach would be to add two methods prefixed with get_ and set_,
This makes perfect sense what the intentions are, but there is a more pythonic way of doing this and that is by using the inbuilt Python @property decorator.
Note that in the above example, there is an extra decorator named @value.setter . This is used for setting the _value attribute.
Along with the above two new getter/setter methods, there is also another method for deleting an attribute called deleter .
Not to be confused with object state, i.e., one of more attributes that can be copied as a snapshot, the State Pattern is more concerned about changing the handle of an object's method dynamically. This makes an object itself more dynamic and may reduce the need of many conditional statements.
Instead of storing a value in an attribute, and then then using conditional statements within an objects method to produce different output, a subclass is assigned as a handle instead. The object/context doesn't need to know about the inner working of the assigned subclass that the task was delegated to.
In the state pattern, the behavior of an objects state is encapsulated within the subclasses that are dynamically assigned to handle it.
This example takes the concept example further and uses an iterator rather than choosing the states subclasses randomly.
When the iterator gets to the end, it raises a StopIteration error and recreates the iterator so that the process can loop again.
Overloading the __call__ attribute makes an instance of a class callable like a function when by default it isn't. You need to call a method within the class directly.
If you want a default method in your class, you can point to it by using by the __call__ attribute.
The Strategy Pattern is similar to the State Pattern, except that the client passes in the algorithm that the context should run.
The algorithm should be contained within a class that implements the particular strategies interface.
An application that sorts data is a good example of where you can incorporate the Strategy pattern.
There are many methods of sorting a set of data. E.g., Quicksort, Mergesort, Introsort, Heapsort, Bubblesort. See https://en.wikipedia.org/wiki/Sorting_algorithm for more examples.
The user interface of the client application can provide a drop-down menu to allow the user to try the different sorting algorithms.
Upon user selection, a reference to the algorithm will be passed to the context and processed using this new algorithm instead.
The Strategy and State appear very similar, a good way to differentiate them is to consider whether the state of the context is choosing the algorithm at runtime, or whether the algorithm is being passed into it.
Software Plugins can be implemented using the Strategy pattern.
A game character is moving through an environment. Depending on the situation within the current environment, the user decides to use a different movement algorithm. From the perspective of the object/context, it is still a move, but the implementation is encapsulated in the subclass at the handle.
In a real game, the types of things that a particular move could affect is which animation is looped, the audio, the speed, the camera follow mode and more.
In the Template Method pattern, you create an abstract class (template) that contains a Template Method that is a series of instructions that are a combination of abstract and hook methods.
Abstract methods need to be overridden in the subclasses that extend the abstract (template) class.
Hook methods normally have empty bodies in the abstract class. Subclasses can optionally override the hook methods to create custom implementations.
So, what you have, is an abstract class, with several types of methods, being the main template method, and a combination of abstract and/or hooks, that can be extended by different subclasses that all have the option of customizing the behavior of the template class without changing its underlying algorithm structure.
Template methods are useful to help you factor out common behavior within your library classes.
Note that this pattern describes the behavior of a method and how its inner method calls behave.
Hooks are default behavior and can be overridden. They are normally empty by default.
Abstract methods, must be overridden in the concrete class that extends the template class.
In the example use case, there is an AbstractDocument with several methods, some are optional and others must be overridden.
The document will be written out in two different formats.
Depending on the concrete class used, the text() method will wrap new lines with <p> tags and the print() method will format text with tabs, or include html tags.
Your object structure inside an application may be complicated and varied. A good example is what could be created using the Composite structure.
The objects that make up the hierarchy of objects, can be anything and most likely complicated to modify as your application grows.
Instead, when designing the objects in your application that may be structured in a hierarchical fashion, you can allow them to implement a Visitor interface.
The Visitor interface describes an accept() method that a different object, called a Visitor, will use in order to traverse through the existing object hierarchy and read the internal attributes of an object.
The Visitor pattern is useful when you want to analyze, or reproduce an alternative object hierarchy without implementing extra code in the object classes, except for the original requirements set by implementing the Visitor interface.
Similar to the template pattern it could be used to output different versions of a document but more suited to objects that may be members of a hierarchy.
In the example, the client creates a car with parts.
The car and parts inherit an abstract car parts class with predefined property getters and setters.
Instead of creating methods in the car parts classes and abstract class that run bespoke methods, the car parts can all implement the IVisitor interface.
This allows for the later creation of Visitor objects to run specific tasks on the existing hierarchy of objects.
The hasattr() method can be used to test if an instantiated object has an attribute of a particular name.
When printing strings to the console, you can include special characters \t that print a series of extra spaces called tabs. The tabs help present multiline text in a more tabular form that appears to be neater to look at.
The number of spaces added depends on the size of the word before the \t character in the string. By default, a tab makes up 8 spaces.
Now, not all words separated by a tab will line up the same on the next line.
The problem occurs usually when a word is already 8 or more characters long.
To help solve the spacing issue, you can use the expandtabs() method on a string to set how many characters a tab will use.
Remember that design patterns will give you a useful and common vocabulary for when designing, documenting, analyzing, restructuring new and existing software development projects now and into the future.
Good luck and I hope that your projects become very successful.
Sean Bradley
(Book) Sometimes you just want to switch off your computer and read from a book. So, all GoF patterns are discussed in my Design Patterns In Python book
https://www.amazon.com/dp/B08XLJ8Z2J : ASIN B08XLJ8Z2J
https://www.amazon.com/dp/B08Z282SBC : ASIN B08Z282SBC
Learn All of the 23 GoF (Gang of Four) Design Patterns and Implemented them in Python.
Design Patterns are descriptions or templates that can be repeatedly applied to commonly recurring problems during in software design.
A familiarity of Design Patterns is very useful when planning, discussing, managing and documenting your applications from now and into the future.
Also, throughout the course, as each design pattern is discussed and demonstrated using example code, I introduce new Python coding concepts along with each new design pattern. So that as you progress through the course and try out the examples, you will also get experience and familiarity with some of the finer details of the Python programming language.
In this course, you will learn about these 23 Design Patterns,
Creational
Factory
Abstract Factory
Builder
Prototype
Singleton
Structural
Decorator
Adapter
Facade
Bridge
Composite
Flyweight
Proxy
Behavioral
Command
Chain of Responsibility
Observer Pattern
Interpreter
Iterator
Mediator
Memento
State
Strategy
Template
Visitor
In the list of patterns above, there are Creational, Structural and Behavioral patterns.
Creational : Abstracts the instantiation process so that there is a logical separation between how objects are composed and finally represented.
Structural : Focuses more on how classes and objects are composed using the different structural techniques, and to form structures with more or altered flexibility.
Behavioral : Are concerned with the inner algorithms, process flow, the assignment of responsibilities and the intercommunication between objects.
Design patterns will give you a useful and common vocabulary for when designing, documenting, analyzing, restructuring new and existing software development projects from now and into the future.
I look forward to having you take part in my course.
Sean Bradley