コンテンツにスキップ

Python のコード例

複数の値を返す・受け取る

1
2
3
4
5
6
def sample() -> tuple[int, int]:
    return 0, 1


x, y = sample()
print(x, y) # 0 1

メインファイル

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
import sys


def main() -> None:
    print("Hello, World!")


if __name__ == "__main__":
    try:
        main()
        sys.exit(0)
    except Exception as e:
        print(e, file=sys.stderr)
        sys.exit(1)

print()

1
2
3
4
5
6
a = 100
# 標準出力に出力する
print(f"Hello, World {a}")

# 標準エラー出力に出力する
print("stderr", file=sys.stderr)

コマンドライン引数

main.py
1
2
3
import sys

print(sys.argv)
1
2
$ python main.py aaa bbb ccc "ddd"
['main.py', 'aaa', 'bbb', 'ccc', 'ddd']

ファイル読み書き/JSON ファイル読み書き

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
import base64
import json


def read_all_text(filename, encoding="utf-8") -> str:
    """テキストファイルを読み込む

    Args:
        filename (_type_): ファイル名
        encoding (str, optional): エンコーディング(例: "utf-8", "shift-jis", "cp932")

    Returns:
        str: テキスト
    """
    with open(filename, "r", encoding=encoding, errors="replace") as f:
        return f.read()


def write_all_text(filename, text: str, encoding="utf-8") -> None:
    """テキストファイルに書き込む

    Args:
        filename (_type_): ファイル名
        text (str): テキスト
        encoding (str, optional): エンコーディング(例: "utf-8", "shift-jis", "cp932")
    """
    with open(filename, "w", encoding=encoding, errors="replace") as f:
        f.write(text)


def read_file_as_base64(filename: str) -> str:
    """ファイルをBASE64エンコードで読み込む

    Args:
        filename (str): ファイル名

    Returns:
        str: _description_
    """
    with open(filename, "rb") as f:
        return base64.b64encode(f.read()).decode("utf-8")


def read_json_as_dict(filename: str, encoding="utf-8") -> dict:
    """JSONファイルをdictとして読み込む

    Args:
        filename (str): ファイル名
        encoding (str, optional): エンコーディング(例: "utf-8", "shift-jis", "cp932")

    Returns:
        dict: dict
    """
    return json.loads(read_all_text(filename, encoding=encoding))


def write_dict_as_json(filename: str, dict: dict, encoding="utf-8", ensure_ascii=True, indent=2) -> None:
    """dictをJSONファイルとして書き込む

    Args:
        filename (str): ファイル名
        dict (dict): dict
        encoding (str, optional): エンコーディング(例: "utf-8", "shift-jis", "cp932")
        ensure_ascii (bool, optional): ASCII文字以外をエスケープする
        indent (int, optional): インデント数
    """
    with open(filename, mode="wt", encoding=encoding) as f:
        json.dump(dict, f, ensure_ascii=ensure_ascii, indent=indent)

素数を数える

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import math
import time


def is_prime(value):
    if value <= 1:
        return False

    if value == 2:
        return True

    if value % 2 == 0:
        return False

    int_sqrt = int(math.sqrt(value))
    for i in range(3, int_sqrt):
        if value % i == 0:
            return False

    return True


def get_prime(value):
    return list(filter(lambda x: is_prime(x), range(1, value)))


then = time.time()
primes = get_prime(100)
now = time.time()
print(f"{now - then}s elapsed")
print(primes)

リトライ処理

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import time


def raise_error():
    raise Exception("Something happened")

def attempt(func, name="無題", retry=3, interval=1) -> None:
    times = 0
    while True:
        try:
            print(f"{name}の試行開始: ({times + 1}/{retry})")
            func()
            print(f"{name}に成功")
            break
        except Exception as e:
            print(e)
            print(f"{name}の試行に失敗")
            times += 1
            if times >= retry:
                print(f"{name}に失敗")
                raise e
            else:
                time.sleep(interval)

attempt(lambda: raise_error(), "処理")

ログ

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import logging
import logging.handlers
import os

logging.basicConfig(level=logging.NOTSET, handlers=[])


def get_logger(name: str) -> logging.Logger:
    logger = logging.getLogger(name)
    format = "%(asctime)s,%(name)s,%(levelname)s,%(message)s"
    datefmt = "%Y-%m-%d %H:%M:%S"

    stream_handler = logging.StreamHandler()
    stream_handler.setLevel(logging.DEBUG)
    stream_handler.setFormatter(logging.Formatter(format, datefmt))

    os.makedirs("logs", exist_ok=True)
    file_handler = logging.handlers.RotatingFileHandler(
        "logs/app.log", encoding="utf-8", maxBytes=256*1024, backupCount=4)
    file_handler.setLevel(logging.DEBUG)
    file_handler.setFormatter(logging.Formatter(format, datefmt))

    logger.addHandler(stream_handler)
    logger.addHandler(file_handler)

    return logger
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
import common


def main():
    logger = common.get_logger(__name__)
    logger.debug("debug")
    logger.info("info")
    logger.warning("warning")
    logger.error("error")
    logger.critical("critical")
    print("Hello, World!")


if __name__ == "__main__":
    main()

MD5 を取得する

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
def get_md5(filename: str) -> str:
    """MD5を取得する

    Args:
        filename (str): ファイル名

    Returns:
        str: MD5文字列
    """
    with open(filename, "rb") as f:
        return hashlib.md5(f.read()).hexdigest()

7-Zip で圧縮する

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
import subprocess
from typing import Union


def compress(seven_zip_path: str, filename: list[str], output: str, compression_level: Union[int, None] = None, multithread=True, encrypt_header=False, password: Union[str, None] = None) -> None:
    """7-Zipで圧縮する

    Args:
        seven_zip_path (str): 7zのパス
        filename (list[str]): ファイル名もしくはディレクトリ名
        output (str): 出力ファイル名
        compression_level (Union[int, None], optional): 圧縮率(0, 3, 5, 7, 9). Defaults to None.
        multithread (bool, optional): マルチスレッドで圧縮する. Defaults to False.
        encrypt_header (bool, optional): ヘッダーを暗号化する. Defaults to False.
        password (Union[str, None], optional): パスワード. Defaults to None.
    """
    commands = [seven_zip_path, "a"]
    if compression_level is not None:
        commands.append(f"-mx={compression_level}")
    if multithread is False:
        commands.append("-mmt=off")
    if encrypt_header is True:
        commands.append(f"-mhe=on")
    if password is not None:
        commands.append(f"-p{password}")
    commands.extend([output, *filename])
    result = subprocess.run(commands)
    if result.returncode != 0:
        raise Exception(f"Failed to compress \"{', '.join(filename)}\"")

snake_case―UpperCamelCase―lowerCamelCase 間を変換する

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
def snake_case_to_upper_camel_case(value: str) -> str:
    """example_value -> ExampleValue

    Args:
        value (str): 値

    Returns:
        str: 変換後の値
    """
    return "".join(v.capitalize() for v in value.lower().split("_"))


def snake_case_to_lower_camel_case(value: str) -> str:
    """example_value -> exampleValue

    Args:
        value (str): 値

    Returns:
        str: 変換後の値
    """
    return "".join(v.capitalize() if i >= 1 else v for i, v in enumerate(value.lower().split("_")))


def lower_camel_case_to_snake_case(value: str) -> str:
    """exampleValue -> example_Value

    Args:
        value (str): 値

    Returns:
        str: 変換後の値
    """
    return re.sub("([A-Z])", lambda x: "_" + x.group(1).lower(), value)

関数型プログラミング

map

1
2
3
before = [0, 1, 2, 3, 4, 5]
after = list(map(lambda x: x * 2, before))
print(after)

JavaScript の find

1
2
console.log(["aaa", "bbb", "ccc"].find((x) => x.includes("a"))); // aaa
console.log(["aaa", "bbb", "ccc"].find((x) => x.includes("d"))); // undefined
1
2
print(next(filter(lambda x: "a" in x, ["aaa", "bbb", "ccc"]), None)) # aaa
print(next(filter(lambda x: "a" in x, ["aaa", "bbb", "ccc"]), None)) # None

map() で連番

1
2
3
4
5
def get_new_filename(i: int, filename: str) -> str:
    print(i, filename)
    return str(i).zfill(2) + "_" + filename

new_filenames = list(map(lambda x: get_new_filename(*x), enumerate(filenames)))

オブジェクトのメンバでソートする

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
class Item:
    def __init__(self, id: int, name: str) -> None:
        self.id = id
        self.name = name

items = [
    Item(0, "ううう"),
    Item(1, "いいい"),
    Item(2, "あああ"),
    Item(3, "ZZZ"),
    Item(4, "YYY"),
    Item(5, "XXX"),
]

sorted_items = sorted(items, key=lambda x: x.name)

for item in sorted_items:
    print(item.id, item.name)
1
2
3
4
5
6
5 XXX
4 YYY
3 ZZZ
2 あああ
1 いいい
0 ううう

UUID4

1
2
3
import uuid
id = str(uuid.uuid4())
print(id)   # UUIDv4の文字列

0 埋め・空白

1
2
3
4
print(str(1).zfill(4))  # 0001
print("{:0>4}".format(str(1)))  # 0001

spaces = " " * 10 # "          "

標準入力

1
result = input()

コマンド実行 (subprocess)

コマンド実行

1
2
3
4
import subprocess

result = subprocess.run(["ls", "-l"], capture_output=True, text=True)
print(result.stdout)

スタックトレースを出力させる

1
2
3
4
5
6
7
import traceback

try:
    raise Exception()
except Exception as e:
    print(traceback.format_exc())
    print(e)

正規表現 (re)

正規表現でマッチするか判定

re.search()が返すのは検索結果のオブジェクトだが、以下のように if 文で有無の判定ができる。

1
2
3
4
import re

if re.search("bbb", "aaabbbccc"):
    print("matched")

正規表現で抽出

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
import re

html = """<html>
  <body>
    <h1>Hello, World</h1>
  </body>
</html>"""

result = re.findall(r'<h1>(.*?)</h1>', html)

print(result) # ['Hello, World']

マッチした文字列の 1 番目を取得する

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
import re

markdown = """# 見出し1

## 見出し2

### 見出し3

"""

title = next(iter(re.findall(r'# (.+)', markdown)), None) # 見出し1

日付・日時 (datetime)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
from datetime import UTC, datetime, timedelta

# 現在時刻の取得
weekdays = "月火水木金土日"
now = datetime.now()
print(f"年: {now.year}, 月: {now.month}, 日: {now.day}, 曜日: {weekdays[now.weekday()]}, 時: {now.hour}, 分{now.minute}, 秒: {now.second}, マイクロ秒: {now.microsecond}")

# YYYYMMDDHHMMSS
print(now.strftime("%Y%m%d%H%M%S"))
print(now.strftime("%Y%m%d%H%M%S"))

# 文字列から日付に変換
threshold = datetime.strptime("2022/01/01 00:00:00", "%Y/%m/%d %H:%M:%S")
print(threshold)

# 日付同士の差分を取る
delta = now - threshold
print(f"{delta.days}日経過")

## UTC時刻をJSON文字列形式で取得する
now = datetime.now(UTC).strftime("%Y-%m-%dT%H:%M:%S.%f")[:-3] + "Z"

# 以下の書き方は古い
# datetime.utcnow().strftime("%Y-%m-%dT%H:%M:%S.%f")[:-3] + "Z"

# 加算・減算する
yesterday = now + timedelta(days=-1)

%Yなどのフォーマット指定子については以下を参照すること。

datetime --- 基本的な日付型および時間型 — Python 3.11.0b5 ドキュメント

JSON の日時

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
import datetime


def json_date_to_datetime(json_date: str) -> datetime.datetime:
    """JSONの日付文字列をdatetimeに変換する

    Args:
        json_date (str): JSONの日付文字列

    Returns:
        datetime: datetime
    """
    return datetime.datetime.strptime(json_date, "%Y-%m-%dT%H:%M:%S.%fZ")


def datetime_to_json_date(dt: datetime.datetime) -> str:
    """datetimeをJSONの日付文字列に変換する

    Args:
        dt (datetime): datetime

    Returns:
        str: JSONの日付文字列
    """
    return dt.strftime("%Y-%m-%dT%H:%M:%S.%f")[:-3] + "Z"


def json_time_to_time(json_time: str) -> datetime.time:
    """JSONの時刻文字列をtimeに変換する

    Args:
        json_time (str): JSONの時刻文字列

    Returns:
        time: time
    """
    return datetime.datetime.strptime(json_time, "%H:%M:%S").time()


def time_to_json_time(t: datetime.time) -> str:
    """timeをJSONの時刻文字列に変換する

    Args:
        t (time): time

    Returns:
        str: JSONの時刻文字列
    """
    return t.strftime("%H:%M:%S")

ファイル・ディレクトリ・パス

ファイル・ディレクトリの存在を確認する

1
2
3
4
5
import os

print(os.path.isfile("C:\\sample.txt")) # -> True or False

print(os.path.isdir("C:\\")) # -> True or False

ファイルの存在チェック

1
2
3
import os

print(os.path.isfile("C:\\")) # -> True or False

ディレクトリを再帰的に作成する(既存の場合は作成しない)

1
2
3
import os

os.makedirs(f"output/{datetime.datetime.now().strftime('%Y%m%d%H%M%S')}", exist_ok=True)

ファイルを削除する

1
2
3
import os

os.remove(filename)

ディレクトリを中身ごと再帰的に削除する

1
2
3
import shutil

shutil.rmtree(directoryname)

ファイルをコピーする

1
2
3
import shutil

shutil.copy(filename, destination)

実行中の py ファイルのディレクトリと py ファイル名を取得する

1
$ python /path/to/pyfile.py
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
import os

print(os.path.dirname(__file__)) # /path/to
print(os.path.basename(__file__)) # pyfile.py

# 実行中のファイルと同階層のsetting.jsonの絶対パス
print(os.path.join(os.path.dirname(__file__), "setting.json"))

# 一階層上を取得する
print(os.path.join(os.path.dirname(__file__), os.pardir)) # /path/to/..

ファイルパスを分解する

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
import os

# ファイルパスからファイル名.拡張子を取得する
print(os.path.basename("path/to/filename.ext")) # filename.ext

# ファイルパスからファイル名と拡張子と分けて取得する
filename, ext = os.path.splitext("path/to/filename.ext")
print(filename, ext)  # filename .ext

print(os.path.splitext("filename.ext")) # ('filename', '.ext')
print(os.path.splitext("path/to/filename.ext")) # ('path/to/filename', '.ext')
print(os.path.splitext("path/to/filename")) # ('path/to/filename', '')

# ファイルパスからディレクトリパスを取得する
print(os.path.dirname("path/to/filename.ext")) # path/to

ファイルパスの一覧を取得する

1
2
3
4
# 同じディレクトリ
filepaths = list(map(lambda x: x.as_posix(), Path(os.path.dirname(__file__)).glob("*.py")))
# 再帰的
filepaths = list(map(lambda x: x.as_posix(), Path(os.path.dirname(__file__)).glob("**/*.py")))

テキストファイル読み書き

Python ユーザー必見!知らなきゃ損する小ネタ集 #Python - Qiita

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
from pathlib import Path

# docsディレクトリ内の再帰的に検索しmdファイルのパスを取得する
filepaths = list(map(lambda x: x.as_posix(), Path("docs").glob("**/*.md")))

# ファイル読み込み
# `encoding`には`utf-8`、`utf-16`、`iso-8859-1`、`shift_jis`、`euc_jp`、`cp932`、`ascii`などが指定できる。
text = Path("path/to/file.md").read_text(encoding="utf-8", errors="replace")

# ファイル書き込み(末尾に改行は自動的に入らないので注意)
Path("path/to/file.md").write_text("Hi\n")

同階層の json ファイル読み込み

1
2
3
4
5
import json
import os
import pathlib

signals = json.loads(pathlib.Path(os.path.join(os.path.dirname(__file__), "signals.json")).read_text(encoding="utf-8", errors="replace"))

辞書 (dict)

ネストされた辞書をマージする

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
import copy
import json

def get_merged_dict(a: dict, b: dict) -> dict:
    """ネストされた辞書をマージした辞書を返す
    競合する値はbで上書きされる

    Args:
        a (dict): 辞書
        b (dict): 辞書

    Returns:
        dict: aとbをマージした辞書
    """
    result = copy.deepcopy(a)
    for key, value in b.items():
        # 以下のすべての条件を満たすと辞書同士となり、辞書同士をマージする
        # 1. マージ対象の値valueの型がdictである
        # 2. マージ元resultにkeyが存在する
        # 3. マージ元result[key]の型がdictである
        if isinstance(value, dict) and key in result and isinstance(result[key], dict):
            result[key] = get_merged_dict(result[key], value)
        else:
            result[key] = value
    return result


a = {"key1": {"key11": "value1"}, "key2": {"key21": "value2"}}
b = {"key1": {"key12": "value3"}, "key2": {"key21": "overrided", "key23": "value5"}, "key3": {"key31": "value6"}}
c = get_merged_dict(a, b)
print("a", json.dumps(a, indent=2))
print("b", json.dumps(b, indent=2))
print("c", json.dumps(c, indent=2))

辞書をソートする

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
import json

def get_sorted_dict(d: dict) -> dict:
    """ソートした辞書を返す
    再帰にも対応しているが、値はstrやintなどプリミティブな値にしか対応していない点に注意

    Args:
        d (dict): 辞書

    Returns:
        dict: ソートした辞書
    """
    j = json.dumps(d, sort_keys=True, ensure_ascii=False)
    return json.loads(j)

print(get_sorted_dict({
    "c": {
        "c3": "c3value",
        "c2": "c2value",
        "c1": "c1value",
    },
    "b": {
        "b3": "b3value",
        "b2": "b2value",
        "b1": "b1value",
    },
    "a": {
        "a3": "a3value",
        "a2": "a2value",
        "a1": "a1value",
    }
}))

# {'a': {'a1': 'a1value', 'a2': 'a2value', 'a3': 'a3value'}, 'b': {'b1': 'b1value', 'b2': 'b2value', 'b3': 'b3value'}, 'c': {'c1': 'c1value', 'c2': 'c2value', 'c3': 'c3value'}}