Format string trong Python – ‘f-strings’

Format string trong Python – ‘f-strings’


Cũng như bất kì ngôn ngữ nào thì Python cũng có tính năng format string. Tuy nhiên có rất nhiều cách format string trong python và ở phiên bản python mới hơn thì có hỗ trợ tính năng format string khác so với phiên bản python cũ hơn.

Dưới đây mình sẽ review các cách format string hiện có trong python3, cả cũ và mới.

Trước Python 3.6: Cách cũ

Trước Python 3.6 thì chúng ta có 2 cách chính để format python. Dùng ‘%’ hoặc dùng str.format() method. Hãy cùng thảo luận cách dùng và giới hạn của 2 phương pháp này.

Cách 1: Dùng % để format

Kí hiệu % để format cũng khá phổ biến ở các ngôn ngữ khác không chỉ python. Đây là cách format string có từ ban đầu (kể cả trong python 2). Bạn có thể tìm hiểu thêm về tính năng ‘%-formatting’ tại document chính thức ở link phía dưới.

%-formatting

Tuy nhiên cách này không được recommend trong chính documentation của python các bạn có thể thấy như bên dưới.

Note The formatting operations described here exhibit a variety of quirks that lead to a number of common errors (such as failing to display tuples and dictionaries correctly). Using the newer formatted string literals, the str.format() interface, or template strings may help avoid these errors. Each of these alternatives provides their own trade-offs and benefits of simplicity, flexibility, and/or extensibility.

Cách dùng

Để dùng % formatting các bạn phải viết string của mình theo mẫu tương tự sau. Hãy bỏ ‘%s’ vào bên trong string và tên biến được đặt bên ngoài nối liền %.

name = "Alan"
"Good day, %s." % name
--->'Good day Alan'

Như name sẽ thay thế %s bên trong string. Nếu có nhiều biến hơn thì chỉ cần bỏ vào một tuple như bên dưới

name = "Alan"
profession = "Data Engineer"
"Hello, %s is a %s" % (name, profession)
----> 'Hello Alan is a Data Engineer'

Tại sao không nên dùng % format thì cũng đã được nhắc đến trong documentation, các bạn có thể tham khảo thêm tại link này, để xem các thảo luận mở rộng khác.

Cách 2: Dùng str.format()

Dùng method format của string. Bởi vì mỗi string đều là một object và có method format nên bạn có thể trực tiếp gọi format từ các string. Hãy nhìn ví dụ bên dưới để hiểu format hoạt động như thế nào.

name = "Alan"
age = 30

"Hi, {}. You are {}.".format(name, age)
'Hi, Alan. You are 30'

"Hi, {1}. You are {0}.".format(name, age)
'Hi, 30. You are Alan'

"Hi, {chicken}. You are {orange}.".format(chicken=name, orange=age)
'Hi, Alan. You are 30'

Như các bạn có thể thấy ở ví dụ trên có một số cách để format variable vào bên trong string.

Cách đầu tiên chỉ đơn thuần là để {} trống, tự động format sẽ trả giá trị theo thứ tự trái qua phải.

Cách thứ 2 là đưa số thứ tự của chuối giá trị bên trong format vào string. Do đó các bạn có thể thấy là do chọn thứ tự là 1 nên sau ‘ Hi, ‘ sẽ là age chứ không phải là name nữa

Cách thứ 3 là tạo ra 2 giá trị {chicken} và {orange} bên trong string sau đó thì gán các biến này vào format. Lưu ý cách này bạn phải define tên biến bên trong string nữa, nếu bạn để như bên dưới thì code sẽ bị lỗi (phía bên dưới là code gây ra lỗi).

"Hi, {name}. You are {age}.".format(name, age)
'Hi, Alan. You are 30'
---->KeyError: 'name'

Như vậy các bạn có thể thấy khi sử dụng format và muốn specify tên biến cụ thể thì nó sẽ phát sinh ra 2 giá trị. Một là giá trị bên trong string (mình đặt là chicken và orange để dễ thấy giá trị này hoàn toàn không liên quan tên biến name,age) và hai là giá trị được gán là name và age. Đây là điểm mấu chốt làm cho cách format string này cũng không tối ưu.

str.format() cũng chưa là cách hay nhất

Check code ví dụ dưới đây

first_name = "Alan"
last_name = "Dao"
age = 50
profession = "Data Dev"
workplace = "tuananalytic"
print(("Hello, {first_name} {last_name}. You are {age}. " + 
       "You are a {profession}. You were a member of {workplace}.") \
       .format(first_name=first_name, last_name=last_name, age=age, \
               profession=profession, workplace=workplace))

Các bạn có thể thấy là khi mà một string thực sự phức tạp và có nhiều giá trị, việc format bằng method format vẫn rất rối rắm. Từng biến bên trong string và bên ngoài được ghi lại 2 lần. Nếu bạn ghi nhầm tên biến của bên trong string, hoặc tên biến được gán bên ngoài, cả hai đều có khả năng tạo ra lỗi.

Có một cách work around là sử dụng dict, tuy nhiên cách này cũng tạo ra 2 lần nhập dữ liệu tương tự như vậy, chứ cũng không hẳn là cách tốt hơn.

Sau Python 3.6: f-strings ngắn gọn, nhiều tính năng

f-strings cơ bản

Hãy cùng xem lại ví dụ phía trên, chúng ta viết lại theo kiểu fstring.

name = "Alan"
age = 30
f"Hello, {name}. You are {age}."
'Hello, Alan. You are 30.'

Chú ý chúng ta thêm ký tự f phía trước dấu ” ‘ ” sau đó điền tên biến vào {} cho từng biến muốn format vào string.

Như vậy string của bạn sẽ tự động có giá trị của name và age bên trong. Bạn có thể dùng F viết hoa cũng được nữa.

f-strings với expression

Ngoài việc gán vào biến, bạn còn có thể viết expression hoặc gọi 1 function trực tiếp trong f-strings ví dụ như.

f"{2 * 5}"
'10'

Hoặc gọi 1 function

def to_lower(input):
    return input.lower()

name = "Alan"
f"{to_lower(name)} is crazy."
'alan is crazy'

Hoặc gọi method từ 1 object (ví dụ ở đây là object string).

f"{name.lower()} is crazy."
'alan is crazy'

f-strings trên nhiều dòng

f-strings có thể được chia ra trên nhiều dòng bằng () (tương tự các string khác)

name = "Alan"
profession = "Data"
workplace = "tuananalytic"
message = (
    f"Hi {name}. "
    f"You are {profession}. "
    f"You work at {workplace}."
)
message

Các bạn nhớ là ở mỗi dòng đều phải bỏ chữ f vào nhé, nếu không có chữ f thì dòng đấy sẽ thành string thường không phải ‘f-strings’ và không format được.

Tại sao dùng f-strings

Lợi ích đầu tiên là để tinh gọn code, điều này có thể thấy rõ bằng mắt thường.

Lợi ích thứ hai rất lớn đều từ việc không phân biệt trong và ngoài khi gán biến vào string nữa. Do đó việc refactor code trở nên dễ dàng hơn.

Ví dụ bạn có biến name được define trong 200 files python khác và bạn đang dùng str.format(name=name).

Giả sử bạn dùng feature refactor code của pycharm, biến name sẽ được thay thế trong toàn bộ dự án thành name_plus. Tuy nhiên do biến được define bên trong string lại là 1 giá trị khác không liên quan tới biến name nên lúc dùng format sẽ thành format(name=name_plus).

Điều này sẽ không xảy ra nếu bạn dùng f-strings, vì khi bạn thay đổi biến name thành name_plus, thì biến phía bên trong string cũng sẽ được thay đổi đồng thời.

Ngoài ra như bạn thấy ở trên nội dung f-strings được chạy trực tiếp chứ không đơn thuần là add data từ các biến vào, cho nên có thể mở đường cho các cách ứng dụng khác nhau, code cũng chạy nhanh hơn (có benchmark trên mạng nhưng mình sẽ không đề cập trong bài viết này).

Kết

Cảm ơn các bạn đã đón đọc bài viết của mình về f-string, follow mình trên linkedin để xem tiếp các bài viết khác nhé.

Support Tuan | Analytic

Trả lời