Python

__repr__ vs. __str__

Xin chào, trong bài viết này chúng ta sẽ tìm hiểu sự khác biệt giữa __repr__ và __str__,  hai method được sử dụng phổ biến để biểu đạt thông tin về đối tượng chứa nó.


Sự cần thiết của __str__

Một trong những điều tuyệt vời nhất của Python chính là tính nhất quán.

Khi tạo một class mới, chúng ta nên cung cấp một method để biểu đạt thông tin về các instance của class đó, nhằm dễ dàng debug khi cần, chẳng hạn như ở ví dụ 1.

Example 1:

class Demo:
    def __init__(self, a, b):
        self.a = a
        self.b = b 

    def info(self):
        print("An instance of {} class. a={!r}, b={!r}".format(self.__class__.__name__, self.a, self.b))

if __name__ == "__main__":
    d1 = Demo(1, 2)
    d2 = Demo('1', '2')
    d1.info()
    d2.info()

Output:
An instance of Demo class: a=1, b=2
An instance of Demo class: a=’1′, b=’2′

Giải thích:
– Flag !r được dùng để giữ nguyên format của kiểu dữ liệu của đối tượng khi in ra. Nếu không có flag !r, tất cả các đối tượng đều sẽ được chuyển về kiểu str và sẽ không phân biệt được giữa str và các kiểu dữ liệu khác.

Giả sử trong project chúng ta đang thực hiện sử dụng nhiều thư viện, một vài trong số đó là thư viện của 3rd-parties. Nếu không có tính nhất quán, mỗi class trong từng thư viện chắc chắn sẽ sử dụng một method riêng để biểu đạt thông tin về chúng, như vậy chỉ cần nhớ tên các method này cũng đủ mệt rồi. Do đó, việc thêm method info() như ví dụ 1 không mang lại tính nhất quán giữa các class.

Để đảm bảo tính nhất quán giữa các class, Python cung cấp Operater Overloading Methods. Áp dụng overloading với __str__, chúng ta có thể viết lại ví dụ 1 như sau:

Example 2:

class Demo:
    def __init__(self, a, b):
        self.a = a
        self.b = b 

    def __str__(self):
        return "An instance of {} class. a={!r}, b={!r}".format(self.__class__.__name__, self.a, self.b)

if __name__ == "__main__":
    d1 = Demo(1, 2)
    d2 = Demo('1', '2')
    print(d1)
    print(d2)

Output:
An instance of Demo class: a=1, b=2
An instance of Demo class: a=’1′, b=’2′

Như vậy, với method __str__, mọi đối tượng đều có thể biểu đạt thông tin về nó bằng cách sử dụng built-in function print().


Sự cần thiết của __repr__

Chúng ta đã có thể sử dụng function print() để biểu đạt thông tin của các đối tượng, vậy tại sao vẫn cần __repr__? Để trả lời cho câu hỏi này, hãy mở Python interpreter session và nhập vào đoạn code sau:

Example 3:

>>> from test import Demo
>>> d = Demo(1,2)
>>> print(d)
An instance of Demo class: a=1, b=2
>>> str(d)
'An instance of Demo class: a=1, b=2'
>>> d
test.Demo object at 0x00...

Có thể thấy rằng 2 function print() và str() đã hoàn thành tốt nhiệm vụ của nó, nhưng vẫn chưa đủ. Trong interpreter session, chúng ta cần nhận được thông tin về đối tượng trực tiếp từ tên của đối tượng đó, và đó là lý do cho sự xuất hiện của __repr__.

Example 4:

class Demo:
    def __init__(self, a, b):
        self.a = a
        self.b = b 

    def __str__(self):
        return "An instance of {} class. a={!r}, b={!r}".format(self.__class__.__name__, self.a, self.b)

    def __repr__(self):
        return "Demo({!r},{!r})".format(self.a, self.b)

Python interpreter session:

>>> from test import Demo
>>> d = Demo(1,2)
>>> print(d)
An instance of Demo class: a=1, b=2
>>> str(d)
'An instance of Demo class: a=1, b=2'
>>> d
Demo(1,2)
>>> repr(d)
'Demo(1,2)'

Như vậy, với __repr__, chúng ta có thể nhận được thông tin từ đối tượng bởi chính tên của nó, và __repr__ là method duy nhất cho phép biểu đạt thông tin về đối tượng trong môi trường Python interpreter session.


Lựa chọn giữa __str__ và __repr__

  • __str__: được sử dụng để cung cấp thông tin tổng quan cho người dùng cuối (người dùng chứ không phải lập trình viên);
  • __repr__: được sử dụng để cung cấp thông tin rõ ràng về đối tượng cho lập trình viên, giúp cho việc debug. Trong trường hợp __str__ không được cung cấp, function print() và str() sẽ gọi __repr__. Do đó, nếu chỉ sử dụng hoặc __repr__ hoặc __str__, hãy chọn __repr__.

Example 5:

import datetime
today = datetime.datetime.now() 

# Prints readable format for endusers
print("Time: ", today) 

# prints the official format of date-time object for dev
print("Object:", repr(today))

Output:
Time: 2019-01-06 19:09:03.943711
Object: datetime.datime(2019, 1, 6, 19, 9, 3, 943711)


SUMMARY

Qua bài viết này chúng ta đã tìm hiểu về __str__ và __repr__. Cảm ơn các bạn đã theo dõi bài viết.

Thân ái và quyết thắng.

Reference:
[1] Operator and Function Overloading in Custom Python Classes.
[2] Python String Conversion 101: Why Every Class Needs a “repr”.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s