2018/08/31

Python Tips: switch 文を使いたい

Python に似た言語にはよくあって Python に無いもののひとつに「 switch 文」があります。今回は switch 文を持たない Python において switch 文を書きたくなったときの代わりの書き方 をご紹介したいと思います。

おそらく Pythonista の多くが使っているのは次の 2 つの方法のどちらかです。

  • switch 文の代わり 1: ifelif
  • switch 文の代わり 2: dict

switch 文の代わり 1: ifelif


第一の方法は単純に ifelif 構文を使うというものです。

def printer_factory(name):
    if name == 'json':
        return JsonPrinter()
    elif name == 'yaml':
        return YamlPrinter()
    elif name == 'csv':
        return CsvPrinter()
    else:
        raise ValueError('Invalid printer: {}'.format(name))

この方法だと name == の部分を分岐の数だけ繰り返す必要がありますが、キーワード elif が短いおかげでわりとシンプルに書くことができます。

最後の else のところに来たときに例外をあげるかフォールバック値を返すかはそのときどきで適切な方を選ぶとよいでしょう。

switch 文の代わり 2: dict


もうひとつの代表的なアプローチは dict を使った方法です。

def printer_factory_改(name):
    printer_map = {
        'json': JsonPrinter,
        'yaml': YamlPrinter,
        'csv': CsvPrinter,
        'xml': XmlPrinter,
        'html': HtmlPrinter,
        'mild': MildPrinter,
        'wild': WildPrinter,
    }

    try:
        return printer_map[name]()
    except KeyError as e:
        raise ValueError('Invalid printer: {}'.format(name))

分岐を表すマップをあらかじめ定義しておき、辞書のキールックアップを使って分岐させます。

指定された値が存在しなかったとき(= switch 文で default に来たときに相当)の挙動として、例外をあげたいのであれば KeyError をキャッチして適切な例外をあげ直せば OK です。フォールバック値を返したければブラケット( [] ではなく get() メソッドを使ってデフォルト値を設定しながら値を返すとよいでしょう。

# 該当するものが見つからなかった場合はフォールバック値を返す
default_value = HtmlPrinter
return printer_map.get(name, default_value)()

こちらの方法で気をつけるべき点は、マップを作成するときに値を評価してしまわないことです。例えば上の printer_factory_改 は次のように書くこともできますが、こうするとマップを用意しているときにすべての Printer クラスのインスタンスが生成されてしまうのであまりよくありません。

def printer_factory_改悪(name):
    printer_map = {
        'json': JsonPrinter(),
        'yaml': YamlPrinter(),
        'csv': CsvPrinter(),
        'xml': XmlPrinter(),
        'html': HtmlPrinter(),
        'mild': MildPrinter(),
        'wild': WildPrinter(),
    }

    try:
        return printer_map[name]
    except KeyError as e:
        raise ValueError('Invalid printer: {}'.format(name))

Python ではクラスそのものもオブジェクトでありクラスを dict の値として格納することができるので、ファクトリクラスはできるだけ printer_factory_改悪 よりも printer_factory_改 の形で書くのがよいでしょう。

以上です。

ifelifdict のどちらを使うべきかはそのときの分岐の数や周辺のコード、コーディングルール等によって変わってくると思うので、そのときどきでより適切な方を選ぶとよいでしょう。

アーキテクチャの良し悪しの観点からいえば、 switch 文を使うべき場面は非常にかぎられてくるはずなので、 switch 文が無いということは Python らしいいい制約、なのかもしれません。

というわけで、 Python における「 switch 文の代わりの書き方」についてでした。

参考

Python 公式のドキュメントの FAQ に「なぜ Python には switch case が無いの?」という項目があるので、経緯等に興味がある方はそちらもご覧になってみるとよいかと思います。

2018/08/14

書籍紹介: Unity ゲーム プログラミング・バイブル

書籍『 Unity ゲーム プログラミング・バイブル 』の一部を出版社ボーンデジタルさんよりご恵贈いただきました。中身を読み実践してみたので、かんたんに書籍の紹介とレビューをしてみたいと思います。

書籍について


『 Unity バイブル プログラミングブックス 』はゲーム開発用ソフトウェア(=開発環境)である Unity でゲームを作る方法を解説した書籍です。サンプルを使った実践的な内容になっているので、読むタイミングとしては「入門書をひと通り終え Unity の基本的な使い方に慣れた後」に読むのが最適だと思います。




今回はその書籍のうち「 TensorFlow を組み合わせて AI プレイヤーを作る」というテーマの章(= Python が関わっている章)の抜粋をご恵贈いただきました。

※ Unity そのものは Python で利用するソフトウェアではありません

やったこと


書籍の内容をざっと読むだけではなかなか評価しづらいところがあるので、 Unity をインストールし、紹介されているプログラムを試しながら読んでみました。この文章を書いている時点で、ご提供いただいた章の内容の半分強ぐらいを動かしました。




出版社さんの公式ページでサンプルの Unity プロジェクト(≒サンプルコード)が公開されているので、それを利用するとコードを書かずともとりあえずプログラムを試すことが可能です。


ちなみに私は Unity / C# に不慣れなので、今のところコードはちらっと確認する程度で、ありもののファイルをほぼそのままの形で動かすだけで読み進めています。

感想


Unity を使ったゲーム作り、または、 Unity 縛りがなくても一般にゲーム作りに関心のある方にはとても興味深い内容ではないかと思います。画像が多く解説も丁寧なので、お勉強感覚ではなく雑誌感覚で楽しみながら読み進めることができます。

こんな方におすすめです。

  • プログラミングの基礎を学んだ後に、「何かおもしろいことがしたいな」と思っている方
  • Unity でどんなことができるのかを知りたい方
  • Unity の基礎を押さえた後に実践的な制作に取り組みたい方
  • ゲーム作りに興味がある方

プログラミングを日頃行っている方であれば高校生――もしかしたら中学生の方でも楽しく読めるかもしれません。

ちなみに、私の印象では、プログラミングがまったくわからないと、スムーズなときは別に問題ありませんが、書いてあることを Unity 上でどう実践すればよいかわからない場合や罠にハマった場合等、イレギュラーが起こったときに前に進めずにつらいと思います。なので、最低限「変数とは何ぞや」「関数とは何ぞや」「クラスとは何ぞや」ぐらいのベースの知識がある方が読むのがよいと思います。

いまひとつなところ

よかったところだけをあげるとアレなので、いまひとつなところをあげてみます。

書かれている操作を実際にどうやればよいのかわからない: これは GUI ソフトウェアの操作説明の常でもありますが、説明されている操作を実際にどうやればよいかわからないところがときどきありました。

概念と操作の説明が混ざっていて迷子になる: これも章によるかと思いますが、概念的な説明と具体的な操作の説明とが混ざっているところがあって、読んでいて「これは何の説明がされているんだろう」「この一連の操作をすると何が起こるんだろう」となることがありました。ただ、このあたりは Unity をよく知る読者であれば行間を読んでカバーできるところなのかもしれません。

よかったところ

画像が多くてわかりやすい: 画像が多めで細かなところにも丁寧な説明があったりするので、わかりやすかったです。文字ばかりのモノクロの技術書は読むのにエネルギーが要るので、「カラーの書籍はわかりやすくていいなぁ」と改めて思いました。

サンプルプロジェクトが公開されている: 上述のとおり、本書の中で紹介されているサンプルプロジェクト(≒サンプルコード)が公式サイト他で公開されています。「コードを書かずにサンプルをさっと動かしたい」「実際に動くプロジェクトを見て学びたい」というときにはとてもよいと思います。私が読んだのはごく一部ですが、書籍全体では数多くのサンプルが紹介されているので、それぞれからアイデアをもらうだけでちょっとしたオリジナルゲームを作ることが可能でしょう。

というわけで、書籍『 Unity ゲーム プログラミング・バイブル 』のご紹介でした。

私自身はゲーム作りはやったことがありませんがこれを機に少しやってみたくなりました。ゲームや Unity に興味のある方はぜひお手に取ってみてください :)

尚、最近同出版社さんが Unity の中でも特に「 Unity ✕ 機械学習 」というテーマに絞った書籍を出版されたそうです(具体的には Unity ML-Agents Toolkit を利用した Unity ✕ 機械学習の書籍です)。ゲーム作りに興味のある方にかぎらず、機会学習・ AI に興味のある方はチェックしてみるとよいかもしれませんよ。



2018/08/10

Python Tips: Enum 型を使いたい

Python で Enum 型(列挙型)を使う方法について、手短に説明してみます。

お断り: 以下に記載するコードについては動作確認はしていますが、私はたくさん Enum 型を使ってきたわけでは無いので、理解が間違っている可能性もあります。ご了承ください。

Python の Enum 型


Python では Python 3.4 から enum という標準モジュールが追加され、 Python 本体に Enum 型が同梱されるようになりました。

import enum

Enum 型の基本的な使い方


定義

オリジナルの Enum 型は enum.Enum 型を継承して作成します。

from enum import Enum

class Status(Enum):
    ACTIVE = 1
    INACTIVE = 0
    CANCELED = -1

アイテム(=インスタンス)はクラスアトリビュートとして定義します。このサンプルの場合は、 Status.ACTIVE Status.INACTIVE Status.CANCELED という 3 つのアイテムを定義していることになります。

ふるまい

Enum 型のふるまいを unittest で確認してみましょう。

from enum import Enum
from collections import OrderedDict
import unittest


class Status(Enum):
    ACTIVE = 1
    INACTIVE = 0
    CANCELED = -1


class TestStatus(unittest.TestCase):
    def test_types(self):
        # クラスのアトリビュートは自動的にインスタンスとして扱われる
        self.assertIsInstance(Status.ACTIVE, Status)
        self.assertIsInstance(Status.INACTIVE, Status)
        self.assertIsInstance(Status.CANCELED, Status)

    def test_for(self):
        # iterable プロトコルを持っているので for ループで回せる
        it = iter(Status)
        self.assertIs(next(it), Status.ACTIVE)
        self.assertIs(next(it), Status.INACTIVE)
        self.assertIs(next(it), Status.CANCELED)
        with self.assertRaises(StopIteration):
            next(it)

    def test_in(self):
        # iterable プロトコルを持っているので in 演算子も使える
        self.assertIn(Status.CANCELED, Status)

    def test_name_and_value(self):
        # キーと値はアトリビュート `name` と `value` で取得できる
        self.assertIs(Status.INACTIVE.name, 'INACTIVE')
        self.assertIs(Status.INACTIVE.value, 0)

    def test_accessors(self):
        # 各アイテムにアクセスする方法として次の 3 種類が要されてている
        self.assertTrue(Status['CANCELED'] == Status(-1) == Status.CANCELED)

    def test_inequality_with_value(self):
        # キーや値と勝手に等しくなったりはしない
        self.assertNotEqual(Status.CANCELED, 'CANCELED')
        self.assertNotEqual(Status.CANCELED, -1)

    def test_dunder_members(self):
        # クラスの __members__ アトリビュートには OrderedDict が入っている
        self.assertEqual(
            Status.__members__,
            OrderedDict(
                {
                    'ACTIVE': Status.ACTIVE,
                    'INACTIVE': Status.INACTIVE,
                    'CANCELED': Status.CANCELED,
                }
            ),
        )

なんだか直感的なふるまいをしてくれる印象がありますが、いかがでしょうか。 enum.Enum を使って定義できる Enum 型は私が Enum 型に期待する機能をひととおり揃えていてかつシンプルなので、私にはとても使いやすい印象です。

自動採番

Python 3.6 以降なら、 enum.auto() を使って各アイテムの値を自動でセットすることができます。

from enum import Enum, auto
import unittest


class Prefecture(Enum):
    HOKKAIDO = auto()
    AOMORI = auto()


class TestPrefecture(unittest.TestCase):
    def test_auto_values(self):
        # auto() を使うと 1 始まりで整数が自動的に振られる
        self.assertEqual(Prefecture.HOKKAIDO.value, 1)
        self.assertEqual(Prefecture.AOMORI.value, 2)

シンプル定義

Enum 型の定義方法としてメジャーなのはおそらく上の enum.Enum クラスを継承する方法ですが、次のように enum.Enum を関数のように使用してワンラインで Enum 型を定義する方法も用意されています。

PublishedStatus = Enum('PublishedStatus', 'PUBLISHED DRAFT')


class TestPublishedStatus(unittest.TestCase):
    def test_instances(self):
        self.assertIsInstance(PublishedStatus.PUBLISHED, PublishedStatus)
        self.assertIsInstance(PublishedStatus.DRAFT, PublishedStatus)

    def test_values(self):
        self.assertEqual(PublishedStatus.PUBLISHED.value, 1)
        self.assertEqual(PublishedStatus.DRAFT.value, 2)

この形で Enum 型を定義する場合は、 enum.Enum の第 1 引数に Enum 型の名前を、第 2 引数にアイテムをスペースや , で区切りの文字列で渡します。第 2 引数は listdict で渡すことも可能です。詳細は公式ページの Functional API の節でご確認ください。


というわけで、 Python の Enum 型の使い方についてでした。

シンプルな用途で Enum 型を使う場合は以上の内容を知っていれば十分ではないかと思いますが、その他細かな機能がもろもろ用意されているので、詳細を知りたい方は公式ページをご覧になってから使うのがよいかと思います。

参考