1

This snippet defines a decorator @mixedmethod similar to @classmethod, which allows the method to access the calling instance when it exists. The decorated functions have two implicit arguments self, cls, the former having a None value when there is no instance in the call.
Using python's descriptor protocol, the implementation of @mixedmethod needs no more than 5 lines of code !

Edited by Gribouillis: n/a

#!/usr/bin/env python
# -*-coding: utf8-*-
# Compatibility: python 2 and py3k
# Title: mixedmethod.py
# Author: Gribouillis
# Created: 2012-01-13 12:10:17.722112 (isoformat date)
# License: Public Domain
# Use this code freely.

from __future__ import print_function
from functools import partial
import sys

"""This module implements a mixedmethod() decorator for class definitions.
"""

version_info = (0, 1)
version = ".".join(map(str, version_info))


class mixedmethod(object):
    """This decorator mutates a function defined in a class into a 'mixed' class and instance method.
    
    Usage:
    
        class Spam:
        
            @mixedmethod
            def egg(self, cls, *args, **kwargs):
                if self is None:
                    pass # executed if egg was called as a class method (eg. Spam.egg())
                else:
                    pass # executed if egg was called as an instance method (eg. instance.egg())

    The decorated methods need 2 implicit arguments: self and cls, the former being None when
    there is no instance in the call. This follows the same rule as __get__ methods in python's
    descriptor protocol.
    """
    def __init__(self, func):
        self.func = func
    def __get__(self, instance, cls):
        return partial(self.func, instance, cls)

if __name__ == '__main__':
    
    class Spam(object):
        
        @mixedmethod
        def ham(self, cls, *args):
            if self is None:
                print("Spam.ham() was called as a class method with {0}.".format((self, cls)+ args))
            else:
                print("Spam.ham() was called as an instance method with {0}.".format((self, cls) + args))
            
        def __repr__(self):
            return '<Spam instance>'

    egg = Spam()
    egg.ham(5)
    Spam.ham(5)

    """ my output -->
Spam.ham() was called as an instance method with (<Spam instance>, <class '__main__.Spam'>, 5).
Spam.ham() was called as a class method with (None, <class '__main__.Spam'>, 5).
    """
2
Contributors
1
Reply
18
Views
5 Years
Discussion Span
Last Post by pyTony
0

It is quite unclear which is the 'five lines'. You could reduce this terrible code bloat ;) by using the short function exception from PEP8 and put __init__ and __get__ definitions in single line also the example could use tertiary if handling the one word difference in printed strings.

Cheers, and thanks for sharing (remind my of wonderfull story in http://www.linuxjournal.com/article/3882, unfortunately the code example's indention is messed up).

Have something to contribute to this discussion? Please be thoughtful, detailed and courteous, and be sure to adhere to our posting rules.