如何在运行时动态更改实例的基类?

2024-06-09 14:38:15 发布

您现在位置:Python中文网/ 问答频道 /正文

This article有一个片段,显示了如何使用__bases__来动态更改某些Python代码的继承层次结构,方法是将一个类添加到继承它的类的现有类集合中。好吧,这很难理解,代码可能更清晰:

class Friendly:
    def hello(self):
        print 'Hello'

class Person: pass

p = Person()
Person.__bases__ = (Friendly,)
p.hello()  # prints "Hello"

也就是说,Person不是从源级别的Friendly继承,而是通过修改Person类的__bases__属性在运行时动态添加此继承关系。但是,如果将FriendlyPerson更改为新样式类(通过从对象继承),则会出现以下错误:

TypeError: __bases__ assignment: 'Friendly' deallocator differs from 'object'

在运行时更改继承层次结构时,在新样式类和旧样式类之间似乎有点像是indicate some incompatibilities。具体来说:"New-style class objects don't support assignment to their bases attribute"

我的问题是,是否可以使用Python 2.7+中的新样式类(可能使用__mro__属性)使上述友好/个人示例正常工作?

免责声明:我完全意识到这是一段晦涩难懂的代码。我完全意识到,在实际的生产中,类似这样的代码技巧往往难以阅读,这纯粹是一个思想实验,对于funzies来说,可以学习一些Python如何处理与多重继承相关的问题。


Tags: 代码hello属性层次结构article动态样式this
3条回答

我也一直在努力解决这个问题,并且对你的解决方案很感兴趣,但是Python3把它从我们身边夺走了:

AttributeError: attribute '__dict__' of 'type' objects is not writable

实际上,我有一个合法的需求,需要一个decorator来替换decorated类的(单个)超类。它需要太长的描述才能包含在这里(我尝试过,但无法将其保持到合理的长度和有限的复杂性——这是在许多基于Python的企业服务器的Python应用程序使用的上下文中出现的,其中不同的应用程序需要一些稍微不同的代码变体)

本页的讨论和其他类似的讨论提供了这样的提示:赋值给__bases__的问题只发生在没有定义超类(即,其唯一的超类是object)的类上。通过将需要替换其超类的类定义为一个平凡类的子类,我能够解决这个问题(对于Python2.7和3.2):

## T is used so that the other classes are not direct subclasses of object,
## since classes whose base is object don't allow assignment to their __bases__ attribute.

class T: pass

class A(T):
    def __init__(self):
        print('Creating instance of {}'.format(self.__class__.__name__))

## ordinary inheritance
class B(A): pass

## dynamically specified inheritance
class C(T): pass

A()                 # -> Creating instance of A
B()                 # -> Creating instance of B
C.__bases__ = (A,)
C()                 # -> Creating instance of C

## attempt at dynamically specified inheritance starting with a direct subclass
## of object doesn't work
class D: pass

D.__bases__ = (A,)
D()

## Result is:
##     TypeError: __bases__ assignment: 'A' deallocator differs from 'object'

好吧,再说一遍,这不是你通常应该做的,这只是为了提供信息。

Python在实例对象上查找方法由定义该对象的类的__mro__属性(esolutionOrder属性)确定。因此,如果我们可以修改Person__mro__,我们将获得所需的行为。类似于:

setattr(Person, '__mro__', (Person, Friendly, object))

问题是__mro__是只读属性,因此setattr不起作用。如果你是一个Python大师,也许有办法解决这个问题,但很明显我没有达到大师的地位,因为我想不出一个。

一种可能的解决方法是简单地重新定义类:

def modify_Person_to_be_friendly():
    # so that we're modifying the global identifier 'Person'
    global Person

    # now just redefine the class using type(), specifying that the new
    # class should inherit from Friendly and have all attributes from
    # our old Person class
    Person = type('Person', (Friendly,), dict(Person.__dict__)) 

def main():
    modify_Person_to_be_friendly()
    p = Person()
    p.hello()  # works!

这不会修改任何先前创建的Person实例,使其具有hello()方法。例如(只是修改main()):

def main():
    oldperson = Person()
    ModifyPersonToBeFriendly()
    p = Person()
    p.hello()  
    # works!  But:
    oldperson.hello()
    # does not

如果type调用的详细信息不清楚,请阅读e-satis' excellent answer on 'What is a metaclass in Python?'

我不能保证其后果,但这段代码符合py2.7.2的要求。

class Friendly(object):
    def hello(self):
        print 'Hello'

class Person(object): pass

# we can't change the original classes, so we replace them
class newFriendly: pass
newFriendly.__dict__ = dict(Friendly.__dict__)
Friendly = newFriendly
class newPerson: pass
newPerson.__dict__ = dict(Person.__dict__)
Person = newPerson

p = Person()
Person.__bases__ = (Friendly,)
p.hello()  # prints "Hello"

我们知道这是可能的。很酷。但我们永远不会用它!

相关问题 更多 >