[Types-sig] Static Methods instead of Interface construct?

Clark C. Evans cce@clarkevans.com
Thu, 22 Mar 2001 06:26:42 -0500 (EST)


Ok... this is a bit off-the-wall, but here goes, please
excuse the semi-PEP format, but I find the structure 
helpful for fleshing things out (kinda interesting how
a bit of structure helps you along, no?)

Clark

...


Abstract

    So far static methods[0] do not exist in Python
    This proposal asks that None be allowed when calling a
    method using the class syntax, e.g.,  the following 
    syntax would be allowed, MyClass.method(None,...).  Of 
    course, it will be no longer guaranteed that a "self" 
    references within a method is an instance, however, with
    this additional flexibility comes power.

Motivation

    Recently in the type special interest group, there have
    been two proposals to introduce an Interface type.  One
    of those proposals introduces a completely new syntax[1]
    and semantics, while the other proposal [2] only uses a 
    this new data type to create interfaces classes with a 
    single static method, __check__.  While this proposal would
    not help the first Interface proposal, the second interface
    proposal could be implemented as an additional static 
    method on *existing* classes rather than introducing a
    completely new construct to Python.

    Furthermore, the Protocol Checking and Adaptation proposed 
    PEP [3] could also make wonderful use of a static method to 
    enable classes to provide wrappers (compliant with that class) 
    for specific object instances of other classes and/or types.
    This is detailed in the example section.

    Overall, static methods have very specific uses in the 
    factory pattern and the singleton design patterns.  These 
    patterns are a useful tool in any programmers toolbox.  
    Although this change may be painful internally, it wont jar 
    the language, and Python may be able to avoid adding new
    static-method like syntax constructs such as Interface.

Specification

    This is very straightforward.  When an unbound method is 
    called from a Class, either None or an instance of the Class 
    may be used as the instance object.  Accessing the "self"
    parameter within the methods body would be allowed, however, 
    calls to methods or attributes would give the familiar 
    AttributeError.  

Example Usage

    In the Protocol Checking and Adaptation proposed PEP [3], it
    is mentioned how it may be possible for a particular protocol
    (in this example, a particular class is a protocol) to 
    provide an adapter for an instance of another class or signal 
    that the object has indeed implemented enough of the required
    protocol to be substitutable for objects of that class.

    Given the current restrictions on static methods, this is not
    easy to do.  However, if None were allowed for the first 
    argument, then an implementation such as the following 
    is possible.  (you may want to read the Protocol Checking and
    Adaptation proposed PEP first).

    -----------------------------------------------------------------
    adapter.py
    -----------------------------------------------------------------
        # <details snipped>
        def interal_adapt(obj,protocol,can_wrap):

            # the obj may have the answer, so ask it about the ident
            # <details snipped>

            # the protocol may have the answer, so ask it about the obj
            if type(protocol) is types.ClassType:
                try:
                    retval = protocol.__adapt__(None,obj)
                    if retval:    return retval
                except:
                    pass   # hmm

            # <remaining details snipped>

    -----------------------------------------------------------------
    test.py
    -----------------------------------------------------------------

     # <details snipped>

        class EggsSpamAndHam (SpamOnly,KnightsWhoSayNi):
            def ham(self,str): print "ham!" + str
            def __adapt__(self,protocol,can_wrap):
                if self is None:
                    # hmm, well protocol is now foreign object
                    if protocol.__class__ is HamOnly:
                        return protocol  # yep, HamOnly can pass off...
                # <details snipped>

        def test():
            x = HamOnly()
            adapt(x,EggsSpamAndHam).ham("Yepee!")

    -----------------------------------------------------------------
    Example Run
    -----------------------------------------------------------------
        >>> import test
        >>> test.test()
        ham!Yepee!

    Anyway, its not perfect (due to the argument name splitting
    its meaning...

References and Footnotes
    [0] http://www.python.org/cgi-bin/faqw.py?req=show&file=faq04.084.htp
    [1] http://python.sourceforge.net/peps/pep-0245.html
    [2] http://mail.python.org/pipermail/types-sig/2001-March/001223.html
    [3] http://mail.python.org/pipermail/python-list/2001-March/034609.html