动态地将基类混合到Python中的实例

Nik*_* B. 33 python oop mixins

是否可以在运行时向对象实例(不是类!)添加基类?关于如何Object#extend在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"
Run Code Online (Sandbox Code Playgroud)

unu*_*tbu 39

这会动态定义一个新类GentlePerson,并p为其重新分配类:

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"
Run Code Online (Sandbox Code Playgroud)

根据你的要求,这会修改p基础,但不会改变p原来的类Person.因此,其他实例Person不受影响(并且会调用AttributeErrorif introduce_self).


虽然在问题中没有直接询问,但我会为googlers和好奇心寻求者添加,只有当类不直接继承时,才可以动态更改类的基础但是(AFAIK)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
Run Code Online (Sandbox Code Playgroud)

  • @neo:没关系.`Gentleman`不是`Person`的子类,反之亦然,所以任何一个都可以在base列表中排在第一位. (2认同)

sle*_*cal 11

稍微清洁的版本:

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),{})
Run Code Online (Sandbox Code Playgroud)

  • 不错的解决方案。我使用它来覆盖现有实例上的方法,在这种情况下,类型顺序应为((cls,base_cls)` (2认同)

rec*_*dev 5

虽然已经回答了,但这里有一个功能:

def extend(instance, new_class):
    instance.__class__ = type(
          '%s_extended_with_%s' % (instance.__class__.__name__, new_class.__name__), 
          (instance.__class__, new_class), 
          {},
          )
Run Code Online (Sandbox Code Playgroud)

  • 有一个可行的解决方案很好,但请记住,应该仔细考虑这种黑魔法的使用,因为它可能是通往难以理解和难以调试的代码地狱的道路。 (2认同)