[Python-3000] ABC's, Roles, etc

Jeff Shell eucci.group at gmail.com
Tue May 8 23:52:48 CEST 2007


Hello. I just joined the list as the whole Abstract Base Class,
Interfaces, and Roles/Traits system is of significant interest to me.
I've tried to catch up on the discussion by reading through the
archives, but I'm sure I missed a few posts and I apologize if I'm
wasting time covering ground that's already been covered.

I have a lengthy post that dissects a major issue that I have with
ABCs and the Interface definition that I saw in PEP 3124:: it all
seems rigidly class and class-instance based. The cardinal sin I saw
in the Interface definition in PEP 3124 (at least, at the time I last
viewed it) was the inclusion of 'self' in a method spec.

It seems to me that Abstract Base Classes and even PEP 3124 are
primarily focused on classes. But in Python, "everything is an
object", but not everything is class-based.

Jim Fulton taught me a long time ago that there are numerous ways to
fulfill a role, or provide an interface. 'self' is an internal detail
of class-instance implementations. In my post, I show some (stupid)
implementations of the 'IStack' interface seen in PEP 3124, only one
of which is the traditional class - instance based style.

http://griddlenoise.blogspot.com/2007/05/abc-may-be-easy-as-123-but-it-cant-beat.html

The rest of this post focuses on what `zope.interface` already
provides - a system for specifying behavior and declaring support at
both the class and object level - and 'object' really means 'object',
which includes modules. You're more than welcome to tune out now. My
main focus is on determining what Abstract Base Classes and/or PEP
3124's Interfaces do better than `zope.interface` (if anyone else is
familiar with that package). I've found great success using
`zope.interface` to satisfy many of the requirements and issues that
these systems may try to solve, and more. In fact, `zope.interface` is
closer to Roles/Traits than anything else.

# .....

I wanted to chime in here and say that `zope.interface` (from Zope 3,
but available separately) is an existing implementation that comes
quite close to what Collin Winter proposed. Even in some of its
spellings.

http://cheeseshop.python.org/pypi/zope.interface/3.3.0.1

The main thing is that `zope.interface` focuses declaration on the
object - NOT the class. You do not use `self` in interface
specifications.

Terms I've grown fond of while using `zope.interface` are "specifies",
"provides", and "implements".

An Interface **specifies** desired *object behavior* - basically it's the API::

    class IAuthVerification(Interface):
        def verify(invoice_number, amount):
            """
            Returns an IAuthResult containing status information about
            success or failure.
            """

An *object* **provides** that behavior::

    >>> IAuthVerification.providedBy(authorizer)
    True
    >>> result = authorizer.verify(invoice_number='KB125', amount=43.40)

Now, a class may **implement** that behavior, which is a way of saying
that "instances of this class will provide the behavior":

    class AuthNet(object):
        def verify(self, invoice_number, amount):
            """ ... (class - instance based implementation) """
    classImplements(AuthNet, IAuthVerification)

    >>> IAuthVerification.providedBy(AuthNet)
    False
    >>> AuthNet.verify(invoice_number='KB125', amount=43.40)
    <UnboundMethod Exception>

Alternatively, class or static methods could be used:

    class StaticAuthNet(object):
        @staticmethod
        def verify(invoice_number, amount):
            """ ... """
    alsoProvides(StaticAuthNet, IAuthVerification)

    >>> IAuthVerification.providedBy(StaticAuthNet)
    True
    >>> result = StaticAuthNet.verify(invoice_number='KB125', amount=43.40)

Or a module could even provide the interfaces. In the first example
above (under 'an object **provides** that behavior'), do you know
whether 'authorizer' is an instance, class, or module? Hell, maybe
it's a function that has 'verify' added as an attribute. It doesn't
matter - it fills the 'IAuthVerification' role.

In my blog post, I also show a dynamically constructed object
providing an interface's specified behavior. An instance of an empty
class is made, and then methods and other supporting attributes are
attached to this specific instance only. Real world examples of this
include Zope 2, where a folder may have "Python Scripts" or other
callable members that, in effect, make for a totally custom object. It
can also provide this same behavior (in fact, I was able to take
advantage of this on some old old old Zope 2 projects that started in
the web environment and transitioned to regular Python
modules/classes).

In any case, there are numerous ways to fulfill a role. I think any
system that was limited to classes and involved 'issubclass' and
'isinstance' trickery would be limiting or confusing if it started to
be used to describe behaviors of modules, one-off objects, and so on.

-- 
Jeff Shell


More information about the Python-3000 mailing list