知識としては知っているけど、実装ではほとんど使ったことはないなというものや、意外と知られていないのかも?と感じたものを集めてみました。
いつか使いたい、知っていることで何かの役に立てたい、あわよくばドヤ顔で披露してみたい、、、
そんな下心を抱えながら溜め込んだ知識を、書き出します。
if以外でのelse
if else で使うことが一般的ですが、else は for や while 句でも使用ができ、break しなかった時、の条件を書くことができます。
これは比較的よく使う知識かもしれません。
for _ in range(10):
if random.randint(0, 100) < 10:
break
else:
print('break しなかった')
過去に「これは else を使うといい感じに書けますよ♪」とレビューしたことがあったのですが、「読みづらくないですか?」と言われ、ん〜、、、確かに?となったことがありました。
普段からがっつり Python を書く人でないと、なんだこれは?と余計な負荷をかけてしまうのかもしれません。読みやすさを優先すると採用率が下がりそうな知識ですね。
四則演算以外での複合代入演算子
+=は多くの人が頻繁に使っていますが、四則演算以外の演算子を使う際になぜか忘れ去られてしまいがちな複合代入演算子。
この辺をさらっと書けると、なんとなく気持ちがいいですよね。
# 集合や辞書の更新ができる '|='
members |= { "山田太郎", "佐藤花子" }
# Pathlib などで便利な '/='
input_dir /= 'input'
# 周期的な計算で重宝する '%='
degree %= 360
# NumPy などで使う、行列積の '@='
vector @= rotation
# ビット演算子
flags ^= toggle_mask
ビット演算やNumPyなど、人によっては普段使わない演算子の場合は読む場合にも、「ん?」となりやすいので、あらためてこの辺りは頭に入れておきたいところ。
yield + send でジェネレーターに値を外部から挿入
ここからは、全く使ったことがない機能になります。
ジェネレーターの返り値は一般的には Iterator[YieldType]ですが、より詳細にGenerator[YieldType, SendType, ReturnType]と書くこともできます。
このSendTypeは .send を使って値を送る時の型を定義する、という説明をみて、send の存在を知りました。
def echo_round() -> Generator[int, float, str]:
sent = yield 0
while sent >= 0:
sent = yield round(sent)
return 'Done'
>>> echo = echo_round()
>>> next(echo)
0
>>> echo.send(4.48)
4
>>> echo.send(-1)
Traceback (most recent call last):
File "<python-input-35>", line 1, in <module>
echo.send(-1)
~~~~~~~~~^^^^
StopIteration: Done
上記のコードはPythonの公式ドキュメントから取得していますが、これを見た上で、いったいいつ使うのか、、、、となっています。
iter()の第2引数
iter() に第2引数を渡すと、第1引数の関数がその値を返すまで続けるという処理がかけます。
これは使えそう!と頭にとどめていますが、まだ使えた試しがない。
# 'STOP' と入力されるまで入力を受け付け続ける
for line in iter(input, 'STOP'):
print(f"入力内容: {line}")
String.isnumeric()
漢数字も数値として判定しくれるメソッドですが、使った試しがない。似たような挙動の isdecimal, isdigit とどっちだっけ?となるのでむしろ記憶から消したくなる時があります。
>>> '壱'.isnumeric()
True
>>> '壱'.isdecimal()
False
global を使った変数名のバインディングの変更
存在は知っているが、実際に使ったことがない機能の一つ。グローバルの名前空間汚すデメリットを上回るほどのメリットを感じずに、実務的なコードでは使ったことがありません。
なくても書けるなら書かない、の精神でいるので今後も使い所がなさそうだなと思っています。ぜひ、この辺の考えは聞いてみたい。
for ループでの変数のスコープ
この挙動は意識しておかないといつかバグを生むかも、、、と構えていながら、一度も出会ったことがありません。幸せなことではある。
>>> for i in range(10):
... pass
...
>>> print(i)
9
内包表記は変数バインドなのでこの挙動にはなりません。なおさら遭遇する確率は低いですね。
小さい int のキャッシュ
-5 から 256 までの整数はすでにメモリ上にキャッシュされているので、is で比較をすると True になります。この範囲外の数値だと False になります。これ起因のバグが、、、と知った時は思いましたがそもそも is の比較をほとんどしないので使われたことのない知識の1つです。
>>> a = 256; b = 256; a is b
True
>>> a = 257; b = 257; a is b
False
その他のニッチな挙動
max() と文字列のリスト
max() 関数は数値以外にも文字列に使えます。ただ、自分は初めて出会った時に「ん?」となったので、その読みやすさを犠牲にしてまで使うかは悩むところです。
>>> max(["apple", "banana", "cherry", "z"])
'z' # 辞書の並び順の最後を返す
>>> sorted(["apple", "banana", "cherry", "z"])[-1] # こっちの方が直感的
all() と any() の空リストに対する挙動
>>> all([])
True
>>> any([])
False
なぜ?となる挙動です。数学的な意味があるようですが、覚えられません。
書いてみて、この知識はもっと普段から意識していないとだなと反省しています。
まとめ
いつか使う機会があればなーと考えていたものを、書き出してみました。
書き出しながら、どうせAIがコードを書くのにこの知識に意味はあるのだろうか?とふと思ってしまいました。
とはいえ何かの役にたつ日があると信じて、「いいコードを書くぞ!」というモチベーションよりも、「そんな書き方できるんだ!」というマインドでキャッチアップを続けていければと思っています。