Python メモ2020年12月27日 | |
モジュールパスモジュールパスの設定は、環境変数 PYTHONPATH で行う。 export PYTHONPATH=~/mymodule:$PYTHONPATH モジュールのパスは sys.path で取得できる。 モジュールのキャッシュを作成させないモジュールをインポートしたときに作成されるキャッシュを作成させないようにするには、Python をオプション "-B" を指定して起動するか、環境変数 PYTHONDONTWRITEBYTECODE に 1 (値はなんでもよい) を設定すればよい。 できてしまったキャッシュを削除するには、Linux なら以下のようにする。 $ find -name __pycache__ | xargs rm -r ソースコードの文字コード文字コードに UTF-8 以外を用いるときは、ファイルの先頭に以下のように書く。 # -*- coding: shift-jis -*- mainif __name__ == "__main__": main() コマンドライン引数import sys argv = sys.argv argc = len(argv) コマンドライン引数の処理ArgumentParser でコマンドライン引数の処理を行える。 from argparse import ArgumentParser def get_args(): parser = ArgumentParser() parser.add_argument("path", type=str, help="file path") parser.add_argument("--enable_opt", action="store_true", help="enable option") return parser.parse_args() def main(args): path = args.path enable_opt = args.enable_opt ... if __name__ == "__main__": args = get_args() main(args) スクリプトの場所の取得import sys from pathlib import Path path = Path(sys.argv[0]).resolve().parent sys.argv[0] にはスクリプト実行時のカレントディレクトリからの相対パスが入り、resolve() は関数実行時のカレントディレクトリのパスをくっつけるので、カレントディレクトリが変更されていないことが前提になる。sys.argv[0] の代りに __file__ を用いる方法もあるが、Cython を用いた時にうまくいかない。 末尾の空白文字を削除# line は文字列 line = line.rstrip() # 末尾の空白文字を削除 line = line.lstrip() # 先頭の空白文字を削除 line = line.strip() # 先頭と末尾の空白文字を削除 ファイルから 1 行読み込んだときに改行文字を削除する場合は rstrip() を使えば良い。 文字を指定することもできる。たとえば、"xxx" から両側の引用符を取り除くには次のようにする。 line = line.strip("\"") 改行文字だけを取り除きたい場合は、次のようにすればよい。 line = line.strip("\r\n") 辞書のキーと値のリストの取得# d は辞書 key = list(d.keys()) value = list(d.values()) パスの結合import os os.path.join(path1, path2) あるいは from pathlib import Path Path(path1).joinpath(path2) ディレクトリ名とベース名の取得import os dirname, basename = os.path.split(path) dirname = os.path.dirname(path) basename = os.path.basename(path) あるいは from pathlib import Path dirname = Path(path).parent basename = Path(path).name 拡張子の取得import os basename, ext = os.path.splitext(path) あるいは from pathlib import Path basename = Path(path).stem ext = Path(path).suffix 正規表現検索 import re r = re.search(pattern, str) たとえば、次のように使う。 >>> s = '<a href="xxx">xxx</a>' >>> r = re.search('href=".+"', s) >>> r.group(0) 'href="xxx"' href の値を取り出すには次のようにする。 >>> r = re.search('href="(.+)"', s) >>> r.group(0) 'href="xxx"' >>> r.group(1) 'xxx' 検索では、最も長いものが取られる。 >>> r = re.search('<.+>', s) >>> r.group(0) '<a href="xxx">xxx</a>' 短いものを取るには、"?" をつける。 >>> r = re.search('<.+?>', s) >>> r.group(0) '<a href="xxx">' 何度も同じ検索を行うなら、コンパイルも使える。 >>> m = re.compile('href="(.+)"') >>> r = m.search(s) >>> r.group(1) 'xxx' 置換も行える。 str2 = re.sub(pattern, replace, str) 次のようになる。 >>> re.sub('href="(.+)"', 'href="yyy"', s) '<a href="yyy">xxx</a>' 検索にマッチしたグループをそのまま使うこともできる。 >>> re.sub('href="(.+)"', r'href="\g<1>2"', s) '<a href="xxx2">xxx</a>' ファイルの読み込みf = open(filename, "r") for line in f: line = line.rstrip() print(line) f.close() あるいは with open(filename, "r") as f: for line in f: line = line.rstrip() print(line) 文字コードを指定するには次のようにする。 f = open(filename, "r", encoding="utf-8") ファイルの最後の行だけ取り出しwith open(file, "r") as f: lines = f.readlines() line = lines[-1].rstrip() print(line) CSV ファイルの読み込みimport csv with open(filename, "r") as f: reader = csv.reader(f) header = next(reader) for line in reader: print(line) テキストファイルのコピーfrom pathlib import Path basename = Path(filename).stem ext = Path(filename).suffix filename_new = basename + "-new" + ext with open(filename, "r") as f1: with open(filename_new, "w") as f2: for line in f1: f2.write(line) テキストの検索と置き換えfrom pathlib import Path import re filename = "test.txt" basename = Path(filename).stem ext = Path(filename).suffix filename_new = basename + "-new" + ext with open(filename, "r") as f1: with open(filename_new, "w") as f2: for line in f1: if re.search("adelie penguin", line) is not None: line = re.sub("adelie penguin", "emperor penguin", line) f2.write(line) "adelie penguin" を "emperor penguin" に置き換える。 カレントディレクトリのファイルのリストを得るカレントディレクトリのファイルのリストを得るにはつぎのようにする。 import glob files = glob.glob("*") たとえば、CSV ファイルのリストが欲しい場合は、"*.csv" とする。"*" ではファイルが対象になるが、"*/" とするとディレクトリが対象になる。recursive オプションを True とすると、サブディレクトリも対象になる。"**/" とするとサブディレクトリも含めたディレクトリのリストが得られる。recursive=True かつ "**" とすると、サブディレクトリ内も含めた全ファイルのリストが得られる。 あるいは from pathlib import path files = list(Path().glob("*")) "**" を指定すると、サブディレクトリも含めたディレクトリのリストが得られる。"**/*" とすると、サブディレクトリ内も含めた全ファイルのリストが得られる。 カレントディレクトリ以下のファイルの巡回import os for root, dirs, files in os.walk("."): print(root) for file in files: print(os.path.join(root, file)) あるいは import glob for path in glob.glob("**", recursive=True): print(path) あるいは from pathlib import Path for path in list(Path().glob("**/*")): print(path) カレントディレクトリ以下のファイルのテキストの置き換えimport os import re def main(): workdir = os.getcwd() for root, dirs, files in os.walk("."): print(root) for file in files: basename, ext = os.path.splitext(file) if ext == ".txt": filename1 = os.path.join(root, file) filename2 = os.path.join(root, basename + ".new" + ext) print(filename1) f1 = open(filename1, "r") f2 = open(filename2, "w") for line in f1: if re.search("adelie penguin", line) != None: line = re.sub("adelie penguin", \ "emperor penguin", line) f2.write(line) f1.close() f2.close() os.remove(filename1) os.rename(filename2, filename1) if __name__ == "__main__": main() カレントディレクトリ以下の ".txt" ファイルの中にある文字列 "adelie penguin" を "emperor penguin" に置き換える。 ファイルの操作import os os.path.exists(path) # ファイルの存在を調べる os.remove(path) # ファイルの削除 os.rename(src, dist) # ファイル名の変更 あるいは from pathlib import Path Path(path).exists() # ファイルの存在を調べる Path(path).unlink() # ファイルの削除 Path(path).rename(new_path) # ファイル名の変更 時刻現在時刻の取得>>> str(datetime.datetime.now()) '2019-12-02 15:24:14.583495' >>> "{:%Y-%m-%d %H:%M:%S}".format(datetime.datetime.now()) 2019-12-02 15:28:02 UTC の変換>>> from datetime import datetime >>> dt = datetime.strptime("2019-07-30T00:00:00.000Z", "%Y-%m-%dT%H:%M:%S.%fZ") >>> dt.strftime("%Y/%m/%d") '2019/07/30' GMT の変換>>> from datetime import datetime >>> dt = datetime.strptime("Tue, 09 Jun 2020 08:01:04 GMT", "%a, %d %b %Y %X %Z") >>> print(dt) 2020-06-09 08:01:04 翌日>>> import datetime >>> datetime.datetime(2020, 10, 30) + datetime.timedelta(days=1) datetime.datetime(2020, 10, 31, 0, 0) >>> datetime.datetime(2020, 10, 31) + datetime.timedelta(days=1) datetime.datetime(2020, 11, 1, 0, 0) 特定の日付を超えているか調べる>>> import datetime >>> dt0 = datetime.datetime.now() >>> dt0 datetime.datetime(2019, 12, 14, 15, 22, 26, 404201) >>> dt1 = datetime.datetime(2019, 12, 16) >>> dt1 - dt0 datetime.timedelta(days=1, seconds=31053, microseconds=595799) >>> dt1 = datetime.datetime(2019, 12, 15) >>> dt1 - dt0 datetime.timedelta(seconds=31053, microseconds=595799) >>> dt1 = datetime.datetime(2019, 12, 14) >>> d = dt1 - dt0 datetime.timedelta(days=-1, seconds=31053, microseconds=595799) >>> dt1 = datetime.datetime(2019, 12, 13) >>> d = dt1 - dt0 datetime.timedelta(days=-2, seconds=31053, microseconds=595799) ある日付の 0:00 を超えているか調べたければ、時刻どうしを引いて days の符号が負かどうかを調べればよい。 ファイルの日付チェックファイルを変換するかどうかをタイムスタンプを比較して判断したりする場合。 from pathlib import Path files = ["fig01", "fig02", "fig03"] for file in files: p_pdf = Path("%s.pdf" % file) p_png = Path("%s.png" % file) if p_png.exists(): dt0 = datetime.fromtimestamp(p_pdf.stat().st_ctime) dt1 = datetime.fromtimestamp(p_png.stat().st_ctime) if(dt1 > dt0): continue ... 変換処理 ... 時刻表示を足す"00:00:00" の形の時刻表示どうしを足し合わせる。 >>> import datetime >>> dt1 = datetime.datetime.strptime("01:45:00", "%H:%M:%S") >>> dt2 = datetime.datetime.strptime("0:20:00", "%H:%M:%S") >>> dt = dt1 + datetime.timedelta(hours=dt2.hour, minutes=dt2.minute) >>> dt.strftime("%H:%M:%S") '02:05:00' CSV から HTML のテーブルを作るcsv2table.py import sys import csv def main(): argv = sys.argv argc = len(argv) if argc < 2: print("usage: csv2table.py <CSV file>") exit() filename = argv[1] with open(filename, "r") as f: reader = csv.reader(f) header = next(reader) print("<html>") print("<body>") print("<table border="1">") print("<thead>") print("<tr>", end="") for r in header: r = r.strip() print("<th>%s</th>" % r, end="") print("</tr>") print("</thead>") print("<tbody>") for line in reader: print("<tr>", end="") for r in line: r = r.strip() print("<td>%s</td>" % r, end="") print("</tr>") print("</tbody>") print("</table>") print("</body>") print("</html>") if __name__ == "__main__": main() 画像処理画像の読み込みと表示from PIL import Image image = Image.open("image.jpg") image.show() image.show() はデフォルトのビューアーで表示される。Jupyter Notebook 内で表示するには、変数名だけ打てばよい。 image あるいは、matplotlib を利用することもできる。 %matplotlib inline from matplotlib import pyplot as plt import numpy as np fig, ax = plt.subplots() ax.axis("off") plt.imshow(np.array(image)) 画像のサイズw, h = image.size 座標の色を取得image.getpixel((0, 0)) 2値化image2 = image.convert('1') グレースケール変換image2 = image.convert('L') 画像のクリップimage2 = image.crop((x1, y1, x2, y2)) 画像の作成image = Image.new("RGB", (w, h), (255, 255, 255)) 画像の貼り付けimage.paste(image1, (x, y)) 画像の保存image.save("image.png") 画像の結合from PIL import Image w = 640 h = 320 image1 = Image.open("image1.jpg") image2 = Image.open("image2.jpg") image = Image.new("RGB", (w*2, h), (255, 255, 255)) image.paste(image1, (0, 0)) image.paste(image2, (w, 0)) image.save("image.jpg")1 画像への文字の書き込みfrom PIL import Image, ImageDraw, ImageFont w = 200 h = 50 image = Image.new("RGB", (w, h), (255, 255, 255)) draw = ImageDraw.Draw(image) draw.font = ImageFont.truetype("Helvetica", 40) text = "Penguin" tw, th = draw.textsize(text) draw.text(((w-tw)/2, (h-th)/2), text, (0, 0, 0)) image.show() 画像サイズの変更w, h = image.size size = (int(w*0.25), int(h*0.25)) image.resize(size, Image.ANTIALIAS) 画像のアスペクト比を維持したままの縮小の場合は、次のようにもできる。 w, h = image.size size = (w*0.25, h*0.25) image.thumbnail(size, Image.ANTIALIAS) バックトレースの表示import traceback traceback.print_exc() 配列を文字列として連結>>> ",".join(["a", "b"]) 'a,b' 文字列処理文字列の正規化import unicodedata s = unicodedata.normalize("NFKC", s) アルファベット文字列かどうかチェックdef check_alphabet(s): alphabet = {chr(i) for i in range(ord("A"), ord("z")+1)} ss = set(s) if ss & alphabet == ss: return True else: return False ひらがな文字列かどうかチェックdef check_hiragana(s): hiragana = {chr(i) for i in range(ord("ぁ"), ord("ん")+1)} ss = set(s) ss -= {"ー"} if ss & hiragana == ss: return True else: return False ひらがなをカタカナに変換するdef convert_hira_to_kata(s): r = "" for c in s: i = ord("ァ") + ord(c) - ord("ぁ") if ord("ァ") <= i <= ord("ン"): r += chr(i) else: r += c return r gnuplot を呼ぶimport subprocess with open(plot_file, "w") as f: # gnuplot のコマンドを書く ... command = ["gnuplot", "-persist"] proc = subprocess.Popen(command, stdin=subprocess.PIPE) command = 'load "%s"\n' % plot_file proc.stdin.write(command.encode("utf-8")) proc.stdin.flush() Ctrl-C を捕捉するtry: ... except KeyboardInterrupt: ... 環境変数環境変数を参照する >>> import os >>> os.environ["MYENV"] 環境変数があるかどうか調べる。 >>> "MYENV" in os.environ ソケットserver.py import socket host = socket.gethostname() port = 4321 buffer_size = 1024 with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: try: s.bind((host, port)) except OSError as e: print_log(e) exit() s.listen(1) print("waiting...") try: while True: (c, address) = s.accept() with c: print("connect from %s:%s" % address) while True: msg = c.recv(buffer_size) if not msg: break msg = msg.decode() print("message: %s" % msg) if msg == "end": break except KeyboardInterrupt: print("exit") server.py (Windows 用) import socket host = socket.gethostname() port = 4321 buffer_size = 1024 with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: try: s.bind((host, port)) except OSError as e: print_log(e) exit() s.listen(1) print("waiting...") s.settimeout(1) try: while True: try: (c, address) = s.accept() except IOError: continue with c: print("connect from %s:%s" % address) while True: msg = c.recv(buffer_size) if not msg: break msg = msg.decode() print("message: %s" % msg) if msg == "end": break except KeyboardInterrupt: print("exit") client.py import socket host = socket.gethostname() port = 4321 buffer_size = 1024 try: while True: msg = input("message: ") with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: s.connect((host, port)) s.send(msg.encode()) if msg == "end": break except KeyboardInterrupt: print("exit") ホスト名の取得host = socket.gethostname() IP アドレスの取得address = socket.gethostbyname(host) ソースコードを調べるモジュールのソースコードを見ることができる。 import mymod import inspect print(inspect.getsource(mymod)) ただし、Cython を使うと見えなくなる。 UUID を作成するimport uuid id = str(uuid.uuid4()) MAC アドレスを取得するimport uuid mac = uuid.uuid1().hex[-12:] ZIP ファイルを展開するファイル名が Shif-JIS のファイルを含む ZIP ファイルの展開 (パスワード対応)。 unzip.py import sys from zipfile import ZipFile from getpass import getpass def main(args): if len(args) <= 1: print("usage: unzip.py 使い方 $ python unzip.py filename サイクルリストclass CycleList: def __init__(self, lst, nitems=1): self.list = lst self.nitems = nitems def iterator(self, n=-1): i = 0 while True: items = [] for j in range(i, i + self.nitems): k = j % len(self.list) items.append(self.list[k]) if len(items) == 1: yield items[0] else: yield tuple(items) i += 1 if i == n: break 使い方 c = CycleList([0, 1, 2], 2) for i in c.iterator(3): print(i) 出力 (0, 1) (1, 2) (2, 0) ソートしたリストを得るsorted_list = sorted(unsorted_list) 大文字小文字関係なしで並び替える。 sorted_list = sorted(unsorted_list, key=str.casefold) "unsorted_list.sort()" という方法もあるが、これだとリストそのものの内容が変更されるので注意。 コマンドを呼ぶimport sys import subprocess as sp import threading def run_command(command, silent=False, func_log=None, background=False): proc = sp.Popen(command, shell=True, stdout=sp.PIPE, stderr=sp.PIPE) def wait(): stdout_data, stderr_data = proc.communicate() if not silent: if func_log is None: sys.stdout.write(stdout_data.decode()) else: func_log(stdout_data.decode()) if proc.wait() != 0: if func_log is None: sys.stderr.write(stderr_data.decode()) else: func_log(stderr_data.decode()) raise OSError("Command execution faied: %s" % command) if background: threading.Thread(target=wait).start() else: wait() func_log に関数が設定されていたら、その関数を用いて出力を行う。 コマンドの存在の有無import shutil as su su.which("ls") | |
PENGUINITIS |