Dynamically mixin a base class to an instance in P

2020-05-15 07:33发布

Is it possible to add a base class to an object instance (not a class!) at runtime? Something along the lines of how Object#extend works in Ruby:

class Gentleman(object):
  def introduce_self(self):
    return "Hello, my name is %s" % self.name

class Person(object):
  def __init__(self, name):
    self.name = name

p = Person("John")
# how to implement this method?
extend(p, Gentleman)
p.introduce_self() # => "Hello, my name is John"

3条回答
Deceive 欺骗
2楼-- · 2020-05-15 07:51

Although it's already answered, here is a function:

def extend(instance, new_class):
    instance.__class__ = type(
          '%s_extended_with_%s' % (instance.__class__.__name__, new_class.__name__), 
          (instance.__class__, new_class), 
          {},
          )
查看更多
我只想做你的唯一
3楼-- · 2020-05-15 08:00

This dynamically defines a new class GentlePerson, and reassigns p's class to it:

class Gentleman(object):
  def introduce_self(self):
    return "Hello, my name is %s" % self.name

class Person(object):
  def __init__(self, name):
    self.name = name

p = Person("John")
p.__class__ = type('GentlePerson',(Person,Gentleman),{})
print(p.introduce_self())
# "Hello, my name is John"

Per your request, this modifies p's bases, but does not alter p's original class Person. Thus, other instances of Person are unaffected (and would raise an AttributeError if introduce_self were called).


Although it was not directly asked in the question, I'll add for googlers and curiosity seekers, that it is also possible to dynamically change a class's bases but (AFAIK) only if the class does not inherit directly from object:

class Gentleman(object):
  def introduce_self(self):
    return "Hello, my name is %s" % self.name

class Base(object):pass
class Person(Base):
  def __init__(self, name):
    self.name = name

p = Person("John")
Person.__bases__=(Gentleman,object,)
print(p.introduce_self())
# "Hello, my name is John"

q = Person("Pete")
print(q.introduce_self())
# Hello, my name is Pete
查看更多
叛逆
4楼-- · 2020-05-15 08:04

Slightly cleaner version:

def extend_instance(obj, cls):
    """Apply mixins to a class instance after creation"""
    base_cls = obj.__class__
    base_cls_name = obj.__class__.__name__
    obj.__class__ = type(base_cls_name, (base_cls, cls),{})
查看更多
登录 后发表回答