Question

Suppose you have two classes X & Y. You want to decorate those classes by adding attributes to the class to produce new classes X1 and Y1.

For example:

class X1(X):
  new_attribute = 'something'

class Y1(Y):
  new_attribute = 'something'

new_attribute will always be the same for both X1 and Y1. X & Y are not related in any meaningful way, except that multiple inheritance is not possible. There are a set of other attributes as well, but this is degenerate to illustrate.

I feel like I'm overcomplicating this, but I had thought to use a decorator, somewhat likeso:

def _xywrap(cls):
  class _xy(cls):
    new_attribute = 'something'
  return _xy

@_xywrap(X)
class X1():
   pass

@_xywrap(Y)
class Y1():
   pass

It feels like I'm missing a fairly common pattern, and I'd be much obliged for thoughts, input and feedback.

Thank you for reading.

Brian

EDIT: Example:

Here is a relevant extract that may illuminate. The common classes are as follows:

from google.appengine.ext import db

# I'm including PermittedUserProperty because it may have pertinent side-effects
# (albeit unlikely), which is documented here: [How can you limit access to a
# GAE instance to the current user][1].

class _AccessBase:
   users_permitted = PermittedUserProperty()
   owner = db.ReferenceProperty(User)

class AccessModel(db.Model, _AccessBase):
    pass

class AccessExpando(db.Expando, _AccessBase):
    pass

# the order of _AccessBase/db.* doesn't seem to resolve the issue
class AccessPolyModel(_AccessBase, polymodel.PolyModel):
    pass

Here's a sub-document:

 class Thing(AccessExpando):
     it = db.StringProperty()

Sometimes Thing will have the following properties:

 Thing { it: ... }

And other times:

 Thing { it: ..., users_permitted:..., owner:... }

I've been unable to figure out why Thing would sometimes have its _AccessParent properties, and other times not.

Was it helpful?

Solution

Use 3-arguments type:

def makeSomeNicelyDecoratedSubclass(someclass):
  return type('MyNiceName', (someclass,), {'new_attribute':'something'})

This is indeed, as you surmised, a reasonably popular idiom.

Edit: in the general case if someclass has a custom metaclass you may need to extract and use it (with a 1-argument type) in lieu of type itself, to preserve it (this may be the case for your Django and App Engine models):

def makeSomeNicelyDecoratedSubclass(someclass):
  mcl = type(someclass)
  return mcl('MyNiceName', (someclass,), {'new_attribute':'something'})

This also works where the simpler version above does (since in simple cases w/no custom metaclasses type(someclass) is type).

OTHER TIPS

Responding to your comments on voyager's answer:

from google.appengine.ext import db

class Mixin(object):
    """Mix in attributes shared by different types of models."""
    foo = 1
    bar = 2
    baz = 3

class Person(db.Model, Mixin):
    name = db.StringProperty()

class Dinosaur(db.polymodel.PolyModel, Mixin):
    height = db.IntegerProperty()

p = Person(name='Buck Armstrong, Dinosaur Hunter')
d = Dinosaur(height=5000)

print p.name, p.foo, p.bar, p.baz
print d.height, d.foo, d.bar, d.baz

Running that results in

Buck Armstrong, Dinosaur Hunter 1 2 3
5000 1 2 3

Is that not what you had in mind?

Why can't you use multiple inheritance?

class Origin:
  new_attribute = 'something'

class X:
  pass

class Y:
  pass

class X1(Origin, X):
  pass

class Y1(Origin, Y):
  pass
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top