S-JIS[2018-01-05/2018-01-09] 変更履歴
Python3.6.4の式(演算子)のメモ。
Pythonで数値の演算を行うときは、基本的に共通の型に変換される。→算術変換
int等の数値クラスは演算子用のメソッドを持っている。
nが数値の変数だとすると、「n + 1
」は「n.__add__(1)
」と同じである。
なお、「1 + 2
」を「1.__add__(2)
」と書くとエラーになる。
「1.
」のピリオドは浮動小数点数を表すものと判断され、メソッド呼び出しでは無い為。
「(1).__add__(2)
」の様に丸括弧で囲めば大丈夫。
Pythonでは演算子をオーバーロードすることが出来る。
つまり、自分独自のクラスでも演算子を使った式が記述できるようになる。
例えば「+」演算子を実装したければ、__add__メソッドを定義すればよい。
→演算子に対応するメソッド
(一般的に、「オーバーロード」は同名の関数(メソッド)で引数の型が異なるものを指す。
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 |
|||
乗除演算 | * @ / // % | 乗算・除算・剰余 | m * n |
@は行列の乗算。(→NumPyのndarray) /は除算。例えば3 / 2は1.5になる。 //は切り捨ての除算。3 // 2は1になる。 %は剰余。(割り算の余り) (divmod関数を使うと商と余りが同時に取得できる) |
加減算 | + - | 加算・減算 | m + n |
|
シフト演算 | << >> | ビットシフト | n << r |
|
ビット演算 | & | ビットAND | m & n |
ビット単位の論理積 |
^ | ビットXOR | m ^ n |
ビット単位の排他的論理和 | |
| | ビットOR | m | n |
ビット単位の論理和 | |
比較演算 | == != < > <= >= is is not in not in |
値の比較 | m == n |
==はJavaのequals相当。 |
同一性の比較 | obj1 is obj2 |
インスタンスが同じかどうか。 (Javaの==、Scalaのeq相当) |
||
帰属検査 | 2 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 |
算術演算子(数値同士の演算)は、基本的に共通の型に変換されて計算される。
(剰余「%」やビットシフト「<<」「>>」等は他の法則になる)
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 |
Javaの「|| 」相当。(短絡評価される。すなわち、左辺が偽のときのみ右辺が評価される) |
0 or 0 |
0 |
ただしJavaの「|| 」とは異なり、orの左辺がTrue相当の場合はその値、そうでなければ右辺の値を返す。 |
|
論理積(and) | False and False |
False |
Javaの「&& 」相当。(短絡評価される。すなわち、左辺が真のときのみ右辺が評価される) |
0 and 0 |
0 |
ただしJavaの「&& 」とは異なり、andの左辺がFalse相当の場合はその値、 andの左辺がTrue相当の場合は右辺の値を返す。 |
|
否定(not) | not False |
True |
Javaの「! 」相当。 |
not 0 |
True |
True, False以外の値を指定した場合もboolが返る。 |
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) |
round |
四捨五入 | round(1.4) |
1 |
__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__()
」という呼び出しと同じ。
演算子 | メソッド | 備考 |
---|---|---|
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) |
値をカンマ区切りにするとタプルが渡ってくる。 (タプルは丸括弧を省略できる) |
c[1:2:3] c[4:] c[:5] c[:] |
slice(1, 2, 3) |
スライス記法にすると、sliceオブジェクトが渡ってくる。 start, stop, stepフィールドでそれぞれの値が取れる。 |
c[0:1, 2:3] c[0:1, 2] c[:, 1] c[:, :] |
(slice(0, 1, None), slice(2, 3, None)) |
スライス記法もカンマ区切りで複数指定でき、タプルが渡ってくる。 |
添字の書き方で(a[1, 2]
のような)タプルを受け付けるかどうかは、__getitem__や__setitem__の実装による。
例えば標準のlistでは添字にタプルは使えないが、NumPyのndarrayでは使える。