Today's post will be long from a scrolling standpoint, but since there's not
much meat to the template-code, it should go pretty quickly. The goal for today
is to generate final templates/snippets for interface, abstract
class, class and final
class constructs that can be
used as starting-points for defining those types in project-code without having to
start from scratch every single time. If you cringed, or started to think out a
non-Pythonic
-themed comment when you saw final,
stop right
now, and read this first.
I'm going to tackle them in the order noted above.
The Interface Template
These cannot be instantiated, and must be extended into concrete implementations. In
most languages that I'm aware of, an interface definition cannot have properties (not even
abstract ones), and cannot have concrete method-implementations. Python doesn't have that
constraint (it doesn't have a formal interface-construct, but allows for abstract
property- and method-definition through the
abc
module, using its
ABCMeta
as a __metaclass__
specification to define a class that
has some abstract members, and the @abstractmethod
and
@abstractproperty
decorator-functions to define which members are
abstract.
@describe.InitClass()
class InterfaceName( object ):
"""TODO: Document InterfaceName
Provides interface requirements and type-identity for objects that can
REPRESENT SOMETHING"""
#-----------------------------------#
# Abstraction through abc.ABCMeta #
#-----------------------------------#
__metaclass__ = abc.ABCMeta
#-----------------------------------#
# Static interface attributes (and #
# default values?) #
#-----------------------------------#
#-----------------------------------#
# Abstract Properties #
#-----------------------------------#
# PropertyName = abc.abstractproperty()
#-----------------------------------#
# Instance Initializer #
#-----------------------------------#
@describe.AttachDocumentation()
@describe.todo( 'Document __init__' )
@describe.todo( 'Implement __init__' )
def __init__( self ):
"""
Instance initializer"""
# InterfaceName is intended to be an interface,
# and is NOT intended to be instantiated. Alter at your own risk!
if self.__class__ == InterfaceName:
raise NotImplementedError( 'InterfaceName is '
'intended to be an interface, NOT to be instantiated.' )
# Call parent initializers, if applicable.
#-----------------------------------#
# Instance Garbage Collection #
#-----------------------------------#
#-----------------------------------#
# Abstract Instance Methods #
#-----------------------------------#
# @abc.abstractmethod
# def RequiredMethod( arg1, arg2=None, *args, **kwargs ):
# raise NotImplementedError( '%s.RequiredMethod is not implemented as '
# 'required by InterfaceName' % self.__class__.__name__ )
#-----------------------------------#
# Abstract Class Methods #
#-----------------------------------#
#-----------------------------------#
# Static Class Methods #
#-----------------------------------#
#---------------------------------------#
# Append to __all__ #
#---------------------------------------#
__all__.append( 'InterfaceName' )
Like the module- and package-templates, a lot of this is just structural,
organizational comment-blocks, indicating where to put verious types of members.
After the initial declaration of the __metaclass__
for the interface,
there are seven major blocks:
- A place to define static(-ish) interface-level attributes/constants;
- A place to define abstract (required) properties;
- A concrete
__init__
, more on that shortly; - A block for a
__del__
method, if one needs to be required in derived classes; - A block for defining abstract instance-methods;
- A block for defining abstract class-methods; and
- A block for defining static methods.
The check for the InterfaceName
is in place so that an interface could
be made more-or-less operational on Python versions that predate the abc
module (sometime in the 2.6.x versions). It'd be kinda tedious, but all that would have
to be done would be to remove all of the abc.[whatever]
calls and references,
and the interface would still be non-instantiable. A similar pattern is also in place for
the templated abstract-method for the same reason.
The Abstract Class Template
These also cannot be instantiated, but can contain concrete implementations of properties and methods both, as well as abstract requirements for properties and methods. Much of it is very similar to the Interface template, for much the same reasons, but there are a couple additional sections:
- Blocks for collecting the (usually
protected
) property-getter, -setter and -deleter methods for any concrete property implementations; and - A concrete- and abstract-property location;
@describe.InitClass()
class AbstractClassName( object ):
"""TODO: Document AbstractClassName
Provides baseline functionality, interface requirements and type-identity for
objects that can REPRESENT SOMETHING"""
#-----------------------------------#
# Abstraction through abc.ABCMeta #
#-----------------------------------#
__metaclass__ = abc.ABCMeta
#-----------------------------------#
# Class attributes (and instance- #
# attribute default values) #
#-----------------------------------#
#-----------------------------------#
# Instance property-getter methods #
#-----------------------------------#
#-----------------------------------#
# Instance property-setter methods #
#-----------------------------------#
#-----------------------------------#
# Instance property-deleter methods #
#-----------------------------------#
#-----------------------------------#
# Instance Properties (abstract OR #
# concrete!) #
#-----------------------------------#
# PropertyName = abc.abstractproperty()
# PropertyName = describe.makeProperty()
#-----------------------------------#
# Instance Initializer #
#-----------------------------------#
@describe.AttachDocumentation()
@describe.todo( 'Document __init__' )
@describe.todo( 'Implement __init__' )
def __init__( self ):
"""
Instance initializer"""
# AbstractClassName is intended to be an abstract class,
# and is NOT intended to be instantiated. Alter at your own risk!
if self.__class__ == AbstractClassName:
raise NotImplementedError( 'AbstractClassName is '
'intended to be an abstract class, NOT to be instantiated.' )
# Call parent initializers, if applicable.
# Set default instance property-values with _Del... methods as needed.
# Set instance property values from arguments if applicable.
#-----------------------------------#
# Instance Garbage Collection #
#-----------------------------------#
#-----------------------------------#
# Instance Methods #
#-----------------------------------#
# @abc.abstractmethod
# def RequiredMethod( arg1, arg2=None, *args, **kwargs ):
# raise NotImplementedError( '%s.RequiredMethod is not implemented as '
# 'required by InterfaceName' % self.__class__.__name__ )
#-----------------------------------#
# Class Methods #
#-----------------------------------#
#-----------------------------------#
# Static Class Methods #
#-----------------------------------#
#---------------------------------------#
# Append to __all__ #
#---------------------------------------#
__all__.append( 'AbstractClassName' )
There are a few more prompt-comments in the __init__
method as well,
keeping with my normal programming structure: I explicitly delete
all of an
instance's properties during initialization so that they will always have a value
after instances are first created.
The Class Template
Classes are intended to be instantiated, and can be extended freely. This template doesn't have anything new, that I haven't shown before, so there's not much that can be said about it.
@describe.InitClass()
class ClassName( object ):
"""TODO: Document ClassName
Represents SOMETHING"""
#-----------------------------------#
# Class attributes (and instance- #
# attribute default values) #
#-----------------------------------#
#-----------------------------------#
# Instance property-getter methods #
#-----------------------------------#
#-----------------------------------#
# Instance property-setter methods #
#-----------------------------------#
#-----------------------------------#
# Instance property-deleter methods #
#-----------------------------------#
#-----------------------------------#
# Instance Properties #
#-----------------------------------#
#-----------------------------------#
# Instance Initializer #
#-----------------------------------#
@describe.AttachDocumentation()
@describe.todo( 'Document __init__' )
@describe.todo( 'Implement __init__' )
def __init__( self ):
"""
Instance initializer"""
# Call parent initializers, if applicable.
# Set default instance property-values with _Del... methods as needed.
# Set instance property values from arguments if applicable.
pass # TODO: Remove this line after __init__ is implemented
#-----------------------------------#
# Instance Garbage Collection #
#-----------------------------------#
#-----------------------------------#
# Instance Methods #
#-----------------------------------#
#-----------------------------------#
# Class Methods #
#-----------------------------------#
#-----------------------------------#
# Static Class Methods #
#-----------------------------------#
#---------------------------------------#
# Append to __all__ #
#---------------------------------------#
__all__.append( 'ClassName' )
The Final Class Template
A final
class is intended to be instantiated, but not extended. The only
significant difference between this template and the Class template above is how the
__init__
handles extension-attempts:
#-----------------------------------#
# Instance Initializer #
#-----------------------------------#
@describe.AttachDocumentation()
@describe.todo( 'Document __init__' )
@describe.todo( 'Implement __init__' )
def __init__( self ):
"""
Instance initializer"""
# FinalClassName is intended to be a nominally-final class
# and is NOT intended to be extended. Alter at your own risk!
#---------------------------------------------------------------------#
# TODO: Explain WHY it's nominally final! #
#---------------------------------------------------------------------#
if self.__class__ != FinalClassName:
raise NotImplementedError( 'FinalClassName is '
'intended to be a nominally-final class, NOT to be extended.' )
# Call parent initializers, if applicable.
# Set default instance property-values with _Del... methods as needed.
# Set instance property values from arguments if applicable.
After taking some time to roll the doc_metadata.py
file into the new
standard template, I can (finally) make good on my promise to make it available for
download, as well as the collection of template-files:
It's been fourteen posts now, and just over a month since I started in on the documentation-process. I'm not sure exactly where I want to go next on the Python side of things, though I have a couple of ideas, so I think for my next post, I'll play around a bit with implementing some design-patterns in JavaScript. If nothing else, that will give me some time to ponder what the next logical step is.
No comments:
Post a Comment