Python基礎文法

Pythonの開発環境を構築する

本記事ではGoogle Colaboratoryの利用を想定している。Google Colaboratoryの使い方については、以下の記事を参照されたい。なお、GPUの設定は必要ない。

Pythonの基本

続くコードを実行して、Pythonの基本を確認しよう。

型と変数

Pythonは動的型付け言語で、明示的に型宣言をしなくても、代入した値に合わせて自動で型付けが行われる。

int_val = 1
type(int_val)


# int
str_val = 'Hello'
type(str_val)


# str
bool_val = True
type(bool_val)


# bool

間違った型を指定するとエラーが発生する。

1 + "a"


# TypeError: unsupported operand type(s) for +: 'int' and 'str'

int関数やstr関数を使うと、文字列型から数値型、数値型から文字列型へ型変換できる。

str_val = '1'
int_val = int(str_val)
type(int_val)


# int
int_val = 1
str_val = str(int_val)
type(str_val)


# str

変数名のあとに:(コロン)を書いて型を明示的に宣言することもできる。(型ヒント)

int_val: int = 1
str_val: str = 'Hello'
bool_val: bool = True

ただし、Pythonでは、明示的に型宣言をしても強制力はなく、違う型の値を入れてもエラーを出してくれない。

str_val: str = 1
type(str_val)


# int

変数名には半角英数字と_(アンダースコア)を組み合わせたものが利用できる。ただし、頭文字が数字の名前は使用できない。また、Pythonが使用している名前(予約語)も使用できない。

1int_val = 1


# SyntaxError: invalid syntax
and = 1


# SyntaxError: invalid syntax
import keyword
keyword.kwlist


# ['False',
#  'None',
#  'True',
#  'and',
#  'as',
#  'assert',
#  ...
# ]

標準出力

print関数を使うと、標準出力によって画面に表示できる。

print('Hello')


# Hello

,(カンマ)で区切ると複数の文字を表示できる。

print('Hello', 'World')


# Hello World

print関数の引数にはsepとendがあり、それぞれ区切り文字と終端文字を指定できる。

print('Hello', 'World', sep=' ', end='\n') # デフォルトの設定


# Hello World
print('Hello', 'World', sep=';', end='.')
print('Hello', 'World', sep=';', end='.')


# Hello;World.Hello;World.

文字列は、’(シングルクォート)、"(ダブルクォート)のどちらで囲んでもよい。

print('Hello')
print("Hello")


# Hello
# Hello

’(シングルクォート)を表示したい場合、"(ダブルクォート)で囲むか、\(バックスラッシュ)を前に記載する。

print("I'm not.")
print('I\'m not.')


# I'm not.
# I'm not.

"(ダブルクォート)を表示したい場合、’(シングルクォート)で囲むか、\(バックスラッシュ)を前に記述する。

print('He say "I\'m not."')
print("He say \"I'm not.\"")


# He say "I'm not."
# He say "I'm not."

文字列の途中に改行を入れたい場合、改行したい場所に\nを入れるか、"""(ダブルクォート3つ)で囲んで改行する。

print('Hello\nWorld')
print("""
Hello
World
""")


# Hello
# World

# Hello
# World

"""(ダブルクォート3つ)を記述している行も1行にカウントされるため、不要な改行を入れたくない場合は、詰めて書くかバックスラッシュを入れる。

print("""Hello
World""")
print("""\
Hello
World\
""")


# Hello
# World
# Hello
# World

文字列の中に\nが含まれていて意図せず改行されてしまう場合は、文字列の前にrを記述する。rはrawの省略で生のデータの意。

print('C:\path\to\new_file')
print(r'C:\path\to\new_file')


# C:\path	o
# ew_file
# C:\path\to\new_file

文字列の前にfを記述すると、変数の値を埋め込むことができる。

str_val = 'World'
print(f"Hello {str_val}")


# Hello World

数値も文字列型に変換することなく、文字列の中に埋め込むことができ、フォーマットを指定することもできる。

pie = 3.14159
print(f"π:{pie}")
print(f"π:{pie:.2f}")


# π:3.14159
# π:3.14

数値型の演算

数字をそのまま使うことで数値型が利用できる。主な数値型に整数型と浮動小数点型がある。

int_val = 1
type(int_val)


# int
float_val = 1.0
type(float_val)


# float

+(プラス)、-(マイナス)、*(アスタリスク)、/(スラッシュ)などの演算子が用意されていて、四則演算も簡単に実行できる。

7 + 3 # 和


# 10
7 - 3 # 差


# 4
7 * 3 # 積


# 21
7 / 3 # 商(実数)


# 2.3333333333333335
7 // 3 # 商(整数)


# 2
7 % 3 # 剰余


# 1
7 ** 2 # べき乗


# 49

足し算と掛け算などが混在している場合、数学と同じく掛け算が優先される。

10 + 3 * 6


# 28

足し算を優先したい場合は()を使用する。

(10 + 3) * 6


# 78

整数型と浮動小数点型の演算を実行すると、浮動小数点型に型を自動で合わせて演算が実施される。

type(1 + 0.1)


# float

round関数を使うと、四捨五入することもできる。

pie = 3.14159
round(pie, 2)


# 3.14

mathライブラリを利用すると、より複雑な計算ができる。

import math
math.sqrt(49)


# 7.0
import math
math.log2(16)


# 4.0

mathを用いてできる計算については、help関数を使って確認できる。

import math
help(math)


# Help on built-in module math:

# NAME
#     math

# DESCRIPTION
#  ...

文字列型の演算

’(シングルクォート)、あるいは"(ダブルクォート)で囲むことで文字列型が利用できる。

str_val = 'Hello'
type(str_val)


# str

*(アスタリスク)を使うと文字列を繰り返すことができる。

'=' * 5


# '====='

+(プラス)を使うと文字列を連結できる。

'=' * 5 + 'o' * 3


# '=====ooo'

リテラル同士の場合、+(プラス)の省略が可能である。

'He''llo'


# 'Hello'

あまり使うことはないかもしれないが、リテラルが長い場合には有効である。

s = (
    'aaaaaaaaaaaaaaaaaaaaaaa'
    'bbbbbbbbbbbbbbbbbbbbbbb'
    'ccccccccccccccccccccccc'
)
s


# 'aaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbccccccccccccccccccccccc'

文字列型は、インデックスを指定して要素を抽出することができる。

str_val = 'Hello'
print(str_val[0])
print(str_val[1])
print(str_val[-1])


# H
# e
# o

ただし、存在しないインデックスを指定するとエラーが出る。

str_val = 'Hello'
str_val[100]


# IndexError: string index out of range

:(コロン)を用いると複数の要素を抽出できる。

str_val = 'Hello'
print(str_val[0:3])
print(str_val[2:4])


# Hel
# ll

:(コロン)の前後の文字を省略すると、最初からあるいは最後まで抽出できる。

str_val = 'Hello'
print(str_val[:3])
print(str_val[2:])
print(str_val[:])


# Hel
# llo
# Hello

len関数を使うと、文字列の長さが取得できる。

str_val = 'Hello'
len(str_val)


# 5

startswithメソッドを使うと、指定の文字列で始まっているか調べることができる。

str_val = 'Hello World'
print(str_val.startswith('He'))
print(str_val.startswith('Wo'))


# True
# False

findメソッドを使うと、指定の文字列がどこにあるかを調べることができる。指定の文字列が存在しない場合は-1を返す。

str_val = 'Hello World'
print(str_val.find('He'))
print(str_val.find('Wo'))
print(str_val.find('xxx'))


# 0
# 6
# -1

findメソッドでは前方から探索するのに対して、rfindメソッドを使うと後方から探索できる。

str_val = 'Hello World Hello World'
print(str_val.find('World'))
print(str_val.rfind('World'))


# 6
# 18

countメソッドを使うと、指定の文字列が何回出現するか調べることができる。

str_val = 'Hello World Hello World'
print(str_val.count('World'))


# 2

capitalizeメソッドを使うと、最初の文字のみ大文字に、それ以外の文字を小文字に変換できる。

str_val = 'Hello World Hello World'
print(str_val.capitalize())


# Hello world hello world

titleメソッドを使うと、単語の最初の文字を大文字に、それ以外の文字を小文字に変換できる。

str_val = 'hello world hello world'
print(str_val.title())


# Hello World Hello World

upperメソッドを使うと、すべての文字を大文字に変換できる。

str_val = 'Hello World Hello World'
print(str_val.upper())


# HELLO WORLD HELLO WORLD

lowerメソッドを使うと、すべての文字を小文字に変換できる。

str_val = 'Hello World Hello World'
print(str_val.lower())


# hello world hello world

replaceメソッドを使うと、指定の文字列に置換できる。

str_val = 'Hello World Hello World'
print(str_val.replace('World', 'Universe'))


# Hello Universe Hello Universe

splitメソッドを使うと、文字列を分割できる。

str_val = 'Hello World Hello World'
print(str_val.split())


# ['Hello', 'World', 'Hello', 'World']

引数で区切り文字を指定すると、スペース以外の文字でも分割できる。

str_val = 'Hello,World,Hello,World'
print(str_val.split(','))


# ['Hello', 'World', 'Hello', 'World']

joinメソッドを使うと、分割した文字列を結合することもできる。

'+++'.join(['Hello', 'World', 'Hello', 'World'])


# Hello+++World+++Hello+++World

リスト型の演算

[](角括弧)で囲むことでリスト型を利用できる。

list_val = [1, 2 ,3 ,4 ,5]
type(list_val)


# list

文字列型と同様に、インデックスを指定して要素を抽出できる。

list_val = [1, 2 ,3 ,4 ,5]
print(list_val[0])
print(list_val[1])
print(list_val[-1])
print(list_val[1:4])
print(list_val[3:])


# 1
# 2
# 5
# [2, 3, 4]
# [4, 5]

[::2]と記述すると1つとばしで要素を取得できる。

list_val = [1, 2 ,3 ,4 ,5]
list_val[::2]


# [1, 3, 5]

[::-1]と記述すると逆順で要素を取得できる。

list_val = [1, 2 ,3 ,4 ,5]
list_val[::-1]


# [5, 4, 3, 2, 1]

インデックスを指定して、要素を書き換えることができる。

list_val = [1, 2 ,3 ,4 ,5]
list_val[0] = 999
list_val


# [999, 2, 3, 4, 5]

:(コロン)を用いると複数の要素を書き換えることができる。

list_val = [1, 2 ,3 ,4 ,5]
list_val[2:4] = [999, 999]
list_val


# [1, 2, 999, 999, 5]

空リストを代入すると要素を削除できる。

list_val = [1, 2 ,3 ,4 ,5]
list_val[2:4] = []
list_val


# [1, 2, 5]

appendメソッドを使うと、リストの最後に要素を追加できる。

list_val = [1, 2 ,3 ,4 ,5]
list_val.append(999)
list_val


# [1, 2, 3, 4, 5, 999]

insertメソッドを使うと、文字列の先頭や途中に追加できる。先頭に追加するときはインデックスに0を指定する。

list_val = [1, 2 ,3 ,4 ,5]
list_val.insert(0, 999)
list_val


# [999, 1, 2, 3, 4, 5]

popメソッドを使うと、最後の値を取り出すことができる。

list_val = [1, 2 ,3 ,4 ,5]
pop_val = list_val.pop()
print(list_val)
print(pop_val)


# [1, 2, 3, 4]
# 5

del文を使うと、指定したインデックスの値を削除できる。

list_val = [1, 2 ,3 ,4 ,5]
del list_val[2]
list_val


# [1, 2, 4, 5]

del文は強力で、インデックスを指定せずに変数そのものを消し、その後その変数を呼び出した場合に定義されていないというエラーがでてしまう。

list_val = [1, 2 ,3 ,4 ,5]
del list_val
list_val # NameError


# NameError: name 'list_val' is not defined

removeメソッドを使うと、指定した要素を前方から探索し、最初に見つけたものを消すことができる。

list_val = [1, 2 ,3 ,2 ,5]
list_val.remove(2)
list_val


# [1, 3, 2, 5]

+(プラス)を使うと、複数のリストを結合できる。

[1, 2 ,3 ,2 ,5] + [6, 7 ,8 ,9 ,10]


# [1, 2, 3, 2, 5, 6, 7, 8, 9, 10]

extendメソッドを使用しても、複数のリストを結合できる。

list_val = [1, 2 ,3 ,2 ,5]
list_val.extend([6, 7 ,8 ,9 ,10])
list_val


# [1, 2, 3, 2, 5, 6, 7, 8, 9, 10]

indexメソッドを使うと、指定した要素のインデックスを逆検索できる。

list_val = [1, 999, 2 ,3 ,4 ,5, 999]
list_val.index(999)


# 1

第2引数に探索を開始したいインデックスを指定することもできる。

list_val = [1, 999, 2 ,3 ,4 ,5, 999]
list_val.index(999, 4)


# 6

countメソッドを使うと、指定した要素の個数を調べることができる。

list_val = [1, 999, 2 ,3 ,4 ,5, 999]
list_val.count(999)


# 2

sortメソッドを使うと、要素が小さい順にソートできる。

list_val = [1, 3, 4, 2, 5]
list_val.sort()
list_val


# [1, 2, 3, 4, 5]

引数のreverseにTrueを指定すると大きい順にソートできる。

list_val = [1, 3, 4, 2, 5]
list_val.sort(reverse=True)
list_val


# [5, 4, 3, 2, 1]

他の変数に代入したリストを書き換えると元のリストも書き換わってしまう。これは、代入によりリストを共有してしまっているためである。

list_val = [1, 2 ,3 ,4 ,5]
new_list_val = list_val
new_list_val[2] = 999
print(list_val)
print(new_list_val)


# [1, 2, 999, 4, 5]
# [1, 2, 999, 4, 5]

copyメソッドを使うことで、リストの共有問題は回避できる。

list_val = [1, 2 ,3 ,4 ,5]
new_list_val = list_val.copy()
new_list_val[2] = 999
print(list_val)
print(new_list_val)


# [1, 2, 3, 4, 5]
# [1, 2, 999, 4, 5]

タプル型の演算

()で囲むことでタプル型を利用できる。

tuple_val = (1, 2 ,3 ,4 ,5)
type(tuple_val)


# tuple

タプルはリストと似ているが値を代入することはできない。

tuple_val = (1, 2 ,3 ,4 ,5)
tuple_val[0] = 999 # TypeError


# TypeError: 'tuple' object does not support item assignment

リスト型と同様に、インデックスを指定して要素を抽出できる。

tuple_val = (1, 2 ,3 ,4 ,5)
print(tuple_val[0])
print(tuple_val[-1])
print(tuple_val[2:4])


# 1
# 5
# (3, 4)

リストと同様に、indexメソッド、countメソッドなども利用できる。

tuple_val = (1, 2 ,3 ,4 ,5)
print(tuple_val.index(3))
print(tuple_val.count(3))


# 2
# 1

+(プラス)を使うと、タプルの結合ができる。

(1, 2 ,3 ,2 ,5) + (6, 7 ,8 ,9 ,10)


# (1, 2, 3, 2, 5, 6, 7, 8, 9, 10)

要素が一つだけのタプルは,(カンマ)をつけて表記する。

(1,) + (2,)


# (1, 2)

タプルは()を省略することもできる。

tuple_val = 1, 2 ,3
tuple_val


# (1, 2, 3)

受け取り側の変数の数を要素数と同じにすると分割代入(アンパッキング)が実施でき、これを利用すると値の入れ替えが簡単になる。

x, y, z = 1, 2 ,3
print(f"x={x}, y={y}, z={z}")
x, y = y, x
print(f"x={x}, y={y}, z={z}")


# x=1, y=2, z=3
# x=2, y=1, z=3

辞書型の演算

{}(波括弧)で囲むことで辞書型が利用できる。

dict_val = {
    # 'キー':値
    'a': 1,
    'b': 2,
    'c': 3,
}
type(dict_val)


# dict

キーを用いて値を抽出できる。

dict_val = {
    'a': 1,
    'b': 2,
    'c': 3,
}
print(dict_val['a'])
print(dict_val['c'])


# 1
# 3

代入によって値の変更、追加ができる。

dict_val = {
    'a': 1,
    'b': 2,
    'c': 3,
}
dict_val['a'] = 999
dict_val['d'] = 'str'
dict_val


# {'a': 999, 'b': 2, 'c': 3, 'd': 'str'}

辞書型は以下のような方法でも作ることができる。

print(dict(a=1, b=2, c=3))
print(dict([('a',1), ('b',2), ('c',3)]))


# {'a': 1, 'b': 2, 'c': 3}
# {'a': 1, 'b': 2, 'c': 3}

keysメソッドを使うと、キーのみを取り出せる。

dict_val = {
    'a': 1,
    'b': 2,
    'c': 3,
}
dict_val.keys()


# dict_keys(['a', 'b', 'c'])

valuesメソッドを使うと、値のみを取り出せる。

dict_val = {
    'a': 1,
    'b': 2,
    'c': 3,
}
dict_val.values()


# dict_values([1, 2, 3])

updateメソッドを使うと、辞書型のデータを更新できる。更新前の辞書に存在しないキーはそのまま追加される。

dict_val = {
    'a': 1,
    'b': 2,
    'c': 3,
}
new_dict_val = {
    'a': 5,
    'b': 6,
    'd': 7,
}
dict_val.update(new_dict_val)
dict_val


# {'a': 5, 'b': 6, 'c': 3, 'd': 7}

getメソッドを使うと、辞書の値を取得できる。getメソッドでは、存在しないキーを指定してもエラーにならず、Noneを返してくれる。

dict_val = {
    'a': 1,
    'b': 2,
    'c': 3,
}
print(dict_val.get('a'))
print(dict_val.get('c'))
print(dict_val.get('d'))


# 1
# 3
# None

clearメソッドを使うと、辞書の中身を消すことができる。

dict_val = {
    'a': 1,
    'b': 2,
    'c': 3,
}
dict_val.clear()
dict_val


# {}

inを使うと、キーが含まれているかどうか調べることができる。

dict_val = {
    'a': 1,
    'b': 2,
    'c': 3,
}
print('a' in dict_val)
print('d' in dict_val)


# True
# False

リストの時と同様に、他の変数に代入した辞書を書き換えると元の辞書も書き換わってしまう。これは、代入により辞書を共有してしまっているためである。

dict_val = {'a': 1}
new_dict_val = dict_val
new_dict_val['a'] = 999
print(dict_val)
print(new_dict_val)


# {'a': 999}
# {'a': 999}

copyメソッドを使うことで、辞書の共有問題は回避できる。

dict_val = {'a': 1}
new_dict_val = dict_val.copy()
new_dict_val['a'] = 999
print(dict_val)
print(new_dict_val)


# {'a': 1}
# {'a': 999}

集合型の演算

辞書型と同じく{}(波括弧)で囲むことで集合型が利用できる。重複した値は同じものとして扱われる。

set_val = {1, 2, 2, 3, 4, 4, 5}
type(set_val)


# set

-(マイナス)を使うと、差集合が求められる。

set_val = {1, 2, 3, 4, 5}
new_set_val = {2, 4, 6}
set_val - new_set_val


# {1, 3, 5}

&(アンパサンド)を使うと、積集合が求められる。

set_val = {1, 2, 3, 4, 5}
new_set_val = {2, 4, 6}
set_val & new_set_val


# {2, 4}

|(パイプライン)を使うと、和集合が求められる。

set_val = {1, 2, 3, 4, 5}
new_set_val = {2, 4, 6}
set_val | new_set_val


# {1, 2, 3, 4, 5, 6}

^(ハット)を使うと、どちらか一方のみに含まれている要素が求められる。

set_val = {1, 2, 3, 4, 5}
new_set_val = {2, 4, 6}
set_val ^ new_set_val


# {1, 3, 5, 6}

addメソッドを使うと、要素を追加できる。

set_val = {1, 2, 3, 4, 5}
set_val.add(6)
set_val


# {1, 2, 3, 4, 5, 6}

removeメソッドを使うと、要素を削除できる。

et_val = {1, 2, 3, 4, 5}
set_val.remove(4)
set_val


# {1, 2, 3, 5}

clearメソッドを使うと、中身が空の集合になる

set_val = {1, 2, 3, 4, 5}
set_val.clear()
set_val


# set()

ブール型の演算

True、Falseを用いるとブール型はが利用する。

bool_val = True
type(bool_val)


# bool

andや&(アンパサンド)を使うと、論理積が求められる。

print(True and True)
print(True and False)
print(False and False)


# True
# False
# False

orや|(パイプライン)を使うと、論理和が求められる。

print(True or True)
print(True or False)
print(False or False)


# True
# True
# False

notを使うと、否定が求められる。

print(not True)
print(not False)


# False
# True

他の型をブール型に変換でき、数値型をブール型に変換した場合、0がFalseを、それ以外はTrueを返す。

print(bool(3))
print(bool(0))


# True
# False

文字列型をブール型に変換した場合、空文字がFalseを、それ以外はTrueを返す。

print(bool('abc'))
print(bool(''))


# True
# False

リスト型をブール型に変換した場合、空リストがFalseを、それ以外はTrueを返す。

print(bool([1, 2, 3]))
print(bool([]))


# True
# False

比較演算

==を使うと、値が等しいか判別できる。

a, b = 1, 1
print(a == b)


# True

!=を使うと、値が異なっているか判別できる。

a, b = 1, 1
print(a != b)


# False

<や>を使うと、大小関係を比較できる。

a, b = 1, 2
print(a < b)


# True

<=や>=を使うと、以上や以下などを判別できる。

a, b = 1, 1
print(a <= b)


# True

その他の演算

inを使うと要素を含んでいるかどうかを判別できる。

2 in [1,2,3]


# True

値がNoneか判定する場合は、isを使う。isと==の違いについては、本記事内の【付録A - isと==の違い】を参照されたい。

a = None
print(a is None)


# True

コメント

#(シャープ)を使うと、コメントを記述できる。

# これはコメントです

複数行にわたるコメントは、"""(ダブルクォート3つ)で囲む。

"""
これは
複数行に
わたる
コメント
です
"""

改行

()を使うと長い式を改行できる。バックスラッシュを使っても改行できるが、可読性が下がるのであまり使わない。

x = (
    1 + 2 + 3 + 4 + 5 + 6 + 7
    + 8 + 9 + 10
)
print(x)


# 55

制御構文

すべてのアルゴリズムは、順接、分岐、繰り返しの組み合わせで記述できる。続くコードを実行して、制御構文の挙動を確認しよう。

if文

if文を使うと、変数の値に応じて処理を分岐できる。

count = 20
if count > 10:
    print('多い')
print('終わり')


# 多い
# 終わり
count = 7
if count > 10:
    print('多い')
print('終わり')


# 終わり

else文を使うと、if文の条件に当てはまらない場合の処理を記述できる。

count = 7
if count > 10:
    print('多い')
else:
    print('少ない')
print('終わり')


# 少ない
# 終わり

elif文を使うと、さらに細かく条件を指定できる。

count = 7
if count > 10:
    print('多い')
elif count > 5:
    print('中くらい')
elif count > 0:
    print('少ない')
elif count == 0:
    print('ない')
else:
    print('異常値')
print('終わり')


# 中くらい
# 終わり

for文

for文を使うと、繰り返し処理を記述できる。

for val in [1, 2, 3, 4, 5]:
    print(val)


# 1
# 2
# 3
# 4
# 5

break文を使うと、for文を抜けることができる。

for val in [1, 2, 3, 4, 5]:
    if val==3:
        break
    print(val)


# 1
# 2

continue文を使うと、現在のループをスキップして、次のループに進む。

for val in [1, 2, 3, 4, 5]:
    if val==3:
        continue
    print(val)


# 1
# 2
# 4
# 5

else文を使うと、forループが終了したあとの処理を記述できる。

for val in [1, 2, 3, 4, 5]:
    print(val)
else:
    print('終わり')


# 1
# 2
# 3
# 4
# 5
# 終わり

break文を用いてforループから抜けた場合は、else文は実行されない。

for val in [1, 2, 3, 4, 5]:
    if val==3:
        break
    print(val)
else:
    print('終わり')


# 1
# 2

range関数を使うと、ループの回数を指定できる。

for val in range(5):
    print(val)


# 0
# 1
# 2
# 3
# 4

range関数の引数を指定すると、はじめの値、増分の値を指定できる。

for val in range(2, 5): # 2から5の範囲
    print(val)


# 2
# 3
# 4
for val in range(2, 10, 3): # 2から10の範囲で3ずつ増える
    print(val)


# 2
# 5
# 8

range関数の数値を使わない場合は、変数の代わりに_(アンダースコア)を用いることで、変数を使わないことを明示できる。

for _ in range(5):
    print('Hello')


# Hello
# Hello
# Hello
# Hello
# Hello

enumerate関数を使うと、インデックス番号も同時に利用できる。

for i, str_val in enumerate(['a', 'b', 'c', 'd', 'e']):
    print(i, str_val)


# 0 a
# 1 b
# 2 c
# 3 d
# 4 e

zip関数を使うと、複数のリストを同時に利用できる。

start = ['a', 'b', 'c']
middle = ['d', 'e', 'f']
end = ['g', 'h', 'i']
for s, m, e in zip(start, middle, end):
    print(s, m, e)


# a d g
# b e h
# c f i

辞書型も利用できる。そのまま使うと、キーのみが出力される。

dict_val = {'a': 1, 'b': 2}
for val in dict_val:
    print(val)


# a
# b

itemsメソッドを使うと、キーと値を両方利用できる。

dict_val = {'a': 1, 'b': 2}
for key, val in dict_val.items():
    print(key, val)


# a 1
# b 2

while文

while文を使うと、for文と同様に繰り返し処理を記述できる。

count = 0
while count < 5:
    print(count)
    count += 1


# 0
# 1
# 2
# 3
# 4

while文では、指定の条件式を満たす限りループし続けるため、意図せず無限にループする可能性がある。無限ループになった際は、停止ボタンを押して処理を停止する。

count = 0
while True:
    print(count)
    count += 1


# 0
# 1
# 2
# 3
# 4
# ...

for文と同様に、while文でもbreak、contimue、elseなどが利用できる。

count = 0
while count < 5:

    if count == 2:
        count += 1
        continue

    print(count)
    count += 1

else:
    print('終わり')


# 0
# 1
# 3
# 4
# 終わり

while文は、しばしばユーザーの入力を利用するinput関数と合わせて利用される。

while True:
    word = input('ループを抜けますか?:')
    if word == 'yes':
        break
        

# ループを抜けますか?:no
# ループを抜けますか?:no
# ループを抜けますか?:yes

リスト内包表記

簡単なfor文はリスト内包表記を使うと、一行で記述できる。

# for文を用いた記述
list_val1 = []
for i in range(10):
    list_val1.append(i)
print(list_val1)

# リスト内包表記を用いた記述
list_val2 = [i for i in range(10)]
print(list_val2)


# [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
# [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

リスト内包表記では、if文を合わせて使う事ができる。

# for文を用いた記述
list_val1 = []
for i in range(10):
    if i%2 == 0:
        list_val1.append(i)
print(list_val1)

# リスト内包表記を用いた記述
list_val2 = [i for i in range(10) if i%2 == 0]
print(list_val2)


# [0, 2, 4, 6, 8]
# [0, 2, 4, 6, 8]

さらにelse文も使うことができる。elseを記載する場合はif文をfor文の前に記述する必要がある。

# for文を用いた記述
list_val1 = []
for i in range(10):
    if i%2 == 0:
        list_val1.append(i)
    else:
        list_val1.append(999)
print(list_val1)

# リスト内包表記を用いた記述
list_val2 = [i if i%2 == 0 else 999 for i in range(10)]
print(list_val2)


# [0, 999, 2, 999, 4, 999, 6, 999, 8, 999]
# [0, 999, 2, 999, 4, 999, 6, 999, 8, 999]

リスト内包表記と同様に、辞書内包表記、集合内包表記なども使用できる。

# 辞書内包表記
print({key: val for key, val in zip(['a', 'b', 'c'], [1, 2, 3])})

# 集合内包表記
print({i%3 for i in range(10)})


# {'a': 1, 'b': 2, 'c': 3}
# {0, 1, 2}

関数・クラス・モジュール・例外処理

重複する処理は関数やクラスに切り出して、再利用できるようにする。続くコードを実行して、関数やクラスの挙動を確認しよう。

関数

関数を使うと、何度も呼び出す処理を一箇所に記載できる。

def func(a, b):
    return a + 2 * b

print(func(1, 1))
print(func(2, 3))
print(func(3, 2))


# 3
# 8
# 7

変数と同様に、引数の型宣言が可能である。

def func(a: int, b: int):
    return a + 2 * b

print(func(1, 1))


# 3

返り値についても型宣言が可能で、()の後ろに矢印を書いて型を指定する。

def func(a: int, b: int) -> int:
    return a + 2 * b

print(func(1, 1))


# 3

ただし、引数と返り値どちらの型宣言も、強制力はなく、異なる型の値が入れられてもエラーにならない。

def func(a: int, b: int) -> int:
    print(f"""
        a : {type(a)}
        b : {type(b)}
        return : {type(a + 2 * b)}
    """
    )
    return a + 2 * b

func(1, 1) # 3
func('a', 'b') # abb


# a : <class 'int'>
# b : <class 'int'>
# return : <class 'int'>

# a : <class 'str'>
# b : <class 'str'>
# return : <class 'str'>

引数が複数存在する場合、記述した順序に従って引数に格納される。

def func(x, y, z):
    print(f"""
        x={x}
        y={y}
        z={z}
    """)
    return x + 2 * y + 3 * z

func(3, 2, 1) # 10


# x=3
# y=2
# z=1

ただし、引数名を明示すれば、順序が変わっても指定した引数に格納される。

def func(x, y, z):
    print(f"""
        x={x}
        y={y}
        z={z}
    """)
    return x + 2 * y + 3 * z

func(z=1, y=2, x=3) # 10


# x=3
# y=2
# z=1

デフォルト値が指定されている引数は、関数呼び出しの際に省略でき、省略した場合はデフォルト値が用いられる。

def func(x=1, y=2, z=3):
    print(f"""
        x={x}
        y={y}
        z={z}
    """)
    return x + 2 * y + 3 * z

func() # 14
func(x=999) # 1012


# x=1
# y=2
# z=3

# x=999
# y=2
# z=3

*(アスタリスク)を使うと、複数の引数をタプル化して受け取る事ができる。

def func(*args):
    print(args)

func(1, 2 ,3, 4, 5)


# (1, 2, 3, 4, 5)

他の引数と一緒に使うこともできる。

def func(x, y, *args):
     print(f"""
        x={x}
        y={y}
        args={args}
     """)

func(1, 2 ,3, 4, 5)


# x=1
# y=2
# args=(3, 4, 5)

関数を呼び出す側で*(アスタリスク)を使うと、アンパッキングして引数を渡すことができる。

def func(x, y, *args):
     print(f"""
        x={x}
        y={y}
        args={args}
     """)

tuple_val = (1, 2 ,3, 4, 5)
func(*tuple_val)


# x=1
# y=2
# args=(3, 4, 5)

**(アスタリスク2つ)を使うと、引数を辞書化して受け取ることができる。

def func(**kwargs):
     print(f"kwargs={kwargs}")

func(x=1, y=2 ,z=3)


# kwargs={'x': 1, 'y': 2, 'z': 3}

他の引数と一緒に使うこともできる。

def func(x, y, **kwargs):
     print(f"""
         x={x}
         y={y}
         kwargs={kwargs}
     """)

func(x=1, y=2 ,z=3)


# x=1
# y=2
# kwargs={'z': 3}

関数を呼び出す側で**(アスタリスク2つ)を使うと、辞書を展開して引数を渡すことができる。

def func(**kwargs):
     print(f"kwargs={kwargs}")

dict_value = {
    'x': 1,
    'y': 2,
    'z': 3,
}
func(**dict_value)


# kwargs={'x': 1, 'y': 2, 'z': 3}

関数、引数、返り値の説明はdocstringという形で記述する。関数の中に”””(ダブルクォート3つ)で囲んで記述する。docstringの内容は、__doc__メソッドやhelp関数から確認できる。

def func(x, y):
    """Returns sum of x and y.

    Args:
        x (int): The first parameter of addition.
        y (int): The second parameter of addition.

    Returns:
        int: The sum of x and y.
    """
    return x + y

print('■ __doc__メソッドの呼び出し')
print(func.__doc__)

print('■ help関数の呼び出し')
help(func)


# ■ __doc__メソッドの呼び出し
# Returns sum of x and y.

#     Args:
#         x (int): The first parameter of addition.
#         y (int): The second parameter of addition.

#     Returns:
#         int: The sum of x and y.
    
# ■ help関数の呼び出し
# Help on function func in module __main__:

# func(x, y)
#     Returns sum of x and y.
    
#     Args:
#         x (int): The first parameter of addition.
#         y (int): The second parameter of addition.
    
#     Returns:
#         int: The sum of x and y.

lambda関数

短い関数はlambdaを使うと、一行で記述できる。

func = lambda x, y: x + y
func(1, 2)


# 3

変数のスコープ

グローバル変数は関数の中からも利用できる。

val= 'GLOBAL'

def func():
    print(val)

func()


# GlOBAL

ただし、グローバル変数と同じ名前のローカル変数を定義した場合は、ローカル変数が優先される。

val = 'GLOBAL'

def func():
    val = 'LOCAL'
    print('関数内部:', val)

func()
print('関数外部:', val)


# 関数内部: LOCAL
# 関数外部: GLOBAL

locals関数、globals関数を使うと、ローカル変数、グローバル変数を確認できる。

def func():
    val = 'LOCAL'
    print('local: ', locals())

func()


# local:  {'val': 'LOCAL'}
val = 'GROBAL'
print('global: ', globals())


# global:  {'__name__': '__main__', '__doc__': ...

クラスとオブジェクト

クラスを使うと、そのクラスに紐づく処理を、クラス内にメソッドとしてまとめて記載することができる。

class Dog(object):
    def walk(self):
        print('Dog is walking.')

    def run(self):
        print('Dog is running.')

dog = Dog()
dog.walk()
dog.run()


# Dog is walking.
# Dog is running.

selfはクラス本体を表していて、これを使うと、同じクラス内の他のメソッドを利用できる。

class Dog(object):
    def walk(self):
        print('Dog is walking.')

    def run(self):
        print('Dog is running.')

    def exercise(self):
        self.walk()
        self.run()
        self.walk()
        self.run()

dog = Dog()
dog.exercise()


# Dog is walking.
# Dog is running.
# Dog is walking.
# Dog is running.

__init__メソッドを使うと、クラスを呼び出してインスタンスを作成する際に、実行する処理を記述できる。

class Dog(object):
    def __init__(self):
        print('Dog is created.')

dog = Dog()


# Dog is created.

__init__メソッドでは、インスタンス変数と呼ばれるクラス内で利用できる変数を定義する。__init__メソッドで受け取る引数は、インスタンス作成時に指定する。

class Dog(object):
    def __init__(self, name):
        self.name = name

    def identified(self):
        print(self.name)

dog = Dog(name='John')
dog.identified()


# John

インスタンス変数は、クラスの外部からも呼び出したり、書き換えたりできる。

class Dog(object):
    def __init__(self, name):
        self.name = name

dog = Dog(name='John')
print(dog.name)
dog.name = 'Keats'
print(dog.name)


# John
# Keats

クラス外部からインスタンス変数を変更してほしくない場合は、_(アンダースコア)を使って明示する。ただし、これだけでは強制力はないため、変更しようと思えば変更できてしまう。

class Dog(object):
    def __init__(self, name):
        self._name = name

dog = Dog(name='John')
print(dog._name)
dog._name = 'Keats' # 強制力はないため、意図しない処理だが実施できる
print(dog._name)


# John
# Keats

@propertyを使うと、クラス外部からインスタンス変数を変更できないように強制できる。これによって、nameをクラス外部から変更しようとするとエラーが発生する。ただし、_nameは変更できてしまう。

class Dog(object):
    def __init__(self, name):
        self._name = name

    @property
    def name(self):
        return self._name

dog = Dog(name='John')
print(dog.name)
dog.name = 'Keats' # AttributeError
print(dog.name)


# John
# AttributeError: can't set attribute

@<変数名>.setterを使うとセッターを記述できる。セッターを使うと、特定の条件を満たすときのみ値を書き換えるといった処理が可能になる。

class Dog(object):
    def __init__(self, name):
        self._name = name

    @property
    def name(self):
        return self._name

    @name.setter
    def name(self, name):
        if type(name)==str:
            self._name = name

dog = Dog(name='John')
print(dog.name)
dog.name = 'Keats'
print(dog.name)


# John
# Keats

__(アンダースコア2つ)を使うと、完全にクラス外部からインスタンス変数を参照できないようにできる。

class Dog(object):
    def __init__(self, name):
        self.__name = name

    @property
    def name(self):
        return self.__name

dog = Dog(name='John')
print(dog.name)
print(dog.__name) # AttributeError


# John
# AttributeError: 'Dog' object has no attribute '__name'

__del__メソッドを使うと、インスタンスが削除された際に実行される処理を記述できる。Pythonではガーベッジコネクションという方式を利用しているので、インスタンスが使われなくなった時点でインスタンスが自動で削除され、__del__メソッドが呼び出される。

class Dog(object):
    def __init__(self, name):
        self.name = name

    def __del__(self):
        print('Dog is deleted.')
        
dog = Dog(name='John')
del dog


# Dog is deleted.

継承を使うと、既存のクラスの機能を引き継いで新しいクラスを作ることができる。

class Animal(object):
    def walk(self):
        print('Animal is walking.')

    def run(self):
        print('Animal is running.')

class Dog(Animal):
    pass

dog = Dog()
dog.walk()
dog.run()


# Animal is walking.
# Animal is running.

子クラスでメソッドを追加したり、上書き(オーバーライド)したりできる。

class Animal(object):
    def walk(self):
        print('Animal is walking.')

    def run(self):
        print('Animal is running.')

class Dog(Animal):
    def run(self):
        print('Dog is running.')

    def sit(self):
        print('Dog is sitting.')

dog = Dog()
dog.walk()
dog.run()
dog.sit()


# Animal is walking.
# Dog is running.
# Dog is sitting.

super関数を使うと、継承元のメソッドを呼び出すことができる。オーバーライドで継承元のメソッドに処理を追加したい場合に利用すると、同じ処理を記述しなくて済む。

class Animal(object):
    def __init__(self, name):
        self.name = name

class Dog(Animal):
    def __init__(self, name, stray=True):
        super().__init__(name)
        self.stray = stray

    def identified(self):
        print(self.name)
        print(self.stray)

dog = Dog(name='John')
dog.identified()


# John
# True

オブジェクト指向プログラミングでは、抽象クラスでインタフェースを規定して、そのクラスを継承した具象クラスで実装を記述することがよくある。

class Animal(object):
    def walk(self):
        pass

    def run(self):
        pass

class Dog(Animal):
    def walk(self):
        print('Dog is walking.')

    def run(self):
        print('Dog is running.')

def exercise(animal):
    animal.walk()
    animal.run()

dog = Dog()
exercise(dog)


# Dog is walking.
# Dog is running.

ただしこのままでは、プログラマーが具象クラスで実装を記述し忘れても、エラーを出してくれない。

class Animal(object):
    def walk(self):
        pass

    def run(self):
        pass

class Dog(Animal):
    # def walk(self):
    #     print('Dog is walking.')

    def run(self):
        print('Dog is running.')

def exercise(animal):
    animal.walk()
    animal.run()

dog = Dog()
exercise(dog)


# Dog is running.

ABCMetaクラスを継承し、@abc.abstractmethodを使うと、具象クラスでそのメソッドを実装し忘れた際にエラーを出してくれるようにできる。

import abc

class Animal(metaclass=abc.ABCMeta):
    @abc.abstractmethod
    def walk(self):
        pass

    @abc.abstractmethod
    def run(self):
        pass

class Dog(Animal):
    # def walk(self):
    #     print('Dog is walking.')

    def run(self):
        print('Dog is running.')

def exercise(animal):
    animal.walk()
    animal.run()

dog = Dog()
exercise(dog)


# TypeError: Can't instantiate abstract class Dog with abstract methods walk

ファイル操作

open関数を使うと、簡単なファイル操作ができる。

str_val = """\
AAA
BBB
CCC
DDD
EEE
FFF\
"""

# ファイルの書き込み
with open('test.txt', 'w') as f:
    f.write(str_val)

# ファイルの読み込み
with open('test.txt', 'r') as f:
    file = f.read()
print(file)

# ファイルの読み込み(一行ずつ)
with open('test.txt', 'r') as f:
    i = 0
    while True:
        line = f.readline()
        if not line:
            break
        print(f"{i}: {line}", end='')
        i += 1


# AAA
# BBB
# CCC
# DDD
# EEE
# FFF
# 0: AAA
# 1: BBB
# 2: CCC
# 3: DDD
# 4: EEE
# 5: FFF

作成したtest.txtファイルは、Google Colaboratoryのコンソールからも確認できる。

Notion Image

このファイルはGoogle Colaboratoryの接続解除をした際に一緒に削除される。

Notion Image

osやshutilなどのライブラリを使うと、その他の様々なファイル操作ができる。osライブラリのlistdirメソッドを使うと、カレントディレクトリのファイル・ディレクトリ一覧を取得できる。

import os
import pathlib
import shutil

os.listdir()


# ['.config', 'sample_data']

pathlibライブラリのtouchメソッドを使うと、空のファイルを作成できる。

pathlib.Path('empty.txt').touch()
os.listdir()


# ['.config', 'empty.txt', 'sample_data']

exists、isfile、isdirメソッドを使うと、存在確認やファイルかディレクトリかの判定ができる。

# ファイル・ディレクトリが存在するかの確認
print(f"exists: {os.path.exists('empty.txt')}")

# ファイルかどうかの確認
print(f"isfile: {os.path.isfile('empty.txt')}")

# ディレクトリかどうかの確認
print(f"isdir: {os.path.isdir('empty.txt')}")


# exists: True
# isfile: True
# isdir: False

shutilライブラリのcopyメソッドを使うと、ファイルのコピーが作成できる。

shutil.copy('empty.txt', 'empty_copy.txt')
os.listdir()


# ['.config', 'empty_copy.txt', 'empty.txt', 'sample_data']

osライブラリのrenameメソッドを使うと、ファイル名の変更ができる。

os.rename('empty.txt', 'rename.txt')
os.listdir()

# ['.config', 'empty_copy.txt', rename.txt', 'sample_data']

osライブラリのremoveメソッドを使うと、ファイルを削除できる。

os.remove('empty_copy.txt')
os.remove('rename.txt')
os.listdir()


# ['.config', 'sample_data']

osライブラリのmkdirメソッドを使うと、新しいディレクトリを作成できる。

# ディレクトリ作成
os.mkdir('test')
os.listdir()


# ['.config', 'test', 'sample_data']

osライブラリのrmdirメソッドを使うと、ディレクトリを削除できる。

os.rmdir('test')
os.listdir()


# ['.config', 'sample_data']

osライブラリのmakedirsメソッドを使うと、深い階層までディレクトリを再帰的に作成できる。

os.makedirs('level1/level2/level3/level4/level5')
os.listdir()


# ['.config', 'level1', 'sample_data']

shutilライブラリのrmtreeメソッドを使うと、ディレクトリを再帰的に削除できる。

shutil.rmtree('level1')
os.listdir()


# ['.config', 'sample_data']

モジュールとパッケージ

Pythonでは、外部ファイルに記載したモジュールをインポートして利用できる。今回は、以下の構成でパッケージを作成し、これを読み込んで利用する。


mypackage ├─__init__.py └─animal.py


Google Colaboratoryにファイルを一つ一つアップロードすることもできるが、面倒なので以下のコードを実行して、上記のパッケージ一式を作成する。

import os
import pathlib

# パッケージ用ディレクトリを作成
os.makedirs('mypackage')

# __init__.pyファイルを作成
pathlib.Path('mypackage/__init__.py').touch()

# パッケージの内容
animal_class = """\
import abc

class Animal(metaclass=abc.ABCMeta):
    @abc.abstractmethod
    def walk(self):
        pass

    @abc.abstractmethod
    def run(self):
        pass

class Dog(Animal):
    def walk(self):
        print('Dog is walking.')

    def run(self):
        print('Dog is running.')

def exercise(animal):
    animal.walk()
    animal.run()
"""

# ファイルの書き込み
with open('mypackage/animal.py', 'w') as f:
    f.write(animal_class)

作成したパッケージをインポートして利用する。インポートの仕方はいくつかあり、代表的なものをあげる。

① 標準的な記述方法。fromでパッケージ名を、importでファイル名を指定する。

from mypackage import animal

dog = animal.Dog()
animal.exercise(dog)


# Dog is walking.
# Dog is running.

② importのみの記述方法。ファイルまでのパスを.(ドット)で区切って指定する。

import mypackage.animal

dog = mypackage.animal.Dog()
mypackage.animal.exercise(dog)


# Dog is walking.
# Dog is running.

③ as以下にエイリアスを指定する方法。npやpdなどの一般によく使われているエイリアスを除き、どのモジュールを読み込んでいるか分かりづらくなるため多用するのは推奨しない。

from mypackage import animal as ani

dog = ani.Dog()
ani.exercise(dog)


# Dog is walking.
# Dog is running.

コードの読みやすさやどのパッケージを読み込んでいるのかのわかりやすさのバランスから、①のように記述することが多い。

その他

例外処理

ログ出力

付録A - isと==の違い

==は値自体を比較するのに対し、isはオブジェクトに紐づけられたidの同一性を比較する。

a = 1
print(id(a))
print(a == a)
print(a is a)


# 11122592
# True
# True
a = [1, 2, 3]
b = [1, 2, 3]
print(id(a))
print(id(b))
print(a == b)
print(a is b)


# 139954105767328
# 139954105760976
# True
# False

付録B - クロージャ

クロージャでは、関数内で関数を定義して、その関数を返り値として返す。クロージャを使うと、関数を段階的に呼び出すことができる。

def outer_func(a):
    def inner_func(b):
        return a + b
    return inner_func

r = outer_func(a=7)
r(b=6)


# 13

付録C - デコレータ

デコレータを使うと、関数をラップして自由に機能を拡張できる。

def dec_func(func):
    def wrapper(*args, **kwargs):
        print('start')
        result =  func(*args, **kwargs)
        print('end')
        return result
    return wrapper

def add_func(a, b):
    return a + b

f = dec_func(add_func)
f(1, 2)


# start
# end
# 3

@を使うと、シンプルに記述できる。

def dec_func(func):
    def wrapper(*args, **kwargs):
        print('start')
        result =  func(*args, **kwargs)
        print('end')
        return result
    return wrapper

@dec_func
def add_func(a, b):
    return a + b

add_func(1, 2)


# start
# end
# 3

付録D - ジェネレータ

ジェネレータを使うと、リストのように反復処理を記述できる。

def func():
    yield 1
    yield 2
    yield 3
    yield 4
    yield 5

for g in func():
    print(g)


# 1
# 2
# 3
# 4
# 5

next関数を使って呼び出すこともできる。

def func():
    yield 1
    yield 2
    yield 3
    yield 4
    yield 5


g = func()
print(next(g))
print(next(g))
print(next(g))
print(next(g))
print(next(g))


# 1
# 2
# 3
# 4
# 5

付録E - クラスメソッドとスタティックメソッド

@classmethodを使うと、オブジェクトに依存しないメソッドを定義でき、オブジェクトを作成することなく呼び出せる。

class Animal(object):
    @classmethod
    def walk(cls, name):
        print(f"{name} is walking.")

Animal.walk("Dog")


# Dog is walking.

@staticmethodを使うと、クラスに依存しないメソッドを定義でき、オブジェクトを作成することなく呼び出せる。クラスに依存しないため、クラス内の別のメソッドなどは利用できない。

class Animal(object):
    @staticmethod
    def walk(name):
        print(f"{name} is walking.")

Animal.walk("Dog")


# Dog is walking.

付録F - 特殊メソッド

__init__や__del__のように_(アンダースコア2つ)がついているメソッドは特殊メソッドと呼ばれていて、特殊メソッドにはその他にも__str__、__len__、__add__、__eq__などがある。

__str__メソッドはオブジェクトの文字列表現を返す。

class Dog(object):
    def __init__(self):
        pass

    def __str__(self):
        return 'DOG CLASS'

dog = Dog()
print(dog)


# DOG CLASS

__len__メソッドはオブジェクトの長さを返す。

class Dog(object):
    def __init__(self, name):
        self.name = name

    def __len__(self):
        return len(self.name)

dog = Dog(name='John')
len(dog)


# 4

__add__メソッドはオブジェクトの和の定義を記述できる。

class Dog(object):
    def __init__(self, name):
        self.name = name

    def __add__(self, dog):
        return self.name + '-' + dog.name

dog1 = Dog(name='John')
dog2 = Dog(name='Keats')
dog1 + dog2


# John-Keats

__eq__メソッドはオブジェクトの等価の定義を記述できる。

class Dog(object):
    def __init__(self, name):
        self.name = name

    def __eq__(self, dog):
        return self.name.lower() == dog.name.lower()

dog1 = Dog(name='john')
dog2 = Dog(name='JOHN')
dog1 == dog2


# True

参考資料


著者画像

ゆうき

2018/04からITエンジニアとして活動、2021/11から独立。主な使用言語はPython, TypeScript, SAS, etc.