頑張らないために頑張る

ゆるく頑張ります

Pythonやライブラリの非推奨あるいは非主流なコード

Posted at — Jun 28, 2023

概要

Pythonやそのライブラリを含む他の要素においても、バージョンアップが行われるたびに、これまでの書き方が非推奨とされるケースが相当あります。ただし、インターネット上には古いコードや非推奨の情報が存在しています。それが非推奨であるということを知らない場合には、新しい書き方があるにもかかわらず古い書き方を使用してしまうケースが考えられます。

「この書き方は何だ?見たことないぞ」と思って調査してみると、現在では非推奨になった昔ながらの書き方だったりすることがあります。時間を費やして調査した結果、それが非推奨だったりすると困るわけです。ムダな時間を省くためにも、そのような「現在は使えない、または将来的に使えなくなる(非推奨)」の書き方についてメモします。

とはいえ、気付いた時に追記する予定ですので、増えすぎることはないでしょう。

書式化演算子%を使った文字列の書式設定

文字列のフォーマットを定義する際、format()f文字列を使った記法が現在の主流です。ただ、C言語のprintf()に準拠したような演算子を使った記法で表現する方法もあります。

古い方法: 書式化演算子 %

name = "山田"
age = 30
print("私の名前は%sで、%d歳です。" % (name, age))

現在の主流: f-strings (Python 3.6以降)

name = "山田"
age = 30
print(f"私の名前は{name}で、{age}歳です。")

こちらも現役: str.format()

name = "山田"
age = 30
print("私の名前は{}で、{}歳です。".format(name, age))

C言語のprintf()では、任意の文字列において「値を出力する位置にどんな型で出力するか」をパーセント付の識別子で表現します。%sだった場合、文字列を指定された箇所に出力する、といった具合です。この記法がPythonでも使えます。

ただ、「せっかく動的型付け言語なのに、文字列編集をするときに型を気にしないといけないのは面倒だぜ!」と思ったのかはわかりませんが、前述のとおりf-stringsが主流だと思います。こっちは読みやすく、式の評価もサポートしているため、f"結果: {2 * 3}"のように直接計算結果を埋め込むことができます。format()関数も一応見かけますね、%ほどは廃れていない印象。

ファイルの操作

古い方法: 明示的なクローズ

f = open("example.txt", "r")
content = f.read()
f.close()  # 忘れると問題が発生する可能性あり

現在の主流: with文(コンテキストマネージャ)

with open("example.txt", "r") as f:
    content = f.read()
# 自動的にクローズされる

withはそのブロックを抜けるときに、closeメソッドを勝手に呼び出してクローズしてくれるので、クローズ忘れがなくなりますしコードも読みやすくなります。しかし、withには更なる利点があります。

ファイルオブジェクトを扱うときに with キーワードを使うのは良い習慣です。 その利点は、処理中に例外が発生しても必ず最後にファイルをちゃんと閉じることです。

オフィシャルのドキュメントに書いてある上記の点だけを鑑みても、withを利用するほうが良いと判断できます。

pandasからNumPyの関数などを使うpd.np

古い方法: pd.np経由でNumPy関数にアクセス

import pandas as pd
result = pd.np.sqrt(4)  # 非推奨

現在の主流: NumPyを直接インポート

import pandas as pd
import numpy as np
result = np.sqrt(4)  # 推奨

pandasをインポートすると、同時にNumPyもインポートされます。このとき、pd.npという書き方をすることで明示的にNumPyをインポートしていなくてもNumPyが持つ機能にアクセスできるようになります。import numpyを書かなくてもいいという、メリット?うーん、メリットかなぁこれ。

ただ、NumPyの関数などを使うならちゃんとインポートした方がコードの保守性が高いと思われたのか、現在では非推奨です。そのうち、この機能が削除されると思います。

辞書のキーチェック

古い方法: dict.has_key()

d = {"key": "value"}
if d.has_key("key"):  # Python 3では削除済み
    print("キーが存在します")

現在の主流: in演算子

d = {"key": "value"}
if "key" in d:
    print("キーが存在します")

さすがに古すぎる話題ですが、Python2には辞書に任意のキーが存在するかをチェックするhas_key()が存在していました。Python3系ではこいつは削除されており、そもそもエラーになります。現在はinを利用します。

xrangeはもうないぞ

古い方法: rangexrange

# Python 2
for i in range(1000000):  # メモリを大量に消費
    pass

for i in xrange(1000000):  # メモリ効率が良い
    pass

現在の主流: range(Python 3)

Python 3では、range()はイテレータになり、xrange()は削除されました。

# Python 3
for i in range(1000000):  # メモリ効率が良い
    pass

Python2では、range()はリストを生成し、xrange()はイテレータを生成していました。Python3ではxrange()がなくなり、range()に統合されました。

データクラスの実装

古い方法: 手動での__init____repr__などの実装

class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age
    
    def __repr__(self):
        return f"Person(name={self.name}, age={self.age})"
    
    def __eq__(self, other):
        if not isinstance(other, Person):
            return False
        return self.name == other.name and self.age == other.age

現代の方法: @dataclassデコレータの使用 (Python 3.7以降)

from dataclasses import dataclass

@dataclass
class Person:
    name: str
    age: int
    # __init__, __repr__, __eq__などが自動的に生成される

dataclassデコレータを使えば、__init__()で引数をわざわざインスタンス変数に代入したり、__eq__()を手で書く必要がなくなりました。Python3.7以降で利用できます。

集約関数のaxis

古い方法: mean()などの関数にaxisパラメータなしで使用

mean_value = df.mean()  # 警告なしだが、axis=0を暗黙的に使用

厳密に「古い方法」かまでははっきりしなかったものの、こういう書き方は多くはないものの見かける。確かに省略できるけども、パッと見で「どっち?」ってなるんだよなぁ。

現代の方法: 明示的にaxisを指定

# 列ごとの平均
mean_value = df.mean(axis=0)

# 行ごとの平均
mean_value = df.mean(axis=1)

単純にこっちの書き方が省略されるよりわかりやすいので、axisは省略できるとしても書いておいたほうがいいと思います。

参考

  1. 書式化演算子%を使った文字列の書式設定(printf形式の書式化)
  2. Python♪「% 演算子」を使った古い文字列書式設定
  3. pandasからNumPyの関数などを使う方法(pd.np)
  4. What’s new in 1.0.0 (January 29, 2020) - Deprecations
  5. Import both Pandas and Numpy?
  6. Pythonで開いたファイルのクローズを忘れやすい人のためのwith文の使い方
  7. rangeとxrange
  8. Python3.7以上のデータ格納はdataclassを活用しよう
  9. pandasのaxisの方向の覚え方
comments powered by Disqus