Friday, 22 August 2014

[Python] __new__ và __init__, zip

Vài ghi chép vụn vặt...

Bài viết dùng python 2.7.5.

1. zip
Trả lời cho các câu hỏi: hàm zip trong python là gì? hàm zip trong python làm gì?


cái tên hàm rất rất rất dễ hiểu nếu bạn biết zip là gì: nó là cái khoá kéo (phéc măng tuya ???). Có 2 dải, và nó sẽ ghép chúng thành từng cặp (tuple). Với function zip, nó có thể zip nhiều list với nhau (2 trở lên, và sẽ cắt ở độ dài của list ngắn nhất - nếu kéo khoá mà 1 bên hết thì phải dừng lại thôi :D)

Code (viết trên ipython):
In [8]: numbs = range(1,10)

In [9]: name = ['C', 'Python', 'Java', 'Ruby', 'Perl', 'Go']

In [10]: zip(numbs, name)
Out[10]: [(1, 'C'), (2, 'Python'), (3, 'Java'), (4, 'Ruby'), (5, 'Perl'), (6, 'Go')]
In [11]: adjs = ['perfect', 'awesome', 'bloated']

In [12]: zip(numbs, name, adjs)
Out[12]: [(1, 'C', 'perfect'), (2, 'Python', 'awesome'), (3, 'Java', 'bloated')]
Chú ý: ở dòng [9], tên biến nên đặt là ``names`` thay vì ``name``, tác giả quá lười nên không đổi lại nên đành để vậy.

2. __new__ và __init__ 
Khi viết class trong python, chắc chắn ai cũng biết tới method __init__ dùng để khởi tạo một class instance. Trước khi đọc tiếp, hãy nhớ rằng TẤT CẢ MỌI THỨ TRONG PYTHON ĐỀU LÀ OBJECT.

2.1 Method __init__
dùng để initialize (khởi tạo - thiết lập các giá trị, ...) một class instance mới. Khi một class instance đã được tạo (bằng cách gọi 1 class object), method __init__ của class instance này sẽ tự động được gọi (trong method __init__ này, programmer làm gì thì tuỳ).

Code ví dụ (được viết trên ipython):
In [13]: class FAMILUG(object):
    def __init__(self, name='Thon'):
   ....:         self.name = name
   ....:         print self.name.upper()
   ....:

In [14]: fml = FAMILUG()
THON
Giải thích:
class FAMILUG(object): define một class tên là FAMILUG, mọi class trong Python cần kế thừa một class nào đó, object là class gốc. Vậy FAMILUG là một class, và nó là một class object (vì class là object).

FAMILUG(): gọi class object FAMILUG, tạo ra một class instance mới của class FAMILUG. Class instance này được gán cho variable fml, hiển nhiên nó cũng là một object (nhắc lại: everything in Python is object) nhưng chỉ gọi là class instance cho ngắn và phân biệt với class object. Do method __init__ tự được gọi nên quá trình tạo class instance này đã in ra chữ ``THON``.

Ở đây không giải thíchchi tiết về cách viết method __init__ hay cú pháp của class. Xem thêm ở https://docs.python.org/2/tutorial/classes.html#a-first-look-at-classes

NOTICE: phần sau chỉ nên đọc nếu bạn nắm được các khái niệm về OOP trong python.

2.2 Method __new__
Nếu bạn đọc link ở trên, có thể thấy trong tut python không viết tới __new__, rõ ràng nó không phải một thứ cơ bản, hay dùng.

Vậy method __new__ là gì?

Method __new__ được gọi để tạo ra một instance mới của class đó.

Vậy toàn bộ dòng code ``FAMILUG()`` có thực hiện ít nhất 2 việc sau:
gọi method __new__
gọi method __init__

Bình thường programmer không cần viết __new__ mà sẽ tự dụng __new__ của class mà nó kế thừa.
Một cách dùng của __new__ là khi implement singleton pattern (nôm na là viết class đảm bảo chỉ có một class instance duy nhất). Đoạn code sau lấy từ package ``nagiosplugin`` version 1.2.1 (cài bằng lệnh: pip install nagiosplugin==1.2.1):
class Runtime(object):

    instance = None
    # bỏ qua vài đoạn không cần thiết ...

    def __new__(cls):
        if not cls.instance:
            cls.instance = super(Runtime, cls).__new__(cls)
        return cls.instance
Trong method __new__ trên thực hiện kiểm tra xem class attribute ``instance`` đã được gán giá trị nào chưa, nếu chưa thì tạo một class instance mới, nếu rồi thì trả về class instance đã tồn tại. Do class object Runtime là duy nhất, nên attribute ``instance`` cũng là duy nhất, nó khác với data attribute của class instance (thường được gán self.xyz = blah ở __init__, mỗi class instance đều có thể có giá trị riêng).

Chú ý __new__ là static method (giống function bình thường (function viết ở module level) nhưng đặt trong một class - chủ yếu để tổ chức code cho hợp lý), nó không được nhận bất kỳ implicit argument nào  (self hay cls).

Tham khảo:
đọc thêm về __new__ và __init__ ở https://docs.python.org/2/reference/datamodel.html#basic-customization

class method vs static method http://stackoverflow.com/questions/136097/what-is-the-difference-between-staticmethod-and-classmethod-in-python