Monday, 13 February 2017

[Python] function là object

Nếu có coi function là một cái gì đó đặc biệt, khác biệt với mọi thứ còn lại trong Python thì có thể bạn đã sai, hay ít nhất cũng cần nghĩ lại sau khi xem các đặc điểm của function trong bài này.

Python lambda
Function là object, như mọi thứ khác trong Python. Integer là object, string là object, và function không phải ngoại lệ.
$ python3
>>> type(5)
<class 'int'>
>>> type('http://pymi.vn')
<class 'str'>
>>> type(True)
<class 'bool'>
>>> type(len)
<class 'builtin_function_or_method'>
Nếu biết về lambda trong Python, bạn sẽ thấy rõ hơn function là cái gì:
>>> double = lambda x: x*2
>>> type(double)
<class 'function'>
>>> double(21)
42
lambda tạo ra 1 object kiểu function (tưởng tượng ra 1 cục tròn tròn), sau đó ta gán (bind) tên ``double`` cho object đó. Nó tương tự như việc gán tên cho 1 string:

>>> name = "PyMI"
>>> type(name)
<class 'str'>
name -----> (string PyMI)
double ------> (function x:x*2)

Bản chất cú pháp định nghĩa function trong python là nó sẽ tạo ra một function object, và bind cái tên của function vào object ấy.
>>> def double(x):
...     return x*2
...
>>> type(double)
<class 'function'>
>>> double(7)
14
Để bind một cái tên khác vào cục function object này, gán tên mới như bình thường:
>>> gap_doi = double
>>> gap_doi(7)
14
Phép gán (=) thực chất nói rằng: tạo ra một cái tên và bind nó vào cái cục object mà double đang bind tới.

gap_doi ----> (function object) <----- double

Function là  callable object

function là object như bao object khác, điều mà khiến nó có vẻ khác biệt là bởi khả năng "gọi" được, tức viết được theo dạng
function(argument) 
khả năng này có được nhờ function object có method tên là __call__.
Một object có method __call__ gọi là 1 callable object: có thể kể tới function, class hay bất cứ kiểu dữ liệu nào thoả mãn điều kiện ấy.
>>> len.__call__([1,2,3])
3
>>> len([1,2,3])
3
>>> class Foo():
...     pass
...
>>> Foo.__call__()
<__main__.Foo object at 0x10d8c3b70>
>>> Foo()
<__main__.Foo object at 0x10d8c3898>

Function nhận đầu vào là function 

Function mà nhận function khác làm đầu vào được gọi là "higher order function".

``len`` là một function
>>> id(len)
4517823904
id là function có thể nhận đầu vào là một function, id là một "higher order function".

Function ``do`` sau đây là một higher order function:
>>> def do(function, argument):
...     return function(argument)
...
>>> result = do(lambda x:x*2, 'Python')
>>> print(result)
PythonPython
Các decorator đều là các higher order function.

map

map là higher order function thường được dùng.
map nhận đầu vào là 1 function và 1 list (chính xác hơn thì là iterable), rồi map gọi function ấy với từng phần tử của list, thu được 1 tập các kết quả sau khi gọi function.
>>> map(lambda x: x*2, [1,3,5])
<map object at 0x10d6b5128>
>>> list(map(lambda x: x*2, [1,3,5]))
[2, 6, 10]
Nếu không thấy quen dùng lambda, có thể viết dài hơn sử dụng cú pháp def:
>>> def double(n):
...     return n*2
...
>>> list(map(double, [1,3,5]))
[2, 6, 10]
Function là object, tức ta có thể tạo 1 list các function:
>>> funcs = [id, type, str, len]
>>> type(funcs[0])
<class 'builtin_function_or_method'>
>>> for func in funcs:
...     print(func.__name__, func(['Python', 'Golang']))
...
id 4520086984
type <class 'list'>
str ['Python', 'Golang']
len 2

Lambda với list comprehension

Tạo 5 function, mỗi function in một số từ 0 đến 4
Tạo 1 list 5 function bằng list comprehension:
>>> fs = [lambda: print(x) for x in range(5)]
>>> for f in fs:
...     f()
...
4
4
4
4
4
Cả 5 function ở đây đều refer đến "i", vì thế ta không thu được 5 functions lần lượt in từ 0 đến 4.
Cách làm đúng:
>>> import functools
>>> for f in [functools.partial(lambda x:print(x), x) for x in range(5)]:
...     f()
...
0
1
2
3
4
Function là object.
Hết.
Welcome to functional programming and Python 😱
HVN at http://www.familug.org/ and http://pymi.vn