Python 2.5和2.6之间的不同实例方法行为

时间:2010-08-02 20:00:22

标签: python

在创建实例后,尝试更改实例上的__unicode__方法会在Python 2.5和2.6上产生不同的结果。

这是一个测试脚本:

class Dummy(object):

    def __unicode__(self):
        return u'one'

    def two(self):
        return u'two'

d = Dummy()
print unicode(d)
d.__unicode__ = d.two
print unicode(d)
print d.__unicode__()

在Python 2.5上,这会产生

one
two
two

也就是说,更改实例的__unicode__也会更改unicode(instance)

在Python 2.6上,这会产生

one
one
two

因此,更改后,unicode(instance)instance.__unicode__()会返回不同的结果。

为什么呢?我怎样才能在Python 2.6上使用它?

(对于它的价值,这里的用例是我想为给定类的所有子类的__unicode__的输出附加一些内容,而不必修改子类的代码。)

编辑以使用例更清晰

我有A类,它有许多子类。这些子类定义了简单的__unicode__方法。我想添加逻辑,以便对于A类子类的实例,unicode(实例)得到一些固定到最后的东西。为了保持代码简单,并且因为有许多子类我不想改变,我宁愿避免编辑子类代码。

这实际上是在Python 2.5中运行的现有代码。它是这样的:

class A(object):

    def __init__(self):
        self._original_unicode = self.__unicode__
        self.__unicode__ = self.augmented_unicode

    def augmented_unicode(self):
        return self._original_unicode() + u' EXTRA'

这段代码不再适用于2.6。有关如何在不修改子类代码的情况下实现此目的的任何建议(如果答案涉及元类,请注意,类A本身是另一个类的子类 - django.db.models.Model - 具有相当精细的元类。)

3 个答案:

答案 0 :(得分:2)

编辑:响应OP的评论:添加一个间接层可以让您在每个实例的基础上更改unicode的行为:

class Dummy(object):

    def __unicode__(self):
        return self._unicode()

    def _unicode(self):
        return u'one'

    def two(self):
        return u'two'

d = Dummy()
print unicode(d)
# one
d._unicode = d.two
print unicode(d)
# two
print d.__unicode__()
# two

答案 1 :(得分:2)

您似乎不允许monkey-patch protocol methods(以双下划线开头和结尾的那些):

  

注意

     

在实践中还有另一个例外   我们这里没有处理过。虽然   你可以用实例覆盖方法   属性(对猴子非常有用)   用于测试目的的修补方法)   你不能用Python做到这一点   协议方法。这些都是'神奇的   名称开头和结尾的方法   双下划线。调用时   通过Python解释器,他们是   直接看了上课   不在实例上(但如果你   直接查找它们 - 例如   x。 repr - 正常属性查找   规则适用)。

在这种情况下,除非你可以使用~unutbu's answer,否则你可能会陷入困境。

编辑:或者,您可以让基类__unicode__方法在实例对象的dict中搜索__unicode__属性。如果它存在,则在实例对象上定义__unicode__,并且类方法调用实例方法。否则,我们回到__unicode__的类定义。

认为这可以让您现有的子类代码无需任何更改即可运行。但是,如果派生类想要调用类实现,它会变得很难看 - 你需要小心避免无限循环。在这个例子中我没有实现这样的黑客攻击;只是评论了他们。

import types

class Dummy(object):
    def __unicode__(self):
        func = self.__dict__.get("__unicode__", None)
        if func:
            // WARNING: if func() invokes this __unicode__ method directly,
            // an infinite loop could result. You may need an ugly hack to guard
            // against this. (E.g., set a flag on entry / unset the flag on exit,
            // using a try/finally to protect against exceptions.)

            return func()

        return u'one'

    def two(self):
        return u'two'

d = Dummy()
print unicode(d)
funcType = type(Dummy.__unicode__)
d.__unicode__ = types.MethodType(Dummy.two, d)
print unicode(d)
print d.__unicode__()

使用Python 2.6进行测试会产生以下输出:

> python dummy.py 
one
two
two

答案 2 :(得分:0)

看起来Dan对于猴子修补协议方法是正确的,这是Python 2.5和Python 2.6之间的变化。

我的修复结果是在类而不是实例上进行更改:

class A(object):
    def __init__(self):
        self.__class__.__unicode__ = self.__class__.augmented_unicode