Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationEventAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
22
Help us understand the problem. What are the problem?

More than 3 years have passed since last update.

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

この記事は 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'

参考

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
22
Help us understand the problem. What are the problem?