Các từ điển trong Python có thể thay đổi hay không thay đổi?

Tại sao Python sử dụng các phương thức cho một số chức năng (e. g. danh sách. index()) nhưng các chức năng khác (e. g. len(danh sách))?

Show
  • Tại sao tham gia () một phương thức chuỗi thay vì một danh sách hoặc phương thức tuple?

  • Ngoại lệ nhanh như thế nào?

  • Tại sao không có câu lệnh switch hoặc case trong Python?

  • Bạn không thể mô phỏng các luồng trong trình thông dịch thay vì dựa vào triển khai luồng dành riêng cho hệ điều hành?

  • Tại sao biểu thức lambda không thể chứa câu lệnh?

  • Python có thể được biên dịch thành mã máy, C hoặc một số ngôn ngữ khác không?

  • Python quản lý bộ nhớ như thế nào?

  • Tại sao CPython không sử dụng sơ đồ thu gom rác truyền thống hơn?

  • Tại sao không giải phóng tất cả bộ nhớ khi Python thoát?

  • Tại sao có các kiểu dữ liệu tuple và list riêng biệt?

  • Danh sách được triển khai trong CPython như thế nào?

  • Từ điển được triển khai như thế nào trong CPython?

  • Tại sao các khóa từ điển phải là bất biến?

  • Tại sao không liệt kê. sort() trả về danh sách đã sắp xếp?

  • Làm cách nào để bạn chỉ định và thực thi một thông số giao diện trong Python?

  • Tại sao không có goto?

  • Tại sao chuỗi thô (chuỗi r) không thể kết thúc bằng dấu gạch chéo ngược?

  • Tại sao Python không có câu lệnh “với” để gán thuộc tính?

  • Tại sao trình tạo không hỗ trợ câu lệnh with?

  • Tại sao cần có dấu hai chấm cho câu lệnh if/while/def/class?

  • Tại sao Python cho phép dấu phẩy ở cuối danh sách và bộ dữ liệu?

  • Tại sao Python sử dụng thụt đầu dòng để nhóm các câu lệnh?¶

    Guido van Rossum tin rằng việc sử dụng thụt đầu dòng để nhóm là cực kỳ tao nhã và đóng góp rất nhiều vào sự rõ ràng của chương trình Python trung bình. Hầu hết mọi người học cách yêu thích tính năng này sau một thời gian

    Vì không có dấu ngoặc bắt đầu/kết thúc nên không thể có sự bất đồng giữa nhóm được trình phân tích cú pháp cảm nhận và người đọc. Thỉnh thoảng các lập trình viên C sẽ gặp một đoạn mã như thế này

    if (x <= y)
            x++;
            y--;
    z++;
    

    Chỉ câu lệnh

    >>> x = 1.2
    
    9 được thực thi nếu điều kiện là đúng, nhưng phần thụt đầu dòng khiến nhiều người tin vào điều ngược lại. Ngay cả những lập trình viên C có kinh nghiệm đôi khi cũng sẽ nhìn chằm chằm vào nó một lúc lâu và tự hỏi tại sao
    1.0011001100110011001100110011001100110011001100110011 (binary)
    
    0 lại bị giảm ngay cả đối với
    1.0011001100110011001100110011001100110011001100110011 (binary)
    
    1

    Vì không có dấu ngoặc bắt đầu/kết thúc nên Python ít bị xung đột kiểu mã hóa hơn nhiều. Trong C có nhiều cách khác nhau để đặt dấu ngoặc nhọn. Sau khi đã quen với việc đọc và viết mã theo một phong cách cụ thể, việc đọc (hoặc được yêu cầu viết) theo một phong cách khác là điều bình thường.

    Nhiều kiểu mã hóa tự đặt dấu ngoặc bắt đầu/kết thúc trên một dòng. Điều này làm cho các chương trình dài hơn đáng kể và lãng phí không gian màn hình có giá trị, khiến việc xem tổng quan về chương trình trở nên khó khăn hơn. Lý tưởng nhất là một chức năng phải vừa trên một màn hình (giả sử 20–30 dòng). 20 dòng Python có thể làm được nhiều việc hơn 20 dòng C. Điều này không chỉ do thiếu dấu ngoặc bắt đầu/kết thúc – thiếu khai báo và các kiểu dữ liệu cấp cao cũng là nguyên nhân – mà cú pháp dựa trên thụt đầu dòng chắc chắn sẽ giúp ích

    Tại sao tôi nhận được kết quả lạ với các phép tính số học đơn giản?¶

    Xem câu hỏi tiếp theo

    Tại sao các phép tính dấu chấm động lại không chính xác như vậy?¶

    Người dùng thường ngạc nhiên trước những kết quả như thế này

    >>> 1.2 - 1.0
    0.19999999999999996
    

    và nghĩ rằng đó là một lỗi trong Python. Nó không thể. Điều này ít liên quan đến Python và liên quan nhiều hơn đến cách nền tảng cơ bản xử lý các số dấu phẩy động

    Loại

    1.0011001100110011001100110011001100110011001100110011 (binary)
    
    2 trong CPython sử dụng C
    1.0011001100110011001100110011001100110011001100110011 (binary)
    
    3 để lưu trữ. Giá trị của đối tượng
    1.0011001100110011001100110011001100110011001100110011 (binary)
    
    2 được lưu trữ ở dạng dấu phẩy động nhị phân với độ chính xác cố định (thường là 53 bit) và Python sử dụng các phép toán C, từ đó dựa vào triển khai phần cứng trong bộ xử lý, để thực hiện các phép toán dấu phẩy động. Điều này có nghĩa là liên quan đến các hoạt động của dấu phẩy động, Python hoạt động giống như nhiều ngôn ngữ phổ biến bao gồm C và Java

    Nhiều số có thể được viết dễ dàng bằng ký hiệu thập phân không thể được biểu thị chính xác bằng dấu phẩy động nhị phân. Ví dụ, sau khi

    >>> x = 1.2
    

    giá trị được lưu trữ cho

    1.0011001100110011001100110011001100110011001100110011 (binary)
    
    5 là giá trị gần đúng (rất tốt) với giá trị thập phân
    1.0011001100110011001100110011001100110011001100110011 (binary)
    
    6, nhưng không chính xác bằng giá trị đó. Trên một máy điển hình, giá trị được lưu trữ thực tế là

    1.0011001100110011001100110011001100110011001100110011 (binary)
    

    đó chính xác là

    >>> x = 1.2
    
    2

    Độ chính xác điển hình là 53 bit cung cấp cho Python số float với độ chính xác 15–16 chữ số thập phân

    Để có giải thích đầy đủ hơn, vui lòng xem chương số học dấu phẩy động trong hướng dẫn Python.

    Tại sao các chuỗi Python là bất biến?¶

    Có một số lợi thế

    Một là hiệu suất. biết rằng một chuỗi là bất biến có nghĩa là chúng ta có thể phân bổ không gian cho nó tại thời điểm tạo và các yêu cầu lưu trữ là cố định và không thay đổi. Đây cũng là một trong những lý do để phân biệt giữa bộ dữ liệu và danh sách

    Một ưu điểm khác là các chuỗi trong Python được coi là “nguyên tố” như các số. Không có lượng hoạt động nào thay đổi giá trị 8 thành bất kỳ giá trị nào khác và trong Python, không có lượng hoạt động nào thay đổi chuỗi “tám” thành bất kỳ giá trị nào khác

    Tại sao phải sử dụng 'self' một cách rõ ràng trong các định nghĩa và lệnh gọi phương thức?¶

    Ý tưởng được mượn từ Modula-3. Hóa ra nó rất hữu ích, vì nhiều lý do

    Đầu tiên, rõ ràng hơn là bạn đang sử dụng một phương thức hoặc thuộc tính thể hiện thay vì một biến cục bộ. Đọc

    1.0011001100110011001100110011001100110011001100110011 (binary)
    
    7 hoặc
    1.0011001100110011001100110011001100110011001100110011 (binary)
    
    8 cho thấy hoàn toàn rõ ràng rằng một biến đối tượng hoặc phương thức được sử dụng ngay cả khi bạn không thuộc lòng định nghĩa lớp. Trong C++, bạn có thể biết do thiếu khai báo biến cục bộ (giả sử biến toàn cầu hiếm hoặc dễ nhận biết) – nhưng trong Python, không có khai báo biến cục bộ, vì vậy bạn phải tra cứu định nghĩa lớp để được . Một số tiêu chuẩn mã hóa C ++ và Java yêu cầu các thuộc tính mẫu phải có tiền tố
    1.0011001100110011001100110011001100110011001100110011 (binary)
    
    9, vì vậy tính rõ ràng này cũng vẫn hữu ích trong các ngôn ngữ đó

    Thứ hai, điều đó có nghĩa là không cần cú pháp đặc biệt nếu bạn muốn tham chiếu rõ ràng hoặc gọi phương thức từ một lớp cụ thể. Trong C++, nếu bạn muốn sử dụng một phương thức từ lớp cơ sở bị ghi đè trong lớp dẫn xuất, bạn phải sử dụng toán tử

    >>> x = 1.2
    
    20 – trong Python bạn có thể viết
    >>> x = 1.2
    
    21. Điều này đặc biệt hữu ích cho các phương thức
    >>> x = 1.2
    
    22 và nói chung trong trường hợp một phương thức của lớp dẫn xuất muốn mở rộng phương thức cùng tên của lớp cơ sở và do đó phải gọi phương thức của lớp cơ sở bằng cách nào đó

    Cuối cùng, đối với các biến thể hiện, nó giải quyết vấn đề cú pháp với phép gán. vì các biến cục bộ trong Python là (theo định nghĩa. ) những biến mà một giá trị được gán trong thân hàm (và không được khai báo rõ ràng là toàn cục), phải có cách nào đó để thông báo cho trình thông dịch biết rằng một phép gán có nghĩa là gán cho một biến thể hiện thay vì cho một biến cục bộ . C++ thực hiện điều này thông qua khai báo, nhưng Python không có khai báo và thật tiếc khi phải giới thiệu chúng chỉ vì mục đích này. Sử dụng

    >>> x = 1.2
    
    23 rõ ràng giải quyết vấn đề này một cách độc đáo. Tương tự, đối với việc sử dụng các biến thể hiện, việc phải viết
    >>> x = 1.2
    
    23 có nghĩa là các tham chiếu đến các tên không đủ tiêu chuẩn bên trong một phương thức không phải tìm kiếm các thư mục của thể hiện. Nói cách khác, các biến cục bộ và biến thể hiện nằm trong hai không gian tên khác nhau và bạn cần cho Python biết nên sử dụng không gian tên nào

    Tại sao tôi không thể sử dụng phép gán trong một biểu thức?¶

    Bắt đầu bằng Python 3. 8, bạn có thể

    Biểu thức gán sử dụng toán tử hải mã

    >>> x = 1.2
    
    25 gán một biến trong một biểu thức

    1.0011001100110011001100110011001100110011001100110011 (binary)
    
    2

    Xem PEP 572 để biết thêm thông tin

    Tại sao Python sử dụng các phương thức cho một số chức năng (e. g. danh sách. index()) nhưng các chức năng khác (e. g. len(danh sách))?¶

    Như Guido đã nói

    (a) Đối với một số thao tác, ký hiệu tiền tố dễ đọc hơn hậu tố – tiền tố (và trung tố. ) các phép toán có truyền thống lâu đời trong toán học, thích các ký hiệu trong đó hình ảnh giúp nhà toán học suy nghĩ về một vấn đề. So sánh sự dễ dàng khi chúng ta viết lại một công thức như x*(a+b) thành x*a + x*b với sự vụng về khi làm điều tương tự bằng cách sử dụng ký hiệu OO thô

    (b) Khi tôi đọc đoạn mã có nội dung len(x) tôi biết rằng nó đang hỏi độ dài của một thứ gì đó. Điều này cho tôi biết hai điều. kết quả là một số nguyên và đối số là một loại vùng chứa. Ngược lại, khi tôi đọc x. len(), tôi phải biết rằng x là một loại bộ chứa nào đó triển khai một giao diện hoặc kế thừa từ một lớp có len() tiêu chuẩn. Chứng kiến ​​sự nhầm lẫn mà chúng tôi thỉnh thoảng gặp phải khi một lớp không triển khai ánh xạ có phương thức get() hoặc keys() hoặc thứ gì đó không phải là tệp có phương thức write()

    —https. //thư. con trăn. org/pipermail/python-3000/2006-Tháng 11/004643. html

    Tại sao join() là một phương thức chuỗi thay vì một phương thức danh sách hoặc bộ?¶

    Các chuỗi trở nên giống như các loại tiêu chuẩn khác bắt đầu từ Python 1. 6, khi các phương thức được thêm vào sẽ cung cấp chức năng giống như chức năng luôn có sẵn bằng cách sử dụng các chức năng của mô-đun chuỗi. Hầu hết các phương pháp mới này đã được chấp nhận rộng rãi, nhưng phương pháp dường như khiến một số lập trình viên cảm thấy không thoải mái là

    1.0011001100110011001100110011001100110011001100110011 (binary)
    
    3

    cái nào cho kết quả

    1.0011001100110011001100110011001100110011001100110011 (binary)
    
    4

    Có hai đối số phổ biến chống lại việc sử dụng này

    Việc đầu tiên chạy dọc theo dòng của. “Nó trông thực sự xấu khi sử dụng một phương thức của một chuỗi ký tự (hằng chuỗi)”, câu trả lời là có thể, nhưng một chuỗi ký tự chỉ là một giá trị cố định. Nếu các phương thức được cho phép trên các tên được liên kết với chuỗi thì không có lý do hợp lý nào để khiến chúng không khả dụng trên các chữ

    Sự phản đối thứ hai thường được coi là. “Tôi thực sự đang nói về một dãy nối các phần tử của nó với nhau bằng một hằng chuỗi”. Đáng buồn thay, bạn không. Vì một lý do nào đó, dường như sẽ ít khó khăn hơn nhiều khi có

    >>> x = 1.2
    
    26 làm phương thức chuỗi, vì trong trường hợp đó, dễ dàng nhận thấy rằng

    1.0011001100110011001100110011001100110011001100110011 (binary)
    
    6

    là một hướng dẫn cho một chuỗi ký tự để trả về các chuỗi con được phân tách bằng dấu phân cách đã cho (hoặc, theo mặc định, các khoảng trắng tùy ý)

    >>> x = 1.2
    
    27 là một phương thức chuỗi bởi vì khi sử dụng nó, bạn đang yêu cầu chuỗi phân tách lặp qua một chuỗi các chuỗi và chèn chính nó vào giữa các phần tử liền kề. Phương pháp này có thể được sử dụng với bất kỳ đối số nào tuân theo các quy tắc cho các đối tượng chuỗi, bao gồm bất kỳ lớp mới nào mà bạn có thể tự định nghĩa. Các phương thức tương tự tồn tại cho các đối tượng byte và bytearray

    Các ngoại lệ nhanh như thế nào?¶

    Khối thử/ngoại trừ cực kỳ hiệu quả nếu không có ngoại lệ nào được nêu ra. Trên thực tế, bắt một ngoại lệ là tốn kém. Trong các phiên bản Python trước 2. 0 người ta thường sử dụng thành ngữ này

    1.0011001100110011001100110011001100110011001100110011 (binary)
    
    8

    Điều này chỉ có ý nghĩa khi bạn mong đợi dict luôn có chìa khóa. Nếu đó không phải là trường hợp, bạn đã mã hóa nó như thế này

    >>> 1.2 - 1.0
    0.19999999999999996
    
    0

    Đối với trường hợp cụ thể này, bạn cũng có thể sử dụng

    >>> x = 1.2
    
    28, nhưng chỉ khi cuộc gọi
    >>> x = 1.2
    
    29 đủ rẻ vì nó được đánh giá trong mọi trường hợp

    Tại sao không có câu lệnh switch hoặc case trong Python?¶

    Bạn có thể làm điều này đủ dễ dàng với một chuỗi

    1.0011001100110011001100110011001100110011001100110011 (binary)
    
    20. Đối với các giá trị bằng chữ hoặc hằng số trong một không gian tên, bạn cũng có thể sử dụng câu lệnh
    1.0011001100110011001100110011001100110011001100110011 (binary)
    
    21

    Đối với những trường hợp bạn cần chọn từ rất nhiều khả năng, bạn có thể tạo một từ điển ánh xạ các giá trị trường hợp tới các hàm để gọi. Ví dụ

    >>> 1.2 - 1.0
    0.19999999999999996
    
    1

    Để gọi các phương thức trên các đối tượng, bạn có thể đơn giản hóa hơn nữa bằng cách sử dụng

    1.0011001100110011001100110011001100110011001100110011 (binary)
    
    22 tích hợp sẵn để truy xuất các phương thức có tên cụ thể

    >>> 1.2 - 1.0
    0.19999999999999996
    
    2

    Bạn nên sử dụng tiền tố cho tên phương thức, chẳng hạn như

    1.0011001100110011001100110011001100110011001100110011 (binary)
    
    23 trong ví dụ này. Nếu không có tiền tố như vậy, nếu các giá trị đến từ một nguồn không đáng tin cậy, kẻ tấn công sẽ có thể gọi bất kỳ phương thức nào trên đối tượng của bạn

    Bạn không thể mô phỏng các luồng trong trình thông dịch thay vì dựa vào triển khai luồng dành riêng cho hệ điều hành?¶

    trả lời 1. Thật không may, trình thông dịch đẩy ít nhất một khung ngăn xếp C cho mỗi khung ngăn xếp Python. Ngoài ra, các tiện ích mở rộng có thể gọi lại Python vào những thời điểm gần như ngẫu nhiên. Do đó, việc triển khai luồng hoàn chỉnh yêu cầu hỗ trợ luồng cho C

    trả lời 2. May mắn thay, có Stackless Python, có vòng lặp thông dịch viên được thiết kế lại hoàn toàn để tránh ngăn xếp C

    Tại sao biểu thức lambda không thể chứa câu lệnh?¶

    Các biểu thức lambda của Python không thể chứa các câu lệnh vì khung cú pháp của Python không thể xử lý các câu lệnh được lồng bên trong các biểu thức. Tuy nhiên, trong Python, đây không phải là vấn đề nghiêm trọng. Không giống như các biểu mẫu lambda trong các ngôn ngữ khác, nơi chúng thêm chức năng, lambdas Python chỉ là một ký hiệu viết tắt nếu bạn quá lười để xác định một hàm

    Các hàm đã là đối tượng hạng nhất trong Python và có thể được khai báo trong phạm vi cục bộ. Do đó, lợi thế duy nhất của việc sử dụng lambda thay vì hàm được xác định cục bộ là bạn không cần đặt tên cho hàm – nhưng đó chỉ là một biến cục bộ mà đối tượng hàm (chính xác là cùng một loại đối tượng

    Python có thể được biên dịch thành mã máy, C hoặc một số ngôn ngữ khác không?¶

    Cython biên dịch một phiên bản Python đã sửa đổi với các chú thích tùy chọn thành các phần mở rộng C. Nuitka là trình biên dịch mới nhất của Python thành mã C++, nhằm mục đích hỗ trợ ngôn ngữ Python đầy đủ

    Python quản lý bộ nhớ như thế nào?¶

    Chi tiết về quản lý bộ nhớ Python phụ thuộc vào việc triển khai. Triển khai tiêu chuẩn của Python, CPython , sử dụng cách đếm tham chiếu để phát hiện các đối tượng không thể truy cập và một cơ chế khác để thu thập các chu kỳ tham chiếu, định kỳ thực thi thuật toán phát hiện chu kỳ trông giống như . Mô-đun

    1.0011001100110011001100110011001100110011001100110011 (binary)
    
    24 cung cấp các chức năng để thực hiện thu gom rác, thu thập số liệu thống kê gỡ lỗi và điều chỉnh các tham số của bộ thu thập.

    Tuy nhiên, các triển khai khác (chẳng hạn như Jython hoặc PyPy) có thể dựa vào một cơ chế khác, chẳng hạn như trình thu gom rác toàn diện. Sự khác biệt này có thể gây ra một số sự cố chuyển nhỏ nếu mã Python của bạn phụ thuộc vào hành vi triển khai đếm tham chiếu

    Trong một số triển khai Python, đoạn mã sau (tốt trong CPython) có thể sẽ hết bộ mô tả tệp

    >>> 1.2 - 1.0
    0.19999999999999996
    
    3

    Thật vậy, bằng cách sử dụng lược đồ hủy và đếm tham chiếu của CPython, mỗi lần gán mới cho f sẽ đóng tệp trước đó. Tuy nhiên, với một GC truyền thống, các đối tượng tệp đó sẽ chỉ được thu thập (và đóng) ở các khoảng thời gian khác nhau và có thể dài

    Nếu bạn muốn viết mã sẽ hoạt động với bất kỳ triển khai Python nào, bạn nên đóng tệp một cách rõ ràng hoặc sử dụng câu lệnh

    1.0011001100110011001100110011001100110011001100110011 (binary)
    
    25;

    >>> 1.2 - 1.0
    0.19999999999999996
    
    4

    Tại sao CPython không sử dụng sơ đồ thu gom rác truyền thống hơn?¶

    Đối với một điều, đây không phải là một tính năng tiêu chuẩn của C và do đó nó không khả dụng. (Vâng, chúng tôi biết về thư viện Boehm GC. Nó có các đoạn mã trình biên dịch chương trình cho hầu hết các nền tảng phổ biến, không phải cho tất cả chúng và mặc dù nó gần như trong suốt, nhưng nó không hoàn toàn trong suốt; . )

    GC truyền thống cũng trở thành một vấn đề khi Python được nhúng vào các ứng dụng khác. Mặc dù trong Python độc lập, bạn có thể thay thế malloc() và free() tiêu chuẩn bằng các phiên bản do thư viện GC cung cấp, một ứng dụng nhúng Python có thể muốn có phiên bản thay thế riêng cho malloc() và free() và có thể không muốn . Ngay bây giờ, CPython hoạt động với mọi thứ triển khai malloc() và free() đúng cách

    Tại sao tất cả bộ nhớ không được giải phóng khi Python thoát?¶

    Các đối tượng được tham chiếu từ các không gian tên chung của các mô-đun Python không phải lúc nào cũng được giải phóng khi Python thoát. Điều này có thể xảy ra nếu có các tham chiếu vòng tròn. Ngoài ra còn có một số bit bộ nhớ được cấp phát bởi thư viện C không thể giải phóng (e. g. một công cụ như Purify sẽ phàn nàn về những điều này). Tuy nhiên, Python rất tích cực trong việc dọn dẹp bộ nhớ khi thoát và cố gắng phá hủy mọi đối tượng

    Nếu bạn muốn buộc Python xóa một số thứ nhất định khi phân bổ, hãy sử dụng mô-đun

    1.0011001100110011001100110011001100110011001100110011 (binary)
    
    26 để chạy một chức năng sẽ buộc những việc xóa đó

    Tại sao lại có các kiểu dữ liệu tuple và list riêng biệt?¶

    Danh sách và bộ dữ liệu, trong khi giống nhau ở nhiều khía cạnh, thường được sử dụng theo những cách cơ bản khác nhau. Các bộ dữ liệu có thể được coi là tương tự như các bản ghi Pascal hoặc các cấu trúc C; . Ví dụ: tọa độ Descartes được biểu diễn thích hợp dưới dạng một bộ gồm hai hoặc ba số

    Mặt khác, danh sách giống mảng hơn trong các ngôn ngữ khác. Chúng có xu hướng giữ một số lượng đối tượng khác nhau, tất cả đều có cùng loại và được vận hành trên từng đối tượng một. Ví dụ:

    1.0011001100110011001100110011001100110011001100110011 (binary)
    
    27 trả về danh sách các chuỗi đại diện cho các tệp trong thư mục hiện tại. Các chức năng hoạt động trên đầu ra này thường sẽ không bị hỏng nếu bạn thêm một hoặc hai tệp khác vào thư mục

    Các bộ dữ liệu là bất biến, nghĩa là khi một bộ dữ liệu đã được tạo, bạn không thể thay thế bất kỳ phần tử nào của nó bằng một giá trị mới. Danh sách có thể thay đổi, nghĩa là bạn luôn có thể thay đổi các thành phần của danh sách. Chỉ các phần tử bất biến mới có thể được sử dụng làm khóa từ điển và do đó, chỉ các bộ chứ không phải danh sách mới có thể được sử dụng làm khóa

    Các danh sách được triển khai như thế nào trong CPython?¶

    Danh sách của CPython thực sự là các mảng có độ dài thay đổi, không phải danh sách được liên kết kiểu Lisp. Việc triển khai sử dụng một mảng tham chiếu liền kề đến các đối tượng khác và giữ một con trỏ tới mảng này và độ dài của mảng trong cấu trúc đầu danh sách

    Điều này làm cho việc lập chỉ mục một danh sách

    1.0011001100110011001100110011001100110011001100110011 (binary)
    
    28 trở thành một hoạt động có chi phí không phụ thuộc vào kích thước của danh sách hoặc giá trị của chỉ mục

    Khi các mục được nối thêm hoặc chèn vào, mảng tham chiếu được thay đổi kích thước. Một số thông minh được áp dụng để cải thiện hiệu suất của các mục nối thêm nhiều lần;

    Các từ điển được triển khai trong CPython như thế nào?¶

    Từ điển của CPython được triển khai dưới dạng bảng băm có thể thay đổi kích thước. So với cây B, điều này mang lại hiệu suất tra cứu tốt hơn (hoạt động phổ biến nhất cho đến nay) trong hầu hết các trường hợp và việc triển khai đơn giản hơn

    Từ điển hoạt động bằng cách tính toán mã băm cho mỗi khóa được lưu trữ trong từ điển bằng cách sử dụng hàm tích hợp sẵn

    1.0011001100110011001100110011001100110011001100110011 (binary)
    
    29. Mã băm rất khác nhau tùy thuộc vào khóa và hạt giống trên mỗi quy trình; . Mã băm sau đó được sử dụng để tính toán vị trí trong một mảng bên trong nơi giá trị sẽ được lưu trữ. Giả sử rằng bạn đang lưu trữ các khóa có giá trị băm khác nhau, điều này có nghĩa là từ điển mất thời gian không đổi – O(1), trong ký hiệu Big-O – để truy xuất khóa

    Tại sao các khóa từ điển phải là bất biến?¶

    Việc triển khai bảng băm của từ điển sử dụng giá trị băm được tính từ giá trị khóa để tìm khóa. Nếu khóa là một đối tượng có thể thay đổi, thì giá trị của nó có thể thay đổi và do đó hàm băm của nó cũng có thể thay đổi. Nhưng vì bất kỳ ai thay đổi đối tượng khóa không thể biết rằng nó đang được sử dụng làm khóa từ điển, nên nó không thể di chuyển mục nhập trong từ điển. Sau đó, khi bạn cố gắng tra cứu cùng một đối tượng trong từ điển, nó sẽ không được tìm thấy vì giá trị băm của nó khác. Nếu bạn cố tra cứu giá trị cũ thì cũng không tìm thấy, vì giá trị của đối tượng được tìm thấy trong thùng băm đó sẽ khác

    Nếu bạn muốn một từ điển được lập chỉ mục với một danh sách, trước tiên, chỉ cần chuyển đổi danh sách thành một bộ; . Các bộ dữ liệu là bất biến và do đó có thể được sử dụng làm khóa từ điển

    Một số giải pháp không thể chấp nhận đã được đề xuất

    • Danh sách băm theo địa chỉ của họ (ID đối tượng). Điều này không hiệu quả vì nếu bạn xây dựng một danh sách mới có cùng giá trị thì sẽ không tìm thấy danh sách đó; . g

      >>> 1.2 - 1.0
      0.19999999999999996
      
      5

      sẽ đưa ra một ngoại lệ

      1.0011001100110011001100110011001100110011001100110011 (binary)
      
      32 vì id của
      1.0011001100110011001100110011001100110011001100110011 (binary)
      
      33 được sử dụng trong dòng thứ hai khác với id trong dòng đầu tiên. Nói cách khác, các khóa từ điển nên được so sánh bằng cách sử dụng
      1.0011001100110011001100110011001100110011001100110011 (binary)
      
      34, không sử dụng
      1.0011001100110011001100110011001100110011001100110011 (binary)
      
      35

    • Tạo một bản sao khi sử dụng danh sách làm khóa. Điều này không hiệu quả vì danh sách, là một đối tượng có thể thay đổi, có thể chứa một tham chiếu đến chính nó, và sau đó mã sao chép sẽ chạy vào một vòng lặp vô hạn

    • Cho phép danh sách dưới dạng khóa nhưng yêu cầu người dùng không sửa đổi chúng. Điều này sẽ cho phép một lớp lỗi khó theo dõi trong các chương trình khi bạn vô tình quên hoặc sửa đổi danh sách. Nó cũng làm mất hiệu lực một bất biến quan trọng của từ điển. mọi giá trị trong

      1.0011001100110011001100110011001100110011001100110011 (binary)
      
      36 đều có thể sử dụng làm khóa của từ điển

    • Đánh dấu danh sách là chỉ đọc khi chúng được sử dụng làm khóa từ điển. Vấn đề là không chỉ đối tượng cấp cao nhất có thể thay đổi giá trị của nó; . Nhập bất kỳ thứ gì dưới dạng khóa vào từ điển sẽ yêu cầu đánh dấu tất cả các đối tượng có thể truy cập từ đó là chỉ đọc - và một lần nữa, các đối tượng tự tham chiếu có thể gây ra một vòng lặp vô hạn

    Có một mẹo để giải quyết vấn đề này nếu bạn cần, nhưng hãy tự chịu rủi ro khi sử dụng nó. Bạn có thể bọc một cấu trúc có thể thay đổi bên trong một cá thể lớp có cả phương thức

    1.0011001100110011001100110011001100110011001100110011 (binary)
    
    37 và
    1.0011001100110011001100110011001100110011001100110011 (binary)
    
    38. Sau đó, bạn phải đảm bảo rằng giá trị băm cho tất cả các đối tượng trình bao nằm trong từ điển (hoặc cấu trúc dựa trên hàm băm khác), vẫn cố định trong khi đối tượng nằm trong từ điển (hoặc cấu trúc khác)

    >>> 1.2 - 1.0
    0.19999999999999996
    
    6

    Lưu ý rằng việc tính toán hàm băm phức tạp do khả năng một số thành viên của danh sách có thể không thể băm được và cũng do khả năng tràn số học

    Hơn nữa, luôn phải là trường hợp nếu

    1.0011001100110011001100110011001100110011001100110011 (binary)
    
    39 (tức là
    1.0011001100110011001100110011001100110011001100110011 (binary)
    
    40) thì
    1.0011001100110011001100110011001100110011001100110011 (binary)
    
    41 (tức là
    1.0011001100110011001100110011001100110011001100110011 (binary)
    
    42), bất kể đối tượng có trong từ điển hay không. Nếu bạn không đáp ứng các từ điển hạn chế này và các cấu trúc dựa trên hàm băm khác sẽ hoạt động sai

    Trong trường hợp ListWrapper, bất cứ khi nào đối tượng trình bao bọc nằm trong từ điển, danh sách được bao bọc không được thay đổi để tránh sự bất thường. Đừng làm điều này trừ khi bạn chuẩn bị suy nghĩ kỹ về các yêu cầu và hậu quả của việc không đáp ứng chúng một cách chính xác. Coi như mình đã được cảnh báo

    Tại sao không liệt kê. sort() trả về danh sách đã sắp xếp?¶

    Trong những tình huống mà hiệu suất quan trọng, việc tạo một bản sao của danh sách chỉ để sắp xếp sẽ rất lãng phí. Do đó,

    1.0011001100110011001100110011001100110011001100110011 (binary)
    
    43 sắp xếp danh sách tại chỗ. Để nhắc nhở bạn về thực tế đó, nó không trả về danh sách đã sắp xếp. Bằng cách này, bạn sẽ không bị lừa vô tình ghi đè lên một danh sách khi bạn cần một bản sao được sắp xếp nhưng cũng cần giữ lại phiên bản chưa được sắp xếp.

    Nếu bạn muốn trả về một danh sách mới, hãy sử dụng hàm

    1.0011001100110011001100110011001100110011001100110011 (binary)
    
    44 có sẵn để thay thế. Hàm này tạo một danh sách mới từ một lần lặp được cung cấp, sắp xếp và trả về nó. Ví dụ: đây là cách lặp lại các khóa của từ điển theo thứ tự được sắp xếp

    >>> 1.2 - 1.0
    0.19999999999999996
    
    7

    Làm cách nào để bạn chỉ định và thực thi một thông số kỹ thuật giao diện trong Python?¶

    Đặc tả giao diện cho một mô-đun được cung cấp bởi các ngôn ngữ như C++ và Java mô tả các nguyên mẫu cho các phương thức và chức năng của mô-đun. Nhiều người cảm thấy rằng việc thực thi thời gian biên dịch các thông số kỹ thuật giao diện giúp xây dựng các chương trình lớn

    Trăn 2. 6 thêm mô-đun

    1.0011001100110011001100110011001100110011001100110011 (binary)
    
    45 cho phép bạn xác định Lớp cơ sở trừu tượng (ABC). Sau đó, bạn có thể sử dụng
    1.0011001100110011001100110011001100110011001100110011 (binary)
    
    46 và
    1.0011001100110011001100110011001100110011001100110011 (binary)
    
    47 để kiểm tra xem một thể hiện hoặc một lớp có triển khai một ABC cụ thể hay không. Mô-đun
    1.0011001100110011001100110011001100110011001100110011 (binary)
    
    48 xác định một tập hợp các ABC hữu ích, chẳng hạn như
    1.0011001100110011001100110011001100110011001100110011 (binary)
    
    49,
    1.0011001100110011001100110011001100110011001100110011 (binary)
    
    60 và
    1.0011001100110011001100110011001100110011001100110011 (binary)
    
    61

    Đối với Python, nhiều ưu điểm của đặc tả giao diện có thể đạt được bằng kỷ luật kiểm tra thích hợp cho các thành phần

    Một bộ kiểm thử tốt cho một mô-đun có thể vừa cung cấp kiểm thử hồi quy, vừa đóng vai trò như một đặc tả giao diện mô-đun và một tập hợp các ví dụ. Nhiều mô-đun Python có thể được chạy dưới dạng tập lệnh để cung cấp tính năng “tự kiểm tra” đơn giản. ” Ngay cả các mô-đun sử dụng giao diện bên ngoài phức tạp thường có thể được kiểm tra độc lập bằng cách sử dụng mô phỏng “sơ khai” tầm thường của giao diện bên ngoài. Các mô-đun

    1.0011001100110011001100110011001100110011001100110011 (binary)
    
    62 và
    1.0011001100110011001100110011001100110011001100110011 (binary)
    
    63 hoặc khung kiểm tra của bên thứ ba có thể được sử dụng để xây dựng các bộ kiểm tra toàn diện thực hiện mọi dòng mã trong một mô-đun

    Một kỷ luật kiểm thử phù hợp có thể giúp xây dựng các ứng dụng phức tạp lớn bằng Python cũng như có các thông số kỹ thuật giao diện sẽ. Trên thực tế, nó có thể tốt hơn vì đặc tả giao diện không thể kiểm tra các thuộc tính nhất định của chương trình. Ví dụ: phương thức

    1.0011001100110011001100110011001100110011001100110011 (binary)
    
    64 dự kiến ​​sẽ thêm các phần tử mới vào cuối danh sách nội bộ nào đó;

    Viết bộ kiểm tra rất hữu ích và bạn có thể muốn thiết kế mã của mình để dễ dàng kiểm tra. Một kỹ thuật ngày càng phổ biến, phát triển dựa trên thử nghiệm, yêu cầu viết các phần của bộ thử nghiệm trước khi bạn viết bất kỳ mã thực tế nào. Tất nhiên Python cho phép bạn cẩu thả và không viết test case gì cả

    Tại sao không có goto?¶

    Vào những năm 1970, mọi người nhận ra rằng goto không bị hạn chế có thể dẫn đến mã "mỳ spaghetti" lộn xộn, khó hiểu và khó sửa đổi. Trong một ngôn ngữ cấp cao, nó cũng không cần thiết miễn là có nhiều cách để rẽ nhánh (trong Python, với các câu lệnh

    1.0011001100110011001100110011001100110011001100110011 (binary)
    
    66 và các biểu thức
    1.0011001100110011001100110011001100110011001100110011 (binary)
    
    67,
    1.0011001100110011001100110011001100110011001100110011 (binary)
    
    68 và
    1.0011001100110011001100110011001100110011001100110011 (binary)
    
    69) và vòng lặp (với các câu lệnh
    1.0011001100110011001100110011001100110011001100110011 (binary)
    
    80 và
    1.0011001100110011001100110011001100110011001100110011 (binary)
    
    81, có thể chứa các câu lệnh
    1.0011001100110011001100110011001100110011001100110011 (binary)
    
    82 và
    1.0011001100110011001100110011001100110011001100110011 (binary)
    
    83)

    Người ta cũng có thể sử dụng các ngoại lệ để cung cấp một “goto có cấu trúc” hoạt động ngay cả trên các lệnh gọi hàm. Nhiều người cảm thấy rằng các ngoại lệ có thể mô phỏng một cách thuận tiện tất cả các cách sử dụng hợp lý cấu trúc “go” hoặc “goto” của C, Fortran và các ngôn ngữ khác. Ví dụ

    >>> 1.2 - 1.0
    0.19999999999999996
    
    8

    Điều này không cho phép bạn nhảy vào giữa vòng lặp, nhưng điều đó thường được coi là lạm dụng goto. Sử dụng một cách tiết kiệm

    Tại sao chuỗi thô (chuỗi r) không thể kết thúc bằng dấu gạch chéo ngược?¶

    Chính xác hơn, chúng không thể kết thúc bằng một số lẻ dấu gạch chéo ngược. dấu gạch chéo ngược không ghép nối ở cuối thoát khỏi ký tự trích dẫn đóng, để lại một chuỗi chưa kết thúc

    Các chuỗi thô được thiết kế để dễ dàng tạo đầu vào cho các bộ xử lý (chủ yếu là các công cụ biểu thức chính quy) muốn thực hiện xử lý thoát dấu gạch chéo ngược của riêng chúng. Những bộ xử lý như vậy coi dấu gạch chéo ngược ở cuối không khớp là một lỗi, vì vậy các chuỗi thô không cho phép điều đó. Đổi lại, chúng cho phép bạn chuyển ký tự trích dẫn chuỗi bằng cách thoát nó bằng dấu gạch chéo ngược. Các quy tắc này hoạt động tốt khi các chuỗi r được sử dụng cho mục đích đã định của chúng

    Nếu bạn đang cố tạo tên đường dẫn Windows, hãy lưu ý rằng tất cả lệnh gọi hệ thống Windows cũng chấp nhận dấu gạch chéo lên phía trước

    >>> 1.2 - 1.0
    0.19999999999999996
    
    9

    Nếu bạn đang cố tạo tên đường dẫn cho lệnh DOS, hãy thử e. g. một trong

    >>> x = 1.2
    
    0

    Tại sao Python không có câu lệnh “với” để gán thuộc tính?¶

    Python có một câu lệnh 'with' kết thúc quá trình thực thi một khối, gọi mã khi vào và thoát khỏi khối. Một số ngôn ngữ có cấu trúc trông như thế này

    >>> x = 1.2
    
    1

    Trong Python, cấu trúc như vậy sẽ không rõ ràng

    Các ngôn ngữ khác, chẳng hạn như Object Pascal, Delphi và C++, sử dụng các kiểu tĩnh, do đó, có thể biết một cách rõ ràng thành viên nào đang được gán cho. Đây là điểm chính của kiểu gõ tĩnh - trình biên dịch luôn biết phạm vi của mọi biến tại thời điểm biên dịch

    Python sử dụng các kiểu động. Không thể biết trước thuộc tính nào sẽ được tham chiếu khi chạy. Các thuộc tính thành viên có thể được thêm hoặc xóa khỏi các đối tượng một cách nhanh chóng. Điều này làm cho nó không thể biết, từ một cách đọc đơn giản, thuộc tính nào đang được tham chiếu. một địa phương, một toàn cầu, hoặc một thuộc tính thành viên?

    Chẳng hạn, lấy đoạn mã không đầy đủ sau đây

    >>> x = 1.2
    
    2

    Đoạn mã giả định rằng “a” phải có một thuộc tính thành viên gọi là “x”. Tuy nhiên, không có gì trong Python nói với trình thông dịch điều này. Điều gì sẽ xảy ra nếu “a” là một số nguyên?

    Tuy nhiên, lợi ích chính của “với” và các tính năng ngôn ngữ tương tự (giảm khối lượng mã) có thể dễ dàng đạt được trong Python bằng cách chuyển nhượng. Thay vì

    >>> x = 1.2
    
    3

    ghi chép lại cái đó

    >>> x = 1.2
    
    4

    Điều này cũng có tác dụng phụ là tăng tốc độ thực thi vì các liên kết tên được giải quyết trong thời gian chạy trong Python và phiên bản thứ hai chỉ cần thực hiện giải pháp một lần

    Tại sao trình tạo không hỗ trợ câu lệnh with?¶

    Vì lý do kỹ thuật, trình tạo được sử dụng trực tiếp làm trình quản lý ngữ cảnh sẽ không hoạt động chính xác. Khi, như thông thường nhất, một trình tạo được sử dụng như một trình vòng lặp chạy cho đến khi hoàn thành, không cần đóng. Khi có, hãy bọc nó dưới dạng “contextlib. đóng (trình tạo)” trong câu lệnh ‘with’

    Tại sao cần phải có dấu hai chấm cho câu lệnh if/while/def/class?¶

    Dấu hai chấm được yêu cầu chủ yếu để nâng cao khả năng đọc (một trong những kết quả của ngôn ngữ ABC thử nghiệm). Xem xét điều này

    >>> x = 1.2
    
    5

    đấu với

    >>> x = 1.2
    
    6

    Lưu ý cách thứ hai dễ đọc hơn một chút. Lưu ý thêm cách dấu hai chấm đặt ra ví dụ trong câu trả lời Câu hỏi thường gặp này;

    Một lý do nhỏ khác là dấu hai chấm giúp người soạn thảo đánh dấu cú pháp dễ dàng hơn;

    Tại sao Python cho phép dấu phẩy ở cuối danh sách và bộ dữ liệu?¶

    Python cho phép bạn thêm dấu phẩy ở cuối danh sách, bộ dữ liệu và từ điển

    >>> x = 1.2
    
    7

    Có một số lý do để cho phép điều này

    Khi bạn có một giá trị bằng chữ cho một danh sách, bộ dữ liệu hoặc từ điển trải dài trên nhiều dòng, việc thêm các phần tử khác sẽ dễ dàng hơn vì bạn không phải nhớ thêm dấu phẩy vào dòng trước đó. Các dòng cũng có thể được sắp xếp lại mà không tạo ra lỗi cú pháp

    Vô tình bỏ qua dấu phẩy có thể dẫn đến lỗi khó chẩn đoán. Ví dụ

    >>> x = 1.2
    
    8

    Danh sách này có vẻ như có bốn yếu tố, nhưng thực ra nó chứa ba. “phí”, “fiefoo” và “fum”. Luôn thêm dấu phẩy để tránh nguồn lỗi này

    Tại sao từ điển không thay đổi trong Python?

    Tại sao khóa từ điển phải là bất biến? . Nếu khóa là một đối tượng có thể thay đổi, thì giá trị của nó có thể thay đổi và do đó hàm băm của nó cũng có thể thay đổi. The hash table implementation of dictionaries uses a hash value calculated from the key value to find the key. If the key were a mutable object, its value could change, and thus its hash could also change.

    Tại sao từ điển trong Python được gọi là có thể thay đổi?

    Bản thân từ điển có thể thay đổi, vì vậy có thể thêm, xóa và thay đổi mục từ bất kỳ lúc nào . Tuy nhiên, xin lưu ý rằng vì các mục được truy cập bằng khóa của chúng nên chúng tôi không thể có hai mục có cùng khóa.