【问题标题】:Correctly performing special method lookup正确执行特殊方法查找
【发布时间】:2013-09-24 03:17:58
【问题描述】:

如果我想查找对象的特殊方法,我可以在对象或其类型上进行。不过,这两个选项似乎都不正确。

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__,这个尝试不会产生我想要的错误。

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

【问题讨论】:

  • 正确的行为是什么?我不明白lookup_add(Bar) 是如何从类对象中获取绑定方法的。
  • 非常有趣...很好奇如何解决这个问题。
  • @li.davidm:我想要一个函数,它为任何对象o 生成一个返回值,该返回值表示当您执行o + whatever 时会调用的__add__ 方法,或者引发错误如果不存在这样的方法。结果是绑定方法还是未绑定方法并不重要。 (请注意,我知道__radd____iadd__;我特别想查找__add__。例如,选择__pos____neg__ 之类的方法可能会更好。)跨度>
  • 我明白了。所以lookup_add(Bar)应该给你Bar.__add__(Class2),即Bar + Class2
  • 您要解决的问题是什么?这是在寻找似乎没有问题的解决方案。

标签: python


【解决方案1】:

我想您可以使用 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

【讨论】:

  • 还是不行。如果有人设置了baz.__add__ = 3,则返回3 而不是方法。
  • 在这种情况下,您可以使用callable 添加支票。
  • bar.__add__ = lambda x: 4,然后。
  • 哦...所以你想要实例在没有任何修改的情况下给出的内容(因为从技术上讲,现在是实例的 add 方法)。嗯。
  • 如果您执行bar + something,则不会调用该方法。我想要 Python 实际用于加法的方法。
【解决方案2】:

好的,我想我明白了。诀窍是始终获取类型的(未绑定)方法并绑定它:

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'

【讨论】:

  • 关闭,但仍然存在失败的边缘情况。例如,如果您使用x.__add__ = y.__add__ 将一个实例的方法猴子修补到另一个实例上,这将返回y 的方法,而不是Python 将用于x + something 的方法。仅仅获得 Python 的特殊方法查找行为是非常困难的;你会认为某处会有一个简单的配方或库函数。
  • @user2357112:我想我明白了……这对你能想到的所有情况都有效吗?
猜你喜欢
  • 1970-01-01
  • 2020-02-27
  • 2012-10-17
  • 2019-09-20
  • 2013-04-14
  • 2012-11-09
  • 1970-01-01
  • 1970-01-01
  • 2012-04-24
相关资源
最近更新 更多