2013-09-24 55 views
2

如果我想查找一个对象的特殊方法,我可以在对象或其类型上执行此操作。虽然这两个选项似乎都不正确。正确执行特殊方法查找

def lookup_add(o): 
    return o.__add__ 
def lookup_sub(o): 
    return type(o).__sub__ 

class Foo(type): 
    def __add__(self, other): 
     return NotImplemented 
    def __sub__(self, other): 
     return NotImplemented 

class Bar(object): 
    __metaclass__ = Foo 

    def __add__(self, other): 
     return NotImplemented 

baz = Bar() 

lookup_add(Bar) # Produces wrong method 
lookup_sub(baz) # Should raise an error. Instead, produces wrong method. 

lookup_add查看对象。它对lookup_add(baz)正常工作,为baz的方法返回一个绑定的方法对象,但它产生了lookup_add(Bar)的错误结果。

lookup_sub查看它的类型。它适用于lookup_sub(Bar),为Bar的方法返回一个未绑定的方法对象,但它产生了lookup_sub(baz)的错误结果。

我可以尝试functools.partial(operator.add, o),但这并不真正查找o的方法。如果o实际上没有实现__add__,则此尝试不会产生我想要的错误。

是否有复制解释器的特殊方法查找行为的好方法?

+0

什么是正确的行为?我没有看到'lookup_add(Bar)'如何从类对象中获得一个绑定的方法。 –

+0

非常有趣...好奇如何解决这个问题。 – Claudiu

+0

@ li.davidm:我想要一个函数,它为任何对象'o'生成一个返回值,该值代表'__add__'方法,当您执行'o + whatever'时被调用,或者如果不存在这样的方法则产生一个错误。结果是绑定还是未绑定的方法并不重要。 (请注意,我知道'__radd__'和'__iadd__';我特别想查找'__add__'。例如,选择'__pos__'或'__neg__'这样的方法可能会更好。) – user2357112

回答

0

我想你可以你的两个使用isinstance结合:

def lookup_method(obj, method): 
    if not isinstance(obj, type): 
     return getattr(obj, method, None) 
    return getattr(type(obj), method, None) 

>>> print(lookup_method(Bar, '__add__')) 
<unbound method Foo.__add__> 
>>> print(lookup_method(baz, '__add__')) 
<bound method Bar.__add__ of <__main__.Bar object at 0x23f27d0>> 
>>> print(lookup_method(baz, '__sub__')) 
None 
+0

仍然不起作用。如果某人设置了'baz .__ add__ = 3',则返回'3'而不是方法。 – user2357112

+0

在这种情况下,您可以使用'callable'添加支票。 –

+0

'bar .__ add__ = lambda x:4',然后。 – user2357112

0

好吧,我想我得到了它。关键是要始终获得该类型的(未绑定)方法,并将其绑定:

import types 

def special_lookup_mimic(obj, name): 
    if not hasattr(obj, name): 
     raise TypeError("No method of that name") 

    meth = getattr(obj, name) 
    if not isinstance(meth, types.MethodType): 
     raise TypeError("Expected method") 

    #always look-up the type's method 
    cls = obj.__class__ 
    return getattr(cls, name).__get__(obj, cls) 

演示:

class Foo(type): 
    def __add__(cls, other): 
     print 'Foo().__add__' 
     return 999 

class Bar(object): 
    __metaclass__ = Foo 

    def __init__(self, id): 
     self.id = id 

    def __add__(self, other): 
     print 'Bar(%d).__add__' % (self.id,) 
     return self.id 

b1 = Bar(1) 
b2 = Bar(2) 

b1 + 10; special_lookup_mimic(b1, '__add__')(10) 
b2 + 10; special_lookup_mimic(b2, '__add__')(10) 

b1.__add__ = b2.__add__ 

b1 + 10; special_lookup_mimic(b1, '__add__')(10) 
b2 + 10; special_lookup_mimic(b2, '__add__')(10) 

Bar + 10; special_lookup_mimic(Bar, '__add__')(10) 

def patched_add(num): 
    def patch_add(cls, other): 
     print "Patched add '%d'" % (num,) 
     return num 
    return patch_add 

print "Patching Bar.__add__..." 
Bar.__add__ = patched_add(1337) 

b1 + 10; special_lookup_mimic(b1, '__add__')(10) 
b2 + 10; special_lookup_mimic(b2, '__add__')(10) 
Bar + 10; special_lookup_mimic(Bar, '__add__')(10) 

print "Patching Foo.__add__..." 
Foo.__add__ = patched_add(10000) 

b1 + 10; special_lookup_mimic(b1, '__add__')(10) 
b2 + 10; special_lookup_mimic(b2, '__add__')(10) 
Bar + 10; special_lookup_mimic(Bar, '__add__')(10) 

输出:

Bar(1).__add__ 
Bar(1).__add__ 
Bar(2).__add__ 
Bar(2).__add__ 
Bar(1).__add__ 
Bar(1).__add__ 
Bar(2).__add__ 
Bar(2).__add__ 
Foo().__add__ 
Foo().__add__ 
Patching Bar.__add__... 
Patched add '1337' 
Patched add '1337' 
Patched add '1337' 
Patched add '1337' 
Foo().__add__ 
Foo().__add__ 
Patching Foo.__add__... 
Patched add '1337' 
Patched add '1337' 
Patched add '1337' 
Patched add '1337' 
Patched add '10000' 
Patched add '10000' 
+0

关闭,但仍有边缘情况失败。例如,如果你将一个实例的方法用'x .__ add__ = y .__ add__'实现了另一个实例,这将返回'y'的方法,而不是Python用于'x + something'的方法。仅仅获得Python的特殊方法查找行为是非常困难的;你会认为会有一个简单的配方或库函数。 – user2357112

+0

@ user2357112:哦,我永远不知道那是行为。非常有趣... – Claudiu

+0

@ user2357112:我想我明白了......对于所有你能想到的情况,这项工作是否有效? – Claudiu