Dynamically determine base classes on instantiation

Steven D'Aprano steve+comp.lang.python at pearwood.info
Wed Aug 15 20:16:03 EDT 2012


On Wed, 15 Aug 2012 23:17:41 +0200, Thomas Bach wrote:

> Hi list,
> 
> I'm confronted with a strang problem I cannot find a clean solution for.
> I want a class that determines on instantiating its base classes
> dynamically. Consider the following two use cases

Some comments:

1) What you show are not "use cases", but "examples". A use-case is a 
description of an actual real-world problem that needs to be solved. A 
couple of asserts is not a use-case.

2) You stated that you have a "strange problem", but you haven't told us 
what that problem is, you went directly to what you think is the 
solution: "a class that determines on instantiating its base classes 
dynamically".

How about you tell us the problem, and we'll suggest a solution?

I'm pretty sure it isn't going to be what you asked for, because that 
goes completely against the fundamentals of object-oriented design.

Consider your two examples:

a = Foo(['a', 'list'])
b = Foo({'blah': 8})

According to your design:

a is a Foo
b is a Foo
therefore a and b are the same type

So far so good: this is perfectly normal object-oriented design.

But you also have 

a is a list, but not a dict
b is a dict, but not a list
therefore a and b are different types

So you contradict yourself: at the same time, a and b are both the same 
and different types.

So now you see why you shouldn't do what you ask for. Now let me tell you 
why you *can't* do what you ask for: Python's classes don't work like 
that. You can't set the base classes of an instance individually. All 
instances of a class share the same base classes.

I think that the right solution here is not inheritance, but composition 
and delegation. You're already on the right track when you give your Foo 
instances an attribute _obj and then operate on that, but you are wrong 
to focus on inheritance.

Instead, Foo should implement only the shared operations, and everything 
else should be delegated to _obj.

Automatic delegation is trivially easy (except see below):

http://code.activestate.com/recipes/52295

This is a *really old* recipe, from ancient days before you could inherit 
from built-in types like list, dict etc., so the description of the 
problem is no longer accurate. But the technique is still good, with 
unfortunately one complication:

If you inherit from builtins, you cannot use automatic delegation on the 
magic "double-underscore" (dunder) methods like __eq__, __len__, etc.

See this thread here for one possible solution:

http://www.velocityreviews.com/forums/t732798-automatic-delegation-in-python-3-a.html



-- 
Steven



More information about the Python-list mailing list