6

このトピックに関する本Core Python Programmingの例がDelegation機能していないようです..または、トピックを明確に理解していなかった可能性があります..

以下は、クラスがオブジェクトをCapOpenラップし、モードで開かれたときの変更された動作を定義するコードです。すべての文字列を大文字のみで書き込む必要があります。filefilewrite

ただし、読み取り用にファイルを開いて、それを繰り返し処理して各行を印刷しようとすると、次の例外が発生します。

Traceback (most recent call last):
  File "D:/_Python Practice/Core Python Programming/chapter_13_Classes/
        WrappingFileObject.py", line 29, in <module>
    for each_line in f:
TypeError: 'CapOpen' object is not iterable

これは奇妙です。イテレータ メソッドを明示的に定義していませんが__getattr__、基になるfileオブジェクトを介して呼び出しが委任されることを期待していたからです。これがコードです。私は何かを逃したことがありますか?

class CapOpen(object):
    def __init__(self, filename, mode='r', buf=-1):
        self.file = open(filename, mode, buf)

    def __str__(self):
        return str(self.file)

    def __repr__(self):
        return `self.file`

    def write(self, line):
        self.file.write(line.upper())

    def __getattr__(self, attr):
        return getattr(self.file, attr)


f = CapOpen('wrappingfile.txt', 'w')
f.write('delegation example\n')
f.write('faye is good\n')
f.write('at delegating\n')
f.close()

f = CapOpen('wrappingfile.txt', 'r')

for each_line in f:   # I am getting Exception Here..
    print each_line,

Python 2.7 を使用しています。

4

2 に答える 2

13

これは、新しいスタイルのクラスに対するPython の実装決定の非直感的な結果です。

__getattribute__()正確さのためにインスタンス属性をバイパスすることに加えて、暗黙的な特別なメソッド検索は通常、オブジェクトのメタクラスのメソッドもバイパスします...

この方法で機構をバイパスする__getattribute__()と、インタープリター内で速度を最適化するための大きな範囲が提供されますが、特別なメソッドの処理の柔軟性がいくらか犠牲になります (インタープリターによって一貫して呼び出されるためには、クラス オブジェクト自体に特別なメソッドを設定する必要があります)。 .

__getattr__これは、 /__getattribute__のドキュメントでも明示的に指摘されています。

このメソッドは、言語構文または組み込み関数を介した暗黙的な呼び出しの結果として、特別なメソッドを検索するときにバイパスされる場合があります。新しいスタイルのクラスの特別なメソッド ルックアップを参照してください。

__getattr__つまり、属性が定義されていない場合、常にメソッド ルックアップをインターセプトすることに依存することはできません。これらの暗黙的なルックアップが、オブジェクトにアクセスする他のすべてのクライアントと同じパスをたどることを期待するのは合理的であるため、これは直感的ではありません。f.__iter__他のコードから直接呼び出すと、期待どおりに解決されます。ただし、言語から直接呼び出された場合はそうではありません。

あなたが引用した本はかなり古いので、元の例はおそらく古いスタイルのクラスを使用していました。から継承を削除するobjectと、コードは意図したとおりに機能します。__iter__そうは言っても、古いスタイルのクラスは Python 3 で廃止されるため、書くことは避けるべきです。必要に応じて、基になる を実装してすぐに委任することで、ここで委任スタイルを維持できますself.file.__iter__

fileまたは、オブジェクトから直接継承し、__iter__通常のルックアップで利用できるようにすることもできます。

于 2012-09-26T06:47:59.167 に答える
2

オブジェクトが反復可能であるためには、そのクラスが持っている__iter__か、__getitem__定義されている必要があります。

__getattr__インスタンスから何かが取得されているときにのみ呼び出されますが、反復がサポートされる方法はいくつかあるため、Pythonは最初に、適切なメソッドが存在するかどうかを確認します。

これを試して:

class Fake(object):
    def __getattr__(self, name):
        print "Nope, no %s here!" % name
        raise AttributeError

f = Fake()
for not_here in f:
    print not_here

ご覧のとおり、同じエラーが発生しますTypeError: 'Fake' object is not iterable

次にこれを行う場合:

print '__getattr__' in Fake.__dict__
print '__iter__' in Fake.__dict__
print '__getitem__' in Fake.__dict__

Pythonが見ているものを見ることができます。それはどちら__iter____getitem__存在しないため、Pythonはそれを反復処理する方法を知りません。Python例外をキャッチしようとするだけで済みますが、そうでない理由は、例外のキャッチがかなり遅いためだと思います。

イテレータを作成する多くの方法については、ここで私の答えを参照してください。

于 2012-09-26T07:53:04.830 に答える