Update 2023.11.16 2017.05.05

Python デザインパターン サンプルコード Factory Method
Mark Summerfield『実践 Python 3』デザインパターンのサンプルコード
Python3(3.11)で動くソースコード(.pyファイル .ipynbファイル)あります
「anaconda3」on .py「PyCharm」.ipynb「Jupyter Notebook」

著作権の問題があるので,本に書いてないことだけを解説します。つまり,視点を変えて解説します。

◆◆Factory Method パターンの使われる場面◆◆

野球は野球ボール,サッカーはサッカーボール,バスケットはバスケットボールであるが,「ボールを作る」「ボールを使う」というメソッドを共通にして「競技」を使い分けられるようにするのがFactory Method パターンです。

Javaのジェネリクスと勘違いしやすいが真逆のことをしているので注意する。

(2023-11-16)Python3.11で動作確認済み

【重要な注意】本プログラムは,.docファイルを出力します。その使い方は不明です。

「PyCharm」上に「Jupyter Notebook」上に出力されるのは出力ファイルのディレクトリだけです。そのディレクトリは「os.path」で指定しています。

wrote 'C:\Users\yamak\AppData\Local\Temp\gameboard1.doc'
wrote 'C:\Users\yamak\AppData\Local\Temp\gameboard2.doc'
wrote 'C:\Users\yamak\AppData\Local\Temp\gameboard3.doc'
wrote 'C:\Users\yamak\AppData\Local\Temp\gameboard4.doc

「AppData」は隠しフォルダであり,

エクスプローラ(ユーザーアカウント)→表示→表示→隠しファイル

により,エクスプローラに他のフォルダのように表示されます。

上のディレクりのフォルダの中にはたくさんのファイルが有りますが,更新日時でソートすれば1番目に見つかります。


◆◆Factory Method パターンとは◆◆

GoFによれば,Factory Method パターンの目的は, 「オブジェクトを生成するときのインタフェースだけを規定して,実際にどのクラスをインスタンス化するかはサブクラスが決めるようにする。Factory Method パターンは,インスタンス化をサブクラスに任せる。 」

GoFによれば,Factory Method パターンの別名は,Virtual Constructor パターンだそうだ。

GoFによれば,Factory Method パターンは,次のような場合に用いることができる。
・クラスが,生成しなければならないオブジェクトのクラスを事前に知ることができない場合。
・サブクラス化により,生成するオブジェクトを特定化する場合。
・クラスが責任をいくつかのサブクラスの中の1つに委譲するときに,どのサブクラスに委譲するのかに関する知識を局所化したい場合。

GoFによれば,Factory Method パターンの関連するパータンは次のようなものである。
Abstract Factory パターン: factory method を使って実装されることが多い。Abstract Factory パターンの「動機」の節の例では,Factory Method パターンについても説明している。
Template Method パターン: factory method は通常,template method の中で呼ばれる。Document クラスの例では,NewDocument オペレーションが template method である。
Prototype パターン: Prototype パターンにより,Creator クラスをサブクラス化する必要はなくなるが,代わりに Product クラスにしばしば初期化オペレーションが必要になる。一方,Factory Method パターンでは初期化オペレーションは必要ない。

◆◆サンプルは4つある◆◆

サンプルは次の4つがあります。
  gameboard1.py
  gameboard2.py
  gameboard3.py
  gameboard4.py

4つとも次の図のように,駒を並べたチェッカーボードとチェスボードを出力します。



gameboard1.py は,Factory Method パターンをまったく使っていません。説明の都合上,Factory Method パターンの手法を段階的に導入しているようです。

このWebページもサンプルごとに解説します。

◆◆gameboard1.py のサンプルのクラスの構成◆◆

ソースコードは巻末にあり,ソースファイルはこのWebの左上隅にありダウンロードできます。

     AbstractBoard          Piece
  (populate_board, __str__)        ()
    ↑         ↑         ↑
  CheckerBoard    ChessBoard   BalckDraught,etc
  (populate_board) (populate_board)  (__new__)

class AbstractBoard は,テンプレートであり,抽象クラスです。
class CheckerBoard は,チェッカーボードをつくります。
class ChessBoard は,チェスボードをつくります。
class Piece のスーパークラスは,str です。
class BalckDraught,etcは,駒の図を表す Unicode をクラス名にします。

4つのモジュールともクラスの構成は同じです。

◆◆gameboard1.py の実装◆◆

サンプルが出力するものは,上の図に示したように,駒を並べたチェッカーボードとチェスボードですが,これらは文字列にすぎません

gameboard1.py では,108 行目から 222 行目までのクラスによって,そのインスタンスに直接に Unicode(UTF-8)を埋め込んでいます。


    
class BlackDraught(Piece):
    __slots__ = ()
    def __new__(Class):
        return super().__new__(Class, "\N{black draughts man}")

__new__は,__init__より前に,空のインスタンスを作ります。\N{}は,名前付き文字列であり,Unicode の名前を採用することにより,インスタンスがUnicodeになります。

一般的に,クラスの属性(名前空間)は,__dict__に保持されますが,__slots__により,メモリが節約され,__dict__がつくられなくなります。

Unicodeを埋め込んだインスタンスは,74行目,89行目の
  def populate_board
でインスタンス化され,インスタンスは配列になっています。

このメソッドは,旧いタイプのTemplate Method パターンになっていて,抽象クラスの52行目の抽象メソッドは次のようになっています。

    
class AbstractBoard:
    def populate_board(self):
        raise NotImplementedError()

この例外は実装解除と呼ばれ,サブクラスでオーバーライドしていないと,スーパークラスが呼ばれ,例外を投げられて実装を促されるということです。

メインルーチンの22行目,25行目で
  checkers = CheckersBoard()
  chess = ChessBoard()
というインスタンスがつくられ,直後にそのインスタンスが print されていますが,普通は,インスタンスのアドレスが出力されるだけになってしまいます。

これらのクラスのスーパークラスは,
  抽象class AbstractBoard
であり,56行目に,
  def __str__
があります。インスタンスが print されると必ずこの特殊メソッドが呼ばれます。このメソッドは,Unicodeが埋め込まれたインスタンスの配列を文字列に変換しているのです。

print関数だけ見ていると見逃してしまうので,特殊メソッドがどのタイミングで呼ばれるのかを注意して見ていてください。

最後に,もし,debug のために,標準出力へ print する場合は,32行目のような書き方をしてください。普通の書き方では,temp フォルダのファイルへ出力されます。

◆◆gameboard2.py の実装◆◆

このモジュールには,インデントが0のところに class と def 以外の次のコードが書かれている。
  37行目の if
  113行目の for
  128行目の if
モジュールとしては,最初に走るのは,128行目であるが,その前にプログラムとして評価され,37行目では,インデントが0の def のコードが確定し,113行目では,インデントが0の class のコードが確定する。

コマンドラインの第1引数により,128行目のメインルーチンが走るときは,37行目と113行目が評価されたあとである。

gameboard1.py からの一番大きい変更は,113行目の多くのクラスをつくるところです。gameboard1.py では,100行ほどを使って14のクラスを1つ1つ書いていました。

このクラスは,チェッカーやチェスの駒の絵をUnicodeの文字としてクラスの属性にしてしまうものです。

Unicodeのコード番号は,イテレータとして itertools.chain で直接呼んでいます。クラスをつくるために gameboard1.py と完全に同じコードを生成するために exec()関数により,文字列をコードに変換しています。トリプルクォートで囲まれたものが文字列です。exec()関数の引数は複数行でもかまいません。

2番目の変更は,74行目と89行目の
  def populate_board
です。このメソッドで,Unicodeが埋め込まれた駒の絵のクラスが,101行目のグローバル関数
  def create_piece
を使ってインスタンス化されます。

ここの eval()関数は,引数の文字列をコードに変換します。ただし,1行だけです。

Pythonでは,インスタンスを生成するところをファクトリと呼びます。上のメソッドはファクトリメソッドと呼んでもいいと思います。

次の2つのクラスがインスタンス化されるとき,上のことがすべて実行されます。そして,インスタンスが print されるとき,普通は,アドレスが出力されるだけですが,代わって,58行目の
  def __str__
が,実行され,出力される文字列が生成されるのです。


    
def main():
    checkers = CheckersBoard()
    print(checkers)

    chess = ChessBoard()
    print(chess)

◆◆gameboard3.py の実装◆◆

gameboard2.py からの一番大きい変更は,
クラスをつくるときに exec()関数を使わない
インスタンスをつくるときに eval()関数を使わない
ということです。

20行目のグローバル変数であり,駒の名前を定数にしています。

72行目で,その定数を使い,辞書を作っています。これはクラス変数です。

92行目のメソッド
  def create_piece
は,gameboard2.py ではグローバル関数でした。Unicode を埋め込んだクラスをインスタンス化するもので,eval()関数を使っていました。

ここでは,116行目と131行目の
  def populate_board
から呼ばれてインスタンスを生成しています。これらは,ファクトリメソッドであります。

Unicode を埋め込んだクラスを生成するのは,60行目からです。exec()関数を使わず,type()関数でクラス宣言なしでクラスを生成しています。また,dict と setattr でクラスの属性が定義しています。ここでは,
  def make_new_method
というグローバル関数を使って,__new__に代入していますが,説明は省略します。

詳細は省略しますが,このような方法で,クラスを生成することができることは覚えておいた方がよいです。

◆◆gameboard4.py の実装◆◆

gameboard3.py からの大きい変更は,クラスを生成するところとインスタンスを生成するところです。

120行目で,Unicode を埋め込んだクラスを生成します。gameboard3.py と違うところは,new を生成する関数が lambda になっていて,すっきりしています。また,setattr を使っていないで globals()を使っています。

107行目の
  def create_piece
は,グローバル関数になっていて,辞書は内蔵されています。この場合は,ファクトリ関数と呼んだ方が良いかもしれません。

この関数を呼ぶのが,76行目と96行目のメソッド
  def populate_board
であり,Unicode を埋め込んだクラスをインスタンス化しています。

◆◆Factory Method パターンとは◆◆

GoFによれば,Factory Method パターンの目的は, 「オブジェクトを生成するときのインタフェースだけを規定して,実際にどのクラスをインスタンス化するかはサブクラスが決めるようにする。Factory Method パターンは,インスタンス化をサブクラスに任せる。 」
ということですが,それを表してるのが,
  def populate_board
なのですが,
デザインパターンとしては,その構成よりも,クラスやインスタンスを普通の方法ではなく,自由自在に生成するところがポイントであると考えます。

◆◆ソースコード◆◆

このWebの左上隅からダウンロードできます。

ソースファイルは4つです。1つのサンプル出力も添付しました。
・gameboard1.py;Factory Method パターンのPythonサンプル(その1)
・gameboard2.py;Factory Method パターンのPythonサンプル(その2)
・gameboard3.py;Factory Method パターンのPythonサンプル(その3)
・gameboard4.py;Factory Method パターンのPythonサンプル(その4)
・gameboard.doc;Factory Method パターンの出力サンプル(共通)

オリジナルを修正したところは,出力ファイルの拡張子を,.txt -> .doc としました。

【gameboard1.py】
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
#!/usr/bin/env python3
# Copyright c 2012-13 Qtrac Ltd. All rights reserved.
# This program or module is free software: you can redistribute it
# and/or modify it under the terms of the GNU General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version. It is provided for
# educational purposes and is distributed in the hope that it will be
# useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.

import io
import os
import sys
import tempfile


BLACK, WHITE = ("BLACK", "WHITE")


def main():
    checkers = CheckersBoard()
    print(checkers)

    chess = ChessBoard()
    print(chess)

    if sys.platform.startswith("win"):
        filename = os.path.join(tempfile.gettempdir(), "gameboard1.doc")
        with open(filename, "w", encoding="utf-8") as file:
            file.write(sys.stdout.getvalue())
        print("wrote '{}'".format(filename), file=sys.__stdout__)


if sys.platform.startswith("win"):
    def console(char, background):
        return char or " "
    sys.stdout = io.StringIO()
else:
    def console(char, background):
        return "\x1B[{}m{}\x1B[0m".format(
                43 if background == BLACK else 47, char or " ")


class AbstractBoard:

    def __init__(self, rows, columns):
        self.board = [[None for _ in range(columns)] for _ in range(rows)]
        self.populate_board()


    def populate_board(self):
        raise NotImplementedError()


    def __str__(self):
        squares = []
        for y, row in enumerate(self.board):
            for x, piece in enumerate(row):
                square = console(piece, BLACK if (y + x) % 2 else WHITE)
                squares.append(square)
            squares.append("\n")
        return "".join(squares)


class CheckersBoard(AbstractBoard):

    def __init__(self):
        super().__init__(10, 10)


    def populate_board(self):
        for x in range(0, 9, 2):
            for row in range(4):
                column = x + ((row + 1) % 2)
                self.board[row][column] = BlackDraught()
                self.board[row + 6][column] = WhiteDraught()


class ChessBoard(AbstractBoard):

    def __init__(self):
        super().__init__(8, 8)


    def populate_board(self):
        self.board[0][0] = BlackChessRook()
        self.board[0][1] = BlackChessKnight()
        self.board[0][2] = BlackChessBishop()
        self.board[0][3] = BlackChessQueen()
        self.board[0][4] = BlackChessKing()
        self.board[0][5] = BlackChessBishop()
        self.board[0][6] = BlackChessKnight()
        self.board[0][7] = BlackChessRook()
        self.board[7][0] = WhiteChessRook()
        self.board[7][1] = WhiteChessKnight()
        self.board[7][2] = WhiteChessBishop()
        self.board[7][3] = WhiteChessQueen()
        self.board[7][4] = WhiteChessKing()
        self.board[7][5] = WhiteChessBishop()
        self.board[7][6] = WhiteChessKnight()
        self.board[7][7] = WhiteChessRook()
        for column in range(8):
            self.board[1][column] = BlackChessPawn()
            self.board[6][column] = WhiteChessPawn()


class Piece(str):

    __slots__ = ()


class BlackDraught(Piece):

    __slots__ = ()

    def __new__(Class):
        return super().__new__(Class, "\N{black draughts man}")


class WhiteDraught(Piece):

    __slots__ = ()

    def __new__(Class):
        return super().__new__(Class, "\N{white draughts man}")


class BlackChessKing(Piece):

    __slots__ = ()

    def __new__(Class):
        return super().__new__(Class, "\N{black chess king}")


class WhiteChessKing(Piece):

    __slots__ = ()

    def __new__(Class):
        return super().__new__(Class, "\N{white chess king}")


class BlackChessQueen(Piece):

    __slots__ = ()

    def __new__(Class):
        return super().__new__(Class, "\N{black chess queen}")


class WhiteChessQueen(Piece):

    __slots__ = ()

    def __new__(Class):
        return super().__new__(Class, "\N{white chess queen}")


class BlackChessRook(Piece):

    __slots__ = ()

    def __new__(Class):
        return super().__new__(Class, "\N{black chess rook}")


class WhiteChessRook(Piece):

    __slots__ = ()

    def __new__(Class):
        return super().__new__(Class, "\N{white chess rook}")


class BlackChessBishop(Piece):

    __slots__ = ()

    def __new__(Class):
        return super().__new__(Class, "\N{black chess bishop}")


class WhiteChessBishop(Piece):

    __slots__ = ()

    def __new__(Class):
        return super().__new__(Class, "\N{white chess bishop}")


class BlackChessKnight(Piece):

    __slots__ = ()

    def __new__(Class):
        return super().__new__(Class, "\N{black chess knight}")


class WhiteChessKnight(Piece):

    __slots__ = ()

    def __new__(Class):
        return super().__new__(Class, "\N{white chess knight}")


class BlackChessPawn(Piece):

    __slots__ = ()

    def __new__(Class):
        return super().__new__(Class, "\N{black chess pawn}")


class WhiteChessPawn(Piece):

    __slots__ = ()

    def __new__(Class):
        return super().__new__(Class, "\N{white chess pawn}")


if __name__ == "__main__":
    main()

【gameboard2.py】
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
#!/usr/bin/env python3
# Copyright c 2012-13 Qtrac Ltd. All rights reserved.
# This program or module is free software: you can redistribute it
# and/or modify it under the terms of the GNU General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version. It is provided for
# educational purposes and is distributed in the hope that it will be
# useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.

import io
import itertools
import os
import sys
import tempfile
import unicodedata


BLACK, WHITE = ("BLACK", "WHITE")


def main():
    checkers = CheckersBoard()
    print(checkers)

    chess = ChessBoard()
    print(chess)

    if sys.platform.startswith("win"):
        filename = os.path.join(tempfile.gettempdir(), "gameboard2.doc")
        with open(filename, "w", encoding="utf-8") as file:
            file.write(sys.stdout.getvalue())
        print("wrote '{}'".format(filename), file=sys.__stdout__)


if sys.platform.startswith("win"):
    def console(char, background):
        return char or " "
    sys.stdout = io.StringIO()
else:
    def console(char, background):
        return "\x1B[{}m{}\x1B[0m".format(
                43 if background == BLACK else 47, char or " ")


class AbstractBoard:

    def __init__(self, rows, columns):
        self.board = [[None for _ in range(columns)] for _ in range(rows)]
        self.populate_board()


    def populate_board(self):
        raise NotImplementedError()


    def __str__(self):
        squares = []
        for y, row in enumerate(self.board):
            for x, piece in enumerate(row):
                square = console(piece, BLACK if (y + x) % 2 else WHITE)
                squares.append(square)
            squares.append("\n")
        return "".join(squares)


class CheckersBoard(AbstractBoard):

    def __init__(self):
        super().__init__(10, 10)


    def populate_board(self):
        for x in range(0, 9, 2):
            for y in range(4):
                column = x + ((y + 1) % 2)
                for row, color in ((y, "black"), (y + 6, "white")):
                    self.board[row][column] = create_piece("draught",
                            color)


class ChessBoard(AbstractBoard):

    def __init__(self):
        super().__init__(8, 8)


    def populate_board(self):
        for row, color in ((0, "black"), (7, "white")):
            for columns, kind in (((0, 7), "rook"), ((1, 6), "knight"),
                    ((2, 5), "bishop"), ((3,), "queen"), ((4,), "king")):
                for column in columns:
                    self.board[row][column] = create_piece(kind, color)
        for column in range(8):
            for row, color in ((1, "black"), (6, "white")):
                self.board[row][column] = create_piece("pawn", color)



def create_piece(kind, color):
    if kind == "draught":
        return eval("{}{}()".format(color.title(), kind.title()))
    return eval("{}Chess{}()".format(color.title(), kind.title()))
    # Using eval() is risky


class Piece(str):

    __slots__ = ()


for code in itertools.chain((0x26C0, 0x26C2), range(0x2654, 0x2660)):
    char = chr(code)
    name = unicodedata.name(char).title().replace(" ", "")
    if name.endswith("sMan"):
        name = name[:-4]
    exec("""\
class {}(Piece):

    __slots__ = ()

    def __new__(Class):
        return super().__new__(Class, "{}")""".format(name, char))
    # Using exec() is risky


if __name__ == "__main__":
    main()


【gameboard3.py】
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
#!/usr/bin/env python3
# Copyright c 2012-13 Qtrac Ltd. All rights reserved.
# This program or module is free software: you can redistribute it
# and/or modify it under the terms of the GNU General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version. It is provided for
# educational purposes and is distributed in the hope that it will be
# useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.

import io
import itertools
import os
import sys
import tempfile
import unicodedata


DRAUGHT, PAWN, ROOK, KNIGHT, BISHOP, KING, QUEEN = ("DRAUGHT", "PAWN",
        "ROOK", "KNIGHT", "BISHOP", "KING", "QUEEN")
BLACK, WHITE = ("BLACK", "WHITE")


def main():
    checkers = CheckersBoard()
    print(checkers)

    chess = ChessBoard()
    print(chess)

    if sys.platform.startswith("win"):
        filename = os.path.join(tempfile.gettempdir(), "gameboard3.doc")
        with open(filename, "w", encoding="utf-8") as file:
            file.write(sys.stdout.getvalue())
        print("wrote '{}'".format(filename), file=sys.__stdout__)


if sys.platform.startswith("win"):
    def console(char, background):
        return char or " "
    sys.stdout = io.StringIO()
else:
    def console(char, background):
        return "\x1B[{}m{}\x1B[0m".format(
                43 if background == BLACK else 47, char or " ")


class Piece(str):

    __slots__ = ()


def make_new_method(char): # Needed to create a fresh method each time
    def new(Class): # Can't use super() or super(Piece, Class)
        return Piece.__new__(Class, char)
    return new


for code in itertools.chain((0x26C0, 0x26C2), range(0x2654, 0x2660)):
    char = chr(code)
    name = unicodedata.name(char).title().replace(" ", "")
    if name.endswith("sMan"):
        name = name[:-4]
    new = make_new_method(char)
    Class = type(name, (Piece,), dict(__slots__=(), __new__=new))
    setattr(sys.modules[__name__], name, Class) # Can be done better!


class AbstractBoard:

    __classForPiece = {(DRAUGHT, BLACK): BlackDraught,
            (PAWN, BLACK): BlackChessPawn,
            (ROOK, BLACK): BlackChessRook,
            (KNIGHT, BLACK): BlackChessKnight,
            (BISHOP, BLACK): BlackChessBishop,
            (KING, BLACK): BlackChessKing,
            (QUEEN, BLACK): BlackChessQueen,
            (DRAUGHT, WHITE): WhiteDraught,
            (PAWN, WHITE): WhiteChessPawn,
            (ROOK, WHITE): WhiteChessRook,
            (KNIGHT, WHITE): WhiteChessKnight,
            (BISHOP, WHITE): WhiteChessBishop,
            (KING, WHITE): WhiteChessKing,
            (QUEEN, WHITE): WhiteChessQueen}

    def __init__(self, rows, columns):
        self.board = [[None for _ in range(columns)] for _ in range(rows)]
        self.populate_board()


    def create_piece(self, kind, color):
        return AbstractBoard.__classForPiece[kind, color]()


    def populate_board(self):
        raise NotImplementedError()


    def __str__(self):
        squares = []
        for y, row in enumerate(self.board):
            for x, piece in enumerate(row):
                square = console(piece, BLACK if (y + x) % 2 else WHITE)
                squares.append(square)
            squares.append("\n")
        return "".join(squares)


class CheckersBoard(AbstractBoard):

    def __init__(self):
        super().__init__(10, 10)


    def populate_board(self):
        for x in range(0, 9, 2):
            for y in range(4):
                column = x + ((y + 1) % 2)
                for row, color in ((y, BLACK), (y + 6, WHITE)):
                    self.board[row][column] = self.create_piece(DRAUGHT,
                            color)


class ChessBoard(AbstractBoard):

    def __init__(self):
        super().__init__(8, 8)


    def populate_board(self):
        for row, color in ((0, BLACK), (7, WHITE)):
            for columns, kind in (((0, 7), ROOK), ((1, 6), KNIGHT),
                    ((2, 5), BISHOP), ((3,), QUEEN), ((4,), KING)):
                for column in columns:
                    self.board[row][column] = self.create_piece(kind,
                            color)
        for column in range(8):
            for row, color in ((1, BLACK), (6, WHITE)):
                self.board[row][column] = self.create_piece(PAWN, color)


if __name__ == "__main__":
    main()

【gameboard4.py】
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
#!/usr/bin/env python3
# Copyright c 2012-13 Qtrac Ltd. All rights reserved.
# This program or module is free software: you can redistribute it
# and/or modify it under the terms of the GNU General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version. It is provided for
# educational purposes and is distributed in the hope that it will be
# useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.

import io
import itertools
import os
import sys
import tempfile
import unicodedata


DRAUGHT, PAWN, ROOK, KNIGHT, BISHOP, KING, QUEEN = ("DRAUGHT", "PAWN",
        "ROOK", "KNIGHT", "BISHOP", "KING", "QUEEN")
BLACK, WHITE = ("BLACK", "WHITE")


def main():
    checkers = CheckersBoard()
    print(checkers)

    chess = ChessBoard()
    print(chess)

    if sys.platform.startswith("win"):
        filename = os.path.join(tempfile.gettempdir(), "gameboard4.doc")
        with open(filename, "w", encoding="utf-8") as file:
            file.write(sys.stdout.getvalue())
        print("wrote '{}'".format(filename), file=sys.__stdout__)


if sys.platform.startswith("win"):
    def console(char, background):
        return char or " "
    sys.stdout = io.StringIO()
else:
    def console(char, background):
        return "\x1B[{}m{}\x1B[0m".format(
                43 if background == BLACK else 47, char or " ")


class AbstractBoard:

    def __init__(self, rows, columns):
        self.board = [[None for _ in range(columns)] for _ in range(rows)]
        self.populate_board()


    def populate_board(self):
        raise NotImplementedError()


    def __str__(self):
        squares = []
        for y, row in enumerate(self.board):
            for x, piece in enumerate(row):
                square = console(piece, BLACK if (y + x) % 2 else WHITE)
                squares.append(square)
            squares.append("\n")
        return "".join(squares)


class CheckersBoard(AbstractBoard):

    def __init__(self):
        self.populate_board()


    def populate_board(self): # Thanks to Doug Hellmann for the idea!
        def black():
            return create_piece(DRAUGHT, BLACK)
        def white():
            return create_piece(DRAUGHT, WHITE)
        rows = ((None, black()), (black(), None), (None, black()),
                (black(), None),            # 4 black rows
                (None, None), (None, None), # 2 blank rows
                (None, white()), (white(), None), (None, white()),
                (white(), None))            # 4 white rows
        self.board = [list(itertools.islice(
            itertools.cycle(squares), 0, len(rows))) for squares in rows]


class ChessBoard(AbstractBoard):

    def __init__(self):
        super().__init__(8, 8)


    def populate_board(self):
        for row, color in ((0, BLACK), (7, WHITE)):
            for columns, kind in (((0, 7), ROOK), ((1, 6), KNIGHT),
                    ((2, 5), BISHOP), ((3,), QUEEN), ((4,), KING)):
                for column in columns:
                    self.board[row][column] = create_piece(kind, color)
        for column in range(8):
            for row, color in ((1, BLACK), (6, WHITE)):
                self.board[row][column] = create_piece(PAWN, color)


def create_piece(kind, color):
    color = "White" if color == WHITE else "Black"
    name = {DRAUGHT: "Draught", PAWN: "ChessPawn", ROOK: "ChessRook",
            KNIGHT: "ChessKnight", BISHOP: "ChessBishop",
            KING: "ChessKing", QUEEN: "ChessQueen"}[kind]
    return globals()[color + name]()


class Piece(str):

    __slots__ = ()


for code in itertools.chain((0x26C0, 0x26C2), range(0x2654, 0x2660)):
    char = chr(code)
    name = unicodedata.name(char).title().replace(" ", "")
    if name.endswith("sMan"):
        name = name[:-4]
    new = (lambda char: lambda Class: Piece.__new__(Class, char))(char)
    new.__name__ = "__new__"
    Class = type(name, (Piece,), dict(__slots__=(), __new__=new))
    globals()[name] = Class


if __name__ == "__main__":
    main()

以上

トップページに戻る
inserted by FC2 system