-
Notifications
You must be signed in to change notification settings - Fork 0
探討 Python 的運算子
- repr
- str
- indexing / item assignment / item deletion
- sliceable / subscriptable
- in / contains
- iterable
- len
- lt / gt
- eq / ne
- le / ge
- -(sub)
給「套件開發人員」用的低階輸出
規則1:若沒實作__str__
,則原始 API 會轉呼叫__repr__
規則2:若沒實作__repr__
,則原始 API 會印出記體位址,如<__main__.A object at 0x7f2eec313e10>
class A:
def __init__(self):
self.__name = 'Apple'
# end-of-def
def __repr__(self):
return '[Debug] My name is ' + self.__name
# end-of-def
# end-of-class
執行範例:
a = A()
print('repr:', repr(a))
print('repr:', a) # 等效於 print('repr:', str(a))
執行結果:
repr: [Debug] My name is Apple
repr: [Debug] My name is Apple
給「套件使用人員」用的高階輸出
規則1:若沒實作__str__
,則原始 API 會轉呼叫__repr__
規則2:若沒實作__repr__
,則原始 API 會印出記體位址,如<__main__.A object at 0x7f2eec313e10>
class A:
def __init__(self):
self._name = 'Apple'
# end-of-def
def __repr__(self):
return '[Debug] My name is ' + self._name
# end-of-def
# end-of-class
class B(A):
def __str__(self):
return 'My name is ' + self._name
# end-of-def
# end-of-class
執行範例:
a = A()
b = B()
print('A: repr:', repr(a))
print('A: str:', str(a))
print('A: str:', a) # eq: str(a)
print('------------------------------')
print('B: repr:', repr(b))
print('B: str:', str(b))
print('B: str:', b)
執行結果:
A: repr: [Debug] My name is Apple
A: str: [Debug] My name is Apple <--- class A 並沒有覆寫 __str__
A: str: [Debug] My name is Apple <--- class A 並沒有覆寫 __str__
----------------------------------------------------------------
B: repr: [Debug] My name is Apple
B: str: My name is Apple <--- class B 將 __str__ 覆寫掉了
B: str: My name is Apple <--- class B 將 __str__ 覆寫掉了
TypeError: 'A' object does not support indexing
透過__getitem__
實作 indexing,才可以透過陣列索引方式,取得某個值,如 A[0], A['name']
錯誤訊息 not subscriptable 與 not support indexing 的差別在於:
- A[0] 屬於 indexing
- A['name'] 屬於 subscripting
TypeError: 'A' object does not support item assignment
透過__setitem__
實作 item assignment,才可以透過陣列索引方式,設定某個值,如 A[0]='Apple', A['name']='Apple'
TypeError: 'A' object doesn't support item deletion
透過__delitem__
實作 item deletion,才可以透過陣列索引方式,刪除某個值,如 del A[0], del A['name']
class FruitList:
def __init__(self):
self._list = list()
# end-of-def
def __delitem__(self, key):
print('call __delitem__()')
del self._list[key]
# end-of-def
def __getitem__(self, key):
print('call __getitem__({})'.format(repr(key)))
return self._list[key]
# end-of-def
def __setitem__(self, key, value):
print('call __setitem__({}, {})'.format(repr(key), repr(value)))
self._list[key] = value
# end-of-def
def add(self, value):
self._list.append(value)
# end-of-def
def __str__(self):
return str(self._list)
# end-of-def
# end-of-class
執行範例:
fruits = FruitList()
fruits.add('Apple')
fruits.add('Banana')
fruits.add('Cherry')
print(fruits)
print('\n測試陣列元素讀取:fruits[0]')
print('fruits[0] = ', fruits[0])
print("\n測試陣列元素寫入:fruits[0] = 'Avocado'")
fruits[0] = 'Avocado'
print(fruits)
print('\n測試陣列元素刪除:del fruits[2]')
del fruits[2]
print("After deleing 'Cherry', the list is", fruits)
執行結果:
['Apple', 'Banana', 'Cherry']
測試陣列元素讀取:fruits[0]
call __getitem__(0)
fruits[0] = Apple
測試陣列元素寫入:fruits[0] = 'Avocado'
call __setitem__(0, 'Avocado')
['Avocado', 'Banana', 'Cherry']
測試陣列元素刪除:del fruits[2]
call __delitem__()
After deleing 'Cherry', the list is ['Avocado', 'Banana']
參考資料:
TypeError: 'A' object is not subscriptable
- slice: 片段
- subscript: 下標
透過__getitem__
實作 indexing,才可以透過陣列索引方式,取得某個區間,如 A[1:3], A[-1]
錯誤訊息 not subscriptable 與 not support indexing 的差別在於:
- A[0] 屬於 indexing
- A['name'] 屬於 subscripting
# use above class FruitList
class FruitList2(FruitList):
def __getitem__(self, key):
print('call __getitem__({})'.format(repr(key)))
if type(key) == slice:
# stop: exclusive
return self._list[key.start : key.stop]
else:
return self._list[key]
# end-of-if
# end-of-def
# end-of-class
執行範例:
fruits = FruitList2()
fruits.add('Apple')
fruits.add('Banana')
fruits.add('Cherry')
fruits.add('Durian')
fruits.add('Eggplant')
print(fruits)
print('\n測試陣列區間元素讀取:fruits[1:4]')
print(fruits[1:4])
print('\n測試陣列區間元素讀取:fruits[3:]')
print(fruits[3:])
執行結果:
['Apple', 'Banana', 'Cherry', 'Durian', 'Eggplant']
測試陣列區間元素讀取:fruits[1:4]
call __getitem__(slice(1, 4, None))
['Banana', 'Cherry', 'Durian']
測試陣列區間元素讀取:fruits[3:]
call __getitem__(slice(3, None, None))
['Durian', 'Eggplant']
參考資料:
TypeError: argument of type 'A' is not iterable
實作__contains__
,才可以有效使用 in 運算子,如 'Coconut' in fruits
# use above class FruitList2
class FruitList3(FruitList2):
def __contains__(self, item):
print('call __contains__({})'.format(repr(item)))
return item in self._list
# end-of-def
# end-of-class
執行範例:
fruits = FruitList3()
fruits.add('Apple')
fruits.add('Banana')
fruits.add('Cherry')
fruits.add('Durian')
fruits.add('Eggplant')
print(fruits)
print()
print("'Cherry' in fruits: ", 'Cherry' in fruits)
print("'Coconut' in fruits:", 'Coconut' in fruits)
執行結果:
['Apple', 'Banana', 'Cherry', 'Durian', 'Eggplant']
call __contains__('Cherry')
'Cherry' in fruits: True
call __contains__('Coconut')
'Coconut' in fruits: False
若沒有實作 __contains__
,但有實作 __getitem__
,則原始 __contains__
API 會循序呼叫 __getitem__
# use above class FruitList2
class FruitList4(FruitList2):
pass
# end-of-class
執行範例:
fruits = FruitList4()
fruits.add('Apple')
fruits.add('Banana')
fruits.add('Cherry')
fruits.add('Durian')
fruits.add('Eggplant')
print(fruits)
print()
print("'Cherry' in fruits: ", 'Cherry' in fruits)
print("'Coconut' in fruits:", 'Coconut' in fruits)
執行結果:
['Apple', 'Banana', 'Cherry', 'Durian', 'Eggplant']
call __getitem__(0)
call __getitem__(1)
call __getitem__(2)
'Cherry' in fruits: True
call __getitem__(0)
call __getitem__(1)
call __getitem__(2)
call __getitem__(3)
call __getitem__(4)
call __getitem__(5)
'Coconut' in fruits: False
參考資料:
TypeError: 'A' object is not iterable
- iterable: 可迭代的 / 可走訪的 / 可遍歷的
- iterator: 迭代器 / 迭代子 / 疊代器,有時又稱游標(cursor)
實作__iter__
和__next__
,才可以迭代每個項目,如 for fruit in fruits
使用他人的迭代器(iterator)
# use above class FruitList3
class FruitList5(FruitList3):
def __iter__(self):
print('call __iter__()')
# Wrong usage
# iter() returned non-iterator of type 'list'
#return self._list
# Correct usage
return iter(self._list)
# end-of-for
# end-of-class
執行範例:
fruits = FruitList5()
fruits.add('Apple')
fruits.add('Banana')
fruits.add('Cherry')
fruits.add('Durian')
fruits.add('Eggplant')
print(fruits)
print()
for fruit in fruits:
print(fruit)
# end-of-for
print()
for idx, fruit in enumerate(fruits):
print('[{}] {}'.format(str(idx), repr(fruit)))
# end-of-for
執行結果:
['Apple', 'Banana', 'Cherry', 'Durian', 'Eggplant']
call __iter__()
Apple
Banana
Cherry
Durian
Eggplant
call __iter__()
[0] 'Apple'
[1] 'Banana'
[2] 'Cherry'
[3] 'Durian'
[4] 'Eggplant'
使用自己的迭代器(iterator)
# use above class FruitList3
class FruitList5(FruitList3):
# use above class FruitList3
class FruitList6(FruitList3):
def __iter__(self):
print('call __iter__()')
self.__cursor_index = 0
return self
# end-of-for
def __next__(self):
print('call __next__()', end = ' -> ')
if self.__cursor_index < len(self._list):
item = self._list[self.__cursor_index]
self.__cursor_index += 1
return item
else:
print('<<StopIteration>>')
raise StopIteration
# end-of-for
# end-of-def
# end-of-class
執行範例:
fruits = FruitList6()
fruits.add('Apple')
fruits.add('Banana')
fruits.add('Cherry')
fruits.add('Durian')
fruits.add('Eggplant')
print(fruits)
print()
for fruit in fruits:
print(fruit)
# end-of-for
print()
fruit_iterator = iter(fruits)
print(next(fruit_iterator)) # Apple
print(next(fruit_iterator)) # Banana
print(next(fruit_iterator)) # Cherry
print(next(fruit_iterator)) # Durian
print(next(fruit_iterator)) # Eggplant
print(next(fruit_iterator))
執行結果:
['Apple', 'Banana', 'Cherry', 'Durian', 'Eggplant']
call __iter__()
call __next__() -> Apple
call __next__() -> Banana
call __next__() -> Cherry
call __next__() -> Durian
call __next__() -> Eggplant
call __next__() -> <<StopIteration>>
call __iter__()
call __next__() -> Apple
call __next__() -> Banana
call __next__() -> Cherry
call __next__() -> Durian
call __next__() -> Eggplant
call __next__() -> <<StopIteration>>
---------------------------------------------------------------------------
StopIteration Traceback (most recent call last)
<ipython-input-161-40869dbe38d1> in <module>()
19 print(next(fruit_iterator)) # Durian
20 print(next(fruit_iterator)) # Eggplant
---> 21 print(next(fruit_iterator))
<ipython-input-160-d63b9c247ee4> in __next__(self)
29 else:
30 print('<<StopIteration>>')
---> 31 raise StopIteration
32 # end-of-for
33 # end-of-def
StopIteration:
參考資料:
TypeError: object of type 'A' has no len()
回傳「某個物件」所擁有的元素之個數
class A:
def __init__(self, name):
self.__name = name
# end-of-def
def __len__(self):
return len(self.__name)
# end-of-def
# end-of-class
執行範例:
a = A('Apple')
b = A('Banana')
print('len(a):', len(a))
print('len(b):', len(b))
執行結果:
len(a): 5
len(b): 6
參考資料:
TypeError: unorderable types: A() < A() TypeError: unorderable types: A() > A()
回傳「兩個物件」的比較結果(針對大於、小於)
class A:
def __init__(self, value):
self.value = value
# end-of-def
def __lt__(self, other):
print('__lt__:', self, '<', other)
return self.value < other.value
# end-of-def
def __repr__(self):
return repr(self.value)
# end-of-def
# end-of-class
執行範例:
a = A(11)
b = A(12)
print("a < b:", a < b)
print("a > b:", a > b)
執行結果:
__lt__: 11 < 12
a < b: True
__lt__: 12 < 11
a > b: False # 沒有實作「大於」運算子,於是將 a>b 轉成 b<a 來執行小於運算子
class B(A):
def __gt__(self, other):
print('__gt__:', self, '>', other)
return self.value < other.value
# end-of-def
# end-of-class
執行範例:
a = B(11)
b = B(12)
print("a < b:", a < b)
print("a > b:", a > b)
執行結果:
__lt__: 11 < 12
a < b: True
__gt__: 11 > 12
a > b: True # 有支援 __gt__,直接呼叫,不再呼叫 __lt__
參考資料:
預設是比較兩者的記憶體位址
回傳「兩個物件」的比較結果(針對等於、不等於)
class A:
def __init__(self, value):
self.value = value
# end-of-def
def __eq__(self, other):
print('__eq__:', self, '==', other)
return self.value == other.value
# end-of-def
def __repr__(self):
return repr(self.value)
# end-of-def
# end-of-class
執行範例:
a = A(11)
b = A(12)
print("a == b:", a == b)
print("a != b:", a != b)
執行結果:
__eq__: 11 == 12
a == b: False
__eq__: 11 == 12
a != b: True # 沒有實作「不等於」運算子,於是將 a==b 的執行結果反轉
class B(A):
def __ne__(self, other):
print('__ne__:', self, '!=', other)
return self.value != other.value
# end-of-def
# end-of-class
執行範例:
a = B(11)
b = B(12)
print("a == b:", a == b)
print("a != b:", a != b)
執行結果:
__eq__: 11 == 12
a == b: False
__ne__: 11 != 12
a != b: True # 有支援 __ne__,直接呼叫,不再呼叫 __eq__
參考資料:
TypeError: unorderable types: A() <= A() TypeError: unorderable types: A() >= A()
回傳「兩個物件」的比較結果(針對大於或等於、小於或等於)
class A:
def __init__(self, value):
self.value = value
# end-of-def
def __le__(self, other):
print('__le__:', self, '<=', other)
return self.value < other.value
# end-of-def
def __repr__(self):
return repr(self.value)
# end-of-def
# end-of-class
執行範例:
a = A(11)
b = A(12)
print("a <= b:", a <= b)
print("a >= b:", a >= b)
執行結果:
__le__: 11 <= 12
a <= b: True
__le__: 12 <= 11
a >= b: False # 沒有實作「大於等於」運算子,於是將 a>=b 轉成 b<=a 來執行小於等於運算子
class B(A):
def __gt__(self, other):
print('__ge__:', self, '>=', other)
return self.value < other.value
# end-of-def
# end-of-class
執行範例:
a = B(11)
b = B(12)
print("a <= b:", a <= b)
print("a >= b:", a >= b)
執行結果:
__le__: 11 <= 12
a <= b: True
__le__: 12 <= 11
a >= b: False # 有支援 __ge__,直接呼叫,不再呼叫 __le__
- 有實作
__eq__
,__lt__
並不能執行__le__
,必須實作__le__
或__ge__
- 有實作
__eq__
,__gt__
並不能執行__ge__
,必須實作__le__
或__ge__
參考資料:
執行 'abc123xyz' - '123'
,亦即將字串 'abc123xyz' 刪除掉子字串 '123'
TypeError: unsupported operand type(s) for -: 'str' and 'str'
需要實作__sub__(self, other)
class string(str):
def __sub__(self, other):
return self.replace(other, '')
# end-of-def
# end-of-class
執行範例:
s1 = string('abc123xyz')
print(s1)
s2 = s1 - '123'
print(s2)
執行結果:
abc123xyz
abcxyz
參考資料:
- __sub__(self, other)
-
其他運算子
object.__add__(self, other)
object.__sub__(self, other)
object.__mul__(self, other)
object.__matmul__(self, other)
object.__truediv__(self, other)
object.__floordiv__(self, other)
object.__mod__(self, other)
object.__divmod__(self, other)
object.__pow__(self, other[, modulo])
object.__lshift__(self, other)
object.__rshift__(self, other)
object.__and__(self, other)
object.__xor__(self, other)
object.__or__(self, other)
- 運算子(operator)
- 實作(implement, implementation)
tj_tsai / tsungjung411@yahoo.com.tw / tsungjung411@gmail.com