Pythonでデータクラス。その後。

Pythonでデータクラス。その後。

Python3.6でデータクラスの使用方法を知ってから、typing.NamedTupleを使ってPythonツールを作ってきた。しかし、今更ながらPython2.7(Jython)のツールを作る必要がでてきた。データクラスが使えない。
と思って、ドキュメントを見ていたら、collections.namedtupleはPython2.6から使えた。じゃあ、これを使えばいいか。

collections.namedtuple

1
2
3
4
5
from collections import namedtuple

Point = namedtuple('Point', 'x,y')
pos = Point(10, 20)
print(pos)

便利だ。__init__や__str__、__repr__まで実装されている。
それにVisual Studio Codeがプロパティを認識してくれている。

しかしtupleなので、プロパティの変更ができない。

1
2
3
4
>>> pos.x = 15
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: can't set attribute

けど方法がないわけではない。

_replace

1
2
pos = pos._replace(x=15)
print(pos)

tupleなので変更はできない。しかし、プロパティを変更した新しいtupleを返してくれる。
これで、プロパティの変更のようなことができた。

namedtupleはクラスだ。したがって継承できる。
初期値として渡すような項目はnamedtupleとして定義し、変更する必要があったり、後から設定するようなプロパティは、そのnamedtupleを継承したクラスの方で定義してもよい。

namedtupleの継承(拡張)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
from collections import namedtuple

Point = namedtuple('Point', 'x,y')
class MyPoint(Point):
    z = 0

    def move_z(self):
        self.z += 1

    def __str__(self):
        return 'X:{} Y:{} Z:{}'.format(
            self.x, self.y, self.z)

pos = MyPoint(10, 20)
pos.z = 30
pos.move_z()
print(pos)

これで不足ないデータクラスが作成できた。
この例では__str__を書き換えているが、本当は__repr__も書き換えておかないといけない。
__repr__を書き換えるなら、__init__も書き換える必要があるが・・・

まとめ

Python2.7でも、ここまでできるとは。
まだまだJythonも使えそうだ。

同じタグの記事
同じカテゴリの記事

pathlib.Pathを調べる

pathlib.Pathを調べる

最近はPython3.4以降の環境でスクリプトを作っているため、osモジュールからpathlibモジュールを利用するように方針を変えている。
そこで、pathlib.Pathの使えそうなメソッドを調べることにした。

まずはimport

1
from pathlib import Path

メインクラスをimportする。

ディレクトリ内のファイル・ディレクトリを取得する。

1
2
3
p = Path('C:/Windows')
for path in p.iterdir():
    print(path)

iterdir()で返却されるのはPathのイテレータ。なので、さまざまなpathのメソッドを呼び出すことができる。

ディレクトリかファイルかを判定する。

1
2
3
4
5
6
7
8
p = Path('C:\', 'Windows')
for path in p.iterdir():
    if path.is_dir():
        print('
Directory:{}'.format(path))
    elif path.is_file():
        print('
File     :{}'.format(path))
    else:
        print('
?        :{}'.format(path))

pathはPathクラスのインスタンスなので、そのメソッドを呼び出せばよい。
なお、Path()の引数は、os.path.joinのように、カンマ区切りで指定しても良い。

パスの追加

1
2
3
4
p = Path('C:\', 'Windows')
q = p / '
System32'
for path in q.iterdir():
    print(path)

演算子’/’がオーバーロードされている。カッコいい。ただ、あまり使うことはない気がする。

ファイルを開く

1
2
3
4
p = Path('C:\', 'Windows', 'System32', 'drivers', 'etc', 'hosts')
with p.open(encoding='
UTF-8') as r:
    for line in r:
        print(line)

pathインスタンスからそのままファイルオープンすることができる。エンコーディングも指定できる。便利。

ドライブ名の取得、パスの分解

1
2
3
p = Path('C:\', 'Windows', 'System32', 'drivers', 'etc', 'hosts')
print(p.drive)
print(p.parts)

ドライブ名はWindows独自。
partsも何かに使えるかな?partsに対してinして、フィルタする?

parentsとparent

1
2
3
4
5
p = Path('C:\', 'Windows', 'System32', 'drivers', 'etc', 'hosts')
print(p.parent)
print(p.parents[0])
print(p.parents[1])
print(p.parents[2])

parentは親、parentsは指定階層上の親。2階層上や3階層上の親が欲しい場合もあるかも。

名前、拡張子、拡張子なしの名前

1
2
3
4
5
p = Path('C:\', 'var', 'log', 'accesslog.tar.gz')
print(p.name)
print(p.suffix)
print(p.suffixes)
print(p.stem)

nameはファイル名またはディレクトリ名(accesslog.tar.gz)。
suffixは末端の拡張子(.gz)。
suffixesは拡張子のリスト([.tar, .gz])。
stemは拡張子(suffix)を除いたファイル名(accesslog.tar)。

カレントディレクトリ、ホームディレクトリ

1
2
print(Path.cwd())
print(Path.home())

地味だけど便利そう。
※ home()はPython3.5以降。

ファイルステータス

1
2
3
4
p = Path('C:\', 'Windows', 'System32', 'drivers', 'etc', 'hosts')
stat = p.stat()
print(stat.st_size)
print(stat.st_mtime)

os.stat()と同様の結果を得られる。ファイルサイズや作成日、更新日など。

パスが存在するかどうか

1
2
3
4
5
p = Path('C:\', 'Windows', 'System32')
if p.exists():
    print('
exists')
else:
    print('
not exists')

ファイル出力前に使えそう。
ファイル入力前はis_file()がよさそう。

再帰的なディレクトリ走査

1
2
3
4
for path in Path('C:\', 'Windows', 'System32').glob('**/*.dll'):
    print(path)
for path in Path('
C:\', 'Windows', 'System32').rglob('*.dll'):
    print(path)

glob()の引数の’**/’が再帰を表す。単一階層であれば’*.dll’を指定する。これは使えそう。
rglob()は常に再帰する。どちらでも覚えやすい方を使えばよいと思う。

ディレクトリ作成

1
2
p = Path('F:/work1/work2/work3')
p.mkdir(parents=True)

ディレクトリを作成する。parent=Trueを指定することで、再帰的にディレクトリを作成してくれる。osモジュールのos.makedirs()の様に動く。
これは便利。

単純なファイルの読み書き ※Python3.5以降

1
2
3
4
p = Path('C:\', 'Windows', 'System32', 'drivers', 'etc', 'hosts')
text = p.read_text(encoding='
UTF-8')
p = Path('
F:\', 'temp.txt')
p.write_text(text, encoding='
UTF-8')

read_text()でファイル全体を読み込み、write_text()で引数の文字列を一気に書き出す。ファイルサイズによっては使えそう。
※ Python3.5以降

名前変更

1
2
p = Path('F:\\temp.txt')
p.replace('F:\\work\new.txt')

replace()でファイル名を変更できる。途中のディレクトリを勝手に作ってくれることはないので、そこはあらかじめ準備しておく。

touch

1
2
p = Path('F:\\temp2.txt')
p.touch()

シェルコマンドのtouchと同様。現在日時で0バイトファイルを作成する。

まとめ

なかなか良いモジュールだ。
再帰走査ができるし、そのままエンコードを指定して読み込むこともできるし、足りないことの方が見つからない。これからはどんどん使っていこう。もうPython2.7には戻れない。

同じタグの記事
同じカテゴリの記事