忘れないうちにメモ。
(コードが多くて煩雑なので、トップページには表示しないようにしました。)
copyモジュール — 浅いコピーおよび深いコピー操作
まずは、普通にオブジェクトのコピー
Pythonでオブジェクトの複製を作りたいときはcopyモジュールを使います。
import copy >>> a=[1,2,3] >>> b=copy.copy(a) >>> b [1, 2, 3]
あくまでも値をコピーしているだけなので、元のオブジェクトを操作しても、コピー先には反映されません
>>> a[2]=[3,4,5] >>> a [1, 2, [3, 4, 5]] >>> b [1, 2, 3]
でも、copy.copyは浅いコピーなので
>>> b=copy.copy(a) #上段から続き >>> b [1, 2, [3, 4, 5]] >>> a[2][0]=5 >>> a [1, 2, [5, 4, 5]] >>> b [1, 2, [5, 4, 5]] #bの値まで変化 >>> a is b False >>> a[2] is b[2] True
こういうことが起きないようにするには深いコピー(deepcopy)を使う
>>> a [1, 2, [5, 4, 5]] >>> b=copy.copy(a) >>> c=copy.deepcopy(a) >>> c==a True >>> c is a False >>> c[2] is a[2] False >>> a [1, 2, [5, 4, 5]] >>> a[2][0]=3 >>> a [1, 2, [3, 4, 5]] >>> b [1, 2, [3, 4, 5]] #一緒に変化してる >>> c [1, 2, [5, 4, 5]] #変化してない
itertools.tee関数
こんなジェネレータ関数を考える
def f():
i=0
for i in xrange(10) :
yield i
i+=1
>>> g=f() >>> g.next() 0 >>> g.next() 1 >>> g.next() 2
この状態でgを普通にコピーするとエラーが発生
>>> g2=copy.copy(g) Traceback (most recent call last): File "<pyshell #106>", line 1, in <module> g2=copy.copy(g) (以下略)
こんなときは itertoolsモジュールのtee関数を使う
>>> import itertools >>> itertools.tee(g) (<itertools .tee object at 0x03FEEEE0>, </itertools><itertools .tee object at 0x03FEEEB8>)
gの複製2個がタプルに入って返ってくる。
第2引数はコピーする個数
>>> g1,g2,g3= itertools.tee(g,3) >>> g1.next() 3 >>> g1.next() 4 >>> g1.next() 5 >>> list(g2) [3, 4, 5, 6, 7, 8, 9]
元のgは操作できなくなる
>>> g.next() Traceback (most recent call last): File "<pyshell #117>", line 1, in <module> g.next() StopIteration
ジェネレータはcopy.copyできないが、itertools.tee objectならcopy.copyできる
>>> g1,g2=itertools.tee(f()) >>> g1 <itertools .tee object at 0x03FFBAA8> >>> g3=copy.copy(g1) >>> list(g1) [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] >>> list(g3) [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
itertools.tee objectを、さらにteeできるので、copy.copyは使う必要が無いかも。
>>> g2.next() 0 >>> g2.next() 1 >>> g3,g4=itertools.tee(g2) >>> list(g3) [2, 3, 4, 5, 6, 7, 8, 9] >>> list(g4) [2, 3, 4, 5, 6, 7, 8, 9]
ググる:バックトラック
で、こんなことをして何が楽しいかというと、何らかの処理のロールバックとか、バックトラックに使えるかも、と思ったので。
(具体例は、まだ作ってないですけれども。)
generatorオブジェクへ信号を送るsendメソッド
7 PEP 342: New Generator Features
In 2.5, yield is now an expression, returning a value that can be assigned to a variable or otherwise operated on:
val = (yield i)
こんなのを書いてみる
>>> def f (): i=1 while i : i=(yield i)
>>> g.next() 1 # いきなりsendを呼ぶと怒られます >>> g.send(100) 100 >>> g.send(1000) 1000 >>> >>> g.send(0) Traceback (most recent call last): File "<pyshell #54>", line 1, in <module> g.send(0) StopIteration
ジェネレータの挙動をある程度コントロールできます。
generatorオブジェクトで例外を発生させるthrowメソッド
>>> g=f() >>> g.next() 1 >>> g.throw(StopIteration) Traceback (most recent call last): File "<pyshell #57>", line 1, in <module> g.throw(StopIteration) File "<pyshell #46>", line 4, in f i=(yield i) StopIteration
強制的にリセットしたい時とか。
かなり凝ったこともできそうだけれど、あまり使わないような気も。
Tags: PythonRelated posts
タグ: Python