読み飛ばしてください
おはようございます、しなもんです。
この記事は↓の続編的立ち位置です。
前回の記事がありがたいことに爆発的にトレンド入りしました。ありがとうございました。
(あれらの機能を知らなかったのが私だけじゃないことが分かって安心しました)
思ったより反応があり、Twitter(自称X)でもいろいろ意見をいただきました。
というわけで、今回は標準ライブラリのドキュメントを読んでみました。
予想通り、知らない裏技がたくさん出てきました。
Pythonすごい。
functools
が便利すぎる
partial
を使った関数の部分適用
partial
関数を使うと、関数の一部の引数を固定した新しい関数を作成できるようです。
コールバック関数を使う際や、関数型プログラミングっぽいコードを書く際に非常に便利なようです。
私は関数型プログラミングには明るくないのですが、
クラスの継承的なことをしなくても、関数で部分適用ができるのは便利ですね。
覚えておきたいです。
from functools import partial
def greet(greeting, name):
return f"{greeting}, {name}!"
say_hello = partial(greet, "Hello")
say_goodmorning = partial(greet, "Good morning")
print(say_hello("Alice")) # 出力: Hello, Alice!
print(say_goodmorning("Bob")) # 出力: Good morning, Bob!
singledispatch
によるシンプルな関数オーバーロード
singledispatch
デコレータを使うと、引数の型に基づいて異なる実装を持つ関数を簡単に作成できます。
from functools import singledispatch
@singledispatch
def convert(obj):
return f"Unknown type: {type(obj)}"
@convert.register(str)
def _(text):
return text.upper()
@convert.register(int)
def _(number):
return f"0x{number:x}"
@convert.register(list)
def _(lst):
return [elem * 2 for elem in lst]
print(convert("hello")) # 出力: HELLO
print(convert(42)) # 出力: 0x2a
print(convert([1, 2, 3])) # 出力: [2, 4, 6]
print(convert(3.14)) # 出力: Unknown type: <class 'float'>
関数の中で、type
かisinstance
で条件分岐すればいいのでは...?と思いましたが、
型ごとの処理が長い場合や、拡張性を求める場合には@singledispatch
デコレータのほうが便利なのかな。
itertools
もいろいろある
groupby
を使ったデータのグルーピング
groupby
関数を使うと、イテラブルの連続する要素をグループ化できるらしい。
from itertools import groupby
data = [1, 1, 1, 2, 2, 3, 4, 4, 5, 1, 1]
for key, group in groupby(data):
print(f"{key}: {list(group)}")
# 出力:
# 1: [1, 1, 1]
# 2: [2, 2]
# 3: [3]
# 4: [4, 4]
# 5: [5]
# 1: [1, 1]
sort()
とはまた違った感じのものがあるんですね。
返ってくるのはイテレータなので注意です。
適当にlist()
等を使うなどで対応しましょう。
ちなみにpandas
に同名の関数があるようなのですが、挙動は異なるので注意です。
データクラス
field
関数を使ったカスタムフィールド
field
関数を使うと、データクラスのフィールドをカスタマイズできます。
正直言ってもっと早く知りたかった。
ちょっと違うかもですがDjangoのModelが標準ライブラリでできたのか...
from dataclasses import dataclass, field
import random
def generate_id():
return random.randint(1000, 9999)
@dataclass
class User:
name: str
email: str
id: int = field(default_factory=generate_id)
active: bool = field(default=True, repr=False)
user = User("Alice", "alice@example.com")
print(user) # 出力例: User(name='Alice', email='alice@example.com', id=5678)
__post_init__
メソッドを活用した初期化後の処理
__post_init__
メソッドを使うと、データクラスの初期化後に勝手に追加の処理を行ってくれるらしい。
しかも、__eq__()
などの特殊メソッドも勝手に実装してくれるらしい。
from dataclasses import dataclass, field
@dataclass
class Rectangle:
width: float
height: float
area: float = field(init=False)
def __post_init__(self):
self.area = self.width * self.height
rect = Rectangle(5, 3)
print(f"Area: {rect.area}") # 出力: Area: 15.0
少し脱線しますが、どこかでこんなの見たことあるなーて思ったら、
私が運営しているサービスに@nikawamikanがコントリビュートしてたコードでした。
こちらはpydantic
でしたが。pydantic
いいですよね。
正規表現モジュール(re)にも裏技がある
名前付きグループを使った複雑なパターンマッチング
名前付きグループを使うと、複雑な正規表現パターンをより読みやすく、管理しやすくできるらしい。
なんだと???
import re
pattern = r"(?P<year>\d{4})-(?P<month>\d{2})-(?P<day>\d{2})"
match = re.match(pattern, "2023-05-15")
if match:
print(f"Year: {match.group('year')}")
print(f"Month: {match.group('month')}")
print(f"Day: {match.group('day')}")
# 出力:
# Year: 2023
# Month: 05
# Day: 15
re.VERBOSE
フラグを使った読みやすい正規表現
re.VERBOSE
フラグを使うと、複雑な正規表現パターンを複数行で書ける上に、コメントも追加できるらしい。
import re
pattern = re.compile(r"""
^ # 行の先頭
(\d{3}) # 3桁の数字(エリアコード)
[-\s]? # ハイフンまたは空白(オプション)
(\d{3}) # 3桁の数字
[-\s]? # ハイフンまたは空白(オプション)
(\d{4}) # 4桁の数字
$ # 行の末尾
""", re.VERBOSE)
phone_numbers = ["123-456-7890", "987 654 3210", "1112223333"]
for number in phone_numbers:
if pattern.match(number):
print(f"{number} is valid")
else:
print(f"{number} is invalid")
# 出力:
# 123-456-7890 is valid
# 987 654 3210 is valid
# 1112223333 is valid
さいごに
もっと早く知りたかった機能たくさんあったな...。
(多分まだまだ初心者なだけ。)