2 Two magic tricks for classes:
5 __metaclass__ = extendabletype
8 # in some other file...
10 ... # and here you can add new methods and class attributes to X
12 Mostly useful together with the second trick, which lets you build
13 methods whose 'self' is a pair of objects instead of just one:
15 class __extend__(pairtype(X, Y)):
17 def method((x, y), other, arguments):
21 pair(x, y).method(other, arguments)
23 This finds methods and class attributes based on the actual
24 class of both objects that go into the pair(), with the usual
25 rules of method/attribute overriding in (pairs of) subclasses.
27 For more information, see test_pairtype.
30 class extendabletype(type):
31 """A type with a syntax trick: 'class __extend__(t)' actually extends
32 the definition of 't' instead of creating a new subclass."""
33 def __new__(cls, name, bases, dict):
34 if name == '__extend__':
36 for key, value in dict.items():
37 if key == '__module__':
39 # XXX do we need to provide something more for pickling?
40 setattr(cls, key, value)
43 return super(extendabletype, cls).__new__(cls, name, bases, dict)
47 """Return a pair object."""
48 tp = pairtype(a.__class__, b.__class__)
49 return tp((a, b)) # tp is a subclass of tuple
53 def pairtype(cls1, cls2):
54 """type(pair(a,b)) is pairtype(a.__class__, b.__class__)."""
56 pair = pairtypecache[cls1, cls2]
58 name = 'pairtype(%s, %s)' % (cls1.__name__, cls2.__name__)
59 bases1 = [pairtype(base1, cls2) for base1 in cls1.__bases__]
60 bases2 = [pairtype(cls1, base2) for base2 in cls2.__bases__]
61 bases = tuple(bases1 + bases2) or (tuple,) # 'tuple': ultimate base
62 pair = pairtypecache[cls1, cls2] = extendabletype(name, bases, {})