文章来自《Python cookbook》. 翻译仅仅是为了个人学习,其它商业版权纠纷与此无关!
::-- KenLai [DateTime(2006-03-20T14:37:31Z)] TableOfContents
描述
Credit: Ken Seehof
问题 Problem
You need to modify the class hierarchy of an instance object that has already been instantiated.
解决 Solution
A rather unusual application of the mix-in concept lets us perform this task in Python 2.0 or later (with some limitations in Python 2.2):
def adopt_class(klass, obj, *args, **kwds):
- 're-class obj to inherit klass; call _ _init_ _ with *args, **kwds' # In Python 2.2, klass and obj._ _class_ _ must be compatible, # e.g., it's okay if they're both classic, as in the 'demo' function classname = '%s_%s' % (klass._ _name_ _, obj._ _class_ _._ _name_ _) obj._ _class_ _ = new.classobj(classname, (klass, obj._ _class_ _), {}) klass._ _init_ _(obj, *args, **kwds)
def demo( ):
- class Sandwich:
- def _ _init_ _(self, ingredients):
- self.ingredients = ingredients
- return ' and '.join(self.ingredients)
class WithSpam:
- def _ _init_ _(self, spam_count):
- self.spam_count = spam_count
- return Sandwich._ _repr_ _(self) + self.spam_count * ' and spam'
adopt_class(WithSpam, pbs, 2) print pbs
- def _ _init_ _(self, ingredients):
讨论 Discussion
Sometimes class adoption, as illustrated by this recipe, is the cleanest way out of class hierarchy problems that arise when you wish to avoid module interdependencies (e.g., within a layered architecture). It's more often useful if you want to add functionality to objects created by third-party modules, since modifying those modules' source code is undesirable.
In the following example, the programmer has these constraints:
There are several classes in objects.py, and more will be added in the future.
objects.py must not import or know about graphics.py, since the latter is not available in all configurations. Therefore, class G cannot be a base class for the objects.py classes.
graphics.py should not require modification to support additional classes that may be added to objects.py.
# objects.py class A(Base): ... class B(Base): ... def factory(...): ... returns an instance of A or B or ...
# graphics.py from oop_recipes import adopt_class import objects
class G: ... provides graphical capabilities
def gfactory(...):
- obj = objects.factory(...) adopt_class(G, obj, ...) return obj
Given the constraints, the adopt_class function provides a viable solution.
In Python 2.2, there are compatibility limitations on which classes can be used to multiply inherit from (otherwise, you get a "metatype conflict among bases" TypeError exception). These limitations affect multiple inheritance performed dynamically by means of the new.classobj function (as in this recipe) in the same way as they affect multiple inheritance expressed in the more usual way.
Classic classes (classes with no built-in type among their ancestors, not even the new built-in type object) can still be multiply inherited from quite peaceably, so the example in this recipe keeps working. The example given in the discussion will also keep working the same way, since class G is classic. Only two new-style classes with different built-in type ancestors would conflict.
参考 See Also
The Library Reference section on built-in types, especially the subsections on special attributes and functions.