动态地为对象分配特殊方法,但不为Python分配类

时间:2010-01-25 01:06:43

标签: python dynamic methods

我想做以下事情:

class A(object): pass

a = A()
a.__int__ = lambda self: 3

i = int(a)

不幸的是,这引发了:

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: int() argument must be a string or a number, not 'A'

如果我将“特殊”方法分配给A类而不是它的实例,这似乎只有作用。有没有追索权?

我想到的一种方式是:

def __int__(self):
    # No infinite loop
    if type(self).__int__.im_func != self.__int__.im_func:
        return self.__int__()
    raise NotImplementedError()

但这看起来很丑陋。

感谢。

3 个答案:

答案 0 :(得分:8)

Python总是在类上查找特殊方法,而不是实例(除了旧的,又称“遗留”类的类 - 它们已经被弃用并且已经在Python 3中消失了,因为奇怪的语义是主要是在实例上查找特殊方法,所以你真的想要使用它们,相信我! - 。)。

要创建一个特殊的类,其实例可以具有彼此独立的特殊方法,您需要为每个实例提供自己的类 - 然后您可以在实例的(个体)上分配特殊方法在不影响其他情况的情况下,从此过上幸福的生活。如果你想让它看起来像你正在为一个属性分配实例,而实际上分配给个性化的每个实例类的一个属性,你当然可以用一个特殊的__setattr__实现来实现。< / p>

这是一个简单的例子,带有明确的“赋值给类”语法:

>>> class Individualist(object):
...   def __init__(self):
...     self.__class__ = type('GottaBeMe', (self.__class__, object), {})
... 
>>> a = Individualist()
>>> b = Individualist()
>>> a.__class__.__int__ = lambda self: 23
>>> b.__class__.__int__ = lambda self: 42
>>> int(a)
23
>>> int(b)
42
>>> 

这里是花哨的版本,你可以“让它看起来像”你将特殊方法指定为一个实例属性(当然在场景后面它仍然属于课程):

>>> class Sophisticated(Individualist):
...   def __setattr__(self, n, v):
...     if n[:2]=='__' and n[-2:]=='__' and n!='__class__':
...       setattr(self.__class__, n, v)
...     else:
...       object.__setattr__(self, n, v)
... 
>>> c = Sophisticated()
>>> d = Sophisticated()
>>> c.__int__ = lambda self: 54
>>> d.__int__ = lambda self: 88
>>> int(c)
54
>>> int(d)
88

答案 1 :(得分:2)

适用于新式类的唯一方法是在类上调用属性的方法(如果存在):

class A(object):
    def __int__(self):
        if '__int__' in self.__dict__:
            return self.__int__()
        raise ValueError

a = A()
a.__int__ = lambda: 3
int(a)

请注意,a.__int__不是方法(只有属于的属性的函数才会成为方法),因此self不会隐式传递。

答案 2 :(得分:0)

关于覆盖__int__的具体细节,我没有任何补充。但是我注意到你的样本有一件事需要讨论。

当你手动为对象分配新方法时,“self”会自动传入。我修改了你的示例代码,使我的观点更加清晰:

class A(object): pass

a = A()
a.foo = lambda self: 3

a.foo()

如果运行此代码,则会抛出异常,因为您将0个参数传递给“foo”,并且需要1。如果你删除“自我”它可以正常工作。

如果必须在对象的类中查找方法并且它找到的函数是“正常”函数,Python只会自动为参数添加“self”。 (“异常”函数的示例:类方法,可调用对象,绑定方法对象。)如果将callables粘贴到对象本身,它们将不会自动获得“self”。

如果您想要自己,请使用闭包。