Python in Practice Flashcards
Abstract Factory Pattern
Diagram with Svg and Text
Designed for situations where we want to create complex objects that are composed of other objects and where the composed objects are all of one particular “family.”
some gui:
has abstact widget factory with three subclass factories.
- MacWidgetFactory
- XfceWidgetFactory
- WindowsWidgetFactory
provide methods for creating the same objects:
- make_button()
- make_spinbox(), etc…
we can create a generic create_dialog() function that takes a factory instance as an argument and produces a dialog with OS X, Xfce, or windows dialog.
“Abstract” usually means the base class also serves as a concrete class in its own right, with other classes inheriting from it.
This abstract class will often contain methods which call subclasses specific to each class that inherits it. make_button() above will call subclasses for windows, Xfce, and Mac OS.
steps:
- One top level function calls a factories
- Abstract factory has class methods that call inner classes (Diagram, Rectangle, Text).
- More complex factory inherits simple one. Methods on abstract class call inner classes and reference private constants, which do all the work.
Builder Pattern
HTML and Tkinter forms
Similar to Abstract Factory in that both patterns are designed to create complex objects composed of other objects
Builder is distinct in that it not only contains methods for building the object, it also holds the representation of the entire complex object itself.
top level calls use the same function, parameterized by appropriate builder object.
>>> tkForm = create_login(TkFormBuilder())
>>> htmlForm = create_login(HtmlFormBuilder())
Both inherit from an abstract base class, AbstractForm - has a bunch of abstract methods
class AbstractForm(metaclass=abc.ABCMeta):
__@abc.abstractmethod
__def form(self):
____pass
any class inheriting this must implement all the abstract methods.
giving a class metaclass=abc.ABCMeta means the class cannot be instantiated. Sets “__abstractmethod__” attribute to true, returns original function
Factory Method Pattern
gameboard
when we want subclasses to choose which subclass they should instantiate when an object is requested.
Good for lots of things, but also for when we can’t know the class in advance.
def make_new_method(char):
__def new(Class): # Can’t use super() or super(Piece, Class) b/c there is no instance/class context for super to access.
____return Piece.__new__(Class, char)
__return new
for code in itertools.chain((code1, code2), codes):
__char = chr(code)
__name = unicodedata.name(char).title().replace(“ “, “”)
__new = make_new_method(char)
__Class = type(name, (bases, ), dict(__slots__=(), __new__=new))
__setattr(sys.modules[__name__], name, Class)
or
example of nesting lambdas
what for?
new = (lambda char: lambda Class: Piece.__new__(Class, char))(char)
name is always ‘lambda’ - not good
new.__name__ = ‘__new__’
within the loop, char has meaning, but Class is a placeholder for when “__new__” is called on the class. We’ll create a new Piece class whos name is the name of the unicode character and who’s value is the characters representation. (game piece)
In the gameboard example, this allowed the dynamic creation of a function in a loop going through values to create classes for game pieces.
“new” was put in the __new__ slot of an attribute dict and called with the name and bases on “type”, then assigned to the global namespace.
Class = type(name, (Piece,), dict(__slots__=(), __new__=new))
globals()[name] = Class
Prototype Pattern:
7 ways to make a class instance
even when names are only known at runtime, Python provides a variety of ways of creating new objects.
class Point:
__ __slots__ = (“x”, “y”)
__def __init__(self, x, y):
____self.x = x
____self.y = y
def make_object(Class, *args, **kwargs):
__return Class(*args, **kwargs)
point1 = Point(1, 2)
point2 = eval(“{}({}, {})”.format(“Point”, 2, 4) #risky
point3 = getattr(sys.modules[__name__], “Point”)(3, 6)
point4 = globals()“Point”
point5 = make_object(Point, 5, 10)
point6 = copy.deepcopy(point5)
point6. x = 6
point6. y = 12
point7 = point1.__class__(7, 14) # any previous instance would work here.
Singleton Pattern
used when the class has only a single instance that is the one and only needed throughout the program.
create a module with the global state that’s needed kept in private variables and access provided by public functions:
ex.
_URL = “http://www.bankofcanada.ca/stats/assets/csv/fx-seven-day.csv”
def get(refresh=False):
__if refresh:
____get.rates = {}
__if get.rates:
____return get.rates
__with urllib.request.urlopen(_URL) as file:
____for line in file:
______line = line.rstrip().decode(“utf-8”)
______if not line or line.startswith((“#”, “Date”)):
________continue
______name, currency, *rest = re.split(r’\s*, \s*’, line)
______key = “{}({})”.format(name, currency)
______try:
________get.rates[key] = float(rest[-1])
______except ValueError as err:
________print(“error {}: {}”.format(err, line)
__return get.rates
###
This is in the currency/Rates.py module. the rates dictionary is an attribute of of the Rates get() function - a private value. When the public get() function is called, the get function either returns the rates or fetches them anew. No class needed.
Structural Design Patterns
in Python
Primary goal of structural design patterns is how objecst are composed together to form new, larger objects. Three themes stand out in structural design patterns:
adapting interfaces, adding functionality and handling collections of objects.
- Adapter pattern
- Bridge pattern
- Composite pattern
- Decorator pattern
- Facade pattern
- Flyweight pattern
- Proxy pattern
Just remember “A B C D F F P”!
Adapter pattern:
or “How to Duck Type”
Text and HTML rendering with a “Renderer” protocol
Technique for adapting an interface so one class can use another class (with an incompatible interface) without changing either of the classes being used.
a class need not know or care what the class of one of its arguments is, only that the given class provides the proper interface.
The page example: page class takes a “renderer” object that needs to inherit from class Renderer ( if not isinstance(renderer, Renderer)), which will mean it has the header, paragraph and footer methods. That’s all.
Duck typing solution (without requiring inheritance):
class Renderer(metaclass=abc.ABCMeta):
__@classmethod
__def __subclasshook__(Class, Subclass):
____if Class is Renderer:
______attributes = collections.ChainMap(*(Superclass.__dict__ for Superclass in Subclass.__mro__))
______methods = (“header”, “paragraph”, “footer”)
______if all(method in attributes for method in methods):
________return True
____return NotImplemented
**Explanation: **
isinstance(instance, Class) calls __subclasshook__ to see if the object given is a subclass of the second argument. So:
isinstance(renderer, Renderer) looks in Renderer for __subclasshook__, and determines if the stuff in the mro of the given renderer satisfies the criteria. The renderer need not actually subclass Renderer.
Qtrac.py has a has_methods(*methods) decorator that does this.
Likewise, you can inherit from Qtrac.requirer, an abstract base class
Bridge Pattern
create class for drawing barcharts, but use other classes for actual rendering of the barcharts
Separates an abstraction (an interface or algorithm) from how it is implemented.
Conventionally, we’d create one or more abstract base classes and subclass them several times for our concrete implementations.
Bridges create independent class hierarchies:
- Abstract: defines the operations (interface and algorithms)
- Concrete: provides implementations that will be called
Abstract class aggregates an instance of one of the concrete implementation classes - this instance serves as a bridge between the abstract interface and the concrete operations.
From the book, HtmlRenderer (has the header, paragraph and footer methods) could be said to have used the bridge pattern, it aggregated an HtmlWriter (which was more complex than TextRenderer, requiring this class) to provide its rendering
class BarRenderer(Qtrack.Requirer):
__required_methods={“initialize”, “draw”, …}
class BarCharter:
__def __init__(self, renderer):
____if not isinstance(renderer, BarRenderer):
…..
__def render(self, caption, pairs): # only method
….
class TextBarRenderer:
__def __init__(self, scaleFactor=40):
____self.scaleFactor = scaleFactor
** define above required functions
class ImageBarRenderer:
__COLORS = # defines colors
** __init__ specific to needs w/ above rendering methods defined.
Bridge Pattern Example
define a class that checks if the concrete class passed to it has the appropriate methods.
this is done with:
if not isinstance(concrete, Abstract) …
the @Qtrac.has_methods works well to define the Abstract Base Class
@Qtrac.has_methods(‘method1’, ‘method2’)
class AbstractClass(metaclass=abc.ABCMeta): pass
###
Your concrete classes (the bridge between the abstract base class and the implementer) now need to implement methods 1 and 2, but they’ll be called when passing the class to the bridge class - for example, in the bridge classes’ “render” method, it calls the initialize, draw_caption, draw_bar, and finalize methods of the TextBarRenderer and ImageBarRenderer classes - each of which are very different.
class BarCharter: # the implementer
class BarRenderer(Qtrac.Requirer): # abstract
__required_methods = …
class TextBarRenderer: # bridge 1
class ImageBarRenderer: # bridge 2
Qtrac.has_methods
class decorator
because it’s cool
def has_methods(*methods):
__def decorator(Base):
____def __subclasshook__(Class, Subclass):
______if Class is Base:
________attributes = collections.ChainMap(*(Superclass.__dict__ for Superclass in Subclass.__mro__))
________if all(method in attributes for method in methods):
__________return True
______return NotImplemented
____Base.__subclasshook__ = classmethod(__subclasshook__)
____return Base
__return decorator
Composite Pattern
describe general idea and example used in book
Designed to support the uniform treatment of objects in a hierarchy, whether they contain other objects or not.
Composite and noncomposite objects normally have the same core methods, with composite objects having additional methods to support adding, removing, and iterating their children.
Often used in drawing programs like Inkscape to support grouping or ungrouping.
book used Stationary as an example:
- each item had a name and a price.
- items can be grouped in sets, the price of which is the sum of the contents prices.
- These can be nested without limit.
Classic Composite/Noncomposite Hierarchy
Stationary example, 2 Abstract classes
composite is abstract, subclasses must implement
Abstract Item base class:
class AbstractItem(metaclass=abc.ABCMeta):
__@abc.abstractproperty
__def composite(self):
____pass
__def __iter__(self):
____return iter([])
class SimpleItem(AbstractItem):
__def __init__(self, name, price = 0.00):
____self.name = name
____self.price = price
__@property
__def composite(self):
____return False
class AbstractCompositeItem(AbstractItem):
__def __init__(self, *items):
____self.children = []
____if items:
______self.add(*items)
there is plenty more, check out stationary 1
Basic idea:
Noncomposite base abstract class is used for items, Abstract Composite Class inherits from it and adds some methods for dealing with items.
Composite items must declare @properties to handle the methods from both bases. (AbstractItem and AbstractCompositeItem)
Single Class for Composites
and Non-composites
no abstract class, bunch of classmethods and properties
decorate a couple class methods
create an Item class that can be either noncomposite or composite.
class Item:
def \_\_init\_\_(self, name, \*items, price=0.00): self.name = name self.price = price self.children = [] if items: self.add(\*items)
@classmethod
def create(Class, name, price):
__return Item(name, price=price)
@classmethod
def compose(Class, name, *items):
__return Item(name, *items)
@property
def composite(self):
__return bool(self.children) # True if exists.
@property
def price(self):
__return (sum(item.price for item in self) if self.children else self.__price
@price.setter #using a setter function
def price(self, price):
__self.__price = price
there’s more. Check the code.
Decorator Pattern
Decorators accept a function and return a function of the same name but with enhanced functionality.
classes, methods and functions can be decorated.
@functools.total_ordering:
class decorator that can be used on a class which implements __eq__() and __lt__() so it will have the full suite of comparison operators.