Mae向きなブログ

Mae向きな情報発信を続けていきたいと思います。

Pythonエンジニア育成推進協会監修 Python実践レシピ(3)

Pythonエンジニア育成推進協会監修 Python実践レシピ(2) - Mae向きなブログ」の続きです。

Chapter 11 ファイルとディレクトリへのアクセス

from pathlib import Path
TEST_PATH = "/Users/foo/tmp/hoge.pdf"

# ファイル名の取得
print(Path(TEST_PATH).name)
# 拡張子なしのファイ名の取得
print(Path(TEST_PATH).stem)
# 拡張子の取得
print(Path(TEST_PATH).suffix)
# 親ディレクトリの取得
print(Path(TEST_PATH).parent)
# CWDの取得
print(Path.cwd())
# ユーザホームディレクトリの取得
print(Path.home())

Chapter 12 データ圧縮、アーカイブと永続化

gzipコマンドがない環境でも、pythonコマンドの-mオプションを指定することで、コマンドラインで実行できる。

% python -m gzip -h
usage: gzip.py [-h] [--fast | --best | -d] [file ...]

A simple command line interface for the gzip module: act like gzip, but do not delete the input file.

positional arguments:
  file

optional arguments:
  -h, --help        show this help message and exit
  --fast            compress faster
  --best            compress better
  -d, --decompress  act like gunzip instead of gzip
日本語のファイル名を扱う

ZIPファイル中の圧縮されたファイル名に日本語が含まれているとき、環境依存の文字コードでファイル名が格納されている場合があり、注意が必要。

Chapter 13 特定のデータフォーマットを扱う

CSVファイルを扱う
  • 辞書データを用いたCSVファイルの読み込みと書き込みは便利そう
  • csvモジュールのSnifferクラスは、CSVファイルのデータ形式を推測するためのクラス
Excelを扱う
  • Excel VBAが苦手なので、仕事でExcelを扱うときは、RubyExcelファイルを操作してきましたが、openpyxlは便利そうですね。
  • Excel自体がインストールされていなくてもExcelファイルの読み書きができる。
Pillow thumbnails.py
  • /tmp/jpegは、Pathlibを用いているのに対し、save_thubnail_pathについては、文字列で扱っている点が少し疑問に感じました。あまり気にすることではないのでしょうか?

Pythonエンジニア育成推進協会監修 Python実践レシピ(2)

Pythonエンジニア育成推進協会監修 Python実践レシピ - Mae向きなブログ」の続きです。

Chapter 6 テキストの処理

  • joinメソッドについて
words = '''Beautiful is better than ugly. 
Explicit is better than implicit.'''.split()
print(words)
print('-'.join(words[:5]))

Rubyだと、words[...5].join('-')と書いていたので、joinメソッドの使い方が新鮮に感じます。

  • Unicode文字と文字の名前の変換
>>> import unicodedata
>>> unicodedata.lookup('BEER MUG')
'🍺'
>>> unicodedata.name('🍺')
'BEER MUG'

Chapter 7 数値の処理

金額などの精度が求められる計算には、Decimalを使うようにする。

>>> 100 * 1.10
110.00000000000001
>>> from decimal import Decimal
>>> Decimal('100') * Decimal('1.10')
Decimal('110.00')

Chapter 8 日付と時刻の処理

rruleはカレンダーアプリケーションなどでよく使われる、繰り返しルールを指定するために使用する。

Chapter 9 データ型とアルゴリズム

デフォルト値を持った辞書 defaultdict
>>> from collections import defaultdict
>>> dataset = [('九州', '福岡'), ('関東', '東京'), ('近畿', '大阪'), ('九州', '宮崎'), ('関東', '神奈川')]
>>> d = defaultdict(list)
>>> for region, prefecture in dataset:
...     d[region].append(prefecture)
...
>>> list(d.items())
[('九州', ['福岡', '宮崎']), ('関東', ['東京', '神奈川']), ('近畿', ['大阪'])]

Chapter 10 汎用OS・ランタイムサービス

CPU数を取得する。

>>> import os
>>> os.cpu_count()
8

Pythonエンジニア育成推進協会監修 Python実践レシピ

Pythonを勉強しようと思ったことはこれまでに何度もあるのですが、仕事などで、ちょっとした処理を行うときなどは、つい使い慣れたRubyで書いてしまうので、なかなかPythonの技術が上達せずにここまできました。 2022年の目標としてPythonをしっかり学んでみようと思います。

以下は、『Python実践レシピ』を読みながら気になることをメモしたものです。

Chapter 1 Pythonの環境

仮想環境について、

1. 仮想環境の作成

python -m venv dir_name

2. 仮想環境の有効化

source dir_name/bin/activate

3. 仮想環境の無効化

deactivate

Chapter 2 コーディング規約

Pythonには、PEP 8というスタイルガイドがあり、Pythonの開発者の間ではPEP 8に従うことが一般的

Chapter 3 Pythonの言語仕様

with文

with文に渡すオブジェクトをコンテキストマネージャーという。with文が実行されると、

  1. __enter__() メソッドの処理が実行される。
  2. withブロックが終了したあとに、__exit__()が実行される。

@contextlib.contextmanagerというデコレータを使用してジェネレータ関数を記述することで、コンテキストマネージャーを定義できる。

ジェネレーター
>>> def multiplier():
...     num = 1
...     while True:
...         yield num
...         num *= 2
...
>>> gen = multiplier()
>>> dir(gen) # ジェネレーターオブジェクトの`__next__()`メソッドが呼び出される
['__class__', '__del__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__iter__', '__le__', '__lt__', '__name__', '__ne__', '__new__', '__next__', '__qualname__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'close', 'gi_code', 'gi_frame', 'gi_running', 'gi_yieldfrom', 'send', 'throw']
>>> next(gen)
1
>>> next(gen)
2
>>> next(gen)
4
デフォルト値付引数でリストオブジェクトを使用するときの注意点
>>> def sample_func(a, b, c=[]):
...     c.append(a + b)
...     return c
...
>>> sample_func(1, 2)
[3]
>>> sample_func(3, 4)
[3, 7]
>>> sample_func(5, 6)
[3, 7, 11]

実行結果は、[3], [7], [11]となって欲しいのですが、原因は、デフォルト値のリストオブジェクトは関数作成時に一度だけ作成され、そのあともこのリストオブジェクトが再利用されるためだそうです。ちょっと直感と動作が違うような気がします。

解決策は、以下の通りです。

>>> def sample_func(a, b, c=None):
...     if c is None:
...         c = []
...     c.append(a + b)
...     return c
...
>>> sample_func(1, 2)
[3]
>>> sample_func(3, 4)
[7]
>>> sample_func(5, 6)
[11]

ちなみに、Rubyだと以下のように動作します。

irb(main):005:1* def sample_func(a, b, c = [])
irb(main):006:1*   c.append(a + b)
irb(main):007:1*   c
irb(main):008:0> end
=> :sample_func
irb(main):009:0> sample_func(1, 2)
=> [3]
irb(main):010:0> sample_func(3, 4)
=> [7]
irb(main):011:0> sample_func(5, 6)
=> [11]
デコレーター
  • デコレーター関数を作成し代入文を使用する例
def my_decorator(func):
    def wrap_function():
        """wrap_functionのドキュメントです"""
        func()
        print(f'function: {func.__name__} called')
    return wrap_function

def greeting():
    """greetingのドキュメントです"""
    print('こんにちは')

greeting = my_decorator(greeting)
print(greeting.__name__)
print(greeting.__doc__)
greeting()

実行すると、

wrap_function
wrap_functionのドキュメントです
こんにちは
function: greeting called

となり、もとの関数名やdocstringが失われる。

  • 「@デコレーター名」構文を使用して置き換える
def my_decorator(func):
    def wrap_function():
        """wrap_functionのドキュメントです"""
        func()
        print(f'function: {func.__name__} called')
    return wrap_function

@my_decorator
def greeting():
    """greetingのドキュメントです"""
    print('こんにちは')

print(greeting.__name__)
print(greeting.__doc__)
greeting()

この方法でも、デコレータ関数を作成し代入文を使用する例と同様の実行結果となる。

  • functools.wrapsを使用する
from functools import wraps
def my_decorator(func):
    @wraps(func)                # この1文を足す
    def wrap_function():
        """wrap_functionのドキュメントです"""
        func()
        print(f'function: {func.__name__} called')
    return wrap_function

@my_decorator
def greeting():
    """greetingのドキュメントです"""
    print('こんにちは')

print(greeting.__name__)
print(greeting.__doc__)
greeting()

実行すると、以下のようになり、名前やdocstringをもとのデコレート対象の関数のものに設定してくれる。通常、デコレーターを作成するときは、functools.wrapsを使用するとよい。

greeting
greetingのドキュメントです
こんにちは
function: greeting called

Chapter 4 Pythonのクラス

  • dataclass
from dataclasses import dataclass

@dataclass
class User:
    name: str
    age: int
    address: str

user = User('mae', 20, 'Miyazaki')
print(user)

Chapter 5 型ヒント

def say_hello(name: str) -> str:
    return f'Hello, {name}'

print(say_hello('Python'))
print(say_hello(0))

を実行すると、以下のようになり、実行することはできる。

Hello, Python
Hello, 0

mypyによる静的型チェックを行うと、以下のように出力され、say_hellointが渡されているとエラーが出力される。

type_hint_prac.py:5: error: Argument 1 to "say_hello" has incompatible type "int"; expected "str"
Found 1 error in 1 file (checked 1 source file)

ムゲンのi

本のタイトルから、数学がテーマになっているんじゃないかなと思って読み始めたのですが、なるほどこんな世界観の小説だったのかといい意味で裏切られた感じでした。

著者の本は、何冊も読んできましたが、また新しい一面に触れられたなという感じです。単なる突拍子もない設定の裏側には、著者のお医者さんとしてのバックグラウンドがあり、社会問題に対して一石を投じるという思い、使命感みたいなものもあるのかなと読み終えた今、勝手に想像しています。

帰ってきたK2 池袋署刑事課 神崎・黒木

K2 池袋署刑事課 神崎・黒木』を読み終えたばかりですが、続きが気になって読んでみました。

読み進めるほど、ストーリが絶妙に絡み合っていく心地よさを感じることができていいですね。前作以上に、楽しめる本でした。神崎・黒木の活躍を今後も期待したいですね。

K2 池袋署刑事課 神崎・黒木

ルパンの娘』シリーズを読んでから、横関大作品を好んで読むようになったのですが、8話からなる短編集。とくに、第7話「因縁」、第8話「決別」は読み応えがありました。