Help us understand the problem. What is going on with this article?

オレオレPythonコードスニペット集

随時更新

文法

# タプル
from typing import Tuple
def some_func() -> Tuple[str, bool]:
    pass

# リストと辞書
from typing import Dict, List
somelist: List[Dict] = []

ワンライナーちっくなの

環境変数の利用

環境変数に定義してあればその値を利用して、なければデフォルト値を設定する。
サンプルは環境変数dryrunがあればその値をboolのTrue/Falseに変換し、なければFalseを設定する。

import os
from distutils.util import strtobool
dryrun = strtobool(os.environ['dryrun']) if 'dryrun' in os.environ else False

リスト生成

セミコロン区切りのhostsを分解して、値があるものだけでリストを作る。
前後の空白は取り除く。

results = [host.strip() for host in hosts.split(';') if not host.strip() == '']

文字列操作

文字列置換

'cp conf/env-%(stage)s.conf %(release)s/settings.conf' % {
    'release': env.current_release,
    'stage': env.stage
}

Python3.6以上なら

f'cp conf/env-{env.current_release}.conf {env.stage}/settings.conf'

処理

スクリプト化

#!/usr/bin/env python

def main():
    print('hoge')

if __name__ == '__main__': main()

日付系

JSTで1日前

from datetime import datetime, timedelta, timezone

JST = timezone(timedelta(hours=+9), 'JST')
yesterday = datetime.now(JST) + timedelta(days=-1)

datetimeオブジェクトの内容を変更
(例:時分秒を0にする)

from datetime import datetime
today = (datetime.today()).replace(hour=0,minute=0,second=0,microsecond=0)

YYYYMMDD形式の文字列をdatetimeへ変換

from datetime import datetime
yyyymmdd_datetime = datetime.strptime("20190321", '%Y%m%d')

datetimeを文字列化

date_str = datetime.now(JST).strftime('%Y%m%d')

月の操作

datetimeでmonthが操作できないのでdateutil.relativedeltaを使う。
dateutilを使うためにpython-dateutilをインストール

$ pip install python-dateutil

17ヶ月前の月頭(1日)にする場合

from dateutil.relativedelta import relativedelta
from datetime import datetime, timedelta, timezone

JST = timezone(timedelta(hours=+9), 'JST')

today = datetime.now(JST)
before_seventeenth_month = today + relativedelta(months=-17)
start_date = before_seventeenth_month.replace(day=1)

ファイル操作

ディレクトリの削除(エラーは無視)

import shutil
shutil.rmtree('ディレクトリパス', ignore_errors=True)

ファイル一覧取得

https://docs.python.org/ja/3/library/glob.html

import glob
file_list = glob.glob("/hoge_dir/*")
# -> file_list = [hoge.txt, hoo.jpg]
from pathlib import Path

def get_target_files(target_dir):
    """
    指定ディレクトリ配下のファイル情報を取得する。
    無ければ空のリストを返す。
    Parameters
    ----------
    target_dir : str
       対象ディレクトリ

    Returns
    -------
    list(PosixPath) :
        対象ディレクトリ配下のファイルパスのリスト
    """
    p = Path(target_dir)
    target_files = list(p.glob("*"))
    return target_files

filepath_list = get_target_files("/hoge_dir")
for filepath in filepath_list:
    print(str(filepath))
    # -> /hoge_dir/hoge.txt

IPアドレスチェック

import ipaddress

allowed_hosts = [
    '127.0.0.1',
    '172.27.0.0/24'
]

def check_host(host_addr):
    for allowed_host in allowed_hosts:
        if is_valid_ip(allowed_host):
            if host_addr == allowed_host:
                return True
        elif is_valid_network(allowed_host):
            if ipaddress.ip_address(host_addr) in ipaddress.ip_network(allowed_host):
                return True
        else:
            raise Exception(f"allowed_hostsに不正な値が設定されている: {allowed_host}")
    return False


def is_valid_ip(ip_addr):
    try:
        ipaddress.ip_address(ip_addr)
        return True
    except ValueError:
        return False


def is_valid_network(ip_addr):
    try:
        if not is_valid_ip(ip_addr):
            ipaddress.ip_network(ip_addr)
            return True
    except ValueError:
        return False

AWS

boto3

SNSのMessage部分を取る方法

event['Records'][0]['Sns']['Message']

Lambda

eventパラメータチェック

キーがあればその値、なければデフォルト値を使う。

ignore_folders = []
if 'ignore_folders' in event and type(event['ignore_folders']) is list:
    ignore_folders = event['ignore_folders']

関数系

コンフィグファイルローダー

特定箇所にあるコンフィグファイルを読み込む。

configディレクトリまでの階層によって、場所指定は適時変える。
以下はconfig/とsrc/utils/config.pyの階層構造を想定した場合。

https://docs.python.jp/3/library/configparser.html

config.py
import os
import configparser


def get_config(config_file_name):
    """
    configディレクトリ配下のコンフィグファイルをロードして返却する

    Parameters
    ----------
    config_file_name : str
        ファイル名

    Returns
    -------
    SafeConfigParser
        ロードしたコンフィグ情報
    """
    config_path = os.path.join(
        os.path.dirname(__file__), '../../config/' + config_file_name)
    config = configparser.SafeConfigParser()
    config.read(config_path, encoding='utf8')

    return config

コンフィグロードをテストする

settings.ini
[dev]
target_names = ["seri", "wb"]
test_sample.py
import os
import configparser
import json


def test_get_config_settings():
    config_file_name = 'settings.ini'
    config_path = os.path.join(
        os.path.dirname(__file__), '../config/' + config_file_name)
    config = configparser.SafeConfigParser()
    config.read(config_path, encoding='utf8')

    actual = json.loads(config['dev']['target_names'])[0]

    expected = 'seri'
    assert actual == expected

設定系

flake8の定義ファイル

コードチェック用

.flake8
[flake8]
ignore = D203, E401, E265
exclude =
    .git,
    __pycache__,
    bin,
    lib,
    build,
    dist
max-line-length = 120

blackの定義ファイル

コードフォーマッタ

pyproject.toml
[tool.black]
line-length = 120
exclude = '''
(
    migrations
    | .mypy_cache
    | .pytest_cache
    | .tox
    | venv
)
'''

VSCode

venv環境を認識させる

.vscode/settings.json
{
    "python.pythonPath": "bin/python3"
}

pythonでのテスト実行

デフォルトだとtest_ほげほげ.pyをテストとして見るので、パターンが違う場合は-pで指定する

python -m unittest discover tests
python -m unittest discover -p "*_test.py" tests

サブディレクトリまでは見ないので、そういう場合はこんな感じのことをするらしい
https://qiita.com/yoichi22/items/2b488dc0696d9b45fad6

テストのスキップ

import unittest

class SampleTest(unittest.TestCase):

    @unittest.skip('スキップ理由などのコメント')
    def test_add_number_plus_double(self):
        pass

Tips

3系からはファイルの先頭にUTF-8指定を書かなくても、デフォルトでUTF-8固定になった。

# -*- coding:utf-8 -*-

Zen of Python

REPLでimport thisを実行すると表示される

>>> import this
The Zen of Python, by Tim Peters

Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Flat is better than nested.
Sparse is better than dense.
Readability counts.
Special cases aren't special enough to break the rules.
Although practicality beats purity.
Errors should never pass silently.
Unless explicitly silenced.
In the face of ambiguity, refuse the temptation to guess.
There should be one-- and preferably only one --obvious way to do it.
Although that way may not be obvious at first unless you're Dutch.
Now is better than never.
Although never is often better than *right* now.
If the implementation is hard to explain, it's a bad idea.
If the implementation is easy to explain, it may be a good idea.
Namespaces are one honking great idea -- let's do more of those!

ナレッジ(おまけ)

Pythonでコーディングしてた際に詰まったところやナレッジのまとめ。

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away