callable 物件

April 26, 2022

函式、方法、類別？

``````>>> def foo():
...     print('foo')
...
>>> callable(foo)
True
``````

``````>>> class Some:
...     def action(self):
...         print(self, 'action')
...
>>> s = Some()
>>> type(s.action)
<class 'method'>
>>> callable(s.action)
True
>>>
``````

``````>>> callable(Some)
True
>>>
``````

__call__ 方法

``````>>> class Some:
...     def __call__(self, *args):
...         print(args)
...
>>> s = Some()
>>> s(1)
(1,)
>>> s(1, 2)
(1, 2)
>>> s(1, 2, 3)
(1, 2, 3)
>>>
``````

``````>>> range
<class 'range'>
>>> zip
<class 'zip'>
>>> type
<class 'type'>
>>>
``````

有狀態的函式？

``````>>> def add(n):
...     return lambda a: n + a
...
>>> [add10(i) for i in range(5)]
[10, 11, 12, 13, 14]
>>>
``````

`add` 函式傳回另一個函式，該函式帶著 `n` 的值，這形式了 closure，呼叫這個傳回的函式時，都會與當時傳入的 `n` 進行相加。

``````>>> def running_total(base):
...     total = 0
...     def _running_total(a):
...         nonlocal total
...         total = base + total + a
...     return _running_total
...
>>> partial_sum_from_10 = running_total(10)
>>> [partial_sum_from_10(i) for i in range(5)]
[10, 21, 33, 46, 60]
>>>
``````

``````>>> class running_total:
...     def __init__(self, base):
...         self.base = base
...         self.total = 0
...     def __call__(self, a):
...         self.total = self.base + self.total + a
...         return self.total
...
>>> partial_sum = running_total(10)
>>> [partial_sum(i) for i in range(5)]
[10, 21, 33, 46, 60]
>>> partial_sum.total
60
>>> partial_sum.base
10
>>>
``````

接受／傳回 callable

Python 可以實作裝飾器（decorator），例如〈屬性與方法〉看過的 `@property`，就是裝飾器，裝飾器就是 callable 物件，簡單的裝飾器可以使用函式實作，複雜的裝飾器可以使用類別實作，`property` 就是使用類別定義的裝飾器，而且它是個接受 callable、傳回 callable 的裝飾器。

``````>>> class Point:
...     def __init__(self, x, y):
...         self.x = x
...         self.y = y
...     def dist(self, p):
...         return ((self.x - p.x) ** 2 + (self.y - p.y) ** 2) ** 0.5
...     def __repr__(self):
...         return f'Point({self.x}, {self.y})'
...
>>> points = [Point(10, 20), Point(2, 3), Point(50, 98), Point(2, 33)]
>>> pt = Point(3, 2)
>>> sorted(points, key = pt.dist)
[Point(2, 3), Point(10, 20), Point(2, 33), Point(50, 98)]
>>>
``````

`pt.dist` 是綁定方法，方才談到，綁定方法是 `method` 的實例，行為上可以像函式進行呼叫，也就是 callable 物件，作為 `sorted``key` 引數，就可以簡單地完成基於與 `pt` 點的距離進行排序。