Python
tool
JSON整形
$ ... | python -m json.tool
httpサーバ
$ python3 -m http.server 8080
$ python2 -m SimpleHTTPServer 8080
インフラ系エンジニアが唯一覚えるべきPythonの使い方 - Qiita
実装
演算子
比較(==)
Pythonは文字列も==
を使う。
以下の比較処理は真。
string = "foobar"
if string == "foobar":
print("string is 'foobar'")
配列の比較も==
で行う。以下はtrue
obj_list = [1, 2, 3, "abc"]
if obj_list == [1, 2, 3, "abc"]:
print("true")
というか==
はオブジェクトの比較に使えるってことかな。
制御構文
for
foreach式のループ処理は以下。
for item in items:
# itemにリストの要素がセットされる
ワンライナーでも書ける。
ループ処理した結果をリストにセットしたい場合など。
その際は[...]
で囲む。
result = [ ..itemに対する処理.. for item in items]
BeautifulSoupを使ったスクレイピングの記述例
links = [li.find('a').get('href') for li in s.find_all('li', class_='toctree-l2')]
print(links)
break
ループを途中で抜けるにはbreak
continue
次のループへ飛ぶにはcontinue
変数
環境変数の参照
import os
aws_access_key = os.environ.get("AWS_ACCESS_KEY_ID")
if aws_access_key is None:
print("AWS_ACCESS_KEY_ID not defined")
型の確認
type(value)
None判定
if data is not None:
# dataがNoneでない場合
引数
コマンドライン引数
sys.argv
を使う
import sys
args = sys.argv
print(args)
print(args[0])
print(args[1])
$ ./argv.py hello
['./argv.py', 'hello']
./argv.py
hello
関数
main
#!/usr/bin/python3
def func():
print("begin func()")
if __name__ == "__main__":
print("begin main()")
func()
assert
特にimport
は不要で使用可能
# aの値が10であること
assert(a == 10)
リスト処理
リストの定義
items = [
'foo',
'bar',
'baz',
]
空リスト
items = []
要素の追加
リストオブジェクトのappend()
メソッドを使う。
items.append('qux')
リストの結合
複数のリストを結合(1つのリストに全要素をまとめる)するには+
を使う
list1 = ['foo', 'bar', 'baz']
list2 = [1, 2, 3]
print(list1 + list2)
実行結果は['foo', 'bar', 'baz', 1, 2, 3]
になる。
+=
で代入も可能。
length
array = []
array.append('2')
array.append('3')
array.append('4')
print(len(array))
3になる。
要素のindex
array = ['curry', 'beef', 'chicken', 'vegetable']
beef_index = array.index('chicken')
print(beef_index)
引数に指定した要素が配列中の何番目にあるかを返す。上記の出力は2
存在しない場合はValueErrorになる。
array = ['curry', 'beef', 'chicken', 'vegetable']
beef_index = array.index('zzz')
print(beef_index)
$ python3 index.py
Traceback (most recent call last):
File "index.py", line 3, in <module>
beef_index = array.index('zzz')
ValueError: 'zzz' is not in list
要素の有無
in
を使う。
array = ['curry', 'beef', 'chicken', 'vegetable']
print('curry' in array)
print('pork' in array)
出力は以下になる。
True
False
スライス
array = ['curry', 'beef', 'chicken', 'vegetable']
print(array[2:])
print(array[:3])
print(array[1:2])
実行結果は以下
$ python3 slice.py
['chicken', 'vegetable']
['curry', 'beef', 'chicken']
['beef']
書式はarray[始点:終点]
。省略時は最初からor最後まで。
始点はindexを「含む」が、終点はindexを「含まない」ので注意。
(要素のindexというより、区切り位置のindexと見なすと良い)
連番の定義
range(開始, 終了)
を使う
r = range(0, 9)
これでr
は[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
となる。
正確にはrange型のためリストではないが、list(r)
を使うとリストになる。
1引数の場合は開始0
とみなされる。(0開始の場合は終了の値のみでOK。↑はrange(9)
と同値)
集合
集合の定義
data_set = {200, 201, 202, 203, 204, 301, 401, 403, 404}
print(type(data_set))
# 出力は <class 'set'>
辞書
辞書の定義
dict1 = {"key1": "value1", "key2": "value2"}
dict2 = dict(key1="value1", key2="value2")
この場合、dict1
とdict2
の中身は同じ。
dict1 == dict2
の結果もtrueになる。
辞書要素のループ(キーと値の組み合わせを取得)
sample_dict = {
"key1": "value1",
"key2": "value2",
"key3": "value3",
}
for k, v in sample_dict.items():
print("key: " + k + ", value: " + v)
辞書.items()
で、キーと値のペアのリストを得られるので、これでループできる。
上のコードの実行結果は以下の通り。
key: key1, value: value1
key: key2, value: value2
key: key3, value: value3
辞書->YAML化
import yaml
print(yaml.dump(dict_data, default_flow_style=False))
子要素がフロースタイルになってブロックスタイルにならない場合はdefault_flow_style
のオプションをFalse
指定する。
YAMLファイル->辞書化
import yaml
with open('config.yaml', 'r') as yml:
config = yaml.safe_load(yml)
これでconfig
変数にYAMLファイルに定義した情報が辞書型オブジェクトとしてセットされる。
辞書->JSON化
import json
print(json.dumps(dict_data))
JSON文字列->辞書(オブジェクト)
import json
obj = json.loads(json_strings)
YAMLと違ってメソッド名はdumps()
でs
が付与される。
dump()
は、ファイル出力を行うメソッドとして別に存在する。
JSONファイル->オブジェクト
STDINの場合は
import json
with sys.stdin as f:
obj = json.load(f)
文字列
指定文字で始まるか
if string_value.startswith("kube"):
# 文字列が"kube"で始まる場合
末尾1文字
sample_str[-1]
末尾1文字を削った文字列
sample_str[:-1]
splitlines()
テキストの末尾に改行がある場合、split('\n')
を使うと末尾の改行もデリミタと認識し、リストの最終要素は空文字になるが、splitlines()
であれば末尾の改行分は無視される。行ごとに処理したい場合はこちらが便利。
ゼロパディング
文字列のzfill()メソッドを使う。
引数の数値の桁数長にゼロパディングされた文字列を返す。
int_sample = '123'
str_sample = 'foo'
print(int_sample.zfill(8))
print(str_sample.zfill(8))
実行結果は以下。
00000123
00000foo
正規表現処理
re.search()で文字列判定
import re
if re.search(regexp, string):
# stringがregexpにマッチ
re.match()
は先頭からマッチを行うため、文字列の途中にはマッチしない。
re.sub()で置換
import re
string = '/path/to/sample.txt'
# s/a/A/
string = re.sub(r'a', 'A', string)
print(string)
# 末尾の.txtを削る
string = re.sub(r'\.txt$', '', string)
print(string)
# 't'と'p'を(t)か(p)に変換
string = re.sub(r'([tp])', r'(\1)', string)
print(string)
# r'...'を使わない場合は'\\1'と書く
# aを変換。ただし大文字小文字無視
string = re.sub(r'a', r'_', string, flags=re.IGNORECASE)
print(string)
# re.sub(pattern, repl, string, count=0, flags=0) なので、countを省略するためには"flags="という名前付き引数を使う
上記コードの実行結果は以下の通り。
$ python replace.py
/pAth/to/sAmple.txt
/pAth/to/sAmple
/(p)A(t)h/(t)o/sAm(p)le
/(p)_(t)h/(t)o/s_m(p)le
出力
標準エラー出力
import sys
print("error", file=sys.stderr)
printで改行無し
print("sample", end="")
ただし行ごとにフラッシュされるため、プログレス表示的に使う場合は追加オプションが必要
print(".", end="", flush=True)
ヒアドキュメント
print("""
東京・お台場にある、自由な校風と専攻の多様さで人気の高校「虹ヶ咲学園」。
スクールアイドルの魅力にときめいた普通科2年の高咲侑は、
幼馴染の上原歩夢とともに「スクールアイドル同好会」の門を叩く。
""")
この場合、「開始の"""
」のあとの改行と、「終了の"""
」の手前の改行も含まれる。
byte型
変数出力するとb'....'
と表記され出力される。
byte_data = b'zzzzzzzz'
print(byte_data)
print(len(byte_data))
print(type(byte_data))
出力は以下。
b'zzzzzzzz'
8
<class 'bytes'>
byte -> string変換
string_data = byte_data.decode()
print(string_data)
print(type(string_data))
出力は<class 'str'>
string -> byte変換
byte_data = string_data.encode()
print(byte_data)
print(type(byte_data))
これで出力は<class 'bytes'>
path
ユーザーディレクトリを展開
os.path.expanduser
を使う。
[zaki@cloud-dev2 ~]$ grep -e ^zaki -e ^root /etc/passwd
root:x:0:0:root:/root:/bin/bash
zaki:x:1000:1000:zaki:/home/zaki:/bin/bash
というユーザーが存在する場合、実行結果は以下の通り。
[zaki@cloud-dev2 ~]$ whoami
zaki
[zaki@cloud-dev2 ~]$ python
Python 3.10.2 (main, Jan 17 2022, 00:00:00) [GCC 11.2.1 20211203 (Red Hat 11.2.1-7)] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import os.path
>>> print(os.path.expanduser('~zaki'))
/home/zaki
>>> print(os.path.expanduser('~root'))
/root
>>> print(os.path.expanduser('~'))
/home/zaki
>>>
外部コマンド実行
subprocess.run()
を使う。(os.system
はもう使わない)
import subprocess
ret = subprocess.run(["ls", "-l"], capture_output=True, text=True)
print(ret.stdout)
リストでなく文字列を指定したい場合はshell=True
を付与
subprocess.run("ls -l", shell=True)
通信
HTTP
Requests
を使うと楽。
import requests
r = requests.get("http://www.example.org")
POST
辞書型データをJSON形式のリクエストヘッダにするならjson.dumps
を使うと楽。
payload = json.dumps(dict_params)
headers = {
'Content-Type': 'application/json',
'Accept': 'application/json',
'Authorization': 'Bearer ' + token
}
requests.post(url, data=payload, headers=headers)
DELETE
POSTと同じ要領
requests.delete(url)
リクエストヘッダ
headers = {
'Accept': 'application/json',
'Authorization': 'Bearer ********'
}
requests.get(url, headers=headers)
レスポンス
print(r.status_code) # ステータスコード(200など)
print(r.request) # Requestオブジェクト
print(r.request.url) # アクセスしたURL
print(r.text) # レスポンスbody
REST APIのようにレスポンスがJSON形式のテキストの場合は、r.json()
を使うとオブジェクトとして値をとれる。
SSL検証無視
r = requests.get("https://www.example.org", verify=False)
Basic認証
requests.get(url, auth=HTTPBasicAuth(username, password))
接続タイムアウト設定
timeout_sec = 3
requests.get(url, timeout=timeout_sec)
接続エラーのハンドリング
例外が発生するのでそれをcatchする。
try:
requests.post(url)
except (requests.ConnectionError, requests.ConnectTimeout) as e:
print(e)
isinstance()で型チェック
bool_val = True
if (isinstance(bool_val, bool)):
print("bool_val is bool")
int_val = 123
if (isinstance(int_val, int)):
print("int_val is int")
list_val = [1, 2, 3]
if (isinstance(list_val, list)):
print("list_val is list")
dict_val = {"key1": "value1", "key2": "value2"}
if (isinstance(dict_val, dict)):
print("dict_val is dict")
実行結果は以下
bool_val is bool
int_val is int
list_val is list
dict_val is dict
例外
例外の捕捉
try
,except
を使う
try:
# 例外が発生するコード
except RuntimeError as e:
# エラーハンドリング処理
# エラー情報は"e"に入っている
print(e)
捕捉する例外が複数の場合は、except
を複数並べるか、カッコでまとめる。
try:
# 例外が発生するコード
except (RuntimeError, TypeError) as e:
print(e)
例外を投げる(raise)
raise RuntimeError('application error: ' + error_value)
標準で使用できる組み込み例外は以下。
組み込み例外
部分適用(partial application)
functiontools#partial()
を使う。-
引数(の一部を)固定した関数を作る
- functools --- 高階関数と呼び出し可能オブジェクトの操作 — Python 3.9.4 ドキュメント
こんな感じ。
https://github.com/ansible/ansible/blob/stable-2.10/lib/ansible/playbook/base.py#L105-L114
method = "_get_attr_%s" % attr_name
if method in src_dict or method in dst_dict:
getter = partial(_generic_g_method, attr_name)
elif ('_get_parent_attribute' in dst_dict or '_get_parent_attribute' in src_dict) and value.inherit:
getter = partial(_generic_g_parent, attr_name)
else:
getter = partial(_generic_g, attr_name)
setter = partial(_generic_s, attr_name)
deleter = partial(_generic_d, attr_name)
なるほどわからん
[Ansible] そのtag設定、想定通りに動いてますか? (継承機能とその実装を確認する) - zaki work log
メタクラス
クラスの定義をコードで動的に実装する感じ。
https://github.com/ansible/ansible/blob/stable-2.10/lib/ansible/playbook/base.py#L105-L114
method = "_get_attr_%s" % attr_name
if method in src_dict or method in dst_dict:
getter = partial(_generic_g_method, attr_name)
elif ('_get_parent_attribute' in dst_dict or '_get_parent_attribute' in src_dict) and value.inherit:
getter = partial(_generic_g_parent, attr_name)
else:
getter = partial(_generic_g, attr_name)
setter = partial(_generic_s, attr_name)
deleter = partial(_generic_d, attr_name)
環境
pip
バージョン指定
pip install ansible==9
「以上」の場合は以下
pip install ansible>=8
requirements.txtの利用
ansible==2.9.7
jmespath==0.10.0
という内容のrequirements.txt
ファイルを作成し、これで
$ pip install -r requirements.txt
※ ファイル名は任意
pipのアップデート
venvとか作った直後はpip
自体のバージョンが古いので(目的にパッケージ入れるときにエラーになる前に先に)アップデートする
※ そもそもvenv作成時に --upgrade-deps
をつけておけば自動更新されて環境が作成される。
$ pip install --upgrade pip
パッケージのダウングレード
基本的には古いバージョンを指定して以下でOK
$ pip install hogehoge=0.0.1
requirements.txt
記載の場合も、記載した指定バージョンがインストール済みのものより古ければ自動で更新される。
以下はrich==11.0.0
がインストール済みの状態でrich==9.5.1
をインストールした場合のログ。
Installing collected packages: typing-extensions, rich
Attempting uninstall: rich
Found existing installation: rich 11.0.0
Uninstalling rich-11.0.0:
Successfully uninstalled rich-11.0.0
Successfully installed rich-9.5.1 typing-extensions-3.10.0.2
パッケージアンインストール
$ pip uninstall docker
Found existing installation: docker 5.0.3
Uninstalling docker-5.0.3:
Would remove:
/home/ubuntu/.local/lib/python3.8/site-packages/docker-5.0.3.dist-info/*
/home/ubuntu/.local/lib/python3.8/site-packages/docker/*
Proceed (y/n)? y
Successfully uninstalled docker-5.0.3
-y
を付ければ強制削除になるっぽい。
Uninstall Options:
-r, --requirement <file> Uninstall all the packages listed in the given requirements file.
This option can be used multiple times.
-y, --yes Don't ask for confirmation of uninstall deletions.
全てアンインストール
$ pip uninstall -r <(pip freeze)
依存関係の確認
$ pip show ansible-core
:
:
Requires: cryptography, jinja2, packaging, PyYAML, resolvelib
Required-by: ansible
Required-byのansible
によってインストールされ、このパッケージによってcryptography
, jinja2
, packaging
, PyYAML
, resolvelib
がインストールされる。
pyenv
pyenv/pyenv: Simple Python version management
インストール
curl https://pyenv.run | bash
設定追加
cat <<'__EOL__' >> $HOME/.bashrc
export PYENV_ROOT="$HOME/.pyenv"
command -v pyenv >/dev/null || export PATH="$PYENV_ROOT/bin:$PATH"
eval "$(pyenv init -)"
__EOL__
. $HOME/.bashrc
依存パッケージのインストール
以下はUbuntuの場合。
sudo apt update; sudo apt install build-essential libssl-dev zlib1g-dev \
libbz2-dev libreadline-dev libsqlite3-dev curl \
libncursesw5-dev xz-utils tk-dev libxml2-dev libxmlsec1-dev libffi-dev liblzma-dev
その他の環境はwiki参照: https://github.com/pyenv/pyenv/wiki#suggested-build-environment
Pythonインストール
pyenv install 3.11.3
インストール可能なPythonはpyenv install -l
で確認
python
の切替
pyenv global 3.11.3
切替可能なPythonはpyenv versions
で確認
venv
作成
$ python3 -m venv venv/ansible
作成時にpipとsetuptoolsを更新する
$ python3 -m venv ~/venv/dev --upgrade-deps
systemのパッケージパスも参照
$ python3 -m venv venv-sys --system-site-packages
pyvenv
RHEL8に入ってるやつ。
[zaki@rhel8-node ~]$ pyvenv-3 venv3
WARNING: the pyenv script is deprecated in favour of `platform-python -m venv`
[zaki@rhel8-node ~]$ . venv3/bin/activate
(venv3) [zaki@rhel8-node ~]$
(venv3) [zaki@rhel8-node ~]$ pip install --upgrade pip
Cache entry deserialization failed, entry ignored
Collecting pip
Downloading https://files.pythonhosted.org/packages/47/ca/f0d790b6e18b3a6f3bd5e80c2ee4edbb5807286c21cdd0862ca933f751dd/pip-21.1.3-py3-none-any.whl (1.5MB)
100% |████████████████████████████████| 1.6MB 641kB/s
Installing collected packages: pip
Found existing installation: pip 9.0.3
Uninstalling pip-9.0.3:
Successfully uninstalled pip-9.0.3
Successfully installed pip-21.1.3
(venv3) [zaki@rhel8-node ~]$ pip list
Package Version
---------- -------
pip 21.1.3
setuptools 39.2.0
--system-site-packages
も併用可
[zaki@rhel8-node ~]$ pyvenv-3.6 venv --system-site-packages
WARNING: the pyenv script is deprecated in favour of `platform-python -m venv`
[zaki@rhel8-ansible-node ~]$ . venv/bin/activate
(venv) [zaki@rhel8-node ~]$ pip list
DEPRECATION: The default format will switch to columns in the future. You can use --format=(legacy|columns) (or define a format=(legacy|columns) in your pip.conf under the [list] section) to disable this warning.
cffi (1.11.5)
chardet (3.0.4)
configobj (5.0.6)
configshell-fb (1.1.28)
cryptography (3.2.1)
dbus-python (1.2.4)
decorator (4.2.1)
ethtool (0.14)
:
: