2012-05-29 5 views
7

免責事項これはメタプログラミングの単なる実践です。実際の目的は ではありません。効果がない関数に__getattr__と__getitem__を定義しても意味がありません

私は関数オブジェクトに __getitem____getattr__メソッドを割り当てた

が、 ...

def foo(): 
    print "foo!" 

foo.__getitem__ = lambda name: name 
foo.__getattr__ = lambda name: name 
foo.baz = 'baz' 

正気我々は機能にプロパティを割り当てることができることを確認してください。

>>> foo.baz 
'baz' 

ニート。 "魔法のゲッター"はどうですか?

>>> foo.bar 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
AttributeError: 'function' object has no attribute 'bar' 

>>> foo['foo'] 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
TypeError: 'function' object is not subscriptable 

>>> getattr(foo, 'bar') 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
AttributeError: 'function' object has no attribute 'bar' 

関数オブジェクトに「マジックゲッター」を付けることはできますか?

答えて

6

Nope!

>>> import types 
>>> types.FunctionType.__getitem__ = lambda name: name 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
TypeError: can't set attributes of built-in/extension type 'function' 

>>> class A(object): 
... pass 
... 
>>> a = A() 
>>> a.__getattr__ = lambda name: name 
>>> a.foo 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
AttributeError: 'A' object has no attribute 'foo' 

そしてはあなたが組み込み関数の型に__getattr__を定義することはできません。上のオブジェクトの任意の種類を動作しないインスタンスに__getitem__の割り当てそしてサブクラス化できませんtypes.FunctionType

>>> import types 
>>> class F(types.FunctionType): 
... pass 
... 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
TypeError: Error when calling the metaclass bases 
type 'function' is not an acceptable base type 
+2

@kindallはこれを以下に指摘しますが、ここではそれを言及して回答にとどまります。新しいスタイルのクラスは、 '__getattr__'とその仲間との"通常の "メソッド解決法を使用しません。 'a.foo'は内部的に' type(a).__ getattr __( 'foo') 'に翻訳されます。したがって 'a____ dict__'に格納されている' __getattr__'プロパティは見つかりません。希望// – colinta

1

AHHA! __call__使用、および(Pythonの3で唯一の種類、あなたは、Python 2で使用されなければならないようなものです)。少なくとも、新しいスタイルのクラスのF()

class F(object): 
    def __init__(self, fn): 
     self.__dict__['fn'] = fn 

    def __call__(self, *args, **kwargs): 
     return self.fn(*args, **kwargs) 

    def __getitem__(self, name): 
     return name 

    def __getattr__(self, name): 
     return name 

>>> foo = F(foo) 
>>> f.bar 
'bar' 
>>> f['foo'] 
'foo' 
>>> foo() 
foo! 
+2

基本的にあなたの記述デコレータ:) –

+1

うん、と私はデコレータとしてこれで遊んだが、面白いものが壊れて...私はクラスでメソッドを飾る場合、メソッドを呼び出すときバウンドメソッドにはなりません。 "装飾としてのクラス"は "装飾としての機能"と同じものではないようです。私はここに私の発見を掲示することになるでしょう。 – colinta

+0

これらはどちらも同じですが、どちらも 'callable'オブジェクトを返します。デコレータのようなクラスはもっと洗練されています –

2

で関数をラップし、Pythonは唯一のマジックメソッドを探しますクラス(およびその祖先)で、決してインスタンスにはありません。 Docs here

もちろん、関数の型を変更したり、関数の型を派生させることはできません。しかし、あなたが見つけたように、__call__()メソッドを持つクラスは呼び出し可能なインスタンスを作成するので、それを行う方法です。

+0

すべて真実 - 私はここで遭遇した誰もがこの物を見つけるためにドキュメントを掘り進む必要がないように、完全なストーリーをもう少し詳しく共有したいと思っていました。しかし、新しいスタイルのクラスは 'type(obj).__ getattr__'を呼び出します。それは、私には直感的ではありませんが、文書化されています...なぜ、通常の方法の解決法を使用するだけではありませんか? – colinta

+0

"この動作の背後にある論理的根拠は、型オブジェクトを含むすべてのオブジェクトによって実装される' __hash __() 'や' __repr __() 'などのいくつかの特別なメソッドにあります。これらのメソッドの暗黙的な参照では、それらは、型オブジェクト自体に対して呼び出されたときに失敗します。 – kindall

関連する問題