2013-09-24 98 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