01/10/2018, 14:37

%s trong Python có tác dụng gì?

Em source 2 code này ra vẫn giống nhau

cars = 100
print (“There are %s cars available.” % cars)

cars = 100
print (“There are”, cars, “cars available.”)

Anh chị nào biết tác dụng của %s giải thích em với ạ.

Vô Thin viết 16:54 ngày 01/10/2018

Nhiều ngôn ngữ bắt chước cách định dạng của ngôn ngữ C. Bạn tra cứu với từ khóa printf format string để xem giải thích rõ hơn. Nói chung là để định dạng việc in ra cho đẹp, dễ hơn trong việc gom các biến/biểu thức lại một chỗ, dễ gỡ rối khi cần.

Tâm Ninja viết 16:40 ngày 01/10/2018

Nhiều ngôn ngữ bắt chước cách định dạng của ngôn ngữ C

Đừng nói thế dễ gây war. Đơn giản chỉ là vì C đi trước và các ngôn ngữ khác follow theo để các lập trình viên không bị rắc rối quá nhiều vì phải học lại nhiều syntax và standard.

Việc sử dụng %s dùng để inject data vào String có hai tác dụng.

  1. Format dữ liệu trước khi truyền vào một cách tự động giúp code ngắn gọn hơn. (Ví dụ hiển thị số thực bao nhiêu chữ số sau dấu phẩy. Ngăn cách số theo phần nghìn hay phần vạn 1,000 và 1,0000. Kí tự dùng để ngăn cách… Còn rất nhiều nữa)
  2. Có thể inject 1 data vào nhiều chỗ mà không cần viết lại nhiều lần biểu thức …" + "…
*grab popcorn* viết 16:41 ngày 01/10/2018

A post was merged into an existing topic: Topic chứa các reply được cho là off-topic - version 2

Nguyen Duc Nam viết 16:41 ngày 01/10/2018

Mình có thể hiểu đc vấn đề rồi , cảm ơn bạn ^^

Hung viết 16:48 ngày 01/10/2018

Có nhiều cách format lắm.

  • toString() trong OOP
  • string interpolation ”number $a == number $b” trong PHP
  • sử dụng Builder Pattern, StringBuilder trong Java là ví dụ
  • string concatenation, ”hello “ + “world”
  • format string, %s, %i, %@

Trong đó, format string là cách low-level, nhưng lại là cách mà developer có nhiều tuỳ chỉnh nhất.

C nó làm luôn kiểu format string và chỉ có support mỗi tính năng đó cho string nên dễ gây ức chế với người mới tiếp cận.

Nguyen Duc Nam viết 16:47 ngày 01/10/2018

Là mỗi format string có tác dụng khác nhau phải không ạ

Giống kiểu %s là giúp code ngắn gọn hơn ( Mình mới nhập môn Python nên chỉ biết mỗi %s )

viết 16:53 ngày 01/10/2018

tác dụng như nhau hết: để định dạng chuỗi, chỉ khác chỗ dễ đọc, hoặc dễ học, hoặc dễ code, hoặc chạy nhanh thôi.

Python có nhiều kiểu lắm: %, {}, hoặc template string: https://docs.python.org/3.6/library/string.html, https://pyformat.info/

%s giúp code ngắn hơn nhưng có khi đọc vào chả hiểu gì, ví dụ

'%a, %d %B %Y'

{} thì dễ học, dễ code hơn, vì ko cần nhớ mấy kí tự sau dấu %, ví dụ

'%s %s' % ('one', 'two')     #one two
'{} {}'.format('one', 'two') #one two

'%d %d' % (1, 2)     #1 2
'{} {}'.format(1, 2) #1 2

hoặc tránh truyền đi truyền lại 1 biến nào đó:
v.v… nói chung là nên xài {} thay cho %xyz

'%d + %d = %d' % (1, 1, 2)     #1 + 1 = 2
'{0} + {0} = {1}'.format(1, 2) #1 + 1 = 2

string + string thì chậm, string builder thì lẹ hơn

v.v…

Hung viết 16:51 ngày 01/10/2018

Tổ chức code và viết sao với đoạn code dài vẫn đọc được. Cái đó quan trọng hơn là làm cho code ngắn hơn.

Nguyen Duc Nam viết 16:49 ngày 01/10/2018

Tại sao “string + string” lại chậm hơn “stringbuilder” vậy ?

viết 16:48 ngày 01/10/2018

tại vì nó tốn thời gian tạo ra chuỗi mới mỗi lần + vào, còn stringbuilder thì nó ko tạo chuỗi mới mà xài thẳng chuỗi cũ luôn, và nó cấp phát bộ nhớ cho mảng ký tự mới cho chuỗi cũ 1 cách “thông minh” hơn so với a + b

python ko có stringbuilder nên string + string với .format cũng gần như nhau rồi, python 3.6 có cái string interpolation nghe nói lẹ hơn tí. Mấy ngôn ngữ khác như C# Java C++ mới thấy string + string nó chậm hơn stringbuilder.

mấy cái tốc độ nhanh chậm thì để sau cùng hẵn tính, viết ra đủ yêu cầu cái đã rồi mới lo optimize tốc độ sau cùng. Nếu viết string + string dễ đoc dễ hiểu thì cứ viết theo dễ đọc dễ hiểu, khi nào chậm quá thì mới tính viết cách khác… Bởi vậy mình viết nó có 1 dòng be bé thôi mà lại gây khó hiểu :V

Nguyen Duc Nam viết 16:47 ngày 01/10/2018

Cảm ơn bạn đã giải thích vấn đề .

Tâm Ninja viết 16:50 ngày 01/10/2018

Tại sao “string + string” lại chậm hơn “stringbuilder” vậy ?

string + string không chậm hơn stringbuilder nhé. Cái này là case by case cần nhận thức đúng. Vấn đề gặp phải của việc cộng hai String là trong Java String sử dụng String pool (Khái niệm tự Google nhé cho lớn) dẫn đến String là immutabe type. Nghĩa là String là không thể thay đổi được. Cụ thể như sau:

Ta có một tổ chức bộ nhớ kiểu.

_ _ _ _ _ _ _ _
0 1 2 3 4 5 6 7

Khi ta lưu một String vào String Pool này ví dụ từ Hello thì biểu thức thực hiện như sau.

H e l l o _ _ _
0 1 2 3 4 5 6 7

Khi thực hiện cộng tiếp một chuỗi vào chuỗi kế tiếp thì điều gì sẽ xảy ra. Có hai trường hợp, 1 là vị trí index == 5 chưa bị chiếm dụng vậy việc đơn giản là cộng tiếp world vào trong pool là xong. SAI. Hoàn toàn không có trường hợp này. String pool được sinh ra để đảm bảo là String sẽ được tối ưu hoá cho việc sử dụng lại nhiều lần. Vậy nên vị trí index 0 chỉ có thể trả về String là Hello vậy điều gì sẽ xảy ra. Các bước thuật toán bao gồm.

  1. Đọc String Hello ra.
  2. Xác định độ dài của Hello.
  3. Cộng độ dài của Helloworld
  4. Tìm một vùng nhớ có độ dài bằng 11 gần nhất
  5. Lưu Hello world vào String pool

Tổng cộng là ta mất 5 bước thuật toán trong tưởng tượng. (Vì thực tế nó không chính xác như thế) Với StringBuilder thì điều gì sẽ xảy ra. Phương án là thay vì lưu trực tiếp chuỗi vào trong StringPool, ta cache lại chuỗi này trong một char array. Vậy khi cộng hai String, ta sẽ làm các bước sau.

  1. Đọc String Hello ra.
  2. Xác định độ dài của Hello.
  3. Cộng độ dài của Helloworld
  4. Tìm một vùng nhớ Array mới có độ dài bằng 11 gần nhất
  5. Lưu Hello world vào Array

Không khác gì việc cộng String trong tưởng tượng. Lí do là vì Array cũng không thể thay đổi được kích thước sau khi đã khai báo. Tuy nhiên điểm khác biệt giữa Array và String đấy là Array thì không phải là một immutabe type. (Bằng chứng là người ta vẫn đi tìm immutable array đó thôi) StringBuilder triển khai một kĩ thuật trong xử lý Array. Tức là thay vì tạo một array có kích thước bằng với kích thước của Hello, StringBuilder tạo ra một đoạn buffer lớn hơn kích thước đó (Cụ thể giá trị mặc định bằng 16) để khi tiếp tục thực hiện việc cộng, sẽ có hai trường hợp xảy ra.

  1. Đọc String Hello ra.
  2. Xác định độ dài của Hello.
  3. Cộng độ dài của Helloworld
    3.1 Vì Array có độ dài bằng 16 nên copy String mới vào đây luôn. Tốn số bước lưu bằng kích thước world bằng 6.
    3.2 Nếu kích thước vượt quá kích thước hiện tại. Tìm một vùng nhớ có độ dài bằng 11 gần nhất. Số bước thuật toán tốn bằng việc thanh ghi nhảy bao nhiêu lần.
  4. Lưu Hello world vào Array. Tốn tiếp 11 bước thuật toán nữa.

Rõ ràng là chỉ tính từ bước số 4 thì 11++ luôn lớn hơn rất nhiều 6. Và nếu String cũ mà dài thì còn lớn hơn rất rất là nhiều nữa. Vậy xét một cách nào đó thì string + string chậm hơn stringbuilder nhưng có một số side effect sau.

  1. StringBuilder không có cơ chế nâng buffer. Nếu bạn cộng hai String có giá trị lớn hơn 16 nghĩa là bạn đang tốn thêm bộ nhớ để lưu Array nhưng lại không tiết kiệm thêm bất kì bước thuật toán nào. Cái này các cụ gọi là Memory leak. Vậy nên hãy chú ý đến hàm StringBuilder(int) dùng để khởi tạo giá trị độ lớn ban đầu cho Char Array.
  2. Bản thân StringBuilder sinh ra không phải để phát huy điểm mạnh trong việc cộng String mà điểm mạnh nhất là xử lý String. Việc xử lý String trên một mảng kí tự bao giờ cũng linh hoạt và nhanh hơn hẳn việc xử lý với String Pool.
  3. Java mặc định hỗ trợ StringBuilder trong việc cộng String với capacity thích hợp. Hãy thử diasemble code java thông qua javap -c ra xem thử. Vì máy mình không có cài Java tại lâu rồi nhảy qua code ML xin được lấy tạm code ở trên SO.
public class Concat {
    String cat(String a, String b) {
        a += b;
        return a;
    }
}

java.lang.String cat(java.lang.String, java.lang.String);
  Code:
   0:   new     #2; //class java/lang/StringBuilder
   3:   dup
   4:   invokespecial   #3; //Method java/lang/StringBuilder."<init>":()V
   7:   aload_1
   8:   invokevirtual   #4; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
   11:  aload_2
   12:  invokevirtual   #4; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
   15:  invokevirtual   #5; //Method java/lang/StringBuilder.toString:()Ljava/lang/    String;
   18:  astore_1
   19:  aload_1
   20:  areturn

Lưu ý là điều này chỉ xảy ra khi bạn cộng String trong cùng một biểu thức (với java 6) hoặc trong cùng một code block (với java 8) (cần dẫn nguồn vì mình không nhớ).

Đọc source code của StringBuilder tại đây. StringBuffer thì tự tìm hiểu nhé dài quá.

Kết luận lại là không phải cứ thấy người ta bảo nó nhanh là nó sẽ nhanh. Trong lập trình thì không có bất kì một solution nào là tốt cho tất cả các trường hợp. Luôn phải trả một cái giá nào đó. Tuy nhiên đó là điều khiến chúng ta tăng lương. LoL…

Bài liên quan
0