Динамически смешивать базовый класс с экземпляром в Python - PullRequest
31 голосов
/ 17 декабря 2011

Можно ли добавить базовый класс к экземпляру объекта (не классу!) Во время выполнения?Что-то вроде того, как 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"

Ответы [ 3 ]

38 голосов
/ 17 декабря 2011

Это динамически определяет новый класс 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"

По вашему запросу это изменяет базы p, но не изменяет исходный класс p Person. Таким образом, другие случаи Person не затронуты (и вызовут AttributeError, если вызов introduce_self).


Хотя это не было прямо задано в этом вопросе, я добавлю для гуглеров и искателей любопытства, что также можно динамически изменять базы класса, но (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
10 голосов
/ 26 июня 2015

Чуть чище версия:

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),{})
5 голосов
/ 17 декабря 2011

Хотя на него уже ответили, вот функция:

def extend(instance, new_class):
    instance.__class__ = type(
          '%s_extended_with_%s' % (instance.__class__.__name__, new_class.__name__), 
          (instance.__class__, new_class), 
          {},
          )
...