最近はPythonとTypeScriptを交互に使うことが多く、頭の中で両言語がごっちゃになっているので、本記事でPythonについてメモします。
情報の網羅性はありません。
気づいた時点で随時追加します。
ループ
ループ中でbreak、continueが使える。
while文
else節は、whileでbreakが実行されなかったときのみ実行される。
while 式:
スイート
else:
スイート
for文
while文同様、else節が書ける。
様々な書き方:
for i in range(n):
for i in range(a, b):
for i in range(a, b, step):
for i in list(range(1, 8)) + list(range(9, 13))
for i, ch in enumerate(s):
# 1からカウント
for i, ch in enumerate(s, 1):
for ch in s:
# カウンタ用変数を使わないなら、_を使うと良い。
for _ in range(n):
データ型
型を調べるには
type(式)
※ 式には型と値がある。
NoneType型
- 他言語のNULLに近いのが、NonType型のNone
if x is None:
if x is not None:
bool型
- False(内部的に0)とTrue(内部的に1)。先頭が大文字。
- 偽とみなされるのは、False、0、0.0、None、空文字''、空リスト[]、空タプル()、空辞書{}等。これら以外とTrueは真とみなされる。
数値型
- int : 整数型(桁数に制限なし)
- float : 浮動小数点型(倍精度)
- お金の計算には、decimalモジュールのDecimal型を使う。
数値リテラル
途中の任意の場所に_を入れられる。
10_000_000_000
算術演算
# 除算演算子(演算は実数で行われる)
x / y
# 切捨て除算演算子(小数部を切り捨てて整数値を生成)
x // y
- ++、--はないが、+=、-=等はある。
文字列型(str型)
# 文字数
len(s)
# 部分文字列
s[begin:end]
# 反転
s[::-1]
reversed(s)
# 文字列が別の文字列に含まれているか
if pattern [not] in text:
- 文字列結合は、+でつなぐよりもjoinメソッドを使った方が高速。
f文字列
a, b, c = 1, 2, 3
print(f"{a} + {b} + {c} = {a + b + c}")
ヒアドキュメント
ヒアドキュメントは'''または"""で囲み、ソースコードでインデントしたら textwrap.dedent() で余計な空白等を削除できる。
import textwrap
if True:
s = '''
SELECT
*
FROM
dual;
'''
print("----")
print(s)
print("----")
print(textwrap.dedent(s)[1:-1])
print("----")
実行結果
----
SELECT
*
FROM
dual;
----
SELECT
*
FROM
dual;
----
バイト列型(bytes型)
バイナリデータ。
b"..."
リスト型(list型)
# リスト表記演算子
[ 式...]
# 反転
list(reversed(x))
- Pythonのリストは、連結リストではなく、配列である。
以下の本の p.173 に、そう説明されている。
新・明解Python入門 柴田望洋/著 SBクリエイティブ 20190524
https://www.amazon.co.jp/dp/4815601526
※ teratailで関連質問している方がいました。
https://teratail.com/questions/113885
リスト内包表記
# [ 式 for 要素 in イテラブル ]
[ n - 1 for n in range(1, 8) ]
# [ 式 for 要素 in イテラブル if 判定式 ]
[ n for n in range(1, 8) if n % 2 == 0 ]
タプル型(tuple型)
# 式結合演算子(タプル表記演算子)
( 式...)
# 複数の変数へ異なる値を一括代入
x, y, z = 1, 2, 3
- イミュータブルなのでリストより低コスト
- ()は省略可
辞書型(dict型)
# 辞書表記演算子
{ キー : 値 }
x = {}
# in演算子で調べられるのはキーで、値ではない。
if key in dic:
for key in dic.keys():
for key, value in dic.items():
- キーはイミュータブルである必要がある。
- キーが存在しないとき、値を取り出そうとしたら KeyError例外が発生する。
辞書内包表記
{ 式 : 式 for 要素 in イテラブル }
集合型(set型)
# 集合表記演算子
{ 式...}
x = set()
# ソート
sorted(x)
集合内包表記
{ 式 for 要素 in イテラブル }
言語について、その他
- 行の終端の\で次の行に継続できる。
- カッコ記号()、[]、{}の中では自由に改行できる。
変数
- Pythonの変数は、値を格納する箱ではなく、オブジェクトを参照するもの(オブジェクトに結びつけられた名前)である。
以下の本の p.114 に、そう説明されている。
新・明解Python入門 柴田望洋/著 SBクリエイティブ 20190524
https://www.amazon.co.jp/dp/4815601526
※ 関数型言語の名前束縛と同じかな。束縛からの解放がdelか。
- ミュータブルな型(値が変更可能):リスト、辞書、集合など
- イミュータブルな型(値が変更不能):数値、文字列、タプルなど
三項演算子(条件演算子)
x if y else z
何も行わない文
pass
文書化文字列
def foo(n: int, s: str) -> None:
"""sをn回表示
仮引数:
n -- 表示する文字列の個数
s -- 表示する文字列
返却値:
無し
"""
for _ in range(n):
print(s)
# 関数fooのドキュメントを表示する
help(foo)
# アノテーションを表示
print(foo.__annotations__)
# 文書化文字列を表示
print(foo.__doc__)
global文、nonlocal文
n = 1
def foo():
global n # この文がないと、次の行のnがローカル変数になる
n = 2
def foo():
n = 1
def bar():
nonlocal n # この文がないと、次の行のnがbar()のローカル変数になる
n = 2
ラムダ式
文ではなく式として関数を実現する。
lambda 仮引数並び : 返却値
foo = lambda x, y: x + y
print((lambda x, y: x + y)(1, 2))
カリー化
参考サイト:
https://kk6.hateblo.jp/entry/20110905/1315221175
モジュール自作
if __name__ == "__main__":
テスト・デバッグ用のコード
モジュール利用
# モジュール名.オブジェクト名でアクセス
import モジュール名
# 別名.オブジェクト名でアクセス
import モジュール名 as 別名
# 単純名でアクセス。多用すべきでない
from モジュール名 import 名前1, 名前2, ...
# 原則として使うべきでない
from モジュール名 import *
# 名前に別名を与える
from モジュール名 import 名前 as 別名
- ディレクトリをPYTHONPATHに設定する。
例外処理
class MyException(Exception):
"""マイ例外"""
pass
try:
raise MyException
except MyException as e:
print(traceback.format_exc()) # トレースバックを表示
except (例外1, 例外2) as e: # 複数の例外をタプルとして書ける
スイート
except Exception as e: # 事実上の最上位クラス
スイート
except: # 全ての例外をキャッチ。ただしExceptionで捕捉すべき。
スイート
else: # 省略可能。例外が捕捉されなかった場合に行う正常処理。
スイート
finally: # 省略可能。例外発生の有無に関わらず行う後処理。
スイート
try:
スイート
finally:
スイート
Tips
ファイル処理
テキストファイルを1行ずつ読み書きするサンプル
with open("input.txt", mode="r", encoding="utf-8") as fi, \
open("output.txt", mode="w", encoding="utf-8") as fo:
for line in fi: # fiはイテラブルオブジェクト
# lineには最後の改行文字も含まれている
fo.write(line)
sleep
import time
time.sleep(秒)
ロギング
参考サイト:
https://qiita.com/knknkn1162/items/87b1153c212b27bd52b4
以下、コード例:
モジュール自作側
import logging
logger = logging.getLogger(__name__)
...
# コード中のログ出力したい場所に埋め込む
logger.debug(文字列)
logger.info(文字列)
モジュール利用側
import logging
from pathlib import Path
import os
import datetime
def make_logfile_path(file: str) -> str:
"""
ログファイル名の作成
Parameters
----------
file: str
__file__が渡されることを想定
Returns
-------
anonymous: str
フルパスのログファイル名
"""
log_dir = Path.cwd() / "log"
# ディレクトリがなければ作成
log_dir.mkdir(parents=True, exist_ok=True)
return log_dir / os.path.basename(os.path.splitext(file)[0] + datetime.datetime.today().strftime("_%Y%m%d_%H%M%S.log"))
def get_logger(name: str, filepath: str):
"""
ロガーの取得
Parameters
----------
name: str
__name__が渡されることを想定
filepath: str
make_logfile_path(__file__)の戻り値が渡されることを想定
Returns
-------
anonymous
ログをコンソールに送るハンドラ
anonymous
ロガー
"""
# 設定例
LOG_LEVEL_FILE = logging.DEBUG # ログファイルには詳細に出力
LOG_LEVEL_CONSOLE = logging.INFO # コンソールには簡便に出力
# フォーマットを指定 (https://docs.python.jp/3/library/logging.html#logrecord-attributes)
_detail_formatting = "\n%(asctime)s %(levelname)-8s [%(module)s#%(funcName)s %(lineno)d]\n%(message)s"
logging.basicConfig(
level=LOG_LEVEL_FILE,
format=_detail_formatting,
filename=filepath
)
# ログをコンソールに送るハンドラconsoleを作成
console = logging.StreamHandler()
console.setLevel(LOG_LEVEL_CONSOLE)
console_formatter = logging.Formatter(_detail_formatting)
console.setFormatter(console_formatter)
# ロガーを取得し、consoleハンドラを追加する
logger = logging.getLogger(name)
logger.addHandler(console)
return console, logger
###################################################################
console, logger = get_logger(__name__, make_logfile_path(__file__))
def main():
logging.getLogger(使うモジュール名1).addHandler(console)
logging.getLogger(使うモジュール名2).addHandler(console)
...
# コード中のログ出力したい場所に埋め込む
logger.debug(文字列)
logger.info(文字列)
pytest
PyCharmでpytestを使う設定:
https://pleiades.io/help/pycharm/pytest.html
- 以前使っていた各種ファイル
[pytest]
#addopts = -rsxX -l --tb=short --strict -v --runxfail
addopts = -rsxX -l --strict -v -x --capture=no
xfail_strict = true
import pytest
import time
import sys
import os
sys.path.append(os.path.join(os.path.dirname(__file__), '..'))
from src import common
from tests import tescommon
console, logger = common.get_logger(__name__, tescommon.stub_make_logfile_path("test"))
@pytest.fixture(autouse=True, scope='session')
def session_scope():
"""Report test durations after each session."""
start = time.time()
logger.info(f"pytest started : {time.strftime('%d %b %X', time.localtime(start))}")
yield
stop = time.time()
delta = stop - start
logger.info(f"pytest finished : {time.strftime('%d %b %X', time.localtime(stop))}")
logger.info("pytest session duration : {:0.3} seconds".format(delta))
@pytest.fixture(autouse=True)
def function_scope():
"""Report test durations after each function."""
start = time.time()
yield
stop = time.time()
delta = stop - start
logger.info("pytest function duration : {:0.3} seconds".format(delta))
@pytest.fixture(scope="session")
def pg_server_conn():
# DB接続 & テスト呼び出し
with common.connect_pg_server() as conn:
yield conn
@pytest.fixture(scope="session")
def sqlite_conn():
if common.is_in_docker_container():
yield None
else:
# DB接続 & テスト呼び出し
with common.connect_sqlite() as conn:
yield conn