Pythonでデザインパターン 陸ノ型「Prototype」
今回はPythonの勉強しながらデザインパターンのPrototypeをやってみました。
他のパターンにつきましては、以下の目次記事をご参照ください。
Prototypeとは
直訳すると「原型」や「模範」という意味になります。
wikiには「生成されるオブジェクトの種別がプロトタイプ(典型)的なインスタンスであるときに使用され、このプロトタイプを複製して新しいオブジェクトを生成する」とあります。
Prototypeパターンとは「オブジェクトからオブジェクト(複製)を作成するパターン」です。
javaやC#とかでいうとclone()のことですね(・`ω´・)b
試行錯誤の時間
Pythonにおけるclone()は標準ライブラリである「copy」を使うことで簡単に実現することができます。
ここで「copy」には浅いコピーであるcopy()と深いコピーであるdeepcopy()があるようです。
copy.copy(x) x の浅い (shallow) コピーを返します。 copy.deepcopy(x[, memo]) x の深い (deep) コピーを返します。
・・・な ん だ そ れ は ?(゚Д゚≡゚Д゚)?
説明には以下のように書かれています。
浅いコピー (shallow copy) は新たな複合オブジェクトを作成し、 その後 (可能な限り) 元のオブジェクト中に見つかったオブジェクトに 対する 参照 を挿入します。 深いコピー (deep copy) は新たな複合オブジェクトを作成し、 その後元のオブジェクト中に見つかったオブジェクトの コピー を挿入します。
これによるとオブジェクトの中に別のオブジェクトが含まれていた場合は、
- 浅いコピーだと中にあるオブジェクトは「参照型」(Cとかでいうポインタ型)になる
- 深いコピーだと中にあるオブジェクトは「値型」になる
・・・ということかな?(;^ν^)
説明だとよくわからないのでいつもどおりコードを書いて試してみます\(^o^)/
まず、以下のようなクラスを用意しました。
この例ではOuterというアウタークラスの中にInnerというインナークラスがあります。
アウタークラスの中でインナークラスのインスタンスを作成して、get/setを介してアウタークラスからインナークラスにアクセスできます。
class Outer(object) : def __init__(self) : self.__value = 0 self.__inner = self.Inner() def getOuterValue(self): return self.__value def setOuterValue(self, value): self.__value = value def getInnerValue(self): return self.__inner.getValue() def setInnerValue(self, value): self.__inner.setValue(value) class Inner(object) : def __init__(self) : pass def getValue(self): return self.__value def setValue(self, value): self.__value = value
このクラスを使ったテストコードは以下のとおりです。
if __name__ == "__main__" : origin = Outer() origin.setOuterValue(1) origin.setInnerValue(2) clone = copy.copy(origin) # 浅いコピー #clone = copy.deepcopy(origin) # 深いコピー print("現在のオブジェクト値") print("origin - Outer:", origin.getOuterValue()) print("origin - Inner:", origin.getInnerValue()) print("clone - Outer:", clone.getOuterValue()) print("clone - Inner:", clone.getInnerValue()) print("クローンで書き換え後のオブジェクト値") clone.setOuterValue(3) clone.setInnerValue(4) print("origin - Outer:", origin.getOuterValue()) print("origin - Inner:", origin.getInnerValue()) print("clone - Outer:", clone.getOuterValue()) print("clone - Inner:", clone.getInnerValue())
まずは浅いコピーcopy.copy(origin)の実行結果です。
クローンで書き換え後のオブジェクト値を見ると、「origin - Inner」の値が「clone - Inner」と同じになっています。
「origin - Outer」は書き換え前のままです。
現在のオブジェクト値 origin - Outer: 1 origin - Inner: 2 clone - Outer: 1 clone - Inner: 2 クローンで書き換え後のオブジェクト値 origin - Outer: 1 origin - Inner: 4 clone - Outer: 3 clone - Inner: 4
続いて深いコピーcopy.deepcopy(origin)の実行結果です。
これによると「clone - Inner」は書き換わっていますが、「origin - Inner」は書き換わっていません。
現在のオブジェクト値 origin - Outer: 1 origin - Inner: 2 clone - Outer: 1 clone - Inner: 2 クローンで書き換え後のオブジェクト値 origin - Outer: 1 origin - Inner: 2 clone - Outer: 3 clone - Inner: 4
これらの結果から浅いコピーだとやはり「参照型」となっているようです。
そのため、cloneを使ってInnerの値を書きかえると、originのInnerも書き換わってしまいます。
したがって、Prototypeパターンを実現するには中のオブジェクトすべてをコピーすべきなので、深いコピー (deep copy)を採用すべきでしょうかね?
サンプルコード
いつものごとくサンプルコードです!
# coding: utf-8 import copy from abc import ABC, ABCMeta, abstractmethod class Cloneable(metaclass=ABCMeta) : @abstractmethod def createClone(self) : pass class Prototype(Cloneable) : def __init__(self, name: str) : self.__name = name def createClone(self) : return copy.deepcopy(self) def getName(self) : return self.__name if __name__ == "__main__" : origin = Prototype("AAA") clone = origin.createClone() print("origin:", origin.getName()) print("clone :", clone.getName())
以下、実行結果。
originに付けた名前がcloneにも引き継がれていますね(・`ω´・)
python Prototype.py origin: AAA clone : AAA
- 価格: 2640 円
- 楽天で詳細を見る