我有一个由外部库提供给我的类。我创建了这个类的一个子类。我也有一个原始类的实例。
我现在想将此实例转换为我的子类的一个实例,而不更改该实例已有的任何属性(无论如何我的子类覆盖的属性除外)。
以下解决方案似乎有效。
# This class comes from an external library. I don't (want) to control
# it, and I want to be open to changes that get made to the class
# by the library provider.
class Programmer(object):
def __init__(self,name):
self._name = name
def greet(self):
print "Hi, my name is %s." % self._name
def hard_work(self):
print "The garbage collector will take care of everything."
# This is my subclass.
class C_Programmer(Programmer):
def __init__(self, *args, **kwargs):
super(C_Programmer,self).__init__(*args, **kwargs)
self.learn_C()
def learn_C(self):
self._knowledge = ["malloc","free","pointer arithmetic","curly braces"]
def hard_work(self):
print "I'll have to remember " + " and ".join(self._knowledge) + "."
# The questionable thing: Reclassing a programmer.
@classmethod
def teach_C(cls, programmer):
programmer.__class__ = cls # <-- do I really want to do this?
programmer.learn_C()
joel = C_Programmer("Joel")
joel.greet()
joel.hard_work()
#>Hi, my name is Joel.
#>I'll have to remember malloc and free and pointer arithmetic and curly braces.
jeff = Programmer("Jeff")
# We (or someone else) makes changes to the instance. The reclassing shouldn't
# overwrite these.
jeff._name = "Jeff A"
jeff.greet()
jeff.hard_work()
#>Hi, my name is Jeff A.
#>The garbage collector will take care of everything.
# Let magic happen.
C_Programmer.teach_C(jeff)
jeff.greet()
jeff.hard_work()
#>Hi, my name is Jeff A.
#>I'll have to remember malloc and free and pointer arithmetic and curly braces.
但是,我不相信这个解决方案不包含我没有想到的任何警告(对不起三重否定),特别是因为重新分配魔法__class__
感觉不对。即使这行得通,我也情不自禁地觉得应该有一种更 Pythonic 的方式来做到这一点。
有没有?
编辑:谢谢大家的回答。这是我从他们那里得到的:
尽管通过分配给实例重新分类的想法
__class__
不是一个广泛使用的习惯用法,但大多数答案(在撰写本文时 6 个中有 4 个)认为它是一种有效的方法。一个答案(由 ojrac 提供)说它“乍一看很奇怪”,我同意这一点(这是提出这个问题的原因)。只有一个答案(来自 Jason Baker;有两个正面评论和投票)积极劝阻我不要这样做,但是这样做是基于示例用例而不是一般技术。没有一个答案,无论是否积极,都无法在这种方法中找到实际的技术问题。一个小例外是 jls,他提到要提防旧式类(这很可能是真的)和 C 扩展。我认为这种新风格的类感知 C 扩展应该与 Python 本身一样好(假设后者是正确的),尽管如果你不同意,请继续给出答案。
至于这是多么pythonic的问题,有一些肯定的答案,但没有给出真正的理由。看看 Zen ( import this
),我想在这种情况下最重要的规则是“显式优于隐式”。不过,我不确定该规则是否支持或反对以这种方式重新分类。
使用
{has,get,set}attr
似乎更明确,因为我们明确地对对象进行更改而不是使用魔法。使用
__class__ = newclass
似乎更明确,因为我们明确地说“这现在是类 'newclass' 的对象,期待不同的行为”,而不是默默地改变属性,但让对象的用户相信他们正在处理旧类的常规对象。
总结:从技术角度来看,方法似乎还可以;pythonicity问题仍然没有答案,偏向于“是”。
我接受了 Martin Geisler 的回答,因为 Mercurial 插件示例是一个非常强大的示例(也因为它回答了一个我什至还没有问过自己的问题)。但是,如果对 pythonicity 问题有任何争论,我仍然想听听。感谢所有到目前为止。
PS 实际用例是一个 UI 数据控件对象,它需要在运行时增加额外的功能。但是,这个问题是非常笼统的。