Skip to content

探討 Python 的運算子

tsungjung411 edited this page Oct 24, 2018 · 33 revisions
  1. repr
  2. str
  3. indexing / item assignment / item deletion
  4. sliceable / subscriptable
  5. in / contains
  6. iterable
  7. len
  8. lt / gt
  9. eq / ne
  10. le / ge
  11. -(sub)

== repr ==

給「套件開發人員」用的低階輸出
規則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

== str ==

給「套件使用人員」用的高階輸出
規則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__ 覆寫掉了

== indexing / item assignment / item deletion ==

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']

參考資料:


== sliceable / subscriptable ==

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']

參考資料:


== in / contains ==

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

參考資料:


== iterable ==

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: 

參考資料:


== len ==

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

參考資料:


== lt / gt ==

TypeError: unorderable types: A() < A() TypeError: unorderable types: A() > A()
回傳「兩個物件」的比較結果(針對大於、小於)

Case1 只實作 __lt__ (less than, 小於)

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 來執行小於運算子

Case2: 補實作 __gt__ (greater than, 大於)

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__

參考資料:


== eq / ne ==

預設是比較兩者的記憶體位址
回傳「兩個物件」的比較結果(針對等於、不等於)

Case1 只實作 __eq__ (equal to, 等於)

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 的執行結果反轉

Case2: 補實作 __ne__ (not equal to, 不等於)

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__

參考資料:


== le / ge ==

TypeError: unorderable types: A() <= A() TypeError: unorderable types: A() >= A()
回傳「兩個物件」的比較結果(針對大於或等於、小於或等於)

Case1 只實作 __le__ (less than or equal to, 小於或等於)

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 來執行小於等於運算子

Case2: 補實作 __ge__ (greater than or equal to, 大於或等於)

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__

參考資料:



== subtraction ==

執行 '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)
Clone this wiki locally