如果我想查找对象的特殊方法,我可以在对象或其类型上进行查找。但是,这两种选择似乎都不正确。
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 :(得分: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
答案 1 :(得分: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'