Python 試験


≫ 参考資料



 <ここから>

Chapter6 テキストの処理 >  323頁 6. 正規表現を扱う ─ re

 

<振り返り>

 


第1章 Pythonの特徴

・コンパイルが不要

 

・インタープリター

 ・インタープリタがモジュールを検索するバスをモジュール検索パスという

 ・インタープリタが検索する順番は「ビルトインモジュール」→「sys.path変数で得られるディレクトリ」

 ・ビルトインモジュールとは組み込み関数のあるモジュールのこと。print()は組み込み関数で、正式には__builtins__.print()

 ・シンボリックリンクを置いてあるディレクトリはモジュール検索パスには入らない

 ・対話モードの終了方法には、関数の入力「quit()」によるものと、キー操作によるもの「ctrl + z」とがある

 ・「python -m timeit -h」を実行すると、timeitモジュールの詳細が出力さ

 ・対話モードでは、直前に評価した式が「_」に代入され、次の式で使用することができる 例 1 + 1; _ + 1

 

・処理のまとまりを表すとき、インデントを用いる

 

・Pythonという名の由来は英国BBCの番組「Monty Python's Flying Circus」にある

 

・他のプログラム言語で書かれたプログラムを使って機能拡張できる

 

・対話モード

 ・WindowsではコマンドプロンプトあるいはPowerShellで「python」と入力

 ・macOSではTerminal.appで「python3」と入力

 ・起動すると

  ・バージョン番号に続いてヘルプおよび著作権情報などを確認するためのコマンドが表示される

  ・一次プロンプトは「>>>」二次プロンプトは「...」

  ・二次プロンプトが表示されるとき、インデントは自動挿入されない

 ・Windows環境ではPythonの環境変数PATHに設定されていないと対話モードが起動されない

  Pythonをインストールする際の「Add Python 3.x to PATH」にチェックを入れる

  対話モードが起動しない場合はPythonの再インストールを行う

 

・文字コード

 ・ソースコードファイルはデフォルトではUTF-8でエンコードされたものとして扱う

 ・UTF-8以外の文字コードを使用するにはプログラムの先頭に以下を追加する。以下はshift_jisの場合

  # -*- coding: shift_jis -*-

 

・多重代入

 ・一度に複数の変数に値を代入する操作

a = 10
b = 20
a, b = b, a
print('%s %s' %(a, b))

Result

20 10

 

》補足

# タプルとリストを代入
a, b = (100, 200), [100, 200]
print('%s %s' %(a, b))

Result

(100, 200) [100, 200]

 

# 複数の変数に同じ値を代入
a = b = c = 100
print('%s %s %s' %(a, b, c))

Result

100 100 100

 

・コーディングスタイル

 ・可能であれがコメントは独立した行に書く

 ・ソースコードの幅が79文字を超えないように折り返す

 ・インデントにタブを使わず、空白4つをつかうことが推奨されている

 ・Pythonがプログラム言語として推奨しているスタイルガイドは通称「PEP8」と呼ばれる

  名前は「Python Enhancement Proposalsの8番目」であることが由来

 

・文字列の記述

 ・シングルクォート「'」、ダブルクォート「”」を使う

 ・関数やクラスの説明を書く際はトリプルクォート「'''」および「"""」を使う

 

・コメントの記述

 ・ハッシュ文字「#」を使う

 

・入力履歴ファイル

 ・自動生成されるファイル

 ・ファイル名「.python_history」

 ・ユーザーディレクトリーに作成される

 ・対話モードでカーソルキーの上下を押すと履歴として表示される

 

》補足

・モジュール検索パス

 ・インタープリタはimportしたモジュールを以下の順番で探す。

  ・ビルトインモジュール (インタープリタの組込モジュール) の中

  ・sys.path 変数で得られるディレクトリのリスト

 ・sys.path は以下の場所で初期化されている。

  ・入力スクリプトのあるディレクトリ

   ファイル名が指定されていない場合はカレントディレクトリ

  ・PYTHONPATH (ディレクトリ名のリスト)

  ・インストールごとのデフォルト

 ・シンボリックリンクを置いてあるディレクトリはモジュール検索パスに入らない。

 ・sys.path は初期化後にプログラムから改変可能。

 ・実行中のスクリプトのあるディレクトリは、検索パスの最初に置かれる。

  ・検索順序は標準ライブラリのパスより前。

   (注意)標準ライブラリのモジュールと同名のスクリプトを置くと、そちらが優先的にロードされてしまう。

Chapter2 コーディング規約

・PEP8

 ・コードのレイアウト

  ・インデント

   ・スペース4つ

   ・関数定義の引数などのカッコ内部で改行した場合、縦に揃える

   ・辞書の要素は2行目スペース4つめから。閉じカッコは変数名に縦に揃える

  ・空行

   ・トップレベルの関数やクラスの間は2行空ける

   ・クラス内のメソッドの定義は1行空ける

  ・import文

   ・異なるモジュールはimport文を分ける

   ・次の順番でグループ化する(グループ間は1行空ける)

    1.標準ライブラリ

    2.サードパーティに関するもの

    3.ローカルのライブラリ

  ・空白文字

   ・代入演算子や比較演算子などの両側に1つ空白文字を入れる

   ・カンマの後ろに空白文字を入れる

   ・閉じカッコなど終わりを表す文字の前に空白文字を入れない

 ・コメント

  ・ブロックコメント

   ・コードと同じインデントで書く

   ・コメント自体は1つの#と1つの空白の後ろに書く

  ・インラインコメント

   ・コードとコメントの間に2つ以上のスペースを書く

   ・コメント自体は1つの#と1つの空白の後ろに書く

  ・docstring

   ・関数やメソッドの説明はdefの直後に書く

   ・"""で始まり、"""で終わる行とする

   ・概要の詳細の間に1行空ける

 ・命名規約

  ・パッケージ、モジュール - 小文字のみ

   クラス - 単語の先頭を大文字

   関数、変数、メソッド、インスタンス変数 - 小文字のみ。必要におうじてアンダースコアで区切る

   定数 - 大文字のみ

   例外 - クラスと同様 + 例外の名前の最後に "Error" をつけるべき

 ・PEP8のチェックツール Flake8

 ・PEP8の整形ツール Black


第2章 テキストと数の操作

・算術演算子の順序

 ・優先順位:べき乗「**」、乗算/除算、加算/減算の順になる

 ・除算の結果は浮動小数点になる 例「100 - 5**2 + 5 / 5 」の結果は「76.0」になる

 ・相手が整数でも浮動小数点で算術すると結果も浮動小数点になる 例「10 % 2.0 」の結果は「0.0」になる

 ・// は切り捨て除算。商の少数部分を切り捨てて整数部分を求める

 ・% は剰余。剰余とは商のあまり

 ・「48 // 6 // 4」は左から演算が行われ演算結果は「48 // 6 = 8 → 8 // 4 = 2」

  「2 ** 1 ** 3」は右から演算が行われ演算結果は「1 ** 3 = 1 → 2 ** 1 = 2」

 

・文字列リテラルの連結

 ・文字列リテラルとはシングルクォートあるいはダブルクオートで囲まれた値を指す

 ・print('Py thon')を実行した結果からは間のスペースが除外されPythonと出力される

 ・複数行で記述する場合は () で囲んで改行しながら列挙する

text = (
  "Usage: "
  "-h help "
  "-v version"
)
print(text)

Result

Usage: -h help -v version

 

・改行文字

 ・改行は\nで表され、1つの文字として扱われる。これを改行文字という

 ・トリプルクォート「"""」を使うとリテラル中の改行が改行文字として反映される

# 空白がそのまま出力。改行(\n)も出力される
text = """aaa
  bbb
    ccc\n  dd
"""
print(text)

Result

aaa

  bbb

    ccc

  dd

 

・len関数

 ・文字列の長さを返す

 

・文字列のスライス

 ・スライスとは指定した範囲の文字列を切り取る操作

 ・文字列 [ 開始位置 : 終了位置 ]

 ・スライスの位置    | a  |  b  |  c  |  d  |  e  |  f  |  g  |

  整数での指定の場合  0     1      2      3      4      5      6      7 

  負数での指定の場合   -7    -6     -5     -4     -3     -2    -1

 

》補足

word = "abcdefg"
print(word[2:5])   # cde
print(word[-5:-2]) # cde
print(word[:2])    # ab
print(word[2:-2])  # cde
print(word[::2])   # aceg
print(word[::-1])  # gfedcba

 

 ・f文字列(フォーマット済み文字列リテラル) ※Python3.6で導入

 ・f"文字列{値:書式指定文字列}" 例 price = 15000; print(f"価格:{price:7d}") ※07dで0埋めされる

 ・f文字列を使うことで文字列の中に式の値を入れられるようになる 例 a = 1; b = 1; print(f'{a + b}')

 ・式の後にフォーマット指定子のオプションを追加することで式の値にさまざまな書式を設定できる

 ・フォーマット指定子

  b  :値を2進数で出力する

  d  :値を10進数で出力する

  x  :値を16進数で出力する

  f   :値を与えられた精度まで少数で出力(.5fで少数第5位で丸める)

  % :値を100倍しパーセント記号が付いた形式で出力

 

》補足

name = "alice"
age = 30
print(f"Hello {name}. You are {age} years old.")

Result

Hello alice. You are 30 years old.

 

# フォーマット指定子
price = 15000
print(f"価格:{price:7d}")   # 数値7桁 右寄せ 空白埋め
print(f"価格:{price:07d}")  # 0埋め

text = "hello"
print(f"'{text:10s}'")  # 文字列10桁 左寄せ 空白埋め

Result

価格: 15000

価格:0015000

'hello '

 

・formatメソッド ※Python3.6以前の方法

 ・"文字列{Field1}{Field2}{Field3}・・・".format(引数1, 引数2, 引数3)  Field:フォーマットフィールド {インデックス:書式指定}

 ・記述方法には3種類ある

  ①フォーマットフィールドに引数のインデックスを記述

   "spam: {0}, ham: {1}, eggs: {2}".format(x, y, z)

  ②空のフォーマットフィールドを使う。その場合は引数の値が順番に埋め込まれる

   "spam: {}, ham: {}, eggs: {}".format(x, y, z)

  ③フォーマットフィールドにキーワード引数を記述 

   "spam: {a}, ham: {b}, eggs: {c}".format(a=x, b=y, c=z)  この場合、キーワード引数はa, b, c

  ※フォーマットフィールドに引数の変数名を直接記述することはできない

 

》補足

# フォーマットフィールドにフォーマット指定子を指定
price = 15000
quantity = 100
print('数量:{1:05d}, 料金:{0:010.3f}'.format(price, quantity))  # インデックスは引数の並び順でなくてもOK

Result

数量:00100, 料金:015000.000

# 文字列の繰り返し
print (3 * "y", "y" * 3, "ab" * 3)

Result

yyy yyy ababab

# 文字列はスライスで参照できるが更新はできない
str = 'abc'
print(str[0])  # a
str[0] = 'A'   # TypeError     
# 文字列も大小があり、大文字のほうが小文字より小さい。大小はunicodeで判定しord関数で取得できる
print(ord('A'))  # 65
print(ord('a'))  # 97

Chapter6 テキストの処理

・一般的な文字列操作

 ・文字列のチェックメソッド

isalnum() 文字列が数字と文字のみの場合にTrueを返す
isalpha() 文字列が文字のみの場合にTrueを返す。日本語等の非ASCII文字列でも数字や記号を含まなければTrueを返す
isdecimal() 文字列が10進数を表す場合にTrueを返す
isdigit() 文字列が数字を表す文字のみの場合にTrueを返す
isidentifier() 識別子(変数名、関数名など)として使用できる文字列の場合にTrueを返す
islower() 文字列がすべて小文字の場合にTrueを返す
isnumeric() 数を表す文字列の場合にTrueを返す。漢数字なども含まれる
isprintable() 印字可能な文字列の場合にTrueを返す
isspace() スペース、タブなどの空白文字mの場合にTrueを返す
istitle() 先頭のみ大文字であとは小文字の文字列の場合にTrueを返す
isupper() 文字列がすべて大文字の場合にTrueを返す
'a123'.isalnum()       # True
'あいう'.isalpha()     # True
'100'.isdecimal()      # True
'①②③'.isdigit()     # True
'a123'.isidentifier()  # True
'abcd'.islower()       # True
'100'.isnumeric()      # True
'abcd'.isprintable()   # True
'    '.isspace()       # True
'A123'.istitle()       # True
'A100'.isupper()       # True

 

 ・文字列の変換を行う

upper()

文字列をすべて大文字に変換

lower() 文字列をすべて小文字に変換
swapcase() 大文字を小文字に、小文字を大文字に変換
capitalize() 先頭1文字を大文字に、それ以外を小文字に変換
title() 単語ごとに大文字1文字+小文字の形式に変換
replace(old, new[, count]) oldをnewに変換した文字列を返す。countが指定された場合は先頭から指定された数だけ変換
strip([chars]) 文字列の先頭および末尾から指定した文字をすべて除去。charsが指定されてない場合は空白文字が削除される
lstrip([chars]) 文字列の先頭から指定した文字をすべて除去。charsが指定されていない場合は空白文字が削除される
rstrip([chars]) 文字列の末尾から指定した文字をすべて除去。charsが指定されていない場合は空白文字が削除される
zfill(width)

長さがwidthになるように左に0を詰めた文字列に変換

removeprefix(prefix) 文字列の先頭からprefixで指定した文字列を除去
removesuffix(prefix) 文字列の末尾からsuffixで指定した文字列を除去
'hello world'.capitalize()             # Hello world
'hello world'.title()                  # Hello World
'hello world'.strip('hd')              # ello worl
'hello'.zfill(8)                       # 000hello
'hello_world'.removeprefix('hello_')   # world

 

 ・その他の文字列メソッド

find(sub[, start[, end]]) 文字列中にsubが出現する位置を返す(戻り値:int)。存在しない場合は-1を返す
split(sep=None, maxsplit=-1)

文字列を分割する(戻り値:listで)。

デフォルトでは空白文字(半角スペース、全角スペース、改行、タブなど)で分割

join(iterable)  引数として指定した複数の文字列を結合(戻り値:str)
startswith(prefix[, start[, end]])

指定した接頭辞を持つ文字列かを調べる(戻り値:bool)。prefixにはタプルで複数の候補が指定できる。

start、endは調査する位置の指定に使用する

endswith(suffix[, start[, end]])  指定した接尾辞を持つ文字列かを調べる(戻り値:bool)。suffixにはタプルで複数の候補が指定できる。

start、endは調査する位置の指定に使用する

encode(encoding="utf-8", errors="strict")

 文字列をencodingに指定したエンコード形式に変換する。errorsでは変換できない文字列があった場合の対応方法を記述。strictの場合は例外が発生し、ignoreの場合はその文字を無視し、replaceの場合は?に変換する

'abac'.find('a', 1)            # 2
ls = "aa,bb,cc".split(',', 1)  # ['aa', 'bb,cc']
','.join(ls)                   # 'aa,bb,cc'
'abac'.startswith(('a', 'b'))  # True
'abac'.endswith(('b', 'c'))    # True

 

 ・文字列定数

import string

print(string.ascii_lowercase) # 英小文字:abcdefghijklmnopqrstuvwxyz print(string.ascii_uppercase) # 英大文字:ABCDEFGHIJKLMNOPQRSTUVWXYZ print(string.ascii_letters) # ascii_lowercase と ascii_uppercase を組み合わせたもの print(string.digits) # 10進数:0123456789 print(string.hexdigits) # 16進数:0123456789abcdefABCDEF print(string.octdigits) # 08進数:01234567 print(string.punctuation) # 記号:!"#$%&'()*+,-./:;<=>?@[\]^_`{|}~ print(string.whitespace) # 空白:スペース (space)、タブ (tab)、改行 (linefeed)、復帰 (return)、改頁 (formfeed) など print(string.printable) # ascii_letters, digits, punctuation, whitespace を組み合わせたもの

 

 ・f-string(フォーマット済み文字列リテラル)

  ・{変数名=}で「変数名=値」の形式で出力される

var = "abc"
print(f'{var=}')

Result

var='abc'

 

・フォーマットの指定方法

 ・「:」の後ろに文字列変換のためのフォーマットを指定

:<30 /:>30 /:^30 指定した幅(ここでは30)で左寄せ /右寄せ /中央ぞろえする
:-<30 /:->30 /:-^30 左寄せ /右寄せ /中央ぞろえでスペースのかわりに指定した文字(ここでは-)で穴埋め
:b /:o /:d /:x /:X 2進数 /8進数 /10進数 /16進数(小文字) /16進数(大文字)に変換
:f 固定小数点の文字列に変換
:%

百分率での表記に変換

:, 数値に3桁ごとにカンマを挿入
:6.2f 表示する桁数を指定(ここでは6は全体の桁数、2は小数点以下の桁数)
:%Y-%m-%d %H:%M:%S 日付型特有の書式で、年月日などに変換
word = "abc"
f"{word:<5}"   # 'abc  '
f"{word:^5}"   # ' abc '
f"{word:-^7}"  # '--abc--'
num = 0.045;   f"{num:6.2%}"  # ' 4.50%'
num = 1234567; f"{num:,}"     # '1,234,567'

 

 ・%演算子

  ・「文字列 % 値」のように書く。文字列リテラル中の値を埋め込む場合には%で始まる変換指定子を書く

'abc=%d' % 123    # 'abc=123'
'abc=%s' % 'def'  # 'abc=def'
a = 2; b = 3; '%d * %d = %d' % (a, b, a * b)  # '2 * 3 = 6'
'%(name)s likes %(language)s' % {'name': 'Taro', 'language': 'Python'}  # 'Taro likes Python'

 

・正規表現


第3章 リストの操作

・リストはPythonで複数の値を扱うための基本的なデータ構造

 

・データの種類は、整数、浮動小数点、文字列など

 

・リストの構造

           0    1    2    3    4 正のインデックス

  data = [ 1,   2,   3,   4,   5] 一つ一つが要素

       -5   -4   -3   -2   -1 負のインデックス

 

 ・角括弧(かくかっこ)とカンマを使う

 ・文字列と同様にインデックスで要素を参照

 ・文字列とは異なりインデックスで更新することが可能

 ・先頭のインデックスは0を使用

 ・末尾のインデックスは要素数-1

 

・リストのスライス範囲

 ・[ 開始位置:終了位置 ] で記述

 ・開始位置を省略すると先頭からになる

 ・終了位置を省略すると末尾までになる

 ・data[:] や data[1000:]、data[:1000]としてもエラーにならない

 

・リストに要素を追加

 ・リスト名.append(値) または リスト名 += [値]

 

・リストの長さを取得

 ・リストの長さとは要素数のこと。len関数で取得できる

 

・リストの入れ子

 ・リストの要素に別のリストを持つことを入れ子にするという [ [ 1, 2 ], [ 2, 3] ] 要素数は2

 

・入れ子の参照方法

 ・先頭の要素にインデックス[0]を使用。[ [ 1, 2 ], [ 2, 3] ]の [ 1, 2 ]のインデックスは[0]。 [ 1, 2 ]の1は[0][0]

 

・リストをスタックとして使う方法

 ・スタックとは複数の値を扱えるでデータ構造の1つ

 ・リストと違うのは「要素を挿入するときも取り出すときも末尾から」という点(last-in, first-out)

 ・append(要素)で末尾に追加、pop()で末尾から削除

stack = [1, 2, 3, 4]
data = []
while stack:
  data.append(stack.pop())
print(stack)
print(data)

Result

[]

[4, 3, 2, 1]

 

・リストをキューとして使う方法

 ・スタックとは複数の値を扱えるでデータ構造の1つ

 ・リストと違うのは「要素を挿入するとき末尾に追加れさ、取り出すときは先頭から削除される」(first-in, first-out)

 ・append(要素)で末尾に追加、pop(0)で先頭の要素を削除

que = [1, 2, 3, 4]
data = []
while que:
  data.append(que.pop(0))
print(que)
print(data)

Result

[]

[1, 2, 3, 4]

 

・リスト内包

 ・リストの角括弧内でfor文を使って記述したもの

 ・書式 [ 式 for 変数 in リストなど ]

data = []
for i in [1, 2, 3]:
  for j in [1, 2]:
    if i != j:
      data.append((i, j))
 print(data)

上記をリスト内包を使って記述

# 2重の内包。後のfor文が内側のループになる
print([(i, j) for i in [1, 2, 3] for j in [1, 2] if i != j])

Result

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

》補足:リストの更新

x = ['a', 'b', 'c']

x.append('d') # 末尾に要素を追加 ['a', 'b', 'c', 'd'] x.insert(2, 'd') # 指定したインデックスに要素を追加 ['a', 'b', 'd', 'c'] x.sort() # 要素を昇順にソート y = sorted(x) # ソート後のリスト生成。元リストは変更されない x.sort(reverse=True) # 要素を降順にソート ['c', 'b', 'a'] x[1] = 'd' # 要素を変更 ['a', 'd', 'c'] x.remove('b') # 左側から検索して最初の要素を削除 ['a', 'c'] del x[2] # 指定したインデックスを削除 ['a', 'b'] y = x.pop(0) # 指定したインデックスの要素を取り出す x=['b', 'c'] y=a

Chapter3 Python言語使用

・内包表記

  ・リスト内包表記の構文 

  ・[変数を使った処理 for 変数 in イテラブルオブジェクト]

  ・[変数を使った処理 for 変数 in イテラブルオブジェクト if 条件式]

 

 ・内容表記の可読性

  ・map()やfilter()といった組み込み関数があるが、それらの関数より内包表記を使うほうが可読性の高いコードが書ける

   map関数 :リストなどの各要素に何らかの関数を適用し別のオブジェクトを作成

   filter関数 :リストなどの各要素に何らかの関数を適用し、関数の戻り値がTrueとなる要素だけからなるオブジェクトを作成

arr = [1.4, 2.0, 3.5, 2.25, 1.98]

list(map(round, arr))    # arrの中身を1つずつround関数で丸めたものをリストにする
[round(n) for n in arr]  # 上記を内包表記で置き換えたもの

list(map(round, filter(lambda n: n > 2, arr)))  # map、filterは第1引数に関数をとるため、lambdaを使用しているが可読性が下がる
[round(n) for n in arr if n > 2]                # 上記を内包表記で置き換えたもの

第4章 判定と繰り返し

《 判定 》

・整数の真偽の判定

 ・0が偽、それ以外は真

・短絡演算子

 ・A and B の場合、Aを判定して偽であればBを評価せずAの評価結果を返す。Aの判定が真であればBを評価してその評価結果を返す

 ・and がずっと続く場合、「最初に偽」になった評価結果を返す。すべて真の場合は最後の評価結果を返す

 ・or がずっと続く場合、「最初に真」になった評価結果を返す。すべて偽の場合は最後の評価結果を返す

var1 = 0 and 1 and 2   # 最初の 0 の評価結果を返す
var2 = 1 and 1 and 2 # 最後の 2 の評価結果を返す var2 = 0 or 1 or 2 # 2番目の 1 の評価結果返す print(var1, var2, var3)

Result

0 2 1

 

・None

 ・値が何も存在しない状態を表す

 ・最も適切な判定は if val is None:

 ・if val == None: でも判定できるが is None: より実行時間がかかるため最適とはいえない

 

《 繰り返し 》

・range関数

 ・range(stop) または range(start, stop) または range(start, stop, step)

 ・start, stop, step には整数を指定

 ・startから始まりstopの直前まで、stepごとに整数を順番に取得

 ・start省略時は0から始まる

 ・step省略時は1ごと

 

・for文

 ・breakはループを直ちに終了する

 ・continueは現在のループを中止して次のループに進む

 ・for文でディクショナリのキーと値を同時に取得するには itemsメソッドを使う

# itemsメソッドでディクショナリからkeyとvalueを取得
data = {'case':'orga', 'cnt':0.0}
for key, val in data.items():
    print(key, val)

Result

case orga

cnt 0.0

 

 ・for文とenumerate関数を組み合わせると反復可能体(繰り返し可能なもの)からインデックスと要素を同時に取得できる

   反復可能体:リスト、タプル、ディクショナリ、文字列などのデータ型

# enumerate関数で反復可能体からインデックスと要素を同時に取得
for i, c in enumerate("AB"):
    print(i, c)

Result

0 A

1 B

# ディレクトリに対するenumerateの返し値は、keyがenumerateの生成したインデックス番号、valueがディレクトリのkeyを返す
dic = {'A1':'red','A2':'blue','A3':'white'}
for key, val in enumerate(dic):
    print(key, val)

Result

0 A1

1 A2

2 A3

 

 ・for文とzip関数を使うと複数の反復可能体から並列で要素を取得できる

for n, c in zip([1,2,3],["1","2","3"]):
    print(c, n, c*n)   # c*n:文字列のn倍は文字列をn個並べたものになる

Result

1 1 1

2 2 22

3 3 333

 

# その他のアンパックパターン
data = [[1, 3, 5], [4, 9, 25], [8, 27, 125]]
res = [[row[i] for row in data] for i in range(3)]  # 後者のfor文の繰り返し回数毎に、前者のfor文の全要素を処理
print(res)

res = list(zip(*data))  # これでも上記と同じ結果が得られる

Result

[[1, 4, 8], [3, 9, 27], [5, 25, 125]]

 

・sorted関数とreversed関数

 ・sorted関数を使うと昇順にソートしたリストを取得できる

 ・reversed関数を使うとリストや文字列などから逆順にしたオブジェクトを取得できる

print(sorted("EAT"))
print(reversed(sorted("EAT")))  # このままでは確認できないので下記のようにリスト化する
print(list(reversed(sorted("EAT"))))

Result

['A', 'E', 'T']

<list_reverseiterator object at 0x0000029A484DB970>

['T', 'E', 'A']

 

》補足

・イテレータとは

 リストなどの複数の要素をもったデータ型に対して、順番にデータを取り出す機能

 for文ではリストを引数に使用した際に内部的に実行される

 

・イテラブル(反復可能体)とは

 for文などで繰り返し可能なオブジェクト。リストやタプルなど

 

・for-else構文

 ・forループが正常に完了するとelseブロックが実行される

 ・forループが一度も実行されなかった場合でもelseブロックは実行される

 ・break文でループが途中で終了した場合、elseブロックは実行されない

for n in range(2, 10):
    for x in range(2 ,n):
        if n % x == 0:
            print(n, 'equals', x, '*', n//x)
            break    # 内側のループを抜ける
    else:
        print(n,'is a prime number')
        continue
    break    # 外側のループを抜ける

Result

2 is a prime number

3 is a prime number

4 equals 2 * 2


第5章 関数

・関数の定義

 ・関数に定義する引数を「仮引数(parameter)」と呼ぶ

 ・def 関数名(仮引数1, 仮引数2, …)

 

・関数の呼び出し

 ・関数を呼び出す際に指定する引数を「実引数(argument)」と呼ぶ

 ・関数名(実引数1, 実引数2, …)

 ・関数呼び出しで()は省略できない

 ・関数そのものを変数に代入して呼び出す方法もある

def greeting(a):
    return (a)

x = greeting  # 関数を変数に代入
y = x("Hello")
print(y)

Result

Hello

 

・関数の変数

 ・ローカル変数は、その関数内で定義する必要がある

 ・同名の変数が関数の内側と外側で定義されていても、それぞれ別の変数として扱われる

 ・関数内で定義されている変数は関数の外側では参照できない

 

・global・nonlocal  

 ・グローバル変数はモジュールのトップレベルで定義される変数のこと。この変数はモジュール内の任意の位置から参照できる。

  関数内でグローバル変数に値を代入するにはglobal文で示す必要がある

x = 100  # グローバル変数

def do_global():
    global x  # グローバル変数であることを示す
    x = 200

do_global()
print(x)

Result

200

 

 ・関数の中で別の関数を定義し、その内部から外部の関数の変数を変更するにはnonlocal 文で示す必要がある

def outer():
    x = 10  # outer関数のスコープにある変数
    def inner():
        nonlocal x  # outer関数のxを変更する
        x = 20
    inner()
    print(x)  

outer()

 

・関数の引数

  ・キーワード引数

  ・「キーワード=値」をキーワード引数という

  ・値だけの引数を位置引数という

  ・キーワード引数の後に位置引数は与えられない

  ・存在しないキーワード引数は与えられない

  ・位置引数で指定済みの引数に対して、キーワード引数を指定できない

  ・関数定義が位置引数に対して、関数呼び出し側をキーワード引数で設定すれば引数を記述する順番は任意

 

 ・引数のデフォルト値

  ・def 関数名(引数=デフォルト値):

  ・引数にはデフォルト値を設定できる

  ・関数の実行時に値を与えられなくてもデフォルト値が関数の中で使われる

  ・仮引数にデフォルト値を指定したら、それ以降の仮引数にもデフォルト値を指定する必要がある

  ・引数のデフォルト値は関数が定義された時点で評価され、関数の実行時に再評価はされない

def_message_1 = "Hello"

def message(message_1=def_message_1, message_2=""):  # 関数が定義された時点で評価
    print(f"{message_1} {message_2}")

def_message_1 = "こんにちは"  # デフォルト値には影響しない
message_2 = "word"

message(message_2=message_2) # 関数の実行時に再評価はされない

Result

Hello word

 

  ・引数のデフォルト値をリストにすると、その引数に値が加えられるたびにデフォルト値も変更される

def func(num, def_list=[]):
    def_list.append(num)
    return def_list

print(func(1))           # デフォルト値が[1]に変更される
print(func(2, [3, 4]))   # デフォルト値なし。[3, 4]に2が加えられる
print(func(3))           # デフォルト値[1]に3が加わり、デフォルト値が[1, 3]に変更される

Result

[1]

[3, 4, 2]

[1, 3]

 

 ・「*」がついた仮引数

  ・*  が先頭に付いた仮引数には、指定済みの実引数を除く位置引数のリストおよびタプルが入る

  ・**が先頭に付いた仮引数には、指定済みの実引数を除くキーワード引数のディクショナリが入る

def func(name, *args, **kwargs):   # 「*」がついた仮引数。argsはタプル
    print(name, args, kwargs)

func("abc", "def", "ghi", kw1="123", kw2="345")

Result

abc ('def', 'ghi') {'kw1': '123', 'kw2': '345'}

 

 ・「*」がついた実引数(アンパックと呼ぶ)

  ・リストおよびタプルは、「*」を先頭に付けることで要素を位置引数に展開して関数に渡せる

  ・ディクショナリは「**」を先頭に付けることで、キーワード引数として指定できる

  ・アンパックとはリストやタプルから1つ1つの要素を取り出して変数などに代入することをいう

def concat(*args, sep="/"): # args:実引数はリスト型だが受け取った仮引数はタプル 
    return sep.join(args)

words = ["spam", "ham", "eggs"]
option = {"sep": "&"}
print(concat(*words, **option))    # 「*」がついた実引数(アンパックと呼ぶ)

Result

spam&ham&eggs

 

・lambda(ラムダ)式

 ・lambda 引数: 式

 ・無名関数と呼ばれる種類の関数

 ・単一の式しか持てない

 ・式の結果が返り値になる

func = lambda a, b: (b + 1, a *2)
x, y = 1, 2
x, y = func(x, y)
print(x, y)

Result

3 2

 

 ・sortに関数を渡してリストのソート基準を設定。関数にlambdaを使って各要素のインデックス「1」でソート

# リスト
lst = [(3, 'b'),(1, 'c'),(2, 'a')]
lst.sort(key =lambda arg : arg[1])
print(lst)

Result

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

# ディクショナリー
a = {"apple": 5, "banana": 2, "cherry": 8}
b = sorted(a.items(), key=lambda x: x[1])
print(b)

Result

[('banana', 2), ('apple', 5), ('cherry', 8)]

 

 ・map関数はfor文を使わなくてもリストの全要素にアクセスできる map(lamba 引数: 戻り値, iterable)

lst = list(map(lambda a: a**3, range(3)))
print(lst)

Result

[0, 1, 8]

 

》補足

・ドキュメンテーション文字列(docstring)

 ・docstringはクラスや関数を説明するためのコメント

 ・1行目にはクラスや関数の目的などを書く。1行空けて呼び出し方や引数などの詳細を書く

 ・docstringは__doc__という属性で参照できる

def func(x, y):
    """ 2つの引数を乗算する関数
    
    x, yは整数であること
    """
    return x * y

print(func.__doc__)

 

・関数アノテーション

 ・関数の引数や返り値にアノテーション(注釈)を記述できる

 ・型は関数アノテーション、詳しい説明文はdocstringと併用して記述する場合が多い

 ・引数名のあとの : 式がそれぞれの引数に対するアノテーション

 ・) と末尾の : の間の -> 式が返り値に対するアノテーション

 ・デフォルト値はアノテーションのあとに記述する

 ・アノテーションはあくまで注釈で、それをもとに特別な処理が行われることはない

 ・アノテーションとして型を指定することもできるが、実行時に型チェックが行われたりはしない

 ・関数アノテーションは__annotations__属性に辞書(dict)として格納されている

def func(x: 'for_x', y: 'for_y' = 1) -> 'for_return':
    return x * y

print(func('abc', 3))

print(func.__annotations__, func.__annotations__['x'])

Result

abcabcabc

{'x': 'for_x', 'y': 'for_y', 'return': 'for_return'} for_x

 

・名前空間

 ・変数や関数の名前が所属している場所のこと。 変数や関数が所属する場所、つまりモジュールやクラスが名前空間になる。
  異なる名前空間にある同じ名前のオブジェクトには何の関係もない

Chapter3 Python言語使用

・関数の引数

 ・位置引数

  ・関数呼び出し時に先頭から順に引数の位置を対応させて渡される

  ・仮引数と実引数の数を一致させる必要がある

 ・キーワード引数

  ・実引数を「kwarg=value」という形式で仮引数名を指定して渡す

  ・関数定義の仮引数の順番どおりに実引数を渡す必要がない

 ・位置引数とキーワード引数の混在

  ・関数呼び出し時、位置引数とキーワード引数が混在する場合、位置引数を先に置く必要がある

 ・デフォルト値付き引数

  ・関数呼び出し時、実引数を省略したときに使用されるデフォルト値を仮引数に設定できる

  ・デフォルト値は変更不可なオブジェクト(文字列、数値、タプル)を指定する

  ・仮引数にデフォルト値を指定したら、それ以降の仮引数にもデフォルト値を指定する必要がある

def sample_func(param1=1, param2, param3=3):  # Param2がSyntaxErrorになる
    print(f'{param1}, {param2}, {param3}')

 ・可変長位置引数

  ・仮引数に「*」を付けると可変長の位置引数(任意の数の引数)を定義できる

  ・慣例として仮引数には*argsという名前が使われることが多い

  ・*argsは複数の引数をタプルとして受け取る

  ・*argsはほかの位置引数よりも後ろに指定する必要がある

  ・可変長位置引数の前にデフォルト値付き引数を定義しない

  ・可変長位置引数に対応した組み込み関数ではリストやタプルに「*」を付けて渡す

lst = [1, 2, 3]
print(*lst) 

Result

1 2 3

 ・可変長キーワード引数

  ・仮引数に「**」を付けると可変長のキーワード引数を定義できる

  ・慣例として仮引数には**kwargsという名前が使われることが多い

  ・**kwargsは複数のキーワード引数を辞書として受け取る

  ・**kwargsは複数は一番最後に指定する必要がある

 ・上記4つを混在させる場合の順番

  1. 位置引数

  2. 可変長位置引数

  3. デフォルト値付き引数

  4. 可変長キーワード引数

 ・キーワード専用引数

  ・キーワードを強要したい際などに使う

  ・「*」のあとに定義された引数は、キーワード引数として指定しなければ呼び出せない

def sample_func(param1, *, keyword1):
    print(f'{param1}, {keyword1}')

sample_func(1, keyword1=False)

 ・位置専用引数

  ・「/」の前に定義された引数は、位置引数として指定しなければ呼び出せない

def add(x, y, /):
    return x + y

add(1, 2)

 

・アンパック

 ・タプルやリスト、辞書などの複数の要素を持つものを展開して、複数の変数に代入できる

tp = (1, 2, 3)
a, b, c = tp                 # print(a, b, c) → 1 2 3

di = {'a':1, 'b':2, 'c':3}
a, b, c = di                 # print(a, b, c) → a b c
a, b, c = di.values()        # print(a, b, c) → 1 2 3

tp = (1, 2, (3, 4, 5))
a, b, c = tp                 # print(a, b, c) → 1 2 (3, 4, 5)
a, b, (c, d, e) = tp         # print(a, b, c, d, e) → 1 2 3 4 5
a, b, c, d, e = tp           # ValueError 

 ・アスタリスクを使ったアンパック

  ・代入される側の変数名の前に「*(アスタリスク)」を付けると、要素がその変数にまとめて代入される

  ・「*」付ける変数はどの変数でも問題ないが1つの変数にしか適用できない

tp = (1, 2, 3, 4)
a, *b, c = tp       # print(a, b, c) → 1 [2, 3] 4
*a, b, c = tp       # print(a, b, c) → [1, 2] 3 4
*a, *b, c = tp      # SyntaxError
a = [[1, 2], [4, 5]]
print( *[y for x in a for y in x], sep="\n")

Result

1

2

4

5

 ・関数の引数のアンパック

  ・関数の実引数でタプルやリストを渡す際に「*」を付けると、中身を展開して渡すことができる

def sample_func(param1, param2, param3):
    print(f'{param1}, {param2}, {param3}')  # 1, 2, 3

args = [1, 2, 3]
sample_func(*args) 

  ・辞書を展開して仮引数に渡す場合は実引数に「*」を2つ付ける

def display_user(name, age, email):
    print(f'{name}, {age}, {email}')

user = {'name': 'John', 'age': 30, 'email': 'John@example.com'}
display_user(**user)  # キーワード引数での呼び出しと同じ display_user(name='John', age=30, email='John@example.com')

 

Chapter5 型ヒント

 

・型ヒントはアノテーションであるため、プログラムの実行時に型はチェックされない

・型ヒントはコードの書きやすさ、読みやすさ、そしてバグを防止するためにある

 

・変数への型付け

name: str = "たろう"  # 変数nameはstr型
age: int = 9          # 変数ageはint型
student: bool = True  # 変数studentはbool型

・関数の引数、戻り値の型付け

def five_years_later(age: int) -> int:               # 関数の引数と戻り値にint型を指定
def five_years_later_students(age: int = 7) -> int:  # 引数のデフォルト値を指定
def say_hello(name: str) -> None:                    # 戻り値がない場合、Noneを指定 

・コンテナーの型付け

hobby: list = ["ゲーム", "マンガ"]
food: tuple = ("バナナ", "ハンバーグ")
favorite: dict = {"study": "プログラミング", "movie": "モンティパイソン"}
like_num: set = {1, 3, 5}

・コンテナー内の要素の型付け

hobby: list[str] = ["ゲーム", "マンガ"]  # listの要素の型にstrを指定
favorite: dict[str, str] = {"study": "プログラミング", "movie": "モンティパイソン"}  # dictのキーと値の型にstrを指定
like_num: set[int] = {1, 3, 5}  # setの要素の型をintで定義 

 ・タプル要素への型付けする際の注意

  ・タプル要素のすべてに対して型を指定する必要がある

  ・同じ型の場合は、「...」で繰り返しを省略できる tuple[str, ...]

 

・ユーザー定義クラスを型として利用する

 ・自分で定義したクラスを型として利用できる

from dataclasses import dataclass
from operator import attrgetter

@dataclass
class Book:
    name: str
    author: str
    price: int

legend_python = Book("伝説のPython", "unknown", 1280)

def books_a_bargain(book_list: list[Book]) -> Book:  # Bookオブジェクトのリストを受け取り、Bookオブジェクトを返す
    """priceでソートして一番安い本を返す"""
    return sorted(book_list, key=attrgetter("price"))[0]

py_books = [
    Book("ハッカーガイド", "terapyon", 2992),
    Book("ゼロから", "takanori", 3200),
    Book("スタートブック", "shingo", 2750),
]

value_book: Book = books_a_bargain(py_books)  # value_bookをBookクラスで型付け
print(value_book)  # Book(name='スタートブック', author='shingo', price=2750)

 

・typingモジュール

 ・型ヒントをサポートする標準ライブラリ。さまざまな型を付けることができる

 ・関数やその戻り値の想定外の利用によるバグを防ぐことができ、生産性が向上する

 ・IDEやエディターでの入力補完があり、実装のスピードが加速する

 

 ・Union 複数の型を指定できる

from typing import Union

def address_code(number: Union[int, str]) -> int:
    pass

your_code: int = address_code(1000001)  # 数値を渡せる
my_code: int = address_code("1000001")  # 文字列を渡せる

 ・Optional 指定した型とNoneを指定できる

from typing import Optional

price: Optional[int]

 ・Python 3.10からは複数の型を許可する「型名 | 型名」と書くことができる。typingモジュールのインポートは不要

  Python 3.7から3.9でも「from __future__ import annotations」で「|」が利用できる

def address_code(number: int | str) -> int:
    pass

price: int | None  price:

 ・Literal 特定の値のみを許可する定義

from typing import Literal

FILETYPE = Literal["csv", "json", "xml"]  #  ["csv", "json", "xml"]のみを許可
def access_file(file: str, file_type: FILETYPE):
    pass

access_file("wheather.csv", "csv")    # FILETYPEに含まれる値のためOK
access_file("wheather.html", "html")  # FILETYPEに含まれない値のためNG

 

 ・Any 任意の型を許可する

  ・Anyで定義しておくことであらゆる型を受け付けることができる

from typing import Any

# 型付けされていない関数の戻り値を受け取る
user_input: Any = util_valid(args)  

# 関数の引数や戻り値にAnyを指定する
def process_by_type(user_input: Any) -> Any:  
    if isinstance(user_input, str):
        pass

 

 ・TypedDict 辞書のキーと値の型を指定する

  ・TypedDictとdataclassの使い分けとして、辞書として利用したい場合はTypedDictを、クラスとして利用したい場合にはdataclassを使うとよい

from typing import TypedDict

class BookDict(TypedDict):  # TypedDictを継承したクラス
    name: str
    author: str
    price: int

fav_book: BookDict = {"name": "スタートブック", "author": “shingo”, "price": 2750}  # BookDict辞書クラスで型付けした辞書を作成

 

・静的型チェックツール ☆ 試験対象外 

 ・Pythonは動的型付け言語(変数の型を実行時に決定する方式で、プログラマーが明示的に変数の型を宣言する必要がない)

 ・型ヒントの利用に不整合があっても、プログラム実行時に型ヒントを使ったチェックはされずエラーにならない

 ・不整合があるコードに対して、静的型チェックツール(mypy)を利用することで不整合を検出できる

 

 ・mypy

  ・mypyのインストール pip install mypy

  ・mypyは、型ヒントが正しく利用されているかチェックし、エラーを出力する

  ・エラーメッセージは左から順に「ファイル名:行番号: エラー種別: エラー内容」が出力

# エラーがある場合
$ mypy mypy_check.py
mypy_check.py:2: error: Incompatible types in assignment (expression has type "int", variable has type "str")  # age: int = "18"

# エラーがない場合
$ mypy mypy_check_ok.py
Success: no issues found in 1 source file

  ・mypyのオプション

  ・mypyのオプションは、設定ファイルやコマンドラインで利用できる

   コマンドライン:--disallow-any-generics 

   設定ファイル :  disallow_any_generics

  ・厳しくするオプションとゆるくするオプションがある

  ・各オプションをTrueに設定すると有効になる

 


第6章 その他のコレクションの操作

・collections.deque

 ・キューのように扱えるデータ構造

 ・append() でキューの末尾に要素を追加

 ・pop()     でキューの末尾の要素を取り出す

 ・appendleft()  でキューの先頭に要素を追加

 ・popleft()   でキューの先頭の要素を取り出す

# deque(デック) スタックやキューとして利用するならリストより高速
from collections import deque

d1 = deque([1, 2])
d1.append(3)
d1.popleft()
d1

Result

deque([2, 3])

 

・タプル

 ・タプルの定義は、カンマ区切りの要素を括弧「()」で囲む tup = ("spam", "ham")

 ・() を省略しても定義可能 tup = "spam", "ham"

 ・要素が1つの場合は末尾にカンマを書く(要素が1でカンマがないと文字列になる) tup = "spam", 

 ・tuple関数を使う場合、その引数はリストなどの反復可能体である必要がある tup = tuple(["spam", "ham"])

 

・リストとタプル

 ・リストとタプルには数値や文字列など異なるデータ型の要素を格納できる

 ・リストは既に存在する要素の値を変更可能だが、タプルは変更不可

 ・リストとタプルはlen関数で要素数を取得できる

 

・set

 ・setとは集合を扱うデータ型のこと

 ・要素を重複しないように保持する

 ・2つのset同士で和集合や差集合といった集合演算を行える

 ・set関数によって定義する

 ・addメソッドで要素を追加できるが、追加した順序を保持しない

 ・removeメソッドで要素を削除できる

 ・集合(メソッドも使用可能)

  s1 - s2 差集合(s1からs2の要素を除いた集合) differenceメソッド  

  s1 ^ s2 対称差集合(どちらか一方にだけ含まれる要素の集合) symmetric_differenceメソッド

  s1 | s2 和集合 unionメソッド

  s1 & s2 積集合 intersectionメソッド

se1 = set([2, 3, 1])
se2 = set([3, 1, 4, 4])
se1.add(0)
se1.remove(3)
print(se1, se2)                                  # {0, 1, 2} {1, 3, 4}
print(se1 - se2, se1.difference(se2))            # 差集合 {0, 2}
print(se1 ^ se2, se1.symmetric_difference(se2))  # 対称差集合 {0, 2, 3, 4}
print(se1 | se2, se1.union(se2))                 # 和集合 {0, 1, 2, 3, 4}
print(se1 & se2, se1.intersection(se2))          # 積集合 {1}
print(1 in se1)                                  # in演算子 True

 

 ・ディクショナリ

 ・変数 = {キー1: 値1, キー2: 値2, …}

 ・リストはインデックスを使って参照・更新するのに対して、ディクショナリではキーを使用する

 ・キーには文字列、タプル、数値も指定可能。可変体であるリストはキーに指定できない

 ・ディクショナリは変更可能(mutable)であるが、キーの型は変更不能(immutable)であり、その値は一意でなければならない

 ・要素の追加 ディクショナリ[キー] = 値 値の変更も同様

 ・要素の削除 del ディクショナリ[キー]

 ・in演算子を使ったキーの存在確認

  ・ディクショナリに対してin演算子を使う

  ・ディクショナリのkeysメソッドでkeyの一覧を取得。これに対してin演算子を使う

  ・list関数にディクショナリを指定するとキーを要素とするリストが生成される。これに対してin演算子を使う

dic = {"apple": 120, "banana": 150}

dic["orange"] = 100
del dic["banana"]

print(dic)                                 # {'apple': 120, 'orange': 100}
print([x for x in dic.keys()])             # ['apple', 'orange']
print([x for x in dic.values()])           # [120, 100]
print([(x, y) for x, y in dic.items()])    # [('apple', 120), ('orange', 100)]

print("apple" in dic)                      # True
print("apple" in dic.keys(), dic.keys())   # True dict_keys(['apple', 'orange'])
print("apple" in list(dic), list(dic))     # True ['apple', 'orange']

 

 ・ディクショナリの内包表記

   ・{キーの式: 値の式 for 変数 in 反復可能体}

dic = {x: x**3 for x in (1, 3, 6)}
print(dic, type(dic))

Result

{1: 1, 3: 27, 6: 216} <class 'dict'>

 

》補足

・初期化

l = []     # 空リスト
t = ()     # 空タプル
d = {}     # 空ディクショナリ
s = set()  # 空集合
print(type(l), type(t), type(d), type(s))

Result

<class 'list'> <class 'tuple'> <class 'dict'> <class 'set'>


第7章 モジュール

・インポート

 ・インタープリターや別のファイルを利用できる仕組みをインポートという

 ・インポートされるファイルをモジュールという

 ・モジュールをインポート 

  ・import モジュール名

 ・インポートしたモジュールの関数を実行 

  ・モジュール名.関数名()

 ・関数のみをインポート 

  ・from モジュール名 import 関数名

 ・名前をすべてインポート 

  ・from モジュール import * 

  ・すべての名前(関数や変数)が使える

  ・アンダースコアー「_」で始まる名前はインポートされない

  ・モジュールの名前はインポートされない

  ・モジュールに__all__という変数のリストが存在した場合は、そのリスト内の名前だけインポートされる 

  ・as を使ったインポート

  ・as でモジュールや関数を別名でインポートできる

  ・モジュールの別名でインポート

   ・import モジュール名 as モジュールの別名

  ・関数の別名でインポート。モジュール名はインポートされない

   ・from モジュール名 import 関数名 as 関数の別名

  ・関数の別名はモジュール名と同じ名前でも大丈夫

 

・メインモジュールか否かの判定

 ・メインモジュールとはスクリプトとして実行された.pyファイルのこと

 ・__name__はモジュールの属性であり、モジュール名が自動で代入される

 ・メインモジュールで実行された場合、"__main__"が代入される

 ・インポートされたモジュールはメインモジュールではない

if __name__ == "_main__" :
    print("Main Module") 

 

・__init__.py

 ・ディレクトリに__init__.pyというファイルを配置すると、ディレクトリ名のモジュールとしてインポートされる

 ・このような構成をパッケージという

 ・パッケージにすることで別の.pyファイルをまとめて扱える。この別のファイルをパッケージのサブモジュールという

 ・__init__.pyではサブモジュールをインポートできない

 ・__init__.pyからサブモジュールをインポートするには、通常、相対インポートで記述する

 ・相対インポート

  ・同じ階層から別サブモジュールをインポート     from .  import 別サブモジュール

  ・同じ階層から別サブモジュールの名前をインポート  from .別サブモジュール import 名前

  ・1つ上の階層から別サブモジュールをインポート   from .. import 別サブモジュール

  ・1つ上の階層の別サブモジュールの名前をインポート from ..別サブモジュール import 名前

bookcard/
    __init__.py
    dump.py
    load/
        __init__.py
        core.py

 

》補足

・dir関数

 ・dir() はそのオブジェクトの属性(変数や関数、オブジェクトなど)をリストで返してくれる

import math
print(dir(math))

Result

['__doc__', '__loader__', '__name__', ... , 'tau', 'trunc', 'ulp']

 


第8章 ファイル入出力

・open関数のモード

 ・open("ファイル名", mode="モード")

 ・r: 読み込み専用(デフォルト) r+:読み書き両用 w:新規書き込み a:追加書き込み b:バイナリモード

 ・wb:バイナリモードで新規書き込み ab:バイナリモードで追加書き込み rb:バイナリモードで読み込み

 

 

・ファイルを閉じる処理

 ・open関数の返り値であるファイルオブジェクトは、ファイルの処理後に閉じる必要がある

 ・閉じるにはcloseメソッドを使用する

 ・with文を使うことでこの閉じる処理が自動で行われる。この処理は例外が発生した時にも行われる

  ・with open("ファイル名") as ファイルオブジェクト変数:

 ・1つのファイルオブジェクトでwithを使っても、別のファイルオブジェクトが閉じられることはない

 ・プログラムが終了する際に全てのファイルオブジェクトが閉じられる。このためすぐ終了する場合は閉じる処理を省略できる

with open("weekly.csv") as fp:
    s = fp.read()
print(s)

 

 ・ファイルを読み込み

 ・ファイルを1行ずつ読み込む for s in fp:

 ・ファイルオブジェクトはリストと同様な反復可能体の1つなのでfor文で繰り返すことができる

 ・以下も1行ずつ処理できるが、ファイル内容をすべて読み込んでから行ごとに分けるため上記にくらべメモリを消費するので避けるべき

  ・for s in readlines(): または for s in list(fp):

 ・fp.read() が処理するのは「ファイル全体の文字列」になる。そのため  for s in fp.read(): と記述すると1行ずつではなく

  「1文字ずつ」の処理になる

fp = open("weekly.csv")
for s in fp:
    print(s)

 

・ファイルの書き込み

 ・ファイルにデータを書き込む fp.write(文字列)

 

・JSON形式の書き込み

 ・オブジェクトをJSON形式で書き込む   json.dump(data, fp) 

 ・キーワード引数を使用することも可能   json.dump(fp=fp, obj=data)

 ・json.dumps() はオブジェクトからJSON形式の文字列を返す関数。ファイルオブジェクトを引数指定できない

 ・json.dumps() で書き込むには fp.write(json.dumps(data)) と記述する

 

・JSON形式を読み込む

 ・JSON形式のデータを読み込む json.load(fp) または json.loads(fp.read())

 

 

》補足

・シリアライズとは

 Pythonのオブジェクトや文字列をJSON形式に変換すること

 

・デシリアライスとは

 JSON形式のデータをPythonのオブジェクトや文字列に変換すること


第9章 例外

・構文エラー

 ・pythonの文法として正しくないコードを実行すると構文エラー(SyntaxError)になる

 

・例外

 ・コードが文法として正しい場合でも実行時にエラーが発生することがある。これを例外(exception)という

 ・例外が起きたことを検知して例外発生時の動作を実装するには try - except文 を使う

 ・try節の中には通常の処理を書き、except節ではtry節で発生しうる例外に対する処理を書く

 ・except節で複数の例外を処理するには、()の中に処理したい例外をすべて列挙する

 ・except節の処理が不要なときは下記のように記述する

try:
    
except StopIteration:
    pass

  ・raise文を使うことで任意の例外を発生させられる

try:
    raise ValueError("ValueErrorです")
except ValueError as error:
    print(error)

Result

ValueErrorです

 

・クリーンアップ動作

 ・try文にfinally節を追加することでクリーンアップ動作を定義できる

 ・例外発生の有無に関わらず実行する処理

def divide(a, b):
    try:
        answer = a / b
        return answer
    except ZeroDivisionError:
        print("ゼロ除算が行われました")
    except TypeError:
        print("引数の型が不正です")
    finally:
        print("--finally節の処理--") # finallyにreturn文が定義されてないので返り値はNone

answer = divide(100, "0")
print(f"結果:{answer}") 

Result

引数の型が不正です

--finally節の処理--

結果:None

 

》補足

・except 例外名 as 変数名

 ・変数に例外オブジェクトを格納して使用できる

 ・例外オブジェクトはエラーメッセージなどが格納されており、エラーの内容を確認できる

try:
    a = int("word")
except ValueError as error:
    print(error)

Result

invalid literal for int() with base 10: 'word'

 

・raiseを除いた全パターン

try:
    print(5 ** 2)
    print((5 ** 2) / 0)
except(ValueError) :
    pass           # この例外に対して何もしない
except(ZeroDivisionError) :
    print('D')
except Exception as e:
    print('e.__class__.__name__')     # その他のエラー
else:
    print('F')     # 例外が発生しなかった
finally:
    print('G')     # 例外の有無に関係なく実行

Result

25

D

G

 

・エラーメッセージ

 ・AttributeError  :存在しないデータの名前を参照したとき  例 リスト名.add(3) リストにaddメソッドは存在しない

 ・TypeError    :意図しないデータ型が与えられたとき   例 リスト名 += 3 リスト以外を代入しようとした。100 / "10"

 ・ValueError      :int型に変換できない             例 int("word")

 ・NameError     :存在しない関数などを参照したとき    例 count(リスト名) countは組み込み関数に存在しない

 ・IndexError      :要素が存在しないインデックスを指定したとき

 ・SyntaxError    :構文に問題がある場合に発生するエラー

 ・ModuleNotFoundError:指定されたモジュールがない

 ・KeyError     :ディクショナリに存在しないキーを参照しようとしたとき

 ・KeyboardInterrupt :「Ctrl + C」を押したとき

 

》補足

・パーサ(構文解釈器)は違反のある行を表示し、最初にエラーが検知された点に小さな矢印が付けられる。エラーは矢印より前のトークンが原因である

Chapter3 Python言語使用

・例外処理

 ・基底クラスで補足

  ・BaseExceptionを親としたExceptionクラスがあり、Exceptionを継承した形で、子・孫クラスの例外が存在している

  ・exceptに捕捉したい例外の基底クラスを指定しても、その子・孫クラスの例外を捕捉できる

  ・基底クラスで補足しても渡される例外オブジェクトは変わらない

 ・独自の例外を定義

  ・Exceptionクラスから派生したクラスであればraiseキーワードを使用して例外を発生できる

class MyError(Exception):
    pass

raise MyError

  ・複数の例外を送出するようなモジュールを作成する際は、そのモジュールで定義されている例外の基底クラスを作成するのが一般的

 ・一般的にtry文で囲む範囲は狭いほがいい

 ・except節で例外の種類を指定しないとエラー原因が特定できない、プログラムを停止できなくなるなどの弊害がしょうじるので非推奨


第10章 クラスとオブジェクトの操作

・クラス定義

 ・クラスを構成する要素には、クラス変数、インスタンス変数、メソッドがある

 ・クラス変数(①)

  ・クラスで保持する変数。そのクラスのすべてのインスタンスで共有される

  ・クラス変数はクラスの直下に定義

  ・通常の変数のように「変数名 = 値」の書式で記述

 ・インスタンス変数(②、③)

  ・インスタンス毎に固有の変数

  ・インスタンスを初期化するための特殊メソッドである__init__メソッドの中で定義する

  ・__init__メソッドには、selfという引数を与える

  ・インスタンス変数は「self.変数名 = 値」の書式で定義

 ・メソッド(④)

  ・クラスに定義された関数

  ・メソッドの定義の仕方は関数と同じだが、第1引数にselfを与える

  ・メソッド内でクラス変数を参照する場合は、self. を付ける必要がある

class Duck:
    # クラス変数familyの定義
    family = "Anatidae"                   # ①

    # 特殊メソッド__init__の定義
    def __init__(self):                   # ②
        # インスタンス変数birdsongの定義
        self.birdsong = "quack"           # ③
    
    # show_familyメソッドの定義
    def show_family(self):                # ④
        return f"Ducks belong to the family {self.family}."

》補足

・self

 ・そのクラスのインスタンスを表す

 ・selfはメソッドの呼び出し元で引数に渡す必要はなく、pythonが自動的に呼び出されたメソッドの第1引数としてインスタンスを渡してくれる

 

・インスタンス化

 ・変数名 = クラス() の書式で行う duck = Duck()

 ・__init__メソッドを定義することで、インスタンス化時の処理を記述できる

 ・このメソッドの第1引数であるselfは、そのクラスのインスタンスを表す特殊な引数

 ・第2引数以降は、呼び出し元から渡された引数をそのまま受け取れる

 ・メソッド内にインスタンス変数と同名のローカル変数がある場合、それらは別の変数として扱われる

 ・メソッド内でインスタンス変数を参照・更新する場合、変数名の先頭に self. を付ける

class Duck:
    def __init__(self, birdsong):
        # インスタンス変数birdsongの定義
        # 引数birdsongの値で初期化する
        self.birdsong = birdsong
    
    def sing(self):
        return self.birdsong

duck = Duck("quack")

 

・メソッド

 ・同一クラスに定義された他のメソッドを呼び出す際は、メソッド名の先頭に self. を付ける

class Duck:
    def __init__(self):
        self.birdsong = "quack"
    
    def change_birdsong(self, birdsong):
        self.birdsong = birdsong

    def show_birdsong(self):
        print(self.birdsong)
        self.change_birdsong("ga-ga-")
        print(self.birdsong)

duck = Duck()
duck.show_birdsong()

Result

quack

ga-ga-

 

・派生クラス

 ・class 派生クラス(基底クラス):

 ・pythonでは定義済みのクラスを基にして、別のクラスを作成できる。これをクラスの継承と呼ぶ

 ・継承元となるクラスを基底クラス、継承先になるクラスを派生クラスと呼ぶ

 ・派生クラスは基底クラスの、クラス変数、インスタンス変数、、メソッドを受け継ぐ

 ・あるクラスを継承するときに、基底クラスと同名のクラス変数、インスタンス変数、メソッドを定義する場合、それらは上書きされる

 ・super().メソッド() で継承した基底クラスのメソッドを呼び出せる。その際引数にselfは不要

class Person():  # 基底クラス
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def say_name(self):
        print("私の名前は" + self.name + "です。年齢は" + str(self.age) + "歳です。")


class JapanesePerson(Person):  # 派生クラス
    def __init__(self, name, age):
        super().__init__(name, age)  # 基底クラスのメソッドの呼び出し

    def say_hello(self):
        print("こんにちは")


yamada = JapanesePerson("山田", 20)
yamada.say_name()
yamada.say_hello()

Result

私の名前は山田です。年齢は20歳です。

こんにちは

 

・オブジェクトの判定

 ・type関数は、引数に指定したオブジェクトの型を返す

 ・isinstance関数は、第1引数に判定したいオブジェクトを指定、第2引数に型や基底クラスを指定する

 ・issubclass関数は、クラスBがクラスAから派生しているかを判定する issubclass(クラスB, クラスA)

print(isinstance(duck, Duck))

Result

True

Chapter4 Pythonのクラス

・メソッドには以下の3種類が存在する

 ・インスタンスメソッド

  ・一般にメソッドというとインスタンスメソッドのことをいう

  ・クラス内で動作するように、第1引数にselfが渡され、インスタンス化されたクラスの属性やメソッドを使うことができる

 ・クラスメソッド

  ・クラスオブジェクトを暗黙的に第1引数にとる、特別なメソッド。

  ・@classmethodデコレーターをメソッドに使うことで定義

  ・外部情報に依存したインスタンスを生成する場合などの特殊なインスタンス化に使用

 ・静的メソッド

  ・インスタンス化せずに使うことを前提にしたメソッドを定義

  ・@staticmethodデコレーターをメソッドに使うことで定義

 

・サンプル

# class_sample.pyモジュール
class User:
    user_type = None

    def __init__(self, name, age, address):
        self.name = name
        self.age = age
        self.address = address

    def __repr__(self):
        return f"<User id:{id(self)} name:{self.name}>"

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

    def __eq__(self, other):  # otherは比較対象のインスタンス
        return self.age == other.age 

    def increment_age(self):
        self.age += 1

    def start_name(self):
        if len(self.name) > 0:
            return self.name[0]
        else:
            return ""

・特殊メソッド

 ・特殊メソッドは、メソッド名の前後にアンダースコア(_)を2個付けたもの

 ・コンストラクターメソッド __init__() も、特殊メソッドの1つ

 ・特殊メソッドでは、関数が実行されたときの挙動や演算子の挙動を決めることができる

 

 ・関数による挙動

  ・__repr__() :オブジェクトをprint()関数で出力したときの文字列表現を変更できる

from class_sample import User

user = User("寺田学", 35, "東京都台東区")
print(user)  # __repr__()でオブジェクトIDとnameを返す

   ・__len__()  :組み込み関数len()は引数に与えたオブジェクトの__len__()メソッドを呼び出す

from class_sample import User

user = User("寺田学", 35, "東京都台東区")
len(user)  # __len__()でnameの桁数を返す

 

 ・演算子による挙動

  ・__add__()       :足し算で使う演算子(+)の挙動には、__add__()という特殊メソッドが呼び出される

  ・__eq__()         :==では__eq__()メソッドが呼び出される

from class_sample import User

user_1 = User("寺田学", 35, "東京都台東区")
user_2 = User("鈴木たかのり", 30, "東京都千代田区")
user_1 == user_2  # __eq__()で年齢を比較

 ・主な特殊メソッド

__init__ コンストラクター(初期化時に使われる)
__call__ 呼び出し可能化(関数のようにインスタンスを呼び出せるようにする)
__repr__ 文字列表現
__str__ 文字列型へお変換
__len__ 要素数の取得(len()関数で呼ばれる)
__lt__ 小なり比較  他(__le__、__eq__、__ne__、__gt__、__ge__)
__add__ 加算演算子 他(__sub__、__mul__、__truediv__)

 

・プロパティ化

 ・インスタンスメソッドをプロパティ化するとカッコを付けずに呼び出せる

# 通常のインスタンスメソッドの呼び出しにはカッコを付ける
user.start_name()

# 関数宣言に@propertyデコレーターを使いプロパティ化したメソッドは、カッコがいらない
user.start_name

 

・クラスメソッドの具体的な使い方

 ・datetimeにはクラスメソッドで宣言されているnow()メソッドが存在する

 ・インスタンスのメソッドを呼び出すのとは違い、datetime.now() とクラスオブジェクトに対し直接メソッド実行できる

from datetime import datetime

dt_now = datetime.now()
dt_now  # datetime.datetime(2025, 5, 23, 16, 10, 5, 596305)

 

・多重継承

class Custom(Base1, Base2):
    pass

 ・多重継承は、継承関係の把握が難しくなり、複雑で混乱を招く場合がありる

 ・このCustomクラスの属性の参照やメソッドの呼び出しをしたとき、Base1の属性やメソッドを最初に調べ、Base1の親クラスの
  属性やメソッドを調べたあとに、Base2の属性やメソッドを調べる

 ・Pythonにおいては、多重継承の継承順位をMRO(Method Resolution Order)という仕組みで、C3アルゴリズムを用いて探索する

 

・dataclass

 ・クラスデコレーターを用いてクラスを宣言し、クラス属性を宣言する

 ・宣言の際に型ヒント(型アノテーション)を用いる

 ・例として、サンプル:class_sample.pyのUserクラスをdataclassとして宣言

from dataclasses import dataclass

@dataclass  # クラスデコレーターでdataclassを宣言
class User:
    name: str    # クラス変数を宣言、型ヒントを宣言
    age: int
    address: str

user = User("manabu", 50, "Chiba")  # クラスをインスタンス化
user  # インスタンス化したクラスを確認  User(name='manabu', age=50, address='Chiba')

 ・detaclass宣言で、コンストラクターメソッドである__init__()や、特殊メソッドである__repr__()などが自動的に定義される

 ・detaclassを使うことで以下の定義を省略でき、データを管理するクラスをシンプルにすることが出来る

class User:
    def __init__(self, name, age, address):
        self.name = name
        self.age = age
        self.address = address

    def __repr__(self):
        return f"User(name='{self.name}', age={self.age}, address='{self.address}')"

 ・dataclassデコレーターに引数を与えて挙動を変えることができる。frozen=Trueで属性の変更ができなくなる

  ここではfrozen引数を設定し、データ変更ができないdataclassを宣言

@dataclass(frozen=True)

 ・コンストラクターの任意引数

  ・デフォルト値を設定する場合は、デフォルト値を持たない属性よりも下に宣言する必要がある

@dataclass
class User:
    name: str
    age: int
    address: str
    active: bool = False  # デフォルト値を設定

user = User("takanori", 40, "Tokyo")  # 任意引数をインスタンス時に設定しない
user                                   # User(name='takanori', age=40, address='Tokyo', active=False)

 ・データ変換

  ・dataclassで宣言したクラスは、辞書やタプルに変換する仕組みが提供されている

@dataclass
class User:
    name: str
    age: int
    address: str

from dataclasses import asdict, astuple

user = User("manabu", 50, "Chiba")
user  # User(name='manabu', age=50, address='Chiba')

asdict(user)  # 辞書型に変換
{'name': 'manabu', 'age': 50, 'address': 'Chiba'}

astuple(user)  # タプル型に変換
('manabu', 50, 'Chiba')

 

・オブジェクト関連関数

 ・id(object) オブジェクトの識別値であるidを整数で返す

 ・type(object) オブジェクトの種類を返す

 ・help(object) オブジェクトのヘルプを表示する。docstringがあるとhelp()関数で確認できる


第11章 標準ライブラリ

・OSモジュール

 ・os.getcwd() でカレントディレクトリを取得

 ・os.chdir("..") でカレントディレクトリから上の階層へ移動

 

・globモジュール

 ・glob.glob("*.ipynb") でカレントディレクトリ下のパターンにマッチするファイルの一覧を取得する

 

・sysモジュール

 ・sys.argv でコマンドライン情報を取得。 python check.py 1 2 をコマンドライン実行すると、sys.argvは[’check.py', '1', '2']が返ってくる

 

・argparseモジュール

 ・argparseモジュールのArgumentParse() を使ってもコマンドライン引数を取得できる

 ・parserという名前でパーサ(構文解釈l器)のオブジェクトを作成(①)

 ・add_argument() で引数の情報を追加。ここでは2種類(--head と body)(②)

  コマンド実行時、"--head"は「--head=値」、"body", nargs="+"は1個以上の「任意形式」の引数の指定

  ハイフン付は任意引数。引数名が1文字の場合はハイフンは1つでよい(-a)。ハイフンがないものは必須引数

 ・parser.parse_args() で引数の情報を取得できる(③)

 ・Resultのように2種類の引数の値を確認できる(④)

import argparse

parser = argparse.ArgumentParser()      # ①
parser.add_argument("--head")           # ②
parser.add_argument("body", nargs="+")  # ②
args = parser.parse_args()              # ③
print(args)                             # ④

Result

python> python parse.py --head=1 2 3

Namespace(head='1', body=['2', '3'])              

 

・mathモジュール

 ・数学に関する定数や関数が定義されている

 ・log() は対数を求める関数。log(値, 底) は返り値をnとした場合、底のn乗が値になる。log(16, 2)は、2の4乗が16なので返り値は「4.0」を返す

 ・pi は円周率を参照する定数(3.141592653589793

 ・tau(タウ) は「円周と半径の比」(2π = 6.283185307179586)

 

・randomモジュール

 ・乱数を生成するための関数を定義

 ・random.choice(range(10)) は0から9のうち1つをランダムに選ぶ関数。連続して実行した場合、出力が重複する可能性がある

 ・random.random()

  ・random.random()    0以上1未満のランダムな値(少数)を返す

  ・random.random() - 1   -1以上0未満のランダムな値(少数)を返す

  ・random.random() * 2   0以上2未満のランダムな値(少数)を返す

  ・random.random() * 2 -1   -1以上1未満のランダムな値(少数)を返す

 ・random.sample(l, n) 第1引数のリストからランダムに第2引数の取得数を選択してリストで返す。出力が重複する可能性がある

 ・random.randrange(1, 6, 2) はrange(1, 6, 2) の要素からランダムに選ばれた要素を返す

 

・statisticsモジュール

 ・数理統計に用いる関数が含まれている

 ・statistics.mean()   データの平均値

 ・statistics.median()   データの中央値

 ・statistics.variance()  データの不偏分散

 ・不偏分散の計算式 平均との差の2乗の和を「データ数 - 1」で割った値

 ・[ -1, -1, -1, -1, 4 ] の場合

  ・平均値: 0

  ・中央値:-1

  ・不平分散:( 1**2+1**2+1**2+1**2+4**2 ) / (5 - 1) = 5

 

・urllib.requestモジュール

 ・urlopen() は引数で指定したURLのWebサイトからさまざまな情報を取得できる

 ・urlopen() は対象のデータをバイナリモードで取得するのでdecode() でバイナリデータを文字列に変換が必要

from urllib.request import urlopen
with urlopen("https://www.yahoo.co.jp/") as rs:
        s = rs.read().decode() 

 

・datetimeモジュール

 ・日付と時刻を扱うさまざまな関数が定義されている

 ・datetime() で指定した日付のdatetimeオブジェクトを作成

 ・strftime() で指定された表示形式で出力される

from datetime import datetime
dt = datetime(2000, 12, 31)
print(f"{type(dt)}\n{dt}\n{dt.strftime("%Y-%m-%d")}")

Result

<class 'datetime.datetime'>

2000-12-31 00:00:00

2000-12-31

 

 ・timedeltaオブジェクトは2つの日付や時間の間の差を表せる機能を定義。daysメソッドで日数部分を取得できる

 ・日付の引き算はdatetimeオブジェクト同士、あるいはdateオブジェクト同士で可能

from datetime import date
dt1 = date(2001, 1, 1)
dt2 = date(2002, 2, 2)
print(type(dt1))
diff = dt2 - dt1
print(f"{type(diff)}\n{diff}\n{diff.days}")

Result

<class 'datetime.date'>

<class 'datetime.timedelta'>

397 days, 0:00:00

397

 

・unittestモジュール

 ・単体テストを実行するのに使用する

 ・一般的に、unittestによる単体テストではテスト対象のモジュールとは別に、テスト実行側の.pyファイルを用意する必要がる

 ・コマンドラインから「python -m unittest」のように実行すると、対象の関数やメソッドのテスト結果を確認できる

# test.pyの処理内容

import unittest
import mod

class TestSample(unittest.TestCase):
    def test_it(self):
        actual = mod.calc(2, 3)              # テスト対象の実行結果
        expected = 5                         # 期待する結果
        self.assertEqual(actual, expected)   # 結果が同じことを確認

 

・リストやディレクトリなどを整形

 ・pprintモジュール

  ・pprint() は1行が80文字を超えると要素ごとに改行する

  ・widthで改行を適宜変更可能

import pprint
lines = [f"sample test string {i:04}" for i in range(3)]
pprint.pprint(lines)

Result

['sample test string 0000',

'sample test string 0001',

'sample test string 0002']

 

 ・textwrapモジュール

  ・widthで指定した幅に収まるよう整形する

  ・pprintのように角括弧を付けず出力される

import textwrap
text = [f"{i} sheep jumped  fence." for i in range(1, 4)]
print(textwrap.fill(", ".join(text), width=24))

Result

1 sheep jumped fence.,

2 sheep jumped fence.,

3 sheep jumped fence.

 

・reモジュール 参考 Pythonの正規表現モジュールreの使い方 、Pythonにおける正規表現の使い方

 ・正規表現を扱う関数

  ・sub() はマッチした部分を他の文字列に置換する

  ・compile() は正規表現パターン文字列をコンパイルして正規表現パターンオブジェクトを作成する

  ・search() は文字列すべてが検索対象で、先頭にない文字列にもマッチする

  ・match() は文字列の先頭位置からだけマッチするどうか調べる

 

 ・正規表現パターン

  ・[a-z]+ は小文字のアルファベットが1文字以上繰り返される文字列にマッチ

  ・[efg] はe,f,g のいづれかの1文字とマッチ

  ・a.{3}e はa...eと同じ。{n]は直前のパターンをn回繰り返し

  ・メタ文字(特別な意味を持つ文字)は\でエスケープが必要

  ・\1, \2, … は'\\1'のようにエスケープが必要だが、raw文字列を使うとr'\'ですむ

 

 ・メタ文字

  ・「.」 改行以外の任意の1文字

  ・「*」 直前のパターンを0回以上繰り返し

  ・「+」 直前のパターンを1回以上繰り返し

  ・「?」 直前のパターンを0回または1回繰り返し

 

 ・フラグ設定

  ・re.IGNORECASE を指定すると大文字小文字を区別せずマッチする

 

import re
s = "tic tac tac toe"
print(re.sub(r"([a-z]+) \1", r"\1", s))  # tic tac toe
import re
prog = re.compile('Kus(a|u)n(a|o|k)g?i(saya|ro)?', re.IGNORECASE)  # Patternオブジェクトを返す
ret = prog.search('KUSANAGI')        # ok KUSANAGI  マッチするとMatchオブジェクトを消す
ret = prog.search('Kusanagi saya')   # ok Kusanagi
ret = prog.search('Kusunoki')        # TypeError 
ret = prog.search('KUSANOGI')        # ok KUSANOGI
ret = prog.search('Kusanoiro')       # ok Kusanoiro
print(ret[0])

 

・loggingモジュール

 ・loggerにはsetLevelがあり、logger毎に扱うログのレベルを設定可能

 ・レベルの高い順:CRITICAL、ERROR、WARNING、INFO、DEBUG

 ・loggerのsetLevel:WARNING

 ・各loggerにはhandlerがありhandlerごとにログの出力先の設定や、handler自体にもsetLevelを設定可能

import logging
logger = logging.getLogger()
logger.info("infomation message")
logger.error("error message")

Result

error message

 

・collectionsモジュール

 ・リスト要素の集計をする

from collections import Counter
a = [1, 2, 3, 1, 2, 1, 4, 5, 2]
print(Counter(a)) 

Result

Counter({1: 3, 2: 3, 3: 1, 4: 1, 5: 1})

 

・Processing(プロセッシング)モジュール

 ・簡単に画面に絵を出したりマウスの操作で絵を動かしたりできる

 

・smtplibモジュール

 ・任意のインターネット上のホストにメールを送ることができる

標準ライブラリ+

Chapter5 型ヒント

・operatorモジュール

 ・attrgetter クラスやdataclassのソート

 ・itemgetter リストや辞書などのソート

from operator import itemgetter, attrgetter
from datetime import date
from dataclasses import dataclass

# attrgetter
@dataclass 
class User:
        name: str
        gold: int

users = [
        User("yusha", 300),
        User("sorho", 200),
        User("maho", 100)
]
print(f"attrgetter: {sorted(users, key=attrgetter("gold"))}")  # 所持金でソート

# itemgetter
data = [(1, 40, 200), (3, 10, 100), (2, 20, 300), (1, 30, 300)]
print(f"itemgetter: {sorted(data, key=itemgetter(2))}")

Result

attrgetter: [User(name='maho', gold=100), User(name='sorho', gold=200), User(name='yusha', gold=300)]

itemgetter: [(3, 10, 100), (1, 40, 200), (2, 20, 300), (1, 30, 300)]


第12章 Python仮想環境とサードパーティパッケージの利用

・仮想環境

 ・複数のPythonの実行環境を目的に応じて使い分けられる

 ・使用するPythonのバージョンは仮想環境ごとに指定できる

 ・使用する外部パッケージの種類とバージョンも選択可能

 ・1つの仮想環境でパッケージのバージョンを変更しても、別の仮想環境のバージョンは変更されない

 ・仮想環境を作成するディレクトリは、コマンドで指定する。このディレクトリの中には、Pythonのインタープリタ本体やパッケージ、

  仮想環境を有効化するスクリプトなどが含まれる

  ・仮想環境に使うバージョンのPythonは、あらかじめインストールしておく必要がある

 

・仮想環境の導入方法

 ・仮想環境の作成には、venv(ブイエンブ)モジュールを使う

 ・仮想環境用のディレクトリの作成 python3 -m venv ディレクトリ名 windowsではpython3の代わりのpythonを使う

 ・UnixやmacOSにおいて、複数のバージョンのPythonがインストール済の場合にバージョンを指定できる python3.10 -m venv ディレクトリ名

 ・仮想環境に切り替える方法

  ・UnixやmacOSの場合 source ディレクトリ名/bin/activate 

  ・Windowsの場合 ディレクトリ名/Scripts/activate.bat

 

・外部パッケージのインストール

 ・外部パッケージはpip(ピップ)モジュールを使ってインストールする

 ・外部パッケージのインストール python -m pip install パッケージ名 あるいは pip install パッケージ名

 ・特定のバージョンのインストール(インストール済みでも変更可能) pip install パッケージ名==バージョン

  ・==3.1 3.1および3.1.*

  ・~=3.1 3.1以上かつ3.*

  ・>=3.1 3.1以上

 ・インストール済みのパッケージをアップグレード pip install -U パッケージ名 あるいは pip install --upgrade パッケージ名

 ・インストール済みのパッケージをアンインストール pip uninstall パッケージ名

 ・インストール、アンインストールは複数パッケージの同時指定可能 例 pip install パッケージ1 パッケージ2 パッケージ3

 ・インストール済みのパッケージを一覧出力 pip list

 ・インストール済みのパッケージをrequirements形式で一覧出力 pip freeze

Chapter1 Pythonの環境

・pip

 ・pipがインストールされていない環境での導入方法  python -m ensurepip

 ・Debian、Ubuntuでのpipの導入方法  sudo apt install -y python3-pip

 

・PyPI(パイピーアイ)

 ・PyPIとはPython用のソフトウェア・リポジトリのこと

 ・pip install パッケージ名に対してPyPIに公開されていれば、そのパッケージがインストールされる

  インストール時に依存パッケージも同時にインストールされる

$ pip install sampleproject
Collecting sampleproject
  Using cached sampleproject-2.0.0-py3-none-any.whl (4.2 kB)
Collecting peppercorn  # 依存パッケージ
  Using cached peppercorn-0.6-py3-none-any.whl (4.8 kB)
Installing collected packages: peppercorn, sampleproject
Successfully installed peppercorn-0.6 sampleproject-2.0.0 

 ・ある範囲のバージョンのなかで最新のものをインストールしたい場合は、>、<、=を使ってバージョンの範囲を指定する

  この場合はダブルクオートでパッケージ名とバージョンを囲む必要がある

  pip install "sampleproject>=1.2.0,<2.0.0" 1.2.0以上2.0.0未満のなかで最新のバージョンをインストール

 ・バージョン指定と組み合わせることでダウングレードも可能

$ pip install --upgrade sampleproject  #最新バージョンにアップグレード
Requirement already satisfied: sampleproject in ./.venv/lib/python3.9/site-packages (1.3.1)
Collecting sampleproject
(省略)
Successfully installed sampleproject-2.0.0
$ pip install --upgrade sampleproject==1.2.0  #バージョン1.2.0にダウングレード
Collecting sampleproject==1.2.0
Successfully installed sampleproject-1.2.0

 ・インストール済みの最新バージョンではないパッケージ一覧 pip list -o または pip list --outdated

 ・個別パッケージの詳細情報の確認(依存関係にある他パッケージも出力される) pip show パッケージ名

 ・依存関係にあるパッケージも含めて完全アンインストールする場会は個別にpip uninstallする必要がある

 ・requirements.txtを参照してパッケージをインストールする pip install -r requirements.txt

 ・複数のアプリケーションを開発していて、そのすべてのアプリケーションで特定パッケージのバージョンを統一したい

  pip install -r requirements.txt -c constraints.txt

  requirements.txt の統一したいパッケージにはバージョンを記載しない

  constraints.txtには統一したパッケージのみをバージョンを付けて記載する。requirements.txtにそのパッケージがないと何もしない

 ・複数バージョンのPythonをインストールした環境でバージョンを指定してインストールする

  pip3.9 install パッケージ名 または python3.9 -m pip install パッケージ名

 

・仮想環境

 ・venvの仮想環境はDockerやVirtualBoxなどの仮想環境とは異なる

 ・仮想環境専用のpipコマンドを最新版に更新 python -m venv --upgrade-deps

 ・仮想環境を有効化するとシェルプロンプトに仮想環境のディレクトリ名が表示される 例 (ディレクトリ名)

 ・仮想環境を有効化したら最初にpipを最新版にアップグレードしておく pip install --upgrade pip

 ・仮想環境の利用が終わったら無効化する deactivate

 ・不要になった仮想環境を削除 rm -rf ディレクトリー名

 ・パッケージのアンインストールを行っても依存関係にあるパッケージはアンインストールされないので

  一旦削除して仮想環境を作り直した方が手間が少ない。同じバージョンで再構築したい場合は予めrequirements.txtを作成しておく


以降は基礎にないカテゴリ

Chapter3 Python言語使用

・with文

 ・コンテキストマネージャー

  ・with文に渡すオブジェクトをコンテキストマネージャー(context manager)と呼ぶ

  ・コンテキストマネージャーとは、__enter__()、__exit__()の特殊メソッドを実装したクラスのインスタンス

  ・open関数の例ではファイルオブジェクトがコンテキストマネージャーオブジェクト

  ・__enter__()が実行され、次にwithブロック内、最後に__exit__()が実行される

  ・withブロック内での例外発生時は__exit__()の引数で例外情報が受け取れる

 ・@contextlib.contextmanager

  ・@contextlib.contextmanagerというデコレーターを使用してジェネレーター関数を記述するとコンテキストマネージャーを定義できる

   それにより、__enter__()と__exit__()メソッドを別々に定義したクラスを書く必要がない

   ・デコレーターとは、既存関数に別の処理を追加する仕組み

   ・ジェネレーター関数とは、リターン時の状態を保持し、再呼び出し時は保持された状態から処理されるもの

 ・よくある使い方

  ・try-finallyの再利用

  ・ファイルのopen/close

  ・組み込み関数の書き換え

  ・ある処理の実行時間を計測したいというケースで、コンテキストマネージャーを使用した例

from time import time, sleep
import contextlib

@contextlib.contextmanager
def timed(func_name):
    start = time()
    try:
        yield
    finally:
        end = time()
        print(f'{func_name}:(total: {end - start})')

with timed('sleep processing'):
    sleep(3)

 

・ジェネレーター

 ・通常の関数とおおよそ同じだが、return文の代わりにyield(ヤイルド)文を使用する

 ・関数内でyield文が使用されるとpythonはこの関数をジェネレーターと判断する

 ・yieldからはジェネレーターオブジェクト(イテラブル)が返される

 ・yield文は実行された時点の値を返し、その位置で一時停止の状態になり次の呼び出しを待つ

 ・ジェネレーターはデータ量が大量になってもメモリリースを消費することなく処理を行うことができる

 ・ジェネレータで使用できる関数の1つのnext関数は、イテレーターから次の値を取り出す組み込み関数で、

  ジェネレーターオブジェクトの__next__()メソッドを呼び出している

 ・無限の数列を扱うことができるのもジェネレーターの特徴

li = []
def multiplier():
    num = 1
    while True:  # 無限
        yield num
        num *= 2

gen = multiplier()
for _ in range(3):
    li.append(next(gen))

print(li)    # [1, 2, 4]

 ・大きいファイルの処理にジェネレーターは効率的

  ・ファイル読み込みのループ処理中に対象のデータを1つずつ取り出しながら処理を行うことができるため1行分のメモリ程度ですむ

  ・対象データを処理するコードはジェネレーターとは別に記述できるため、コードが読みやすくなる

# ファイルからキーワードを含む行を抜き出すのにジェネレーターを使用
def text_retrieve(text):
    with open('sample.txt', 'r') as f:
        for row in f:
            if text in row:
                yield row       # 引数で指定されたキーワードを含む場合に値を返す

def do_something_func(txt):
    print(txt)

for txt in text_retrieve('A'):  # 'A'というキーワードが含まれる行の取得
    do_something_func(txt)      # 対象の行に対して別の処理が行われたあとにファイル読み込みループが再開される

 

・デコレーター

 ・関数やメソッド、クラスをデコレート(装飾)する機能。中身を変えずに共通のロジックを適用できる

 ・適用したい対象の上に「@デコレーター名」と1行追加する

 ・デコレーターはシンタックスシュガー(複雑でわかりにくい書き方をよりシンプルでわかりやすい別の記法で書くこと)

 ・デコレーターの構文と代入文は等価

# デコレーターの構文 
@my_decorator
def func():
    pass

# 代入文 
def func():
    pass
func = my_decorator(func)

 ・デコレーターの利点は、デコレーター対象の関数に何も変更を加えなくてもデコレーター対象の関数の前後で処理を追加できること

# ファイルからキーワードを含む行を抜き出すのにジェネレーターを使用
def text_retrieve(text):
    with open('sample.txt', 'r') as f:
        for row in f:
            if text in row:
                yield row       # 引数で指定されたキーワードを含む場合に値を返す

def do_something_func(txt):
    print(txt)

for txt in text_retrieve('A'):  # 'A'というキーワードが含まれる行の取得
    do_something_func(txt)      # 対象の行に対して別の処理が行われたあとにファイル読み込みループが再開される

 

Result

raise ValueError

raise ValueError

5です

 

 ・クラスデコレーター

  ・クラスや、クラスメソッドにもデコレーターを適用できる

  ・使い方は関数デコレーターと同様にクラスやメソッド定義の前に「@デコレーター名」で1行追加するだけ

 

 ・複数デコレーター

  ・1つの関数やメソッド、クラスに複数のデコレーターを適用できる。1行ごとに1つのデコレーターを指定する

  ・デコレーターは内側から外側に向かって順に適用される

# func()関数にmy_decorator2が適用されたものがmy_decorator1に渡される
@my_decorator1
@my_decorator2
def func():
   pass

 

  ・関数デコレーターを自作

  ・Pythonの関数の仕様

#(例1)
def func_greeting(name):       # 関数内にprint_greeting()という1つの関数を定義し、その関数をreturnしている
    def print_greeting():
        print(f'{name} さん')  # nameという外側の関数に定義されている仮引数を、内側の関数内で利用できる
    return print_greeting

func = func_greeting('john')   # デコレーター関数が呼ばれ、ラッパー関数が戻される ※まだラッパー処理はされない
func()                         # ここでラッパー関数が呼ばれる処理される

Result

john さん

#(例2)
def after_greeting(func, name):
    func(name)
    print('今日はいいお天気ですね')

def greeting(name):
    print(f'{name} さん')

after_greeting(greeting, 'john')  # greeting()という関数を別の関数の引数として渡している

Result

john さん

今日はいいお天気ですね

 

  ・上記(例1)(例2)をふまえ、以下にシンプルなデコレーターを代入文で作成

   以下はデコレーターの実装まとめ

   1) デコレーター関数を定義する

   2) デコレーター関数はデコレート対象の関数を引数として受け取るようにする

   3) デコレーター関数のなかにデコレート対象の関数の代わりに呼び出されるラッパー関数を定義

   4) ラッパー関数のなかでデコレーター関数の引数で受け取った関数を呼び出す

   5) 関数呼び出しの前後に追加、変更の処理を加える

   6) デコレーター関数の戻り値としてラッパー関数を返す

   7) wrap_functionは呼び出し可能オブジェクトなので、()を使用してコールできる。このオブジェクトを呼び出すと、

    my_decorator()内のwrap_function()が呼び出されて処理が実行される

# デコレーター対象の関数を呼び出したあとログ出力するデコレーター関数を代入文で作成したもの
def my_decorator(func):              # 1), 2)
    def wrap_function():             # 3)
        func()                       # 4)
        print(f'{func.__name__}')    # 5)
    return wrap_function             # 6)

# デコレーター対象の関数
def greeting():
    print('こんにちは')

greeting = my_decorator(greeting)
greeting()                           # 7)

Result

こんにちは

greeting

 

  ・上記を「@デコレーター名」構文を使用して置き換える

@my_decorator
def greeting():
    print('こんにちは')

Chapter3 Python言語使用

・hoge