Pythonのススメ10

はじめに

Pythonでプログラムを書くにあたり、文法や言語仕様などの個人的なメモを記載する。
今回のネタはsetterとgetter。


propertyデコレータ

Pythonではsetterやgetterを定義するときはpropertyデコレータを用いる。
@propertyで定義されたメソッドはgetterとして機能する。
@属性名.setterで定義されたメソッドはsetterとして機能する。
※「Javaみたいにアクセッサメソッド作ればええやん、なんでわざわざ@propertyを使うんだ」と思われるかもしれないが(自分も最初はそう思った)、それについては後述する。

@property
def 属性名(self):
    return self._param

@属性名.setter
def 属性名(self, param):
    #何かしらの処理
    self._param = param

#引数名や属性名は任意。必ず「param」を使わないといけないというわけではない。

サンプルプログラムを以下に示す。
getterではセットされたメンバ変数の値(属性値)をそのまま返す。
setterでは引数のバリデーションチェックを行い、チェックに通れば値をセットする。通らなければエラーを投げる。

class MyClass:
    def __init__(self, param):
        self._param = param

    @property
    def param(self):
        return self._param

    @param.setter
    def param(self, param):
        if type(param) is int or type(param) is float:
            self._param = param
        else:
            raise ValueError("intかfloatを指定してください.")

MyClassを使う側のソースコード例は以下の通り。

cls = MyClass(100)
print(cls.param)
# コンソール上には100が表示される。
cls.param = 50
print(cls.param)
# コンソール上には50が表示される。


propertiesデコレータを使う意義

結論を一言で言い表すなら、「Pythonでサポートしている通常のメンバ変数へのアクセス方法を使いつつ、自分が意図する動作をプログラムとして記載する」だろうか。

そもそも、PythonJava等のように厳密な意味でのメンバの隠蔽が言語仕様としてできない。
Pythonで属性にアクセスする際は「オブジェクト名.メンバ変数名」と記述する。
なので、ただ値をsetしてgetするだけなら、以下のようにするだけでよい。

class MyClass:
    def __init__(self, param):
        self._param = param

cls = MyClass(100)
print(cls._param)
# コンソール上には100が表示される。

cls._param = 50
print(cls._param)
# コンソール上には50が表示される。

だが、これでは良くも悪くもどんな値でもメンバ変数にsetできてしまう。
例えばintかfloat以外の値はセットさせたくない場合、setterを用意してその中でバリデーションチェックを行う必要がある。

・・・
def set_param(self, param):
    if type(param) is int or type(param) is float:
        self._param = param
    else:
        raise ValueError("intかfloatを指定してください.")
    
def get_param(self):
    return self._param

しかし、先述の通り、値のセットや取得はわざわざ上に書いたようなアクセッサメソッドを経由しなくてもできる。
そのため、クラスを使う側が必ずしもアクセッサメソッドを呼んでset/getしてくれる保証もない。
(ドキュメントに残すことである程度抑止力にはなるだろうが、それでも限界はある)

そこで登場するのがpropertyデコレータである。
これを使うことで、メンバ変数に対して通常のアクセスの記述をしつつ専用メソッド経由で処理を行うことができるようになる。


終わりに

本投稿では、Pythonにおけるsetterとgetterの定義方法および、そうするべきであるという理由や背景を述べた。

  • setterやgetterを定義するときはpropertyデコレータを使う。
  • Javaのようにアクセッサメソッドを定義してもよいが、Pythonの性質上、メンバ変数へのアクセスは「オブジェクト名.メンバ変数名」で事足りるため、わざわざそのクラスを使う側がアクセッサメソッドを使ってくれる保証もない。
  • とはいえ、何でもかんでも値がセットされてしまうと困る場合もある。例えば、バリデーションチェックを行い、それに通った場合にのみ値をセットしたい場合もある。
  • そういった場合、propertyデコレータを使うことで、「Pythonでサポートしている通常のメンバ変数へのアクセス方法を使いつつ、自分が意図する動作をプログラムとして記載する」ことができるようになる。
  • サンプルプログラムでは、propertyデコレータを使って引数のバリデーションチェックを行いつつ、値のsetとgetを行った。


参考文献

あまり良くない書き方 その3 getter/setter - Python学習講座
プロパティ - Python学習講座

Pythonのススメ9

はじめに

Pythonでプログラムを書くにあたり、文法や言語仕様などの個人的なメモを記載する。
今回のネタは変数や関数につくアンダースコア。
いろんなところでアンダースコアが1つの変数/関数定義の場合と2つの場合の定義を見るが、これらには意味があるのかが気になったので調べた。
結論を言うと意味はある


アンダースコア(_)が1つの場合と2つの場合の違い

  • アンダースコア(_)が1つの場合

 - 参照はできるが、基本的に外部から参照しない ということを慣習化させたもの。

  • アンダースコア(_)が2つの場合

 - 外部の参照を受けないもの。


また、Python - Pythonの変数やメソッドの命名について(アンダーバー)|teratail では、アンダースコアの使い分けの質問に対して、Pythonの標準コーディングスタイルを定めるPEP 8に記されているガイドラインを用いて回答されている。

実践されている命名方法

【_single_leading_underscore】
 “内部でだけ使う” ことを示します。たとえば「from M import *」は、アンダースコアで始まる名前のオブジェクトをインポートしません。
【__double_leading_underscore】
 クラスの属性に名前を付けるときに、名前のマングリング機構を呼び出します (クラスFoobarの__booという名前は_FooBar__booになります)

メソッド名とインスタンス変数

公開されていないメソッドやインスタンス変数にだけ、アンダースコアを先頭に付けてください。
サブクラスと名前が衝突した場合は、Pythonのマングリング機構を呼び出すためにアンダースコアを先頭に二つ付けてください。

継承の設計

・公開されている(public)属性の先頭にはアンダースコアを付けない
・もしあなたが公開している属性の名前が予約語と衝突する場合は、属性の名前の直後にアンダースコアを追加します。省略語を使ったり、スペルミスをするよりはマシです。
・サブクラス化して使うクラスがあるとします。サブクラスで使って欲しくない属性があった場合、その名前の最後ではなく、先頭にアンダースコアを二つ付けることを検討してみましょう。これによって Python のマングリングアルゴリズムが呼び出され、その属性にはクラス名が付加されます。これはサブクラスにうっかり同名の属性が入ってしまうことによる属性の衝突を避けるのに役立ちます。

ここで述べられているのは、「アンダースコアを2つつけることはprivateを実現するためではない。サブクラスとの名前の衝突を防ぐため」ということに注意。


サンプルプログラム

例えば、以下のようなParentクラスとそれを継承したChildクラスがあるとする。

class Parent:
    def __hoge(self):
        print("Parent #___hoge is called.")

    def _hoge(self):
        print("Parent #_hoge is called.")

    def hoge(self):
        print("Parent #hoge is called.")

    def call__hoge(self):
        self.__hoge()

    def call_hoge(self):
        self._hoge()

    def callhoge(self):
        self.hoge()


class Child(Parent):
    def __hoge(self):
        print("Child #___hoge is called.")

    def _hoge(self):
        print("Child #_hoge is called.")

    def hoge(self):
        print("Child #hoge is called.")

このとき、以下のコードを実行すると、

parent = Parent()
parent.call__hoge()
parent.call_hoge()
parent.callhoge()

実行結果は以下の通り。

Parent #___hoge is called.
Parent #_hoge is called.
Parent #hoge is called.

この実行結果はイメージしやすい。
では、以下のコードを実行するとどうなるか。

child = Child()
child.call__hoge()
child.call_hoge()
child.callhoge()

実行結果は以下の通り。

Parent #___hoge is called.
Child #_hoge is called.
Child #hoge is called.

Childクラスで定義されたメソッドはいずれもオーバーライドできているように思えるが、実際動かしてみるとそうでないことがわかる。
具体的には、

  • アンダースコアを2つつけたメソッドは子クラスでオーバーライドされない。

 - ParantクラスのメソッドはChildクラスの__hogeメソッドは参照しない。
 - ダブルアンダースコアはそれが定義されたクラス内でしか参照できない。
 - アンダースコアを2つつけることには、文法的な意味はある。

  • アンダースコアを2つつけないメソッドは子クラスでオーバーライドされる。

 - ParantクラスのメソッドはChildクラスの_hogeメソッドおよびhogeメソッドを参照する。


終わりに

本投稿では、変数やメソッドにつけられるアンダースコア(_)について、何か文法的な意味があるのかどうかを調べ、その結果について述べた。
結論として、アンダースコアを2つつけることは文法的な意味がある。
アンダースコアを2つつけると、「それが定義されたクラス内でしか参照できない」という意味が付与される。
サンプルプログラムでは、アンダースコアが2つつけられたメソッドは子クラスでオーバーライドされないことを示した。
アンダースコアが1つまたはアンダースコア無しの場合は外部から参照される。
サンプルプログラムでは、これらのメソッドは子クラスでオーバーライドされることを示した。


参考文献

【備忘録】Python3系でアンダーバー2つ付きのクラスメソッドをオーバーライドしようとしてコケた話 - Qiita
Python - Pythonの変数やメソッドの命名について(アンダーバー)|teratail
【備忘録】Pythonにおけるアンダースコア"_"の役割について - Qiita
Python初心者がステップアップするために覚えたいこと | 株式会社キャパ CAPA,Inc.

Pythonのススメ8

はじめに

Pythonでプログラムを書くにあたり、文法や言語仕様などの個人的なメモを記載する。
今回のネタは関数。


関数(基本)

関数定義時、「これは関数ですよ」と示すためにPythonではdefを使う。

def 関数名(引数):
    # 何かしらの処理

サンプルプログラムを以下に示す。

def myPrint(param):
    print(param)

myPrint("Hello, world!")
# 出力結果:Hello, world!

クラス内に関数を定義するときは、第一引数にselfを指定する必要がある。
※@staticmethodや@classmethodデコレータが修飾されている場合など、例外はある。
サンプルプログラムを以下に示す。

class MyClass:
    def __init__(self, param):
        self.param = param
    def myPrint(self):
        print(self.param)

myClass = MyClass("Hello, world!")
myClass.myPrint()
# 出力結果:Hello, world!


関数(オーバーロード

前記事(Pythonのススメ7 - Hello, world.)でも述べたとおり、Pythonでは関数のオーバーロードができない
例えば1.引数を受け取り、2.その引数の型と内容を表示する関数を定義するとき、オーバーロードができないので以下のような関数定義が考えられる。

def show_parameter(param):
    if type(param) == int:
        print("[引数はint型です.]")
    elif type(param) == float:
        print("[引数はfloat型です.]")
    elif type(param) == list:
        print("[引数はlist型です.]")
    elif type(param) == str:
        print("[引数はstr型です.]")
    else:
        print("[その他の型:" + str(type(param)) + "]")
    show_parameter_content(param)


def show_parameter_content(param):
    print(param)


show_parameter("AAA")
show_parameter([1, 2, 3])
show_parameter(123)
show_parameter(1.23)
show_parameter((1, 2, 3))

動作結果は以下の通り。

[引数はstr型です.]
AAA
[引数はlist型です.]
[1, 2, 3]
[引数はint型です.]
123
[引数はfloat型です.]
1.23
[その他の型:<class 'tuple'>]
(1, 2, 3)

意図したとおりに動いているのでこれで良いといえば良いが、いくつか課題点がある。

  • サンプルプログラムではパラメタの型に応じて条件分岐しているが、条件分岐が増えたとき、例えば、分岐させる条件としてtupleが増えたときは都度show_parameter関数に手を加える必要が出てくる。
  • 1関数当たりのステップ数が増えてしまいうる。上記サンプルプログラムではかなり単純な処理しかしていないので気にならないが、各条件ごとに処理を都度書いていると、1関数のステップ数が大きくなってしまう。1関数にあらゆる処理をまとめて書くのはソースコードの可読性の低下にもなりかねない。


@singledispatchデコレータ

じゃあどうすればいいのか。答えは、@singledispatchデコレータを使って処理をdispatchさせることである。
具体的には、以下のように記載する。
※ディスパッチは1つ目の引数の型で行われる。2つ目の引数の型でディスパッチとかはできないみたい。

@singledispatch
def 関数名(引数):
    # 処理

@関数名.register(型)
def 関数名A(引数):
    # 処理

@関数名.register(型)
def 関数名B(引数):
    # 処理

・・・

…おそらくサンプルプログラムを書いた方がイメージがつかみやすいと思うので、@singledispatchを使ったサンプルプログラムを以下に示す。これは、↑で書いたサンプルプログラムを、@singledispatchを使って書き換えたものである。

from functools import singledispatch


@singledispatch
def show_parameter(param):
    print("[その他の型:" + str(type(param)) + "]")
    show_parameter_content(param)


@show_parameter.register(int)
def _show_parameter(param):
    print("[引数はint型です.]")
    show_parameter_content(param)


@show_parameter.register(float)
def _show_parameter(param):
    print("[引数はfloat型です.]")
    show_parameter_content(param)


@show_parameter.register(list)
def _show_parameter(param):
    print("[引数はlist型です.]")
    show_parameter_content(param)


@show_parameter.register(str)
def _show_parameter(param):
    print("[引数はstr型です.]")
    show_parameter_content(param)


def show_parameter_content(param):
    print(param)


show_parameter("AAA")
show_parameter([1, 2, 3])
show_parameter(123)
show_parameter(1.23)
show_parameter((1, 2, 3))

実行結果は、@singledispatchを使う前と同じ。
このようにする利点は以下の通り。

  • 分岐対象が増えたとしても、「@関数名.register(型)」をデコレートして関数定義するだけでよい。例えばtuple型の場合の処理も追加したいな、となったときは以下のようにするだけでよい。
@show_parameter.register(tuple)
def _show_parameter(param):
    # 処理
  • 型によって処理内容が別の関数に定義されるので、@singledispatchを使う前のような、(全ての場合の処理を1つの関数にまとめて書くので)1関数のステップ数が膨大になりうる、という懸念を回避することができる。


終わりに

本投稿では、Pythonの関数について、サンプルプログラムを交えて、言語仕様とともに述べた。
関数を定義するときはdefを使う。
Pythonの言語使用上、そのままでは関数のオーバーロードはできないが、@singledispatchデコレータを使うことでオーバーロード(のような実装)を実現することができる。

…ただ、ここで思うことが一つ。「ディスパッチは1つ目の引数の型で行われる」のであれば、クラス内に定義された関数においては@singledispatchによるオーバーロード(のような実装)は実現できないということでは?クラス内に定義された関数の引数はselfなので。

クラス内に定義された関数でオーバーロード(のような実装)を実現しようと思ったらどうするんだろう…?


参考文献

10.2. functools — 高階関数と呼び出し可能オブジェクトの操作 — Python 3.6.5 ドキュメント
Python 3.4.0 の新機能 (3) - Single-dispatch generic functions - Qiita
Python の singledispatch で引数の型ごとに処理を分ける | CUBE SUGAR STORAGE
OOP in Python

Pythonのススメ7

はじめに

Pythonでプログラムを書くにあたり、文法や言語仕様などの個人的なメモを記載する。
今回のネタはコンストラクタ。


コンストラクタ(基本)

オブジェクト生成時の初期値を設定するメソッド。
例えばJavaでは以下のように書くところを、Pythonで書くとどうなるか。

public class MyClass() {
    private String msg;
    public MyClass(String msg) {
        this.msg = msg;
    }
    ...
}

答えは以下の通り。
Pythonではメソッドを定義するときにdefを使う。
Pythonではコンストラクタを定義するときは__init__ という特殊関数を使う。
また、その際、自分自身を意味するselfを引数にすることが求められている。これはコンストラクタに限らず、メソッド全般において言えることである。

class MyClass():
    def __init__(self, msg):
        self.msg = msg

    ...


コンストラクタ(オーバーロード

コンストラクタは何も1つだけとは限らない。
例えばJavaの場合、以下のようにコンストラクタのオーバーロードをする場合もある。

public class MyClass() {
    private String msg;
    public MyClass(String msg) {
        this.msg = msg;
    }

    public MyClass() {
        this("DefaultMessage");
    }
    ...
}

これ(と似たようなこと)をPythonで表記しようとしたらどうなるか。

まず前提として、Pythonではメソッドのオーバーロードができない
つまり、Pythonでは__init__ メソッドは1つのクラスに1つしか定義できない。
ではPythonではオーバーロードのようなことができないのかと言うと、答えはNoである。

以下のように、デフォルト値を定義することでコンストラクタのオーバーロードのような記述が可能。

class MyClass():
    def __init__(self, msg = None):
        self.msg = msg

    ...

このように記載しておくことで、MyClassオブジェクト生成時に以下のように動作する。

  • MyClassオブジェクト生成時に、呼び出し元が何かしらの引数を渡していれば、それが変数msgに代入される。
  • MyClassオブジェクト生成時に、呼び出し元が引数が渡されていない場合は、変数msgはNoneになる。

具体的なソースコードの例を次の章で記載する。


サンプルプログラム

これまでの内容を踏まえたサンプルプログラムを以下に記載する。

class MyClass():
    def __init__(self, msg = None):
        self.msg = msg

    def myPrint(self):
        print(self.msg)

#####
instance1 = MyClass("Hello, world!")
instance1.myPrint()

instance2 = MyClass()
instance2.myPrint()

これの実行結果は以下の通り。

Hello, world!
None


終わりに

本投稿では、Pythonでオブジェクトを生成する際のコンストラクタについて、サンプルプログラムを交えて、言語仕様とともに述べた。
メソッド(コンストラクタ含む)を定義するときはdefを使う。
コンストラクタは__init__ という特殊関数を用いる。
メソッド(コンストラクタ含む)では、自分自身を意味するselfを記述する必要がある。
Pythonではメソッドのオーバーロードができない。これはつまり、コンストラクタも1つしか定義できないことを意味する。
デフォルト値を用意することで、オーバーロードのような挙動をするメソッド(コンストラクタ含む)を定義できる。それについては、上記サンプルプログラムに記載した。


参考文献

【Python入門】クラスの使い方やオブジェクト指向の概念を理解しよう | 侍エンジニア塾ブログ | プログラミング入門者向け学習情報サイト
Python クラスについて - Qiita
オダろぐ : Python>Pythonはメソッドのオーバーロードが使えないので?
pythonで複数のコンストラクタを定義したい - Qiita
Pythonを書き始める前に見るべきTips - Qiita
Python 3.4.0 の新機能 (3) - Single-dispatch generic functions - Qiita

Pythonのススメ6

はじめに

Pythonでプログラムを書くにあたり、文法や言語仕様などの個人的なメモを記載する。
今回のネタはコレクション(辞書と集合)。
コレクションには「リスト(list)」「タプル(tuple)」「辞書(dict)」「集合(set)」が含まれる。

辞書(dict)

保持する値をリストやタプルのように順番で管理せず、キー(key)と、それに対応する値(value)で管理する。
Javaでいうところの、Mapに(限りなく)似たものと考えるとよい。
辞書の管理は以下のように行う。

dict = {"key1" : "value1", "key2" : "value2", ... , "keyN" : "valueN"}

基本的な使い方

辞書の登録

生成済みの辞書オブジェクトに対して新しいkey-valueを登録する方法は以下の通り。

dict[key名] = value

Javaでいうところの、Map #putに相当する。)

指定したkey名が辞書に無いのであれば、指定したkey-valueが新規に追加される。
指定したkey名が辞書に既にあるのであれば、それに対するvalueが上書きされる。
サンプルプログラムは以下の通り。

dict = {"name": "Ichiro", "place": "Japan"}
# キー"work"は辞書に未登録→キー"work" 値"Engineer"が追加される。
dict["work"] = "Enginerr"
print(dict)
# キー"work"は辞書に存在する→値が"Engineer"から"Teacher"に上書きされる。
dict["work"] = "Teacher"
print(dict)

実行結果は以下の通り。

{'name': 'Ichiro', 'place': 'Japan', 'work': 'Enginerr'}
{'name': 'Ichiro', 'place': 'Japan', 'work': 'Teacher'}
キーの有無

inを使うことでキーの有無を確認できる。
JavaのMap #containsKeyに相当)

(キー名) in 辞書

サンプルプログラムは以下の通り。

# 実行するとコンソール上に"True"が表示される。
print("name" in dict)
# 実行するとコンソール上に"False"が表示される。
print("test" in dict)
値の取得

getメソッドで取得できる。
JavaのMap #getに相当。

辞書.get(キー名)

サンプルプログラムは以下の通り。

val = dict.get("name")
print(val)

# hogeというkeyは無いのでコンソール上にNoneが出力される。
val = dict.get("hoge")
print(val)

# 指定したキーが登録されていないときにNone以外の値を取得したい場合はgetメソッドの第二引数に任意の値を指定する。
val = dict.get("hoge", "そんなキーは未登録!")
print(val)

実行結果は以下の通り。

Ichiro
None
そんなキーは未登録!
素数の取得

len関数で取得可能。

dict = {"key" : "value1"}

# コンソール上に1が出力される。
print(len(dict))
全てのkeyを取得/全てのvalueを取得/全てのkey-valueを取得
  • keysメソッド

  辞書内の全てのkeyを取得。

  • valuesメソッド

  辞書内の全てのvalueを取得。

  • itemsメソッド

  辞書内の全てのkey-valueを、要素が2のタプルで取得。

サンプルプログラムは以下の通り。

dict = {"name": "Ichiro", "place": "Japan", "work" : "Teacher"}
print("☆全てのkeyを取得して表示")
for key in dict.keys():
    print(key)

print("\n☆全てのvalueを取得して表示")
for value in dict.values():
    print(value)

print("\n☆全てのkey-valueを取得して表示")
index_key = 0
index_value = 1
for key_value in dict.items():
    print(key_value)
    print("    - key: " + key_value[index_key] + "/value: " + key_value[index_value])

実行結果は以下の通り。

☆全てのkeyを取得して表示
name
place
work

☆全てのvalueを取得して表示
Ichiro
Japan
Teacher

☆全てのkey-valueを取得して表示
('name', 'Ichiro')
    - key: name/value: Ichiro
('place', 'Japan')
    - key: place/value: Japan
('work', 'Teacher')
    - key: work/value: Teacher

ちなみに、辞書に対してそれぞれkeysメソッド、valuesメソッド、itemsメソッドを実行したときの実行結果は以下の通り。

dict_keys(['name', 'place', 'work'])
dict_values(['Ichiro', 'Japan', 'Teacher'])
dict_items([('name', 'Ichiro'), ('place', 'Japan'), ('work', 'Teacher')])

dict_keysクラス:
 key名が格納されているリストのようなもの。
 listメソッドを適用させることでリスト化して管理することができる。
dict_valuesクラス:
 valueが格納されているリストのようなもの。
 listメソッドを適用させることでリスト化して管理することができる。
dict_itemsクラス:
 key-valueのタプルが格納されているリストのようなもの。
 listメソッドを適用させることでリスト化して管理することができる。

集合(set)

リストやタプルのように値しか持たないし、順序も持たない。
辞書と違ってキーを持たない。
1つの集合内には同じ値が1つしか存在しない。
JavaのSetのようなもの。

基本的な使い方

集合の定義

以下のようにして集合を定義する。

set = {"value1", "value2", ... , "valueN"}
要素の追加

addメソッドを使うことで要素の追加が可能。
サンプルプログラムを以下に示す。

set = {"aaa", "bbb"}
set.add("ccc")
print(set)

実行結果は以下の通り。

{'ccc', 'aaa', 'bbb'}
素数の取得

len関数で取得可能。

set = {"aaa", "bbb"}
set.add("ccc")

# コンソール上に3が出力される。
print(len(set))
set.add("ccc")
# コンソール上に3が出力される。
print(len(set))

上記サンプルで示した通り、2回目のlen関数実行により得られる値は3である。
なぜなら、先述の通り、集合内には同じ値は2つ以上格納されないためである。
今回の例の場合、cccという値はすでに存在しているため、2回目のaddで要素は追加されない。
結果、集合setの要素数は4ではなく3となる。

値の有無

inを使うことで値の有無を確認できる。
JavaのSet #containsに相当)

(value) in 集合

サンプルプログラムは以下の通り。

set = {"aaa", "bbb", "ccc"}
# 実行するとコンソール上に"True"が表示される。
print("aaa" in set)
# 実行するとコンソール上に"False"が表示される。
print("zzz" in set)
論理積(AND) / 論理和(OR)

2つの集合の論理積をとる際は、2つの集合に対してAND(&)演算子を使う。

set1 = {"aaa", "bbb", "ccc"}
set2 = {"bbb", "zzz"}

print(set1 & set2)

動作結果は以下の通り。

{'bbb'}

2つの集合の論理和をとる際は、2つの集合に対してOR( | )演算子を使う。

set1 = {"aaa", "bbb", "ccc"}
set2 = {"bbb", "zzz"}

print(set1 | set2)

動作結果は以下の通り。

{'zzz', 'bbb', 'ccc', 'aaa'}

終わりに

Pythonのコレクション(辞書と集合)について、サンプルプログラムを交えて記載した。

辞書はJavaのMapのようなものと考えれば理解しやすい。
valueの取得はgetメソッドを使う。
キーの有無はinを使う。
keysメソッド、valuesメソッド、itemsメソッドにより、それぞれ辞書内のすべてのkey、すべてのvalue、すべてのkey-valueを取得できる。

集合はJavaのSetのようなものと考えれば理解しやすい。
値の追加はaddメソッドを使う。
addの引数に指定した値が、既に集合にある場合、それは集合に追加されない。集合は、同じ値は1つしか存在しない。
集合の論理積をとるときはAND演算子(&)を使う。
集合の論理和をとるときはOR演算子( | )を使う。

Pythonのススメ5

はじめに

Pythonでプログラムを書くにあたり、文法や言語仕様などの個人的なメモを記載する。
今回のネタはコレクション(リストとタプル)。
コレクションには「リスト(list)」「タプル(tuple)」「辞書(dict)」「集合(set)」が含まれる。

リスト(list)

複数の型のデータをひとまとめにする。
角カッコ[]を使い、含める要素をカンマで区切る。

list = ["hoge", 100, 0.5]

基本的な使い方

結合やスライス等のサンプルプログラム
# リストの中身繰り返し表示
print(list * 3)

# リスト同士の結合
print(list + ["Hello", "Happy", "World"])

# インデクスを指定した要素抽出
elem = list[0]
print(elem)

# スライス
print(list[1:])

# 要素数取得
len = len(list)
print(len)

# 要素の有無
isExist = "hoge" in list
print(isExist)

上記プログラムの実行結果は以下の通り。

['hoge', 100, 0.5, 'hoge', 100, 0.5, 'hoge', 100, 0.5]
['hoge', 100, 0.5, 'Hello', 'Happy', 'World']
hoge
[100, 0.5]
3
True
for文 with リスト
for (変数名) in (リスト):
    # 何かしらの処理

サンプルプログラムは以下の通り。

list = ["hoge", 100, 0.5]
for elem in list:
    print(elem)

実行結果は以下の通り。

hoge
100
0.5
要素の追加

appendメソッドを利用する。

(リスト).append(何かしらの値)

サンプルプログラムを以下に示す。

list = ["hoge", 100, 0.5]
list.append("Hello")

実行結果は以下の通り。

# print(list)  の実行結果
['hoge', 100, 0.5, 'Hello']
リスト内包表記

Pythonの機能の一つで、新しいリストをより高速に、シンプルに生成する技法。
基本的に以下のような構成で記載する。さらにifを組み合わせたテクニックもある。

(変数elemの処理) for (変数elem) in (配列など)

例えば、リスト内の各要素の文字列を出力するプログラムを考える。
通常のfor文を使った表記は以下のようになる。

# 並び順に特に意味はありません。
list = ["Giants", "Tigers", "Carp", "Swallows", "Dragons", "BayStars"]

ret = []
for elem in list:
    ret.append(len(elem))
print(ret)

これを、リスト内包表記を使うと以下のようになる。

ret = [len(elem) for elem in list]
print(ret)

実行結果はともに以下の通り。

[6, 6, 4, 8, 7, 8]

☆リスト内包表記を使うメリット
・処理が速い。
・記載がシンプルになる。

★リスト内包表記を使うデメリット
・それによってかえって記載が複雑になる場合はリスト内包表記は使わない方がよい。
 - これはPythonの「誰がソースコードを読んでも理解できる(理解しやすい)」という方針に反するため。

タプル(tuple)

複数の型のデータをひとまとめにする。
カッコ()を使い、含める要素をカンマで区切る。

tuple = ("hoge", 100, 0.5)

基本的な使い方

結合やスライス等のサンプルプログラム
# タプルの中身繰り返し表示
print(tuple * 3)

# タプル同士の結合
print(tuple + ("Hello", "Happy", "World"))

# インデクスを指定した要素抽出
elem = tuple[0]
print(elem)

# スライス
print(tuple[1:])

# 要素数取得
len = len(tuple)
print(len)

# 要素の有無
isExist = "hoge" in tuple
print(isExist)

上記プログラムの実行結果は以下の通り。

('hoge', 100, 0.5, 'hoge', 100, 0.5, 'hoge', 100, 0.5)
('hoge', 100, 0.5, 'Hello', 'Happy', 'World')
hoge
(100, 0.5)
3
True


リストとの違いは以下の通り。
1.タプルの場合、要素が1つでも、カンマが必要。

tuple = ("Hello",)

2.リストは可変(mutable)だが、タプルは不変(immutable)
・タプルでは、appendメソッド、またはそれに類する破壊的な操作を行うメソッドを使えない。
 - appendを使うということは、対象のオブジェクトを変更するということなので。
 - appendのような操作をするにはタプルの結合により新しいタプルを作る必要がある。

tuple = ("hoge", 100, 0.5)
tuple += ("Hello", "Happy", "World")

・関数の戻り値や不変としたい設定用の値に使える。

for文 with タプル
for (変数名) in (タプル):
    # 何かしらの処理

サンプルプログラムは以下の通り。

tuple = ("hoge", 100, 0.5)
for elem in tuple:
    print(elem)

実行結果は以下の通り。

hoge
100
0.5

終わりに

Pythonのコレクション(リストとタプル)について、サンプルプログラムを交えて記載した。
リストは可変(mutable)なのに対して、タプルは不変(immutable)である。
それゆえ、リストではappendのような、破壊的な操作を行うメソッドを実行できるが、タプルではそれを使えない。
また、リストではリスト内包表記という技法がある。
これを使えばシンプルな記述で、より高速にリストを生成することができる。
今後はコレクション(辞書と集合)などについても記載していく。

参考

リスト内包表記の活用と悪用 - Qiita
Pythonリスト内包表記の使い方 | note.nkmk.me
5. データ構造 — Python 3.6.5 ドキュメント
タプルの連結と繰り返し - タプル - Python入門

Pythonのススメ4

はじめに

Pythonでプログラムを書くにあたり、文法や言語仕様などの個人的なメモを記載する。
今回のネタは条件分岐と繰り返し。

条件分岐

条件分岐をする際はifを使う。
構成は以下の通り。
※ifにしろif-elifにしろ、if-elseまたはif-elif-elseにしろ、末尾に頃に「:」を付けることを忘れないこと。

条件分岐1

if (条件1):
    「条件1」がtrueだったときの処理。

条件分岐を複数指定したいときは以下のようにif-elifを使う。

条件分岐2

if (条件1):
    「条件1」がtrueだったときの処理。
elif (条件2):
    「条件2」がtrueだったときの処理。
・・・

ifまたはif-elifのいずれにも条件がマッチしなかったときに処理をしたい場合はelseを使う。

条件分岐3

if (条件1):
    「条件1」がtrueだったときの処理。
else:
    「条件1」にマッチしなかったときの処理。

条件分岐4

if (条件1):
    「条件1」がtrueだったときの処理。
elif (条件2):
    「条件2」がtrueだったときの処理。
else:
    「条件1」にも「条件2」にもマッチしなかったときの処理。

・AND条件を指定したい場合はandを使う。
 CやJavaの「&&」に相当する。

if 条件1 and 条件2:
    「条件1」と「条件2」の両方を満たすときの処理。

・OR条件を指定したい場合はorを使う。
 CやJavaの「||」に相当する。

if 条件1 or 条件2:
    「条件1」と「条件2」の少なくともどちらか片方を満たすときの処理。

繰り返し

繰り返しをする際はforを使う。
構成は以下の通り。
※末尾に頃に「:」を付けることを忘れないこと。

繰り返し

for 変数 range(範囲):
    何かしらの処理

例えば以下のように記載した場合、変数iに0~29の値の代入が30回行われる。
すなわち、30回処理が行われる。

繰り返し例1

for i range(30):
    何かしらの処理

これは、以下のように記載したときの動作と同じ。

繰り返し例2

for i range(0, 30):
    何かしらの処理

スタートを0ではなく1にしたい場合は以下のようにする。
この場合、1~29の29回、処理が繰り返される。

繰り返し例3

for i range(1, 30):
    何かしらの処理

繰り返しの処理では以下の文がよくつかわれる。
・break: 繰り返し処理を終える。
・continue: その回の処理を終了し、次の繰り返しを実行する。

サンプルプログラム

条件分岐と繰り返しを使ったFizzBuzzプログラムを以下に記す。
・仕様1: 1~30の数字を表示する。
・仕様2: 表示する数字が3で割り切れる場合は数字とともに「Fizz」を表示する。
・仕様3: 表示する数字が5で割り切れる場合は数字とともに「Buzz」を表示する。
・仕様4: 表示する数字が3と5の両方で割り切れる場合は数字とともに「FizzBuzz」を表示する。

FuzzBizzプログラム

for i in range(30):
    num = i + 1
    if num % 3 == 0 and num % 5 == 0:
        print(str(num) + ": FizzBuzz")
    elif num % 3 == 0:
        print(str(num) + ": Fizz")
    elif num % 5 == 0:
        print(str(num) + ": Buzz")
    else:
        print(str(num))

実行結果

1
2
3: Fizz
4
5: Buzz
6: Fizz
7
8
9: Fizz
10: Buzz
11
12: Fizz
13
14
15: FizzBuzz
16
17
18: Fizz
19
20: Buzz
21: Fizz
22
23
24: Fizz
25: Buzz
26
27: Fizz
28
29
30: FizzBuzz

終わりに

Pythonの条件分岐と繰り返しについて、サンプルプログラムを交えて記載した。
条件分岐と繰り返しは、プログラムの基本ともいえる。
これにより、今後Pythonでより実用的なプログラムを書くことができるようになる。
今後はコレクションなどについても記載していく。