メタクラスと__getattribute__()

演算子オーバーロードがどう動いているのかを確かめるため、いろいろ意地悪なサンプルを書いてみる。

class MyType(type):
    def __init__(self, name, bases, dict):
        type.__init__(self, name, bases, dict)
        self.__add__ = lambda self, rhs: 1

    def __getattribute__(self, name):
        if name == '__add__':
            return lambda self, rhs: 2
        return type.__getattribute__(self, name)

class Foo(object):
    def __metaclass__(name, bases, dict):
        return MyType(name, bases, dict)

f = Foo()
# Prints "2"
print f.__class__.__add__(f, 0)
# "1"? or "2"?
print f + 0

とか

class MyType(type):
    def __init__(self, name, bases, dict):
        type.__init__(self, name, bases, dict)

    @property
    def __add__(self):
        return lambda self, rhs: 0

class Foo(object):
    def __metaclass__(name, bases, dict):
        return MyType(name, bases, dict)

f = Foo()
# Prints "0"
print f.__class__.__add__(f, 0)
# Does this work?
print f + 0

とか。

最初の例は 1 を出力し、2 つ目の例は

Traceback (most recent call last):
  File "/tmp/test.py", line 17, in <module>
    print f + 0
TypeError: unsupported operand type(s) for +: 'Foo' and 'int'

となる。

意外性があるという意味で謎な仕様だと思った。