23
23

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Pythonのコードを短く簡潔に書くテクニックAdvent Calendar 2017

Day 24

演算子をオーバーロードしてメソッド呼び出しを簡略化する

Last updated at Posted at 2017-12-23

この記事は Pythonのコードを短く簡潔に書くテクニック Advent Calendar 2017 の24日目です。

はじめに

Pythonでは自作クラスの演算子をオーバーロードできます。

演算子を使うことで通常のメソッド呼び出しより短く書けるだけでなく、直感的でわかりやすいコードになる場合もあります。

メソッド呼び出しで書いた場合
Command('ls').pipe('cat').redirect_to('out.txt')

演算子を使った場合
Command('ls') | 'cat' > 'out.txt'

標準モジュールで演算子を使っている例

先ずは標準モジュールで演算子を使っている例を紹介します。

pathlib.Pathの"/"

Python3.4から追加でされた pathlib.Path では joinpath() で2つのパスを連結できますが、演算子"/" でも同じことができます。

joinpath()
>>> from pathlib import Path
>>> Path('/aaa/bbb').joinpath('ccc/ddd')
PosixPath('/aaa/bbb/ccc/ddd')
演算子"/"
>>> from pathlib import Path
>>> Path('/aaa/bbb') / 'ccc/ddd'
PosixPath('/aaa/bbb/ccc/ddd')

strの"%"

文字列の書式化には現在は str.format() がよく使われていますが、それ以前は演算子"%"を使ったprintf形式の文字列書式化が使われていました。

これを文字列専用の特殊な構文だと思っている人がいるかもしれませんが、実は演算子"%"を使っているだけなんです。

>>> 'Hello %s' % ('Python',)
'Hello Python'
>>> 'Hello %s'.__mod__(('Python',))
'Hello Python'

自作クラスで演算子を使えるようにするには

自作クラスに特殊メソッドを実装することで演算子が使えるようになります。

例えば二項算術演算子を使えるようにするには以下の特殊メソッドを実装します。

演算子 特殊メソッド 備考
+ __add__
- __sub__
* __mul__
@ __matmul__ バージョン 3.5 で追加.
/ __truediv__ ※2.7の仕様はこちらを参照
// __floordiv__
% __mod__
** __pow__
<< __lshift__
>> __rshift__
& __and__
^ __xor__
__or__

実例:subprocessでパイプとリダイレクトを実装する

subprocessをラップしてシェルスクリプトのようにパイプ"|"やリダイレクト">"を使えるようにしてみます。

先ずはsubprocessをラップしたクラスを普通に作成します。
ここではまだ演算子は実装しません。

import shlex
import subprocess
from subprocess import PIPE


class Command:

    def __init__(self, command, stdin=PIPE):
        self.p = subprocess.Popen(shlex.split(command),
                                  shell=True,
                                  stdin=stdin,
                                  stdout=PIPE)

    def pipe(self, command):
        next_command = Command(command, self.p.stdout)
        return next_command

    def redirect_to(self, file):
        with open(file, 'w') as f:
            for line in self.p.stdout:
                f.write(line.decode())

この状態ではパイプやリダイレクトは以下のようなメソッド呼び出しをしなければなりません。

Command('ls').pipe('cat').redirect_to('out.txt')

次に以下のメソッドをCommandクラスに追加します。

    def __or__(self, command):
        return self.pipe(command)

    def __gt__(self, file):
        return self.redirect_to(file)

以下のように演算子でパイプとリダイレクトが使えるようになり、シェルスクリプトのような記述ができるようになります。

Command('ls') | 'cat' > 'out.txt'

参考

23
23
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
23
23

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?