S-JIS[2018-01-05/2018-01-09] 変更履歴

Pythonの式

Python3.6.4の式(演算子)のメモ。


概要

Pythonで数値の演算を行うときは、基本的に共通の型に変換される。→算術変換


int等の数値クラスは演算子用のメソッドを持っている。
nが数値の変数だとすると、「n + 1」は「n.__add__(1)」と同じである。

なお、「1 + 2」を「1.__add__(2)」と書くとエラーになる。
1.」のピリオドは浮動小数点数を表すものと判断され、メソッド呼び出しでは無い為。
(1).__add__(2)」の様に丸括弧で囲めば大丈夫。


Pythonでは演算子をオーバーロードすることが出来る。
つまり、自分独自のクラスでも演算子を使った式が記述できるようになる。

例えば「+」演算子を実装したければ、__add__メソッドを定義すればよい。
演算子に対応するメソッド

(一般的に、「オーバーロード」は同名の関数(メソッド)で引数の型が異なるものを指す。
 Pythonのメソッドはオーバーロードできない(メソッド名でしか判断しない為)。
 したがって、演算子のオーバーロードというよりオーバーライドと言うべきなのではないかと思うが、ドキュメントにはオーバーロードと書いてあった^^;)


Pythonの演算子優先順位

グループ 演算子 概要 説明
エンクロージャー ( )
[ ]
{ }
優先する式 (1 + 2) 丸括弧内に1つでもカンマが入っているとタプルになる。
タプル (1, "2", 3.0)
リスト表記 [1, 2, 3] リスト
辞書表記 {"a": 1, "b": 2, "c": 3} dict
集合表記 {1, 2, 3} set
ジェネレーター式 (i * 2 for i in range(10))  
yield式 (yield 123)  
プライマリー .
[ ]
属性参照 object.field  
添字表記 list[0]  
スライシング list[0: 10]  
関数呼び出し func(1, 2, 3)  
await式 await await式    
べき乗 ** べき乗 m ** n mn(→pow関数
単項演算 + -
~
符号 +n
-n
 
ビット否定 ~n  
乗除演算 * @ / // % 乗算・除算・剰余 m * n
m @ n
m / n
m // n
m % n
@は行列の乗算。(→NumPyのndarray
/は除算。例えば3 / 2は1.5になる。
//は切り捨ての除算。3 // 2は1になる。
%は剰余。(割り算の余り)
divmod関数を使うと商と余りが同時に取得できる)
加減算 + - 加算・減算 m + n
m - n
 
シフト演算 << >> ビットシフト n << r
n >> r
 
ビット演算 & ビットAND m & n ビット単位の論理積
^ ビットXOR m ^ n ビット単位の排他的論理和
| ビットOR m | n ビット単位の論理和
比較演算 == !=
< >
<= >=
is
is not
in
not in
値の比較 m == n
m != n
m < n
m > n
m <= n
m >= n
==はJavaのequals相当。
同一性の比較 obj1 is obj2
obj1 is not obj2
インスタンスが同じかどうか。
(Javaの==、Scalaのeq相当)
帰属検査 2 in [1, 2, 3]
4 not in [1, 2, 3]
contains
ブール演算 not 条件否定 not b Javaの!相当→notの優先順位
and 条件AND b1 and b2 Javaの&&相当
or 条件OR b1 or b2 Javaの||相当
条件式 if else 条件 m if b else n Javaの?:相当(値の並び順は違うが)
ラムダ式 lambda ラムダ式 lambda x, y: x + y  

算術変換

算術演算子(数値同士の演算)は、基本的に共通の型に変換されて計算される。
(剰余「%」やビットシフト「<<」「>>」等は他の法則になる)

  1. 片方の引数が複素数型であれば、他方も複素数型に変換される。
  2. 片方の引数が浮動小数点数であれば、他方も浮動小数点数に変換される。
  3. それ以外は両方とも整数ということなので、変換されない。

評価順序

Pythonは、基本的に左から右へ評価する。つまり、「a + b」は、aを先に評価し、bを後で評価する。
ただし、代入「=」を評価する場合は、「=」の右側が先に評価される。


比較演算子

Pythonの比較演算では「a < b < c」といった書き方を許容する。
これは「a < b and b < c」と(ほぼ)等価である。(1つの項は1回しか評価されないところが異なる)

3つ以上の項でも同じ事ができ、それぞれをandでつないだものと同じになる。


また、Javaでは「<」「>」「<=」「>=」より「==」「!=」の方が優先順位が低いが、
Pythonでは同順位である。


ブール演算子

Javaの論理演算では、boolean型しか演算できない。
Pythonのブール演算では真偽値に変換できる値であれば演算できる。
このため、orやandの戻り値は左辺か右辺の値がそのまま返るようになっている。

ブール演算の例
演算 説明
論理和(or) False or False
False or True
True or False
True or True
False
True
True
True
Javaの「||」相当。
(短絡評価される。すなわち、左辺が偽のときのみ右辺が評価される)
0 or 0
0 or ""
0 or 11
22 or 0
33 or 44
0
""
11
22
33
ただしJavaの「||」とは異なり、
orの左辺がTrue相当の場合はその値、そうでなければ右辺の値を返す。
論理積(and) False and False
False and True
True and False
True and True
False
False
False
True
Javaの「&&」相当。
(短絡評価される。すなわち、左辺が真のときのみ右辺が評価される)
0 and 0
"" and 0
0 and 11
22 and 0
33 and 44
0
""
0
0
44
ただしJavaの「&&」とは異なり、
andの左辺がFalse相当の場合はその値、
andの左辺がTrue相当の場合は右辺の値を返す。
否定(not) not False
not True
True
False
Javaの「!」相当。
not 0
not 1
True
False
True, False以外の値を指定した場合もboolが返る。

notの優先順位

Pythonのnotは、Javaの「!」に相当するが、演算子の優先順位は異なる。
Javaだと「!」は単項演算子の位置なのでかなり優先順位が高いが、
Pythonだとandやorのすぐ上という順位である。

このため、Javaで「a != b」の意味で「!」と「==」を使うなら「!a == b」とは書けず、「!(a == b)」と書かなければならない。
(単なる比較演算だと「!」を使う事はあまり無いが^^; Javaだとinstanceofという演算子があるのだが、「a instanceof C」の否定は「!a instanceof C」とは書けず、「!(a instanceof C)」と書かなければならない)
Pythonだとnotの優先順位が比較演算子より低いので、「a != b」の意味で「not a == b」と書ける。


条件式

C言語やJavaの条件演算子(三項演算子)「? :」に相当するのがPythonの条件式。
ただ、Pythonの条件式とJavaの条件演算子は項の並び順が異なる。

# Python
真のときの値 if 条件 else 偽のときの値
// Scala
if (条件) 真のときの値 else 偽のときの値
// C言語やJava
条件 ? 真のときの値 : 偽のときの値

エルビス演算子「?:」(真のときの値を省略したもの。条件が真のときは条件の値、それ以外のときは偽のときの値を返す)に相当するものを
Pythonの条件式で書くことは出来ないが、これに関してはor演算子を使えばよい。

s = arg or "empty" # argが真のときはその値、偽であれば"empty"

代入

C言語やJavaでは代入は式(演算)だが、Pythonでは文(代入文)である。
(したがって、Javaの演算子の表には「=」や「+=」が出てくるが、Pythonの演算子の表には出てこない)

「a = b = 123」という文は、Javaでは「b = 123」という式を評価し、その結果(123)をaに代入するという解釈だが、
Pythonは代入文として定義されている。

「a += b += 1」という文はJavaでは正しいが、Pythonでは複数の変数に値を入れる代入文は「=」しか許していないので、エラーとなる。


他言語との比較

他のプログラミング言語とPythonの演算子の比較。

演算子 説明
++
--
Pythonにはインクリメント・デクリメント演算子は無い。
まぁ、+=や-=は有るので、そんなに困らない。
?: C言語やJavaの条件演算子は、Pythonでは条件式
: Pythonではコロン「:」が角括弧内で使われている場合はスライス記法。「a[1:3]」「a[1:]」「a[:-1]」「a[:]
dictにおいてキーと値を区切るのにもコロンが使われる。「{key: value}
# Pythonではシャープ「#」はコメントの開始。
// C言語・C++やJavaでは「//」は行コメントの開始だが、
Pythonでは「//」は除算である。
, C言語ではカンマ「,」は演算子であり、一番右側の値が返る。
Pythonで(関数の引数やリスト等の括弧内以外で)カンマ区切りで何かやっている箇所があったら、大抵はタプルである。
(タプルは丸括弧で囲んでカンマ区切りで値を並べるのだが、丸括弧を省略できる)

組み込み関数

演算子ではないが、数値演算に関して便利に使える組み込み関数がある。

関数名 説明 例の結果 自分で実装する場合のメソッド
abs 絶対値 abs(-1) 1 __abs__(self)
divmod 除算を行い、商と余り(のタプル)を返す divmod(10, 3) (3, 1) __divmod__(self, other)
__rdivmod__(self, other)
round 四捨五入 round(1.4)
round(1.5)
round(1.55, 1)
1
2
1.6
__round__(self, ndigits = 0)
pow べき乗(「pow(m, n)」は「m**n」と同じ) pow(2, 3) 8  
complex 複素数への変換 complex("1") (1+0j) __complex__(self)
int 整数への変換 int("1") 1 __int__(self)
float 浮動小数点数への変換 float("1") 1.0 __float__(self)
repr REPLで表示される文字列を返す repr(123) '123' __repr__(self)
str 文字列を返す str(123) '123' __str__(self)
__str__が無い場合、reprが呼ばれる)
bin 二進数の文字列を返す bin(0b11001100) '0b11001100' __index__(self)
(→__index__は添字のときに呼ばれる)
oct 八進数の文字列を返す oct(0b11001100) '0o314'
hex 十六進数の文字列を返す hex(0b11001100) '0xcc'

例えば自分のクラスで「abs関数が呼ばれたときの処理」を書きたい場合は、__abs__メソッドを定義しておけばよい。


演算子に関する特殊メソッド名

自分独自のクラスでも演算子を使うことが出来る。

演算子に対応したメソッド名が決まっており、そのメソッドを自分のクラスで定義すればよい。

class C:

    def __init__(self, arg): # コンストラクター
        self.value = arg

    def __add__(self, other): # +演算子
        return C(self.value + other.value)

    def __repr__(self): # REPLで表示される文字列を返すメソッド
        return f"C({self.value})"
c1 = C(1)
c2 = C(2)
s = c1 + c2 # __add__の呼び出し

二項演算子のメソッド
演算子 メソッド 左右入れ替えメソッド 累算代入 備考
演算子 メソッド
+ __add__(self, other) __radd__(self, other) += __iadd__(self, other)  
- __sub__(self, other) __rsub__(self, other) -= __isub__(self, other)  
* __mul__(self, other) __rmul__(self, other) *= __imul__(self, other)  
@ __matmul__(self, other) __rmatmul__(self, other) @= __imatmul__(self, other)  
/ __truediv__(self, other) __rtruediv__(self, other) /= __itruediv__(self, other)  
// __floordiv__(self, other) __rfloordiv__(self, other) //= __ifloordiv__(self, other)  
% __mod__(self, other) __rmod__(self, other) %= __imod__(self, other)  
divmod関数 __divmod__(self, other) __rdivmod__(self, other)      
** __pow__(self, other) __rpow__(self, other) **= __ipow__(self, other)  
<< __lshift__(self, other) __rlshift__(self, other) <<= __ilshift__(self, other)  
>> __rshift__(self, other) __rrshift__(self, other) >>= __irshift__(self, other)  
& __and__(self, other) __rand__(self, other) &= __iand__(self, other)  
^ __xor__(self, other) __rxor__(self, other) ^= __ixor__(self, other)  
| __or__(self, other) __ror__(self, other) |= __ior__(self, other)  
== __eq__(self, other)       __eq__が無いとisで判定される。
!= __ne__(self, other)       __ne__が無いと__eq__が呼ばれる。
< __lt__(self, other)       __lt__が無いとotherの__gt__が呼ばれる。
<= __le__(self, other)       __le__が無いとotherの__ge__が呼ばれる。
> __gt__(self, other)       __gt__が無いとotherの__lt__が呼ばれる。
>= __ge__(self, other)       __ge__が無いとotherの__le__が呼ばれる。
in   __contains__(self, other)      

二項演算子では、「obj + other」は「obj.__add__(other)」という呼び出しと同じ。
もしobjに__add__メソッドが無い場合、otherに“左右を入れ替えたメソッド”__radd__があれば、「other.__radd__(obj)」が呼ばれる。
(inに関しては、「obj in seq」は「seq.__contains__(obj)」と同じ)

「+=」といった累算代入演算子は、__iadd__メソッドが定義されていればそれが呼び出される。 (__iadd__等の「i」はinplaceのこと?)
__iadd__が無くて__add__があれば、__add__が呼び出された後に代入される。


単項演算子のメソッド
演算子 メソッド
- __neg__(self)
+ __pos__(self)
~ __invert__(self)

単項演算子では、「-obj」は「obj.__neg__()」という呼び出しと同じ。


添字スライス表記のメソッド[2018-01-09]
演算子 メソッド 備考
other[自分] __index__(self) __index__は整数を返す。
__index__と__int__は同じ値を返すことが推奨されている。
__index__はhex関数やbin関数からも呼ばれる。
自分[key] __getitem__(self, key) 値の取得用。
自分[key] = value __setitem__(self, key, value) 値の設定用。

__index__の例[2018-01-09]

class MyClass:

    def __index__(self):
        return 1
a = ["aa", "bb", "cc"]

i = MyClass() # インスタンス生成
print(a[i])   #→'bb'

__getitem__, __setitem__の例[2018-01-09]

class MyClass:
    def __init__(self):
        self.dict = dict()

    def __setitem__(self, key, value):
        print(f"set {key} {value}")
        self.dict[key] = value

    def __getitem__(self, key):
        print(f"get {key}")
        if isinstance(key, slice):
            print(key.start, key.stop, key.step)
        return self.dict[key]
添字に指定する例 引数keyに渡ってくる値 説明
c = MyClass()
c[0]
0  
c["abc"]
'abc'  
c[1, 2]
c[(1, 2)]
(1, 2)
(1, 2)
値をカンマ区切りにするとタプルが渡ってくる。
(タプルは丸括弧を省略できる)
c[1:2:3]
c[4:]
c[:5]
c[:]
slice(1, 2, 3)
slice(4, None, None)
slice(None, 5, None)
slice(None, None, None)
スライス記法にすると、sliceオブジェクトが渡ってくる。
start, stop, stepフィールドでそれぞれの値が取れる。
c[0:1, 2:3]
c[0:1, 2]
c[:, 1]
c[:, :]
(slice(0, 1, None), slice(2, 3, None))
(slice(0, 1, None), 2)
(slice(None, None, None), 1)
(slice(None, None, None), slice(None, None, None))
スライス記法もカンマ区切りで複数指定でき、タプルが渡ってくる。

添字の書き方で(a[1, 2]のような)タプルを受け付けるかどうかは、__getitem__や__setitem__の実装による。
例えば標準のlistでは添字にタプルは使えないが、NumPyのndarrayでは使える。


Pythonへ戻る / 技術メモへ戻る
メールの送信先:ひしだま