[PEAK] 'Categories' in Python

Bob Ippolito bob at redivi.com
Fri Dec 10 17:29:03 EST 2004


On Dec 10, 2004, at 4:30 PM, Andy Gross wrote:

> I was playing around with decorators and metaclasses last night, and I 
> threw together a quick implementation of Objective-C style 
> "Categories" for Python.  In a nutshell, Categories are classes 
> without data members, just methods.
> I have a bunch of application-specific classes that suffer from 
> creeping Mixins - I have to implement some subject-oriented 
> functionality to a bunch of related classes, so I originally used 
> several Mixins to achieve this.  The result just doesn't feel right, 
> and the existence extra base classes gets in the way from time to 
> time.  Furthermore, if users need to change some relatively simple 
> behavior, they have to dig into the code, find the Mixins, change 
> them, and hope things don't break.

I think that categories come from Smalltalk originally..

> My little experiment last night uses a Category metaclass and 
> decorators to formalize and simplify this kind of class extension.  An 
> overly simple example:
>
> class StringyThings(Category):
> 	def toLower(self):
> 		return self.string.lower()
>
> class StringyThing(B1, B2, ... BN):
> 	@categories(StringyThings)
> 	def __init__(self, string):
> 		self.string = string
>
> @category(StringyThings):
> def toUpper(self):
> 	return self.string.upper()
>
> By defining that toUpper function, any class belonging to 
> StringyThings (and its subclasses) gains a toUpper method.
>
> For my application, I'm thinking of adding some kind of 
> getCategoryForInterface(IFoo) to build dynamic event handler classes
> from user defined functions.
>
> I was hoping the wizards here could offer some comments or 
> suggestions.  You can see/download the code at:
>
> http://argv0.net/pycategories

Your implementation is backwards.  Categories are added to existing 
classes without their consent.  Don't call what you're doing 
categories.

Also, it's not really useful to distinguish between methods that a 
class originally had, and the methods they acquired from a category.

This is an implementation of Category that works in Objective-C terms:

class MetaCategory(type):
     def __new__(cls, name, bases, dct):
         if '__category_of__' in dct:
             return type.__new__(cls, name, bases, dct)
         if not len(bases) == 1 and isinstance(bases[0], cls):
             raise TypeError("Categories may only have a Category(...) 
as their base")
         cls = bases[0].__category_of__
         for k,v in dct.iteritems():
             if k == '__module__':
                 continue
             setattr(cls, k, v)
         return cls

def Category(cls):
     return MetaCategory(
         'Category(%s)' % (cls.__name__),
         (object,),
         {'__category_of__': cls}
     )

class Foo(object):
     pass

foo = Foo()
print "foo has a default repr: %r" % (foo,)

class Foo(Category(Foo)):
     def __repr__(self):
         return type(self).__name__ + '()'

print "now it has a nicer one: %r" % (foo,)

-bob




More information about the PEAK mailing list