2021-03-17に更新

【学習ノート】Python学習

読了目安:20分

概要

Pythonの言語学習におけるメモを残す。
主にC言語との違いが多い。

学習教材

  • TechAcademy Pythonコース

オブジェクト

メソッドと関数の違い

メソッドと関数の違いは、主に「.」(ドット)の有無。
「.」(ドット)は、助詞の「の」と読み替えることができる。

○○.△△  →  ○○ の △△を実行する
  • 関数
関数名(引数)

・メソッド

データ.メソッド名(引数)

標準のメソッド

Pythonにあらかじめ備わっているメソッドの例。

半角英字の操作

大文字、小文字に変換した文字を出力するメソッド。
メソッドは戻り値として出力するだけなので、コールしただけでは変数のデータは変更されない。(非破壊的メソッド

>>> a = "Text"
>>> print(a)
Text
>>> a.lower()
'text'
>>> print(a)
Text
>>> a.upper()
'TEXT'
>>> print(a)
Text
>>> a = a.upper()
>>> print(a)
TEXT

制御文

制御構造(分岐やループ)に関する内容。

インデント

Pythonはインデントも制御構造の仕組みの一つとなっている。
C言語がブレースでスコープを区切るのに対し、Pythonではインデントでスコープを区切っている。

正常なコード

以下のコードは、エラーが発生しない正常なコードである。

score = int(input("点数を入力してください:"))
if score >= 60:
    print("合格です")
    print("おめでとうございます!")

print("処理を終了します")

インデントを忘れた場合

if文のあとには、条件を満たした場合に実行する字下げされた命令が必要となる。
字下げをしなかった場合のコードを実行してみる。

score = int(input("点数を入力してください:"))
if score >= 60:
print("合格です")
print("おめでとうございます!")

print("処理を終了します")

字下げをしなかった場合はエラー(Indentationerror)になる。

  File "<ipython-input-13-31fa8ffec306>", line 3
    print("合格です")
    ^
IndentationError: expected an indented block

インデントが不適切な場合

また、字下げのレベルが不適切な場合もエラーになる。
たとえば、以下のコードのprint("おめでとうございます!")のインデントを、if文でもそれ以外でもないインデントにしてみる。

score = int(input("点数を入力してください:"))
if score >= 60:
    print("合格です")
  print("おめでとうございます!")

print("処理を終了します")

すると、どこにも当てはまらないインデントレベルの処理がエラーとなる。

  File "<tokenize>", line 4
    print("おめでとうございます!")
    ^
IndentationError: unindent does not match any outer indentation level

これについては、逐次処理においても同様である。

print("合格です")
    print("おめでとうございます!")
print("ありがとうございます?")

やはり、インデントの必要がないところでインデントした命令がエラーとなる。

  File "<ipython-input-12-91a21539abf4>", line 2
    print("おめでとうございます!")
    ^
IndentationError: unexpected indent

if文

if文における注意点

elif

C言語ではelse if ~であったが、Pythonはelif ~となる。(プリプロセスみたい)

こんな感じ。

season = input("季節を入力してください:")

if season == "春":
    print("春はあけぼの")
elif season == "夏":
    print("夏は夜")
elif season == "秋":
    print("秋は夕暮れ")
elif season == "冬":
    print("冬はつとめて")
else:
    print("エラー")

print("処理を終了します")

else if ~と書くとエラーになってしまう。

season = input("季節を入力してください:")

if season == "春":
    print("春はあけぼの")
elif season == "夏":
    print("夏は夜")
elif season == "秋":
    print("秋は夕暮れ")
else if season == "冬":
    print("冬はつとめて")
else:
    print("エラー")

print("処理を終了します")

実行結果:

  File "<ipython-input-31-ec4d3fac25a4>", line 9
    else if season == "冬":
         ^
SyntaxError: invalid syntax

for文

繰り返し構造のうち、for文について説明する。

in

in は、その後ろに続く要素から1件ずつ取り出して変数に格納する。

数字の列挙

例えば、数字が並んでいる場合。

for i in 0, 1, 2:
    print(i)

数字を1つずつ、iに代入して繰り返している。

0
1
2

range()を使った場合(引数が1つ)

また、数字の列挙であればrange()が便利である。
ただし、range()は単体で数字の列挙を出力する関数というわけではないので注意。

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

引数が1つの場合、range()は0から引数で指定された数値までを列挙する。
また、forループはrange()で列挙された数値をすべて使用するまで繰り返される。

0
1
2
3
4

range()を使った場合(引数が2つ)

引数が2つの場合、第1引数の数値から、第2引数未満の数値を列挙する。

for i in range(3, 5):
    print(i)

この場合、列挙する範囲は「3以上5未満」である。

3
4

range()を使った場合(引数が3つ)

引数が3つの場合、1つめと2つめは同様で、3つめは増分を示す。

for i in range(1, 5, 2):
    print(i)

この場合、列挙する範囲は「3以上5未満で増分2」である。

1
3

また、増分を負の数(デクリメント方向)にすることも可能(範囲指定に注意)。

for i in range(5, 1, -2):
    print(i)

この場合、列挙する範囲は「5以下1より大きく増分-2」である。

5
3

try文

「例外」が発生した場合に対応するための制御文。

エラーの種別

  1. 文法エラー :ソースコードの記述に問題がある。
  2. 実行エラー(例外) :処理内容に問題がある。(キャストの失敗など)

エラー発生時の対応

実行エラー(例外)が発生すると、プログラムは処理を終了してしまう。
しかし、try文を用いることで、上記エラーが発生しても処理を継続することができる。(try文が終了したあとの処理も)

try文の使い方

ざっくり以下のとおりである。

try:
    エラー有無の条件となる処理
except エラー名:
    指定したエラーが発生に対する処理
except:
    それ以外のエラーに対する処理
else:
    エラー未発生時の処理
finally:
    エラー有無に関わらず必ず実行する処理

エラー有無の条件となる処理の例:

score = int(input("数値を入力してください:"))

キーボードから数値以外が入力されると、キャストに失敗し、実行エラー(例外)が発生する。

指定のエラーの例:

except ValueError:
    print("エラー:数値を入力してください")

値に関するエラーであるValueErrorなどがある。
参考:https://www.sejuku.net/blog/72383
except:よりも前に記述しないと、先に左記に当てはまって実行されないので注意すること。

シーケンス

テキストシーケンス型

配列に近いが、それ以上の役割がある。
参考:https://techacademy.jp/magazine/19204

文字列のスライス

文字列のうち、特定の文字や文字列を取り出す操作をスライスという。

文字のスライス

実際に文字列を用意して、スライスしてみる。

s = "abcdefghijklmnopqrstuvwxyz"

print(s[0])
print(s[1])
print(s[2])

C言語でいう配列と同じ要領で扱われる。

a
b
c

ただし、文字列の持つ領域を超えるとIndexError(インデックスエラー)となる。

print(s[25])
print(s[26])

文字数は26字であったため、27番目(s[27])をスライスするとエラーとなる。

z
---------------------------------------------------------------------------
IndexError                                Traceback (most recent call last)
<ipython-input-6-7ba20cf8c27e> in <module>
      1 print(s[25])
----> 2 print(s[26])

IndexError: string index out of range

C言語は配列の変数にインデックスを付けたものは単なる簡便法であり、変数の持つ領域を超えてもアクセスは可能である。
これに対してPythonのように領域を超えた場合にエラーとなるのはバグを防ぐのに有効である。つまり優秀。

マイナスにすると末端から順にアクセスできる。
この辺はスーパーマリオの感覚。(左端は右端とつながっている)
ただし、負方向も存在しない領域にアクセスするとインデックスエラーとなるので注意。

print(s[0])
print(s[-1])
print(s[-2])
print(s[-26])
print(s[-27])
print(s[-28])
a
z
y
a
---------------------------------------------------------------------------
IndexError                                Traceback (most recent call last)
<ipython-input-8-6bd2365f764f> in <module>
      3 print(s[-2])
      4 print(s[-26])
----> 5 print(s[-27])
      6 print(s[-28])

IndexError: string index out of range

文字列のスライス(連続)

一部の文字列をスライスする場合は、以下のように記述する。

文字列変数名[開始位置:終了位置] 

このとき、取り出される文字列の位置は 開始位置 から 終了位置 - 1 である。(終了位置が紛らわしいけど)
このあたりは range() と同じ要領。

print(s[0:7])
print(s[7:14])
print(s[14:7])
print(s[-5:-1])
print(s[-1:-5])

ただし、開始位置と終了位置の大小関係を間違えると、文字列はスライスされないため注意。
正の数、負の数のいずれも、取り出せる方向は同じ(左から右)であると考えれば良い。

abcdefg
hijklmn

vwxy

また、開始位置や終了位置は省略が可能。
このときも、文字列を取り出す方向は同じであることをイメージすること。

print(s[:10])
print(s[10:])
print(s[-5:])
print(s[:-20])
abcdefghij
klmnopqrstuvwxyz
vwxyz
abcdef

開始位置と終了位置の両方を省略すると、すべての文字列が出力される。

print(s[:])
abcdefghijklmnopqrstuvwxyz

文字列のスライス(増分指定)

増分を指定することで、「何個おきに」取り出すといった操作ができるようになる。

print(s[0:10:2])
print(s[1:20:3])
acegi
behknqt

なんと、増分を負方向に指定することで、逆方向の取り出しが可能。

print(s[-1:-6:-1])
print(s[-1:-10:-2])
zyxwv
zxvtr

増分を指定する記法の場合も、開始位置・終了位置・増分量の省略が可能である。

print(s[::-1])
print(s[::])
zyxwvutsrqponmlkjihgfedcba
abcdefghijklmnopqrstuvwxyz

リスト

シーケンスと似ているけど、違うもの。
この辺も見とくと良い:
https://docs.python.org/ja/3.8/tutorial/datastructures.html#more-on-lists

リスト型

リスト型の変数の値は [ と ] で囲んで表現される。

a = [2, 3, 4]
print(type(a))
print(a)

どうやらリスト型というものがあるらしく、[ と ] を使って表現される。

<class 'list'>
[2, 3, 4]

数値と文字(文字列)の混在もできるみたいだが、明確な意図がない限りは 数値だけ、文字列だけ、のように1種類のみで要素を構成させることが推奨されるらしい。

b = [3, "test", 7, "hoge", "fuga", 10]
print(b)
[3, 'test', 7, 'hoge', 'fuga', 10]

空のリストも作成できる。(メリットがあるかはしらんけど)

c = []
print(c)
[]

list()を使った宣言

シーケンスを使用してリスト変数を作成する場合は、list()を使うと楽みたい。

d = list("Test")
e = list(range(3))
print(d)
print(e)

文字列やrange()がシーケンスであることは「シーケンス」の章を参照。

['T', 'e', 's', 't']
[0, 1, 2]

リストのスライス

シーケンスのスライスと同様に、リストも要素を取り出すことをスライスするという。
リスト変数内の要素の取り出しかた(アクセス方法)は、シーケンスの場合と同様である。

リスト変数名[番号]
リスト変数名[開始位置:終了位置]
リスト変数名[開始位置:終了位置:増分]

こんなかんじ。

b = [3, "test", 7, "hoge", "fuga", 10]
b[0:5:2]

[3, 7, 'fuga']

ただし、空のリストには要素がないため、スライスはできない。

c = []
c[]
  File "<ipython-input-36-763d39ca79da>", line 2
    c[]
      ^
SyntaxError: invalid syntax

内包表記

リスト作成時に、制御文を組み合わせることができる。

for文

for文を使ってリストを作成することができる。

list1 = [i for i in range(11)]
list2 = [(i * 2) for i in range(11)]

print(list1)
print(list2)

for i in range(11)で繰り返すときのiの値を作成し、iまたは(i * 2)に代入したものを要素としてリストを作成している。

[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
[0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20]

for文+if文

for文とif文を組み合わせたリスト作成も可能。
if文はfor文よりも後に記述すること。

list3 = [i for i in range(11) if i % 3 == 0]
list4 = [i for i in range(11) if i % 2 == 0 and i % 3 == 0]
print(list3)
print(list4)

for文の繰り返しに対し、if文の条件を満たす場合だけ、最初の式iに代入してリストを作成する処理である。
論理演算式も使用できる。

[0, 3, 6, 9]
[0, 6]

for文+if-else文

if-else文を使用する場合だけ特殊で、for文よりも前に書く必要がある。

list5 = ["even" if i % 2 == 0 else "odd" for i in range(11)]
print(list5)

条件を満たす場合はeven、そうでないときはoddであり、for文の条件に従って繰り返す。

['even', 'odd', 'even', 'odd', 'even', 'odd', 'even', 'odd', 'even', 'odd', 'even']

elifは内包することができない。
3つ以上の条件を指定する場合は、以下のような書き方となる。

list6 = ["low" if i < 3 else "mid" if i < 8 else "hi" for i in range(11)]
print(list6)
['low', 'low', 'low', 'mid', 'mid', 'mid', 'mid', 'mid', 'hi', 'hi', 'hi']~~~~

リストの管理(CRUD )

リストは作りっぱなしじゃなく、データの変更が可能。
CRUDは、以下の言葉からなる。

  • Create:新規追加
  • Read:参照
  • Update:更新
  • Delete:削除

Create:新規追加

リストの最後尾に要素を追加するには、リストがもつメソッドのひとつappend()を使う。
参考:https://www.tech-teacher.jp/blog/python-append/

リストの途中に要素を追加するには、insert()メソッドを使う。
参考:https://techacademy.jp/magazine/22300

リストを結合する場合は、++=を使う。
繰り返しの場合は、**=を使う。
このあたりは、文字列の結合や繰り返しと同じイメージ。
参考:https://techacademy.jp/magazine/29364
参考:https://www.javadrive.jp/python/list/index6.html#section5

Read:参照

要素を参照する方法として、以下の2つがある。

  1. スライス:省略
  2. アンパック
    アンパックは以下のページを参考。
    要素数と変数の数が一致していない場合、エラー(ValueError)が発生するので注意。
    参考:https://techacademy.jp/magazine/30050

Update:更新

スライスを使用して、要素を上書きします。(専用のメソッドはない)
詳細な手順は省略。

Delete:削除

リストの要素は以下の方法で削除できる。(いずれも破壊的メソッド

  1. pop()メソッドを使用
    引数なし:一番最後の要素を削除し、メソッドの戻り値として出力する。
    引数あり:指定した要素の削除と出力を行う。(もちろん0から開始)

  2. clear()メソッドを使用
    すべての要素を削除します。メソッドの戻り値はなし。

リストの探索

リストの中に指定の文字があるかどうかなどを調べる。
探索の仕方として、count()メソッド、index()メソッド、inがある。
参考:https://techacademy.jp/magazine/18930

リストの並び替え

並び替えはsort()メソッド、reverse()メソッドが簡単。(破壊的メソッド)
非破壊的にリストを扱うにはsorted()関数を使用すること。

2次元リスト

C言語の2次元配列と同じように考えてよい。
C言語でいう{ }は、Pythonでは[ ]で表現される。
行、列といったスコープ概念も、C言語とPythonで同じである。
★「行、列」と覚えるよりも、シンタックスのスコープ順で参照する認識にしておいたほうが、多次元配列において理解がしやすい。

append()メソッドの引数をリスト型のデータにすれば、二次元配列としてリスト要素が追加されていく。
また、C言語と違って要素数の異なるリストを扱うことができる。

タプル

型を変更できないリストのようなもの。
スライスしてアクセスする場合にその効果が発揮されるのかも。

宣言

リスト:[ ]で囲む
タプル:( )で囲む

ただし、要素数が1つの場合は注意が必要。

a = (1)     # int型
a = (1,)    # tuple型

tuple()

リストではlist()を使っていたが、タプルではtuple()を使う。
引数はシーケンス(同じ)。

タプル変数のアクセス

参照

リストと同様、スライスを使う。

変更

要素の追加・消去などはできないが、値を代入して変数の値を変更することは可能。
値の代入については、タプル型の値を代入するとその値で上書きが可能であるが、スライスによる代入はエラーとなる。

a = (0, 1, 2, 3, 4)
print(a)
a = (0, 3, 5)
print(a)

a[0] = 10
(0, 1, 2, 3, 4)
(0, 3, 5)
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-12-5072b42910db> in <module>
      4 print(a)
      5 
----> 6 a[0] = 10

TypeError: 'tuple' object does not support item assignment

探索

リストと同様にcount()index()inが使用可能。

zip()

複数のリストから1つのタプルを作成する。
戻り値はタプル型。
行と列の関係が特殊なので注意。(単なる連結ではない)

list()の引数にすると

そのリストがタプル型になるみたい。(タプルを格納したリスト)
構造的には以下のような感じ。

[ ( ), ( ), ( )]

enumerate()

リストを引数に、タプルを格納したリストを生成することができるみたい。

Pythonのオブジェクト認識

オブジェクトのID

変数等のオブジェクトにはIDが割り振られている。
これはC言語のような変数自体を識別するものではなく、値の識別と考えたほうが良い。
値が同じ変数は、異なる変数でもあっても同じIDとなる。
ただし、タプルは同じ要素であったとしても異なるIDを持つ。
調べ方:

id(オブジェクト名)

オブジェクトのハッシュ値

オブジェクトの値の重複は、オブジェクトのハッシュ値で判断される。
オブジェクトが異なるものでも、中身のデータが完全に一致していれば同じハッシュ値になる 。
調べ方:

hash(オブジェクト名)

セット変数を作成する際に、set()に同じ値のタプルを引数にした場合には、作成される要素は一つとなる。
このときになぜ異なるIDであるのに同じ値であるかを判断できるかというと、ハッシュ値で判断されていたから。
ハッシュ値はデータの値に応じて決まるもので、タプルであっても同じ値であれば、同じハッシュ値を持つ。

==とis

2つの変数を比較する場合、==isで比較結果が異なる場合がある。

演算子 比較する値 比較の内容
== ハッシュ値 データが同じかどうか
is ID 同じオブジェクトかどうか

オブジェクトと値は異なる存在であると認識したほうが良いのかも。(確認すること)

辞書

値の参照

インデックス(数字)ではなく、キー名(要素につける名前)を指定してアクセスする。

辞書の作成

要素はキー:値で記述し、{ }で囲む。
キー名は文字列以外にも、数字で定義しても良い。

d = {"foo": 1, "foo": 3, "bar": 5, "hoge": 7}
print(d)

キーが重複する場合、そのキーの値は上書きされてしまうことに注意すること。

{'foo': 3, 'bar': 5, 'hoge': 7}

タプルも要素として定義できるみたい。
タプルは、要素の値が変化しないからである。
一方、値が変化するリストはキーにすることができない。

キーの追加

以下の形式で記述することで、要素を追加することができる。

変数[キー] = 値

例えばこんなかんじ。

dic={}
print(type(dic))
print(dic)

dic["test"] = 1
dic["hoge"] = "hogege"
dic[5] = "日本語"
print(type(dic))
print(dic)

dic["test"] = "OK"
print(dic)

定義済みのキーがある場合は、上書きされる。

<class 'dict'>
{}
<class 'dict'>
{'test': 1, 'hoge': 'hogege', 5: '日本語'}
{'test': 'OK', 'hoge': 'hogege', 5: '日本語'}

キーの参照

2つの方法がある。
どちらにするかは、例外処理の要否で決めれば良い。
キーがタプルの場合も、タプルを渡せばよい。

①スライス

print(dic["hoge"])
print(dic["super"])

キーが存在しない場合は「KeyError」となる。

hogege
---------------------------------------------------------------------------
KeyError                                  Traceback (most recent call last)
<ipython-input-5-0cb8b3fad786> in <module>
      1 print(dic["hoge"])
----> 2 print(dic["super"])

KeyError: 'super'

get()メソッド

print(dic.get("hoge"))
print(dic.get("fuga"))

キーが存在しない場合には何も起こらず、エラーにもならない。(print()ではNoneと出力される)

hogege
None

キーの削除

2つの方法がある。

pop()メソッド

引数にキーを渡すこと。

dic={}
dic["test"] = 1
dic["hoge"] = "hogege"
dic[5] = "日本語"
print(dic)

dic_p = dic.pop("hoge")
print(dic_p)
print(dic)
{'test': 1, 'hoge': 'hogege', 5: '日本語'}
hogege
{'test': 1, 5: '日本語'}

引数を渡さなかった場合は、TypeErrorとなる。

dic_p = dic.pop()
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-12-040e53f5bd46> in <module>
----> 1 dic_p = dic.pop()

TypeError: pop expected at least 1 argument, got 0

clear()メソッド

他の型と同じで、すべての要素をクリアする。

dic={}
dic["test"] = 1
dic["hoge"] = "hogege"
dic[5] = "日本語"
print(dic)

dic.clear()
print(dic)
{'test': 1, 'hoge': 'hogege', 5: '日本語'}
{}

キーの検索

inを使用する。(省略)

繰り返し処理への利用

辞書型には以下のメソッドがある。(詳細は省略)

  • keys()
  • values()
  • items()

これらのメソッドは、返し値をシーケンスとして扱うことができる。

dic = {'Hokkaido': 1, 'Aomori': 2, 'Tokyo': 13, 'Osaka': 27}
print(dic.items())

for key, value in dic.items():
    print(f"{key} : {value}")

dic.items()では、各要素をタプルとして出力している。
そのため、forで使用する場合は、タプルの形式と合うように2つの変数を用いていることに注意する。

dict_items([('Hokkaido', 1), ('Aomori', 2), ('Tokyo', 13), ('Osaka', 27)])
Hokkaido : 1
Aomori : 2
Tokyo : 13
Osaka : 27

関数

他のファイルの関数を使う

ファイルの先頭でインポートする。
ただし、拡張子までは必要なく、ファイル名.pyをインクルードする場合は、以下のとおりでよい。

import ファイル名

C言語はソース(.c)とヘッダ(.h)が存在するが、この時点でPythonは「*.pyのみ」と考えてよい。

ファイルをインポートした場合、インポートしたファイルはオブジェクトとして扱われる。
つまり、以下のように扱われる。
* ファイル → オブジェクト
* 関数 → メソッド

C言語とは勝手が異なるので、このあたりは気をつけること。

一部の関数だけインポートする

他のファイルの関数をメソッドではなく、関数として扱う場合は、以下の記述でインポートする。

from ファイル名 import 関数名

メソッドではなくなるため、関数コールを直接記述することができる。

モジュール名や関数名に別名をつける

インポートしたオブジェクトやメソッド、関数の名称が長い場合や、可読性が低い場合に有効。
インポート文のあとに、as 別名を加えることで、インポートしたものを別名で扱うことができる。

import ファイル名 as 別名        # オブジェクトを別名で読み替え(メソッド名はそのまま)
from ファイル名 import 関数名    #  関数を別名で読み替え
ツイッターでシェア
みんなに共有、忘れないようにメモ

caliglacy

組み込み系エンジニアです。 最近はWeb系にも興味があり、学習記録を残すことを目的としてCrieitを使っています。

Crieitは誰でも投稿できるサービスです。 是非記事の投稿をお願いします。どんな軽い内容でも投稿できます。

また、「こんな記事が読みたいけど見つからない!」という方は是非記事投稿リクエストボードへ!

有料記事を販売できるようになりました!

こじんまりと作業ログやメモ、進捗を書き残しておきたい方はボード機能をご利用ください。
ボードとは?

コメント

in