- Object-orientated Design
- design doesn’t only occur at the beginning, usually analysis, programming, design tend to overlap
- objects are models of things that can do things and have certain things done to them
- formally, an object is a collection of data and associated behaviors
- OO Analysis:
- looking at a problem, system or task and indentifying the objects and their interactions
- Output is set of requirements
- OO Design:
- convert requirements into implementation specifications
- name objects, define behaviors, and specify what objects can activate specific behaviors on other objects
- focus on how things should be done
- output is implementation specification e.g. a set of classes and interfaces that could be implemented in any object-orientated language
- OO Programming:
- process of converting design into a working program
- In modern world, most development happens in an iterative development model
- classes describe objects and distinguish between different types of objects
- UML (Unified Modeling Language) diagrams describes relationships or associations between classes
- Data is represented as properties or attributes
- Actions are called methods and can take parameters or access data of an object
- Hiding details and creating the public Interface
- key purpose of modeling an object is to determine what the public interface will be
- attributes and methods that other objects can use to interact with that object
- hiding implementation or functional details is called information hiding
- sometimes interchanged with encapsulation, but in encapsulation not necessarily hidden
- designing public interface very important because difficult to change later, because will break client objects calling it
- abstraction: dealing with the level of detail that is most appropriate to a given task
- abstraction is the process of encapsulating information with separate public and private interfaces
- Composition
- composition is the act of collecting together several objects to compose new ones
- composition is good when objects are part of other objects, but in computer programming usually describing abstract ideas
- object diagram describes system at a specific state in time and interaction between objects
- if outside object controls when related object is created or destroyed composition is more suitable
- if related object is created independently and can outlast outside object an aggregate relationship makes more sense
- Inheritance
- The is a relationship is formed by inheritance
- Inheritance is like a family tree, classes can inherit attributes and methods from another class
- e.g. individual chess pieces can inherit from a generic Piece class and override moves
- abstract classes can declare abstract methods that indicates a method, but doesn’t specify an implementation
- Polymorphism
- Polymorphism is ability to treat a class differently based on the subclass
- Python uses duck-typing: ignores an object’s type and determine suitability based on presence of certain properties and methods
- e.g. a board implemented in Python could take any object that has a move method and use it
- Multiple Inheritance
- subclasses can inherit functionality from multiple parent classes
- useful if creating object with two distinct set of behaviors
- usually want to avoid if overlapping interface
- Inheritance is good when there is a clear is a relationship, programmers often use when objects only distantly related
- Objects in Python
- PEP8 recommends classes named using CamelCase convention
- Can assign arbitrary values to objects with dot notation
- Docstrings
- when doing OOP, important to write API documentation that clearly summarizes what each object and method does
- Python supports this with docstrings
- docstrings should clearly summarize purpose of class or method and explain parameters whose usage is not immediately obvious
- also good place to include short examples
- first line following the definition and should be indented like the following code can have (‘,”, or ”’)
- help([class or function name]) will show docstrings
- Modules
- for small programs, can have classes in one file
- for larger ones, use modules (just python files)
- don’t use ‘from database import *’
- it makes it hard to know where something may be coming from and you will forget as time passes, maintenance becomes a nightmare
- can also import many unnecessary objects, modules or classes
- no variables should come from thin air
- Packages
- package: collection of modules in a folder. place a empty file named init.py
- absolute imports: specify complete path
- from ecommerce.products improt Product; product = Product()
- from ecommerce import products; produt = products.Product()
- relative imports: find a class, function or module relative to current module
- one period, for files next to each other, two periods to check in parent file
- best to always put code into functions otherwise will execute upon import
- can also put classes into functions/methods, but usually better for one-off items
- Access to Data
- Python doesn’t have access control
- can prefix internal methods with an underscore (understood by python programmers)
- most python programmers won’t touch name-mangled variables (one or two underscores) unless compelling reason to do so
- can import more directly if included in init.py file
- When Objects are Alike
- inheritance is one way to avoid duplicate code
- all python classes are subclasses of ‘object’
- a subclass is derived from a parent or extends the parent
- class variables are shared among all instances
- you can also add functionality to existing built-in classes (i.e. list, dict, set, file, str)
- overriding is altering or replacing a method of the superclass with a new method
- super lets you call parent class directly
- Multiple inheritance
- as a rule of thumb, if you think you need multiple inheritance you probably don’t, if you know you need it, you probably do
- most useful form of multiple inheritance is called a mixin
- mixins meant to be inherited to provide extra functionality
- diamond inheritance where two subclasses override a method and get extended by another subclass, may result in multiple calls to same thing
- generally makes code messy and hard to follow
- Polymorphism
- same method name can have multiple implementations per subclass
- because of duck typing, can use any object that provide required behavior without forcing ti to be a subclass
- because any object can provide the correct interface, the need for polymorphic common superclasses reduced in Python
- Expecting the Unexpected
- exceptions are special error objects that are derived from BaseException
- handle exceptions by wrapping in a try…except clause
- might be good to specify the type of exception you are trying to catch (i.e. ZeroDivisionError, NameError, etc)
- can catch specific errors and general errors
- can use try…except…else(if no exceptions)…finally(always runs)
- Most exceptions inherit from Exception class which is a subclass of BsaeException
- SystemExit and KeyboardInterrupt derive directly from BaseException
- except will catch everything including the two above, usually want to catch Exception which excludes them
- can define own exceptions as classes that inherit ‘Exception’
- can handle the information regarding the error object
- better to use exceptions than handle directly with if more flexible and less messy (what do you return? how will you check for it?)
- exception help you design the flow of your program and should not be avoided
- exceptions are good communication tools
- use exceptions to handle failure, not return values
- When to Use Object-orientated Programming
- When to use?
- no reason to add an extra level of abstraction if it doesn’t help organize code
- OO code is relatively self documenting
- generally the more complicated a set of data is, more likely it is to have functions specific to that data
- focus on how it will be used? once? multiple times? for different types of things?
- focus on interaction between objects and inheritance relationships
- Properties to add behavior to class data
- some languages like Java recommend getters and setters
- in python can use property object to check if valid
- can take a getter, setter, delete and docstring
- usually only defined with the first two parameters
- e.g. under class [example]: name=property(_get_name, _set_name)
- Using Decorators with property
- modify functions dynamically by passing in a function and returning a new function
- can use property function to turn a get function into a property
- methods should represent actions
- diff between attribute and property is we can invoke custom actions when a property is retrieved, set, or deleted
- e.g. when lookup is expensive or want to cache
- good for things that need to be calculated on the fly
- Managing Objects
- high-level objects that manage other objects tend to represent concrete ideas
- breaking out steps for readability, extensiblity (can modify easier), and partitioning (another class may want to use only certain parts)
- Remove Duplicate code (DRY)
- boils down to readability and maintainability
- code should always be written to be readable first
- Inheritance is one way to reuse code
- Can also pass an object with object with appropriate method
- any inheritance relationship can be modeled as a composition relationship, but not the other way around
- Python Data Structures
- initialized objects from the object class can’t be assigned values to save space
- tuples are in order and immutable
- generally should have values that are different type from each other
- generally used to aggregated different pieces of data
- suffers from readability though
- named tuples are good for grouping read-only data / object w/ no behavior
- Dictionaries great where the keys represent different instances of similar objects
- also good where each key represents some aspect of a single object
- dictionaries are more flexible than named tuples
- Lists
- good when wanting to store several instances of same type of object
- also keeps order
- can sort and define own sort functions
- Sets
- great at separating groups and letting you know if an item is in it or not
- must use list if want to sort or order
- powerful when using multiple sets
- e.g. union, intersection, symmetric difference, issubset, issuperset, difference,
- Extending built-ins
- can either create a new object that holds container or subclass the object and add methods
- composition usually best if just focused on storing
- inheritance if want to change the way the container actually works
- all non-object orientated syntax maps to OO methods underneath the hood
- use dir(list) to see all special methods
- help(list.__add__) to see what it does
- usually if you’re extending a built-in data type may be using wrong thing
- Python Object-orientated Shortcuts
- Len
- counts number of objects in a container
- Reversed
- takes any sequence as input and returns a copy in reverse order
- returns list_reverseiterator object
- can use on for loops
- Enumerate
- creates a list of tuples with the object and the index
- Zip
- takes sequence and creates a new sequence of tuples
- can pass result in (key,value) format into dict(z)
- can also unzip into two separate tuples
- sorted
- broader than sort (list specific) and works on all iterables
- can take a key argument that lets you define a function
- also accepts reverse argument
- Others
- min, max and sum also work on sequences. can also use key = lambda for min and max
- all and any accept iterables and return true or false
- eval, exec, and compile execute string as code
- hasattr, getattr, setattr, and delattr allow attribtues on an object to be manipulated
- Comprehensions
- supporting iterable protocol means an object has an iter method
- fancy way of saying it has a next method
- list comprehensions are faster than for loops
- can use to filter as well
- can also use with sets and dictionaries comprehensions
- Generator Expressions
- lets you use comprehension syntax without creating a final container object
- wrap in () instead of [] or {}
- great when going through large files and space/memory is a concern
- generally best to use generator expression if don’t need the list, set or dictionary
- Generators
- Yield is the key to generators
- function using it will return and then start where it left off
- is creating generator objects
- Method Overloading
- refers to having methods with same name that accept different sets of arguments
- is useful when we want a method with the same name to accept different numbers or sets of arguments
- if we want to make an argument optional, can specify default values using equal sign in parameters
- cannot have dynamically generated default values because is evaluated when function is interpreted/defined not when called
- must be careful about passing empty list into default, will only create one list
- Variable Argument Lists
- python can also accept an arbitrary number of positional or keyword arguments
- varargs, specify with a * in the function definition
- takes any number of arguments and puts them into a list of strings
- **kwargs arrive into function as a dict. commonly used with setup
- can have both * and ** in one function
- Unpacking arguments
- can also use * to unpack a list in the function call
- can use ** to unpack a dictionary in function call
- useful when mapping info that has been collected from user
- can also add into custom object
- functions are objects
- don’t need to wrap methods in objects because functions are already objects
- can pass around functions as callbacks
- functions as attributes
- can also set new function
- e.g. a.print = fake_print
- can be confusing to maintain
- adding or replacing mthods at run time (monkey-patching) is used in automated testing
- can also be used to fix bugs or add features in third-party code that does not act the way we want it to
- typically should be avoided
- Callable objects
- any object can be turned into a callable by giving it a “__call__” method
- foo_instance = Foo()
- foo_instance() #will call the __call__ method
- functions are callable objects
- Python Design Patterns I
- Decorator Pattern
- allows us to wrap objects with other objects that alters functionality
- enhances response of a component that is sending data to a second component
- supports multiple optional behaviors
- decorators will do some added processing before or after calling the core functionality
- mostly used with functions in Python, but can be used with callable objects or classes
- good when needs to be dynamic
- Observer Pattern
- useful for state monitoring and event handling
- a core object can be monitored by observer objects
- whenever something changes core object calls an update method letting observers know
- core doesn’t care what the observers do and observers don’t care what other observers do
- Strategy Pattern
- implements different solutions to a single problem each in a different object
- because python’s first-class functions allow this to be implemented in a more straightforward way
- can just create a set of top-level functions and pass them around
- State pattern
- Allows an object to alter its behavior when its internal state changes
- goals is to represent state-transition systems
- need a manager or context class that provides an interface for switching states
- this class contains a pointer to the current state
- there is a context class and a multiple state class
- the state or the context needs to know which other states that it can switch to
- Singleton pattern
- In python, usually doing something wrong if using this one
- basic idea is to allow exactly one instance of a certain object to exist
- usually some sort of manager class
- can use __new__ to make sure only one instance is ever created
- In Python, this pattern can be sufficiently mimicked by using modules
- Template Pattern
- common steps are implemented in a base class
- different steps are overridden in sub-classes to provide custom behavior
- Python Design Patterns II
- Adapter Pattern
- designed to allow two pre-existing objects to work together
- sole purpose is to provide a translating job
- usually create a class as an adapter
- Facade Pattern
- simple interface to a complex system
- there may be a “typical” usage
- Flyweight pattern
- Memory optimization pattern
- usually implement after running into memory problems
- premature optimization can result in programs that are too complicated to maintain
- can have a shared model that has properties that other models might all share
- only store shareable state
- e.g. you don’t need to instantiate multiple instances of one card object, check if already exists and return it if it does
- Command Pattern
- adds level of abstraction between actions that must be done, and the object that invokes those actions normally at a later time
- in python, can add the command from another object of a class that has the method you need
- Abstract Factory Pattern
- multiple possible implementations of a system depending on some configuration
- e.g. Django returns a set of object relational classes for interacting with specific databases
- often you can use a separate module for each factory
- Composite Pattern
- allows complex tree-like structures from components
- files have a list of children
- files and folders can have similar behaviors
- good for folder/file-like systems, hierarchies, HTML DOM
- Files and Strings
- Strings
- In python3, strings represented in Unicode, which can represent virtually any character in any language
- methods available to manipulate strings
- can also format using braces and ‘{}’.format(‘x’)
- format can also handle other primitive types, container data structures, or objects
- Unicode
- important to be able to convert unicode to bytes
- can use .decode method to convert an array of bytes
- decode accepts a string for the name of the character encoding
- File IO
- OS’ represent files as a sequences of bytes
- encoding and decoding lets you deal with the text object
- passing in ‘b’ lets you open/read/write binary files
- make sure to close to ensure any buffered writes are written to file
- Context
- Open/close files pythonically with “with open(‘name’) as file:”
- context managers used to properly manage resources, if you don’t close, eventually will crash
- Storing objects
- python pickle module allows storage of objects directly in a special object storage format
- any object can be pickled
- Serializing web objects
- JSON is widely used
- most often for transmitting data from a web server to a Javascript-capable web browser
- python json module can convert
- Testing Object-orientated Programs
- Testing may be more important in Python because of its dynamic nature
- Changing code in one area can affect other parts of the program and automated testing can catch this
- Main Reasons to Write Tests
- Ensure code is working the way the developer thinks it should
- Ensure the code continues working when making changes
- Ensure developer understood the requirements
- Ensure the code has a maintainable interface
- Good idea to run tests whenever you’ve changed code to check you didn’t inadvertently break anything
- TDD (Test-Driven Development)
- First ensures that tests are written
- Second forces us to consider exactly how the code is going to be interacted with
- Unit Testing
- focuses on testing the least amount of code possible in any one test
- unittest module and its class TestCase helps to compare values, set up tests and clean up after
- methods must start with ‘test’
- each test should be independent of other tests
- key to writing good unit tests is to keep them as short as possible
- if you can’t break into such testable units, probably sign your design needs rethinking
- Assertion methods
- assertEqual
- assertNotEqual
- assertTrue
- assertFalse
- assertRaises
- each method accepts an optional argument named ‘msg’ which can be included in the error message
- Additional assertion methods in Python 3.1
- assertRaises can take advantage of with
- assertGreater, assertGreaterEqual, assertLess, assertLessEqual
- assertIn, assertNotIn
- assertIsNone
- assertSameElements (checks items are same regardless of order)
- assertSequenceEqual: enforces order
- assertDictEqual, assertSetEqual, assertListEqual, assertTupleEqual: also checks container objects are the correct type
- assertMultilineEqual: checks two multiline strings are equal
- assertRegexpMatches
- Reducing boilerplate and cleaning up
- sometimes may need to do a certain setup multiple times
- can use setUp method on TestCase class to do initialization for each test e.g. self.blank = blah
- setUp is called individually for each test
- tearDown method can also be used for cleaning up, useful if test involves more than just letting an object be garbage collected
- Usually group test methods into separate TestCase subclasses depending on what setup code they have in common
- Organizing and Running Tests
- doesn’t take long for tests to grow very large
- Can collect groups of TestCase objects or modules into collections called TestSuites
- however, most programmers can use test discovery which automatically finds and run tests with the discover module
- looks for any names that start with the characters ‘test’
- if it finds any TestCase or TestSuite objects it will execute them
- just run based on whatever version you have
- Ignoring broken Tests
- tests may fail under some circumstances
- python provides decorators to mark tests as expected to fail
- decorators: expectedFailure(), skip(reason), skipIf(condition, reason), skipUnless(condition, reason)
- Testing with py.test
- unittest module is very verbose and based on JUnit test framwork for Java
- py.test and nose are popular frameworks that follow python convention
- py.test doesn’t require a class and lets any properly named function to behave like a test
- just uses assert statement to verify results
- looks for folders with ‘test_’ and executes any function that starts with ‘test’
- also any classes that starts with ‘Test’ will have any methods that start with ‘test_’ will also be executed
- Classes are still useful for organizing and setting up initial conditions, don’t need to pass in any special object
- only allows print statement if test fails – very useful don’t need to remove
- One way to do Setup and Cleanup
- if writing a class-based test, can use setup_method and teardown_method
- they both accept an argument for the function object representing the method being called
- setup_class and teardown_class accept class in question
- setup_module and teardown_module are run once, good for creating sockets or database connections
- need to be careful to not introduce dependencies if the object being set pu stores state
- Completely different way to set up variables
- can use funcarg, which will be passed into the test as a parameter
- usually want to define in a file called conftest.py to separate config from the test
- funcargs must be named ‘pytest_funcarg__’ + <valid identifier> where the identifier is the parameter for the test functions
- funcarg is created afresh for each call
- request object gives us flexibility with managing modules, cls, and functions as well as with setup and teardown
- can cache setups if have an expensive setup that you want to use multiple times
- Test skipping with py.test
- can skip any tests with py.test.skip function
- if in a function, will skip the function
- if in module, will skip tests in the module
- if in funcarg, will skip all tests that call that funcarg
- usually only want to skip tests if certain conditions are met
- usually use in an if statement
- usually test python version, operating system, or some_library version
- @py.test.mark.skipif(“executable code that reduces to a Boolean”)
- @py.test.mark.xfail: expected to fail
- py.test extras
- very powerful tools w/ more capabilities
- distributed testing (across a network)
- numerous plugins
- extensive customization
- passing -x or –exitfirst will exit after first failure
- –pdb: instead of failing drops to a python debugger shell
- –looponfail or -f (only py.test xdist plugin is installed): automatically rerun itself after failing code is encountered. lets you fix, save and will run
- -k (important): accepts a keyword to search for tests. used to run specific tests that contain given keyword.
- How much testing is enough
- onus of designing complete test suites lies with the programmer
- code coverage: number of lines of code that are executed by the program
- figleaf and coverage.py are tools to figure this out
- e.g. “coverage run coverage_unittest.py”
- will create a .coverage file that holds data on the run
- “coverage report” command to see it
- tells how many lines of code in file and how much was executed
- if you pass in -m option will tell you which lines
- “coverage html” shows a fancier interactive HTML report viewable on the web browser
- just because you have code coverage doesn’t mean it was tested properly
- Common Python 3 Libraries
- SQLAlchemy
- TkInter: graphical library
- construct widgets and command them
- PyQt: another graphical toolkit
- XML:
- may have to interact with XML data from other programs
- SAX: can parse XML document on the fly, but can be unwieldy, good for streaming and working with huge documents
- DOM: treats document like tree of connected nodes.
- ElementTree: allow XML documents to be treated like Python objects
- lxml: allow XML documents to be treated like Python objects
- ElementTree
- represents entire XML document in memory
- can also construct XML
- lxml
- more advanced tool
- parse invalid SML
- advanced XPath
- CSS selectors
- CherryPy
- web application server
- is not a full framework
- does not supply templating and data storage
- Jinja for templating
Like this:
Like Loading...