Có gì mới trong Python 2.0

tác giả:

A.M. Kuchling và Moshe Zadka

Giới thiệu

Bản phát hành mới của Python, phiên bản 2.0, được phát hành vào ngày 16 tháng 10 năm 2000. Bài viết này đề cập đến các tính năng mới thú vị trong 2.0, nêu bật một số thay đổi hữu ích khác và chỉ ra một số thay đổi không tương thích có thể yêu cầu viết lại mã.

Sự phát triển của Python không bao giờ dừng lại hoàn toàn giữa các bản phát hành và một loạt các bản sửa lỗi và cải tiến ổn định luôn được gửi đi. Một loạt các bản sửa lỗi nhỏ, một số tối ưu hóa, chuỗi tài liệu bổ sung và thông báo lỗi tốt hơn đã được đưa vào phiên bản 2.0; liệt kê tất cả chúng là điều không thể, nhưng chúng chắc chắn rất quan trọng. Hãy tham khảo nhật ký CVS có sẵn công khai nếu bạn muốn xem danh sách đầy đủ. Tiến trình này là do năm nhà phát triển làm việc cho PythonLabs hiện đang được trả tiền để dành thời gian sửa lỗi và cũng do giao tiếp được cải thiện nhờ chuyển sang SourceForge.

Còn Python 1.6 thì sao?

Python 1.6 có thể được coi là bản phát hành Python có nghĩa vụ theo hợp đồng. Sau khi nhóm phát triển cốt lõi rời CNRI vào tháng 5 năm 2000, CNRI đã yêu cầu tạo bản phát hành 1.6, chứa tất cả công việc trên Python đã được thực hiện tại CNRI. Do đó, Python 1.6 thể hiện trạng thái của cây CVS kể từ tháng 5 năm 2000, với tính năng mới quan trọng nhất là hỗ trợ Unicode. Tất nhiên, quá trình phát triển vẫn tiếp tục sau tháng 5, vì vậy cây 1.6 đã nhận được một số bản sửa lỗi để đảm bảo rằng nó tương thích về phía trước với Python 2.0. Do đó, 1.6 là một phần trong quá trình phát triển của Python chứ không phải là một nhánh phụ.

Vậy bạn có nên quan tâm nhiều đến Python 1.6 không? Có lẽ là không. Bản phát hành 1.6final và 2.0beta1 được thực hiện trong cùng một ngày (5 tháng 9 năm 2000), kế hoạch là hoàn thiện Python 2.0 trong vòng một tháng hoặc lâu hơn. Nếu bạn có các ứng dụng cần bảo trì, có vẻ như không có ích gì khi phá vỡ mọi thứ bằng cách chuyển sang 1.6, sửa chúng và sau đó gặp một đợt hỏng hóc khác trong vòng một tháng khi chuyển sang 2.0; tốt hơn hết là bạn nên chuyển thẳng lên 2.0. Hầu hết các tính năng thực sự thú vị được mô tả trong tài liệu này chỉ ở phiên bản 2.0 vì rất nhiều công việc đã được thực hiện từ tháng 5 đến tháng 9.

Quy trình phát triển mới

Thay đổi quan trọng nhất trong Python 2.0 có thể không phải ở mã mà là ở cách Python được phát triển: vào tháng 5 năm 2000, các nhà phát triển Python bắt đầu sử dụng các công cụ do SourceForge cung cấp để lưu trữ mã nguồn, theo dõi các báo cáo lỗi và quản lý hàng đợi gửi bản vá. Để báo cáo lỗi hoặc gửi bản vá cho Python 2.0, hãy sử dụng các công cụ quản lý bản vá và theo dõi lỗi có sẵn trên trang dự án của Python, có tại https://sourceforge.net/projects/python/.

Dịch vụ quan trọng nhất hiện được lưu trữ tại SourceForge là cây Python CVS, kho lưu trữ được kiểm soát theo phiên bản chứa mã nguồn cho Python. Trước đây, có khoảng 7 người có quyền ghi vào cây CVS và tất cả các bản vá phải được một trong những người trong danh sách rút gọn này kiểm tra và đăng ký. Rõ ràng, điều này không có khả năng mở rộng cao. Bằng cách di chuyển cây CVS sang SourceForge, có thể cấp quyền truy cập ghi cho nhiều người hơn; tính đến tháng 9 năm 2000 đã có 27 người có thể kiểm tra các thay đổi, tăng gấp bốn lần. Điều này tạo ra những thay đổi quy mô lớn có thể thực hiện được nếu chúng phải được lọc qua một nhóm nhỏ các nhà phát triển cốt lõi. Ví dụ, một ngày nọ, Peter Schneider-Kamp nảy ra ý định loại bỏ khả năng tương thích K&R C và chuyển đổi nguồn C cho Python thành ANSI C. Sau khi nhận được sự chấp thuận trong danh sách gửi thư của python-dev, anh ấy đã tiến hành một loạt các cuộc kiểm tra kéo dài khoảng một tuần, các nhà phát triển khác đã tham gia trợ giúp và công việc đã hoàn thành. Nếu chỉ có 5 người có quyền ghi, có lẽ nhiệm vụ đó sẽ được coi là "tốt, nhưng không xứng đáng với thời gian và công sức cần thiết" và nó sẽ không bao giờ được thực hiện.

Việc chuyển sang sử dụng các dịch vụ của SourceForge đã dẫn đến tốc độ phát triển tăng lên đáng kể. Giờ đây, các bản vá được gửi, nhận xét, sửa đổi bởi những người không phải là người gửi ban đầu và được gửi qua lại giữa mọi người cho đến khi bản vá được coi là đáng kiểm tra. Các lỗi được theo dõi ở một vị trí trung tâm và có thể được chỉ định cho một người cụ thể để sửa và chúng tôi có thể đếm số lượng lỗi đang mở để đo lường tiến độ. Điều này không phải là không có cái giá phải trả: các nhà phát triển giờ đây có nhiều e-mail hơn để xử lý, nhiều danh sách gửi thư hơn để theo dõi và các công cụ đặc biệt phải được viết cho môi trường mới. Ví dụ: SourceForge gửi các tin nhắn e-mail thông báo lỗi và bản vá mặc định hoàn toàn không hữu ích, vì vậy Ka-Ping Yee đã viết một trình quét màn hình HTML để gửi nhiều tin nhắn hữu ích hơn.

Việc dễ dàng thêm mã đã gây ra một số khó khăn ngày càng tăng ban đầu, chẳng hạn như mã đã được kiểm tra trước khi nó sẵn sàng hoặc không nhận được sự đồng ý rõ ràng từ nhóm nhà phát triển. Quá trình phê duyệt đã xuất hiện có phần giống với quy trình được nhóm Apache sử dụng. Nhà phát triển có thể bỏ phiếu +1, +0, -0 hoặc -1 cho một bản vá; +1 và -1 biểu thị sự chấp nhận hoặc từ chối, trong khi +0 và -0 có nghĩa là nhà phát triển hầu như không quan tâm đến sự thay đổi, mặc dù có hơi hướng tích cực hoặc tiêu cực. Thay đổi đáng kể nhất so với mô hình Apache là việc bỏ phiếu về cơ bản mang tính chất tư vấn, cho phép Guido van Rossum, người có trạng thái Nhà độc tài nhân từ vì cuộc sống, biết ý kiến ​​​​chung là gì. Anh ta vẫn có thể bỏ qua kết quả bỏ phiếu và chấp thuận hoặc từ chối một thay đổi ngay cả khi cộng đồng không đồng ý với anh ta.

Tạo một bản vá thực sự là bước cuối cùng trong việc thêm một tính năng mới và thường dễ dàng so với nhiệm vụ tạo ra một thiết kế tốt trước đó. Các cuộc thảo luận về các tính năng mới thường có thể bùng nổ thành các chuỗi danh sách gửi thư dài, khiến cuộc thảo luận khó theo dõi và không ai có thể đọc mọi bài đăng lên python-dev. Do đó, một quy trình tương đối chính thức đã được thiết lập để viết Đề xuất cải tiến Python (PEP), được mô hình hóa trên quy trình RFC trên internet. PEP là các tài liệu dự thảo mô tả một tính năng mới được đề xuất và liên tục được sửa đổi cho đến khi cộng đồng đạt được sự đồng thuận, chấp nhận hoặc từ chối đề xuất đó. Trích dẫn từ phần giới thiệu về PEP 1, "Mục đích và nguyên tắc của PEP":

PEP là viết tắt của Đề xuất cải tiến Python. Zz001zz là tài liệu thiết kế cung cấp thông tin cho cộng đồng Python hoặc mô tả một tính năng mới cho Python. Zz002zz phải cung cấp thông số kỹ thuật ngắn gọn về tính năng và lý do căn bản cho tính năng đó.

Chúng tôi dự định PEP sẽ là cơ chế chính để đề xuất các tính năng mới, thu thập ý kiến đóng góp của cộng đồng về một vấn đề và ghi lại các quyết định thiết kế đã được đưa vào Python. Tác giả PEP chịu trách nhiệm xây dựng sự đồng thuận trong cộng đồng và ghi lại những ý kiến ​​bất đồng.

Đọc phần còn lại của PEP 1 để biết chi tiết về quy trình biên tập, phong cách và định dạng của PEP. PEP được lưu giữ trong cây Python CVS trên SourceForge, mặc dù chúng không phải là một phần của bản phân phối Python 2.0 và cũng có sẵn ở dạng HTML từ https://peps.python.org/. Tính đến tháng 9 năm 2000, có 25 PEP, từ PEP 201, "Lockstep Iteration", đến PEP 225, "Toán tử theo nguyên tố/đối tượng".

bảng mã Unicode

Tính năng mới lớn nhất trong Python 2.0 là kiểu dữ liệu cơ bản mới: chuỗi Unicode. Unicode sử dụng số 16 bit để biểu thị các ký tự thay vì số 8 bit được ASCII sử dụng, nghĩa là có thể hỗ trợ 65.536 ký tự riêng biệt.

Giao diện cuối cùng để hỗ trợ Unicode đã được đưa ra thông qua vô số cuộc thảo luận thường xuyên sôi nổi trong danh sách gửi thư của python-dev và hầu hết được triển khai bởi Marc-André Lemburg, dựa trên cách triển khai kiểu chuỗi Unicode của Fredrik Lundh. Giải thích chi tiết về giao diện được viết dưới dạng PEP 100, "Tích hợp Python Python". Bài viết này sẽ chỉ đề cập đến những điểm quan trọng nhất về giao diện Unicode.

Trong mã nguồn Python, chuỗi Unicode được viết dưới dạng u"string". Các ký tự Unicode tùy ý có thể được viết bằng chuỗi thoát mới, \uHHHH, trong đó HHHH là số thập lục phân gồm 4 chữ số từ 0000 đến FFFF. Chuỗi thoát \xHH hiện có cũng có thể được sử dụng và ký tự thoát bát phân có thể được sử dụng cho các ký tự lên đến U+01FF, được biểu thị bằng \777.

Chuỗi Unicode, giống như chuỗi thông thường, là một loại chuỗi bất biến. Chúng có thể được lập chỉ mục và cắt lát nhưng không được sửa đổi tại chỗ. Chuỗi Unicode có phương thức encode( [encoding] ) trả về chuỗi 8 bit ở dạng mã hóa mong muốn. Mã hóa được đặt tên theo chuỗi, chẳng hạn như 'ascii', 'utf-8', 'iso-8859-1' hoặc bất cứ thứ gì. Codec API được xác định để triển khai và đăng ký các mã hóa mới có sẵn trong toàn bộ chương trình Python. Nếu mã hóa không được chỉ định thì mã hóa mặc định thường là ASCII 7 bit, mặc dù nó có thể được thay đổi để cài đặt Python bằng cách gọi hàm sys.setdefaultencoding(encoding) trong phiên bản tùy chỉnh của site.py.

Việc kết hợp các chuỗi 8 bit và Unicode luôn ép buộc thành Unicode, sử dụng mã hóa ASCII mặc định; kết quả của 'a' + u'bc'u'abc'.

Các hàm dựng sẵn mới đã được thêm vào và các hàm dựng sẵn hiện có được sửa đổi để hỗ trợ Unicode:

  • unichr(ch) trả về chuỗi Unicode dài 1 ký tự, chứa ký tự ch.

  • ord(u), trong đó u là chuỗi thông thường hoặc chuỗi Unicode gồm 1 ký tự, trả về số lượng ký tự dưới dạng số nguyên.

  • unicode(string [, encoding]  [, errors] ) tạo chuỗi Unicode từ chuỗi 8 bit. encoding là một chuỗi đặt tên mã hóa sẽ sử dụng. Tham số errors chỉ định cách xử lý các ký tự không hợp lệ đối với mã hóa hiện tại; chuyển 'strict' làm giá trị sẽ gây ra ngoại lệ đối với bất kỳ lỗi mã hóa nào, trong khi 'ignore' khiến lỗi bị âm thầm bỏ qua và 'replace' sử dụng U+FFFD, ký tự thay thế chính thức, trong trường hợp có bất kỳ sự cố nào.

  • Câu lệnh exec và nhiều phần dựng sẵn khác như eval(), getattr()setattr() cũng sẽ chấp nhận các chuỗi Unicode cũng như các chuỗi thông thường. (Có thể quá trình sửa lỗi này đã bỏ sót một số phần dựng sẵn; nếu bạn tìm thấy một hàm dựng sẵn chấp nhận chuỗi nhưng hoàn toàn không chấp nhận chuỗi Unicode, vui lòng báo cáo đó là lỗi.)

Một mô-đun mới, unicodedata, cung cấp giao diện cho các thuộc tính ký tự Unicode. Ví dụ: unicodedata.category(u'A') trả về chuỗi 2 ký tự 'Lu', 'L' biểu thị đó là một chữ cái và 'u' có nghĩa là chữ hoa. unicodedata.bidirectional(u'\u0660') trả về 'AN', nghĩa là U+0660 là số Ả Rập.

Mô-đun codecs chứa các chức năng tra cứu bảng mã hiện có và đăng ký bảng mã mới. Trừ khi bạn muốn triển khai một mã hóa mới, bạn sẽ thường xuyên sử dụng hàm codecs.lookup(encoding), hàm này trả về bộ dữ liệu 4 phần tử: (encode_func, decode_func, stream_reader, stream_writer).

  • encode_func là hàm nhận chuỗi Unicode và trả về (string, length) gồm 2 bộ. string là một chuỗi 8 bit chứa một phần (có thể là tất cả) chuỗi Unicode được chuyển đổi thành mã hóa nhất định và length cho bạn biết số lượng chuỗi Unicode đã được chuyển đổi.

  • decode_func ngược lại với encode_func, lấy một chuỗi 8 bit và trả về một (ustring, length) gồm 2 bộ, bao gồm chuỗi Unicode ustring thu được và số nguyên length cho biết số lượng chuỗi 8 bit đã được tiêu thụ.

  • stream_reader là lớp hỗ trợ giải mã đầu vào từ luồng. stream_reader(file_obj) trả về một đối tượng hỗ trợ các phương thức read(), readline()readlines(). Tất cả các phương thức này sẽ dịch từ bảng mã đã cho và trả về các chuỗi Unicode.

  • Tương tự, stream_writer là lớp hỗ trợ mã hóa đầu ra thành luồng. stream_writer(file_obj) trả về một đối tượng hỗ trợ các phương thức write()writelines(). Các phương thức này yêu cầu các chuỗi Unicode, dịch chúng sang mã hóa đã cho ở đầu ra.

Ví dụ: đoạn mã sau ghi một chuỗi Unicode vào một tệp, mã hóa nó thành UTF-8:

nhập codec

unistr = u'\u0660\u2000ab ...'

(UTF8_encode, UTF8_decode,
 UTF8_streamreader, UTF8_streamwriter) = codecs.lookup('UTF-8')

đầu ra = UTF8_streamwriter( open( '/tmp/output', 'wb') )
đầu ra.write( unistr )
đầu ra.close()

Đoạn mã sau sẽ đọc đầu vào UTF-8 từ tệp

đầu vào = UTF8_streamreader( open( '/tmp/output', 'rb') )
in rapr(input.read())
đầu vào.close()

Các biểu thức chính quy nhận biết Unicode có sẵn thông qua mô-đun re, có cách triển khai cơ bản mới gọi là SRE do Fredrik Lundh của Secret Labs AB viết.

Một tùy chọn dòng lệnh -U đã được thêm vào để khiến trình biên dịch Python diễn giải tất cả các chuỗi ký tự dưới dạng ký tự chuỗi Unicode. Điều này nhằm mục đích được sử dụng để kiểm tra và kiểm tra mã Python của bạn trong tương lai, vì một số phiên bản Python trong tương lai có thể ngừng hỗ trợ cho chuỗi 8 bit và chỉ cung cấp chuỗi Unicode.

Danh sách hiểu

Danh sách là một kiểu dữ liệu đặc biệt trong Python và nhiều chương trình thao tác với danh sách tại một số thời điểm. Hai thao tác phổ biến trên danh sách là lặp lại chúng và chọn ra các phần tử đáp ứng một tiêu chí nhất định hoặc áp dụng một số chức năng cho từng phần tử. Ví dụ: cho một danh sách các chuỗi, bạn có thể muốn lấy ra tất cả các chuỗi chứa một chuỗi con nhất định hoặc loại bỏ khoảng trắng ở cuối mỗi dòng.

Các hàm map()filter() hiện có có thể được sử dụng cho mục đích này, nhưng chúng yêu cầu một hàm làm một trong các đối số của chúng. Điều này ổn nếu có một hàm dựng sẵn có thể được truyền trực tiếp, nhưng nếu không có, bạn phải tạo một hàm nhỏ để thực hiện công việc được yêu cầu và các quy tắc phạm vi của Python sẽ khiến kết quả trở nên xấu nếu hàm nhỏ đó cần thêm thông tin. Lấy ví dụ đầu tiên trong đoạn trước, tìm tất cả các chuỗi trong danh sách chứa một chuỗi con đã cho. Bạn có thể viết như sau để làm điều đó

# Given danh sách L, tạo danh sách tất cả các chuỗi
# containing chuỗi con S.
danh sách con = filter( lambda s, chuỗi con=S:
                     string.find(s, chuỗi con) != -1,
                  L)

Do các quy tắc phạm vi của Python, một đối số mặc định được sử dụng để hàm ẩn danh được tạo bởi biểu thức lambda biết chuỗi con nào đang được tìm kiếm. Việc hiểu danh sách làm cho việc này sạch hơn:

danh sách con = [ s cho s trong L if string.find(s, S) != -1 ]

Việc hiểu danh sách có dạng:

[ biểu thức cho expr trong dãy 1
             cho expr2 trong dãy2 ...
             cho exprN theo chuỗiN
             nếu điều kiện]

Mệnh đề for...:keyword:!in chứa các chuỗi được lặp lại. Các chuỗi không nhất thiết phải có cùng độ dài, vì chúng được not lặp lại song song, nhưng từ trái sang phải; điều này được giải thích rõ ràng hơn trong các đoạn sau. Các phần tử của danh sách được tạo sẽ là các giá trị liên tiếp của expression. Mệnh đề if cuối cùng là tùy chọn; nếu có, expression chỉ được đánh giá và thêm vào kết quả nếu condition đúng.

Để làm cho ngữ nghĩa trở nên rõ ràng, việc hiểu danh sách tương đương với mã Python sau:

cho expr1 trong dãy 1:
    cho expr2 trong dãy 2:
    ...
        cho exprN theo chuỗiN:
             nếu (điều kiện):
                  # Append giá trị của
                  biểu thức # the tới
                  danh sách # resulting.

Điều này có nghĩa là khi có nhiều mệnh đề for...in, danh sách kết quả sẽ bằng tích độ dài của tất cả các chuỗi. Nếu bạn có hai danh sách có độ dài 3 thì danh sách đầu ra dài 9 phần tử

seq1 = 'abc'
seq2 = (1,2,3)
>>> [ (x,y) cho x trong seq1 cho y trong seq2]
[('a', 1), ('a', 2), ('a', 3), ('b', 1), ('b', 2), ('b', 3), ('c', 1),
('c', 2), ('c', 3)]

Để tránh gây ra sự mơ hồ trong ngữ pháp của Python, nếu expression đang tạo một bộ dữ liệu thì nó phải được bao quanh bằng dấu ngoặc đơn. Việc hiểu danh sách đầu tiên bên dưới là một lỗi cú pháp, trong khi cách hiểu thứ hai là đúng

lỗi # Syntax
[ x,y cho x trong seq1 cho y trong seq2]
# Correct
[ (x,y) cho x trong seq1 cho y trong seq2]

Ý tưởng về khả năng hiểu danh sách ban đầu xuất phát từ ngôn ngữ lập trình chức năng Haskell (https://www.haskell.org). Greg Ewing đã lập luận hiệu quả nhất về việc thêm chúng vào Python và viết bản vá hiểu danh sách ban đầu, sau đó được thảo luận trong một khoảng thời gian dường như vô tận trên danh sách gửi thư của python-dev và được Skip Montanaro cập nhật.

Bài tập tăng cường

Toán tử gán tăng cường, một tính năng được yêu cầu từ lâu khác, đã được thêm vào Python 2.0. Các toán tử gán tăng cường bao gồm +=, -=, *=, v.v. Ví dụ: câu lệnh a += 2 tăng giá trị của biến a lên 2, tương đương với a = a + 2 dài hơn một chút.

Danh sách đầy đủ các toán tử gán được hỗ trợ là +=, -=, *=, /=, %=, **=, &=, |=, ^=, >>=<<=. Các lớp Python có thể ghi đè các toán tử gán tăng cường bằng cách xác định các phương thức có tên __iadd__(), __isub__(), v.v. Ví dụ: lớp Number sau đây lưu trữ một số và hỗ trợ sử dụng += để tạo một phiên bản mới với giá trị tăng dần.

Số lớp:
    def __init__(tự, giá trị):
        self.value = giá trị
    def __iadd__(tự, tăng):
        Số trả về (self.value + tăng)

n = Số(5)
n += 3
in n.value

Phương thức đặc biệt __iadd__() được gọi với giá trị tăng dần và sẽ trả về một phiên bản mới có giá trị được sửa đổi phù hợp; giá trị trả về này bị ràng buộc là giá trị mới của biến ở phía bên trái.

Các toán tử gán tăng cường lần đầu tiên được giới thiệu trong ngôn ngữ lập trình C và hầu hết các ngôn ngữ có nguồn gốc từ C, chẳng hạn như awk, C++, Java, Perl và PHP cũng hỗ trợ chúng. Bản vá bài tập tăng cường được thực hiện bởi Thomas Wouters.

Phương thức chuỗi

Cho đến nay, chức năng thao tác chuỗi vẫn có trong mô-đun string, thường là giao diện người dùng cho mô-đun strop được viết bằng C. Việc bổ sung Unicode gây khó khăn cho mô-đun strop vì tất cả các chức năng đều cần phải được viết lại để chấp nhận chuỗi 8 bit hoặc Unicode. Đối với các hàm như string.replace(), có 3 đối số chuỗi, nghĩa là có thể có tám hoán vị và mã phức tạp tương ứng.

Thay vào đó, Python 2.0 đẩy vấn đề lên loại chuỗi, cung cấp chức năng thao tác chuỗi thông qua các phương thức trên cả chuỗi 8 bit và chuỗi Unicode.

>>> 'andrew'.capitalize()
'Andrew'
>>> 'tên máy chủ'.replace('os', 'linux')
'hlinuxtname'
>>> 'moshe'.find('sh')
2

Một điều không thay đổi, mặc dù có một trò đùa Cá tháng Tư đáng chú ý, đó là các chuỗi Python là bất biến. Do đó, các phương thức chuỗi trả về các chuỗi mới và không sửa đổi chuỗi mà chúng hoạt động trên đó.

Mô-đun string cũ vẫn được sử dụng để tương thích ngược, nhưng nó chủ yếu hoạt động như một giao diện người dùng cho các phương thức chuỗi mới.

Hai phương thức không có song song trong các phiên bản trước 2.0, mặc dù chúng đã tồn tại trong JPython khá lâu, là startswith()endswith(). s.startswith(t) tương đương với s[:len(t)] == t, trong khi s.endswith(t) tương đương với s[-len(t):] == t.

Một phương pháp khác đáng được đề cập đặc biệt là join(). Phương thức join() của một chuỗi nhận một tham số, một chuỗi các chuỗi và tương đương với hàm string.join() từ mô-đun string cũ, với các đối số được đảo ngược. Nói cách khác, s.join(seq) tương đương với string.join(seq, s) cũ.

Thu gom rác theo chu kỳ

Việc triển khai C của Python sử dụng tính năng tham chiếu để triển khai việc thu thập rác. Mỗi đối tượng Python duy trì số lượng tham chiếu trỏ đến chính nó và điều chỉnh số lượng khi tham chiếu được tạo hoặc hủy. Khi số lượng tham chiếu đạt đến 0, đối tượng sẽ không thể truy cập được nữa vì bạn cần có một tham chiếu đến một đối tượng để truy cập vào nó và nếu số lượng bằng 0 thì không còn tham chiếu nào tồn tại nữa.

Việc đếm tham chiếu có một số đặc tính thú vị: dễ hiểu và dễ thực hiện, đồng thời việc triển khai đạt được có tính di động, khá nhanh và phản ứng tốt với các thư viện khác triển khai sơ đồ xử lý bộ nhớ của riêng họ. Vấn đề chính với việc đếm tham chiếu là đôi khi nó không nhận ra rằng các đối tượng không còn có thể truy cập được nữa, dẫn đến rò rỉ bộ nhớ. Điều này xảy ra khi có chu kỳ tham chiếu.

Hãy xem xét chu trình đơn giản nhất có thể, một thể hiện của lớp có tham chiếu đến chính nó

 dụ = SomeClass()
instance.myself =  dụ

Sau khi hai dòng mã trên được thực thi, số tham chiếu của instance là 2; một tham chiếu là từ biến có tên 'instance' và tham chiếu còn lại là từ thuộc tính myself của phiên bản.

Nếu dòng mã tiếp theo là del instance thì chuyện gì sẽ xảy ra? Số lượng tham chiếu của instance giảm đi 1, do đó nó có số lượng tham chiếu là 1; tham chiếu trong thuộc tính myself vẫn tồn tại. Tuy nhiên, phiên bản này không thể truy cập được thông qua mã Python nữa và nó có thể bị xóa. Một số đối tượng có thể tham gia vào một chu trình nếu chúng có tham chiếu với nhau, khiến tất cả các đối tượng bị rò rỉ.

Python 2.0 khắc phục sự cố này bằng cách thực thi định kỳ thuật toán phát hiện chu trình để tìm kiếm các chu trình không thể truy cập được và xóa các đối tượng liên quan. Mô-đun gc mới cung cấp các chức năng để thực hiện thu thập rác, lấy số liệu thống kê gỡ lỗi và điều chỉnh các tham số của trình thu thập.

Việc chạy thuật toán phát hiện chu trình sẽ mất một chút thời gian và do đó sẽ phát sinh thêm một số chi phí. Hy vọng rằng sau khi chúng tôi đã có kinh nghiệm về việc thu thập chu trình từ việc sử dụng 2.0, Python 2.1 sẽ có thể giảm thiểu chi phí sử dụng bằng cách điều chỉnh cẩn thận. Vẫn chưa rõ hiệu suất bị mất bao nhiêu, vì việc đo điểm chuẩn rất phức tạp và chủ yếu phụ thuộc vào tần suất chương trình tạo và hủy các đối tượng. Tính năng phát hiện chu trình có thể bị vô hiệu hóa khi Python được biên dịch, nếu bạn không đủ khả năng chịu một hình phạt nhỏ về tốc độ hoặc nghi ngờ rằng bộ sưu tập chu trình có lỗi, bằng cách chỉ định khóa chuyển --without-cycle-gc khi chạy tập lệnh configure.

Một số người đã giải quyết vấn đề này và đóng góp vào một giải pháp. Việc triển khai sớm phương pháp phát hiện chu kỳ được viết bởi Toby Kelsey. Thuật toán hiện tại được đề xuất bởi Eric Tiedemann trong chuyến thăm CNRI, Guido van Rossum và Neil Schenauer đã viết hai cách triển khai khác nhau, sau này được Neil tích hợp. Rất nhiều người khác đã đưa ra gợi ý trong quá trình thực hiện; kho lưu trữ tháng 3 năm 2000 của danh sách gửi thư python-dev chứa hầu hết các cuộc thảo luận có liên quan, đặc biệt là trong các chủ đề có tiêu đề "Bộ sưu tập chu trình tham chiếu cho Python" và "Hoàn thiện lại".

Những thay đổi cốt lõi khác

Nhiều thay đổi nhỏ khác nhau đã được thực hiện đối với cú pháp và các hàm dựng sẵn của Python. Không có thay đổi nào có ảnh hưởng sâu rộng nhưng chúng rất tiện lợi.

Thay đổi ngôn ngữ nhỏ

Cú pháp mới giúp thuận tiện hơn khi gọi một hàm nhất định với một bộ đối số và/hoặc từ điển các đối số từ khóa. Trong Python 1.5 trở về trước, bạn sẽ sử dụng hàm dựng sẵn apply(): apply(f, args, kw) gọi hàm f() với bộ đối số args và các đối số từ khóa trong từ điển kw. apply() tương tự ở phiên bản 2.0, nhưng nhờ có bản vá từ Greg Ewing, f(*args, **kw) là cách ngắn hơn và rõ ràng hơn để đạt được hiệu ứng tương tự. Cú pháp này đối xứng với cú pháp xác định hàm:

def f(*args, **kw):
    # args là một bộ các đối số vị trí,
    # kw là từ điển của từ khóa args
    ...

Câu lệnh print giờ đây có thể hướng đầu ra của nó đến một đối tượng giống như tệp bằng cách theo sau print với >> file, tương tự như toán tử chuyển hướng trong hệ vỏ Unix. Trước đây, bạn phải sử dụng phương thức write() của đối tượng giống như tệp, phương pháp này thiếu sự tiện lợi và đơn giản của print hoặc bạn có thể gán một giá trị mới cho sys.stdout và sau đó khôi phục giá trị cũ. Để gửi đầu ra tới lỗi tiêu chuẩn, việc viết điều này dễ dàng hơn nhiều

print >> sys.stderr, "Cảnh báo: trường hành động không được cung cấp"

Giờ đây, các mô-đun có thể được đổi tên khi nhập chúng bằng cú pháp import module as name hoặc from module import name as othername. Bản vá được gửi bởi Thomas Wouters.

Kiểu định dạng mới có sẵn khi sử dụng toán tử %; '%r' sẽ chèn repr() vào đối số của nó. Điều này cũng được thêm vào từ những cân nhắc về tính đối xứng, lần này là về tính đối xứng với kiểu định dạng '%s' hiện có, chèn str() vào đối số của nó. Ví dụ: '%r %s' % ('abc', 'abc') trả về một chuỗi chứa 'abc' abc.

Trước đây không có cách nào để triển khai một lớp vượt qua toán tử in tích hợp của Python và triển khai một phiên bản tùy chỉnh. obj in seq trả về true nếu obj có trong chuỗi seq; Python tính toán điều này bằng cách thử mọi chỉ mục của chuỗi cho đến khi tìm thấy obj hoặc gặp IndexError. Moshe Zadka đã đóng góp một bản vá bổ sung phương pháp ma thuật __contains__() để cung cấp cách triển khai tùy chỉnh cho in. Ngoài ra, các đối tượng tích hợp mới được viết bằng C có thể xác định ý nghĩa của in đối với chúng thông qua một vị trí mới trong giao thức tuần tự.

Các phiên bản trước của Python sử dụng thuật toán đệ quy để xóa các đối tượng. Cấu trúc dữ liệu được lồng sâu có thể khiến trình thông dịch lấp đầy ngăn xếp C và gặp sự cố; Christian Tismer đã viết lại logic xóa để khắc phục vấn đề này. Trên một lưu ý liên quan, so sánh các đối tượng đệ quy được đệ quy vô hạn và bị lỗi; Jeremy Hylton đã viết lại mã để không còn gặp sự cố nữa, thay vào đó tạo ra một kết quả hữu ích. Ví dụ: sau mã này:

một = []
b = []
a.append(a)
b.chắp thêm(b)

Phép so sánh a==b trả về giá trị đúng vì hai cấu trúc dữ liệu đệ quy là đẳng cấu. Xem chủ đề "thùng rác và PR#7" trong kho lưu trữ tháng 4 năm 2000 của danh sách gửi thư python-dev để biết cuộc thảo luận dẫn đến việc triển khai này và một số liên kết hữu ích có liên quan. Lưu ý rằng sự so sánh bây giờ cũng có thể đưa ra những ngoại lệ. Trong các phiên bản Python trước đó, một thao tác so sánh chẳng hạn như cmp(a,b) sẽ luôn tạo ra câu trả lời, ngay cả khi phương thức __cmp__() do người dùng xác định gặp lỗi, vì ngoại lệ kết quả sẽ đơn giản bị nuốt chửng trong âm thầm.

Công việc chuyển Python sang Windows 64 bit trên bộ xử lý Itanium, chủ yếu được thực hiện bởi Trent Mick của ActiveState. (Thật khó hiểu, sys.platform vẫn là 'win32' trên Win64 vì có vẻ như để dễ chuyển, MS Visual C++ xử lý mã là 32 bit trên Itanium.) PythonWin cũng hỗ trợ Windows CE; xem trang Python CE tại https://pythonce.sourceforge.net/ để biết thêm thông tin.

Một nền tảng mới khác là Darwin/MacOS X; hỗ trợ ban đầu cho nó là bằng Python 2.0. Tải động sẽ hoạt động nếu bạn chỉ định "configure --with-dyld --with-suffix=.x". Tham khảo README trong bản phân phối nguồn Python để biết thêm hướng dẫn.

Một nỗ lực đã được thực hiện nhằm giảm bớt một trong những nhược điểm của Python, ngoại lệ NameError thường gây nhầm lẫn khi mã đề cập đến một biến cục bộ trước khi biến đó được gán một giá trị. Ví dụ: đoạn mã sau đưa ra một ngoại lệ trên câu lệnh print ở cả 1.5.2 và 2.0; trong 1.5.2 một ngoại lệ NameError được nêu ra, trong khi 2.0 đưa ra một ngoại lệ UnboundLocalError mới. UnboundLocalError là một lớp con của NameError, vì vậy mọi mã hiện có dự kiến ​​:exc:NameError được nâng lên vẫn hoạt động.

chắc chắn f():
    in "i=", tôi
    tôi = tôi + 1
f()

Hai ngoại lệ mới, TabErrorIndentationError, đã được giới thiệu. Cả hai đều là lớp con của SyntaxError và được nâng lên khi mã Python được phát hiện thụt lề không đúng cách.

Thay đổi đối với các chức năng tích hợp

Một tính năng tích hợp mới, zip(seq1, seq2, ...), đã được thêm vào. zip() trả về danh sách các bộ dữ liệu trong đó mỗi bộ chứa phần tử thứ i từ mỗi chuỗi đối số. Sự khác biệt giữa zip()map(None, seq1, seq2)map() đệm các chuỗi bằng None nếu các chuỗi không có cùng độ dài, trong khi zip() cắt bớt danh sách trả về theo độ dài của chuỗi đối số ngắn nhất.

Các hàm int()long() hiện chấp nhận tham số "cơ sở" tùy chọn khi đối số đầu tiên là một chuỗi. int('123', 10) trả về 123, trong khi int('123', 16) trả về 291. int(123, 16) đưa ra một ngoại lệ TypeError với thông báo "không thể chuyển đổi chuỗi không có cơ sở rõ ràng".

Một biến mới chứa thông tin phiên bản chi tiết hơn đã được thêm vào mô-đun sys. sys.version_info là một bộ (major, minor, micro, level, serial). Ví dụ: trong phiên bản 2.0.1beta1 giả định, sys.version_info sẽ là (2, 0, 1, 'beta', 1). level là một chuỗi như "alpha", "beta" hoặc "final" cho bản phát hành cuối cùng.

Từ điển có một phương thức mới kỳ lạ, setdefault(key, default), hoạt động tương tự như phương thức get() hiện có. Tuy nhiên, nếu khóa bị thiếu, setdefault() vừa trả về giá trị của default như get() sẽ làm, vừa chèn nó vào từ điển làm giá trị cho key. Vì vậy, các dòng mã sau:

if dict.has_key( key ): trả về dict[key]
khác:
    dict[khóa] = []
    trả về dict[key]

có thể được rút gọn thành một câu lệnh return dict.setdefault(key, []).

Trình thông dịch đặt độ sâu đệ quy tối đa để bắt đệ quy chạy trốn trước khi lấp đầy ngăn xếp C và gây ra kết xuất lõi hoặc GPF.. Trước đây, giới hạn này đã được khắc phục khi bạn biên dịch Python, nhưng ở phiên bản 2.0, độ sâu đệ quy tối đa có thể được đọc và sửa đổi bằng cách sử dụng sys.getrecursionlimit()sys.setrecursionlimit(). Giá trị mặc định là 1000 và có thể tìm thấy giá trị tối đa gần đúng cho một nền tảng nhất định bằng cách chạy tập lệnh mới, Misc/find_recursionlimit.py.

Chuyển sang 2.0

Các bản phát hành Python mới cố gắng hết sức để tương thích với các bản phát hành trước đó và thành tích khá tốt. Tuy nhiên, một số thay đổi được coi là đủ hữu ích, thường là do chúng khắc phục các quyết định thiết kế ban đầu hóa ra lại bị nhầm lẫn, nên không phải lúc nào cũng có thể tránh được việc phá vỡ tính tương thích ngược. Phần này liệt kê những thay đổi trong Python 2.0 có thể khiến mã Python cũ bị hỏng.

Thay đổi có thể phá vỡ nhiều mã nhất là thắt chặt các đối số được một số phương pháp chấp nhận. Một số phương thức sẽ lấy nhiều đối số và coi chúng như một bộ dữ liệu, đặc biệt là các phương thức danh sách khác nhau như append()insert(). Trong các phiên bản Python trước đây, nếu L là một danh sách, L.append( 1,2 ) sẽ thêm bộ (1,2) vào danh sách. Trong Python 2.0, điều này khiến ngoại lệ TypeError được nêu ra, với thông báo: 'nối thêm yêu cầu chính xác 1 đối số; 2 đã cho'. Cách khắc phục là chỉ cần thêm một bộ dấu ngoặc đơn bổ sung để chuyển cả hai giá trị dưới dạng một bộ dữ liệu: L.append( (1,2) ).

Các phiên bản trước của những phương pháp này dễ sử dụng hơn vì chúng sử dụng một hàm cũ trong giao diện C của Python để phân tích các đối số của chúng; 2.0 hiện đại hóa chúng để sử dụng PyArg_ParseTuple(), chức năng phân tích đối số hiện tại, cung cấp nhiều thông báo lỗi hữu ích hơn và coi các lệnh gọi nhiều đối số là lỗi. Nếu bạn nhất thiết phải sử dụng 2.0 nhưng không thể sửa mã của mình, bạn có thể chỉnh sửa Objects/listobject.c và xác định ký hiệu tiền xử lý NO_STRICT_LIST_APPEND để duy trì hành vi cũ; điều này không được khuyến khích.

Một số chức năng trong mô-đun socket vẫn ổn định theo cách này. Ví dụ: socket.connect( ('hostname', 25) ) là dạng đúng, chuyển một bộ dữ liệu biểu thị địa chỉ IP, nhưng socket.connect('hostname', 25) cũng hoạt động. socket.connect_exsocket.bind đều dễ sử dụng như nhau. 2.0alpha1 đã thắt chặt các chức năng này, nhưng vì tài liệu thực sự sử dụng biểu mẫu nhiều đối số sai nên nhiều người đã viết mã sẽ không thể kiểm tra chặt chẽ hơn. GvR đã ủng hộ những thay đổi trước phản ứng của công chúng, vì vậy, đối với mô-đun socket, tài liệu đã được sửa và biểu mẫu nhiều đối số chỉ được đánh dấu là không dùng nữa; will nó sẽ được thắt chặt lại trong phiên bản Python trong tương lai.

Lối thoát \x trong chuỗi ký tự hiện có chính xác 2 chữ số hex. Trước đây, nó sẽ sử dụng tất cả các chữ số hex theo sau 'x' và lấy 8 bit thấp nhất của kết quả, vì vậy \x123456 tương đương với \x56.

Các trường hợp ngoại lệ AttributeErrorNameError có thông báo lỗi thân thiện hơn, có văn bản giống như 'Spam' instance has no attribute 'eggs' hoặc name 'eggs' is not defined. Trước đây thông báo lỗi chỉ là tên thuộc tính bị thiếu eggs và mã được viết để lợi dụng thực tế này sẽ bị hỏng trong phiên bản 2.0.

Một số công việc đã được thực hiện để làm cho số nguyên và số nguyên dài có thể hoán đổi cho nhau dễ dàng hơn một chút. Trong 1.5.2, hỗ trợ tệp lớn đã được thêm vào cho Solaris, cho phép đọc các tệp lớn hơn 2 GiB; điều này làm cho phương thức tell() của các đối tượng tệp trả về một số nguyên dài thay vì một số nguyên thông thường. Một số mã sẽ trừ hai độ lệch tệp và cố gắng sử dụng kết quả để nhân một chuỗi hoặc cắt một chuỗi, nhưng điều này tạo ra một TypeError. Trong 2.0, các số nguyên dài có thể được sử dụng để nhân hoặc cắt một chuỗi và nó sẽ hoạt động như bạn mong đợi về mặt trực giác; 3L * 'abc' tạo ra 'abcabcabc' và (0,1,2,3)[2L:4L] tạo ra (2,3). Số nguyên dài cũng có thể được sử dụng trong nhiều ngữ cảnh khác nhau mà trước đây chỉ chấp nhận số nguyên, chẳng hạn như trong phương thức seek() của đối tượng tệp và trong các định dạng được toán tử % hỗ trợ (%d, %i, %x, v.v.). Ví dụ: "%d" % 2L**64 sẽ tạo ra chuỗi 18446744073709551616.

Thay đổi số nguyên dài nhỏ nhất là str() của số nguyên dài không còn ký tự 'L' ở cuối, mặc dù repr() vẫn bao gồm nó. Chữ 'L' gây khó chịu cho nhiều người muốn in các số nguyên dài trông giống như các số nguyên thông thường, vì họ phải tìm mọi cách để cắt bỏ ký tự. Đây không còn là vấn đề trong phiên bản 2.0 nữa, nhưng mã có str(longval)[:-1] và giả sử có 'L' ở đó, giờ đây sẽ mất chữ số cuối cùng.

Lấy repr() của float hiện sử dụng độ chính xác định dạng khác với str(). repr() sử dụng chuỗi định dạng %.17g cho sprintf() của C, trong khi str() sử dụng %.12g như trước. Kết quả là repr() đôi khi có thể hiển thị nhiều vị trí thập phân hơn str() đối với một số số nhất định. Ví dụ: số 8.1 không thể được biểu diễn chính xác dưới dạng nhị phân, vì vậy repr(8.1)'8.0999999999999996', trong khi str(8.1) là '8.1'.

Tùy chọn dòng lệnh -X, biến tất cả các ngoại lệ tiêu chuẩn thành chuỗi thay vì lớp, đã bị xóa; các ngoại lệ tiêu chuẩn bây giờ sẽ luôn là các lớp. Mô-đun exceptions chứa các ngoại lệ tiêu chuẩn đã được dịch từ Python sang mô-đun C tích hợp, được viết bởi Barry Warsaw và Fredrik Lundh.

Mở rộng/nhúng thay đổi

Một số thay đổi chưa được che đậy và sẽ chỉ rõ ràng đối với những người viết mô-đun mở rộng C hoặc nhúng trình thông dịch Python vào một ứng dụng lớn hơn. Nếu bạn không xử lý C API của Python, bạn có thể bỏ qua phần này một cách an toàn.

Số phiên bản của Python C API đã được tăng lên, do đó các phần mở rộng C được biên dịch cho 1.5.2 phải được biên dịch lại để hoạt động với 2.0. Trên Windows, Python 2.0 không thể nhập tiện ích mở rộng của bên thứ ba được xây dựng cho Python 1.5.x do cách hoạt động của Windows DLL, vì vậy Python sẽ đưa ra một ngoại lệ và quá trình nhập sẽ không thành công.

Người dùng mô-đun ExtensionClass của Jim Fulton sẽ hài lòng khi biết rằng các hook đã được thêm vào để ExtensionClass hiện được hỗ trợ bởi isinstance()issubclass(). Điều này có nghĩa là bạn không còn phải nhớ viết mã như if type(obj) == myExtensionClass mà có thể sử dụng if isinstance(obj, myExtensionClass) tự nhiên hơn.

Tệp Python/importdl.c, là một khối #ifdefs để hỗ trợ tải động trên nhiều nền tảng khác nhau, đã được Greg Stein dọn dẹp và tổ chức lại. importdl.c hiện khá nhỏ và mã dành riêng cho nền tảng đã được chuyển vào một loạt tệp Python/dynload_*.c. Một cách dọn dẹp khác: cũng có một số tệp my*.h trong thư mục Bao gồm/ chứa nhiều bản hack tính di động khác nhau; chúng đã được hợp nhất thành một tệp duy nhất, Include/pyport.h.

Quá trình tái cấu trúc malloc được chờ đợi từ lâu của Vladimir Marangozov đã được hoàn thành, giúp trình thông dịch Python dễ dàng sử dụng bộ cấp phát tùy chỉnh thay vì malloc() tiêu chuẩn của C. Để biết tài liệu, hãy đọc nhận xét trong Include/pymem.hInclude/objimpl.h. Để biết các cuộc thảo luận kéo dài trong đó giao diện đã được hoàn thiện, hãy xem kho lưu trữ web về danh sách 'bản vá' và 'python-dev' tại python.org.

Các phiên bản gần đây của môi trường phát triển GUSI dành cho MacOS hỗ trợ các luồng POSIX. Do đó, hỗ trợ phân luồng POSIX của Python hiện hoạt động trên Macintosh. Hỗ trợ phân luồng bằng thư viện GNU pth trong không gian người dùng cũng được đóng góp.

Hỗ trợ phân luồng trên Windows cũng được nâng cao. Windows hỗ trợ các khóa luồng chỉ sử dụng các đối tượng kernel trong trường hợp tranh chấp; trong trường hợp phổ biến khi không có tranh chấp, chúng sử dụng các hàm đơn giản hơn và nhanh hơn rất nhiều. Phiên bản có luồng của Python 1.5.2 trên NT chậm gấp đôi so với phiên bản không có luồng; với những thay đổi 2.0, sự khác biệt chỉ là 10%. Những cải tiến này được đóng góp bởi Ykov Markovitch.

Nguồn của Python 2.0 hiện chỉ sử dụng các nguyên mẫu ANSI C, do đó, việc biên dịch Python hiện yêu cầu trình biên dịch ANSI C và không thể thực hiện được bằng trình biên dịch chỉ hỗ trợ K&R C nữa.

Trước đây máy ảo Python sử dụng số 16 bit trong mã byte của nó, giới hạn kích thước của tệp nguồn. Đặc biệt, điều này ảnh hưởng đến kích thước tối đa của danh sách chữ và từ điển trong nguồn Python; đôi khi những người đang tạo mã Python sẽ gặp phải giới hạn này. Bản vá của Charles G. Waldman tăng giới hạn từ 2**16 lên 2**32.

Ba chức năng tiện lợi mới nhằm mục đích thêm các hằng số vào từ điển của mô-đun tại thời điểm khởi tạo mô-đun đã được thêm vào: PyModule_AddObject(), PyModule_AddIntConstant()PyModule_AddStringConstant(). Mỗi hàm này lấy một đối tượng mô-đun, một chuỗi C kết thúc bằng null chứa tên cần thêm và đối số thứ ba cho giá trị được gán cho tên. Đối số thứ ba này tương ứng là một đối tượng Python, một chuỗi C dài hoặc một chuỗi C.

Một trình bao bọc API đã được thêm vào cho bộ xử lý tín hiệu kiểu Unix. PyOS_getsig() có bộ xử lý tín hiệu và PyOS_setsig() sẽ thiết lập bộ xử lý mới.

Distutils: Làm cho các mô-đun dễ cài đặt

Trước Python 2.0, việc cài đặt các mô-đun là một công việc tẻ nhạt - không có cách nào để tự động tìm ra nơi Python được cài đặt hoặc những tùy chọn trình biên dịch nào sẽ sử dụng cho các mô-đun mở rộng. Các tác giả phần mềm đã phải trải qua một quá trình khó khăn trong việc chỉnh sửa các tệp Makefile và cấu hình, những tệp này chỉ thực sự hoạt động trên Unix và không hỗ trợ Windows và MacOS. Người dùng Python phải đối mặt với các hướng dẫn cài đặt cực kỳ khác nhau giữa các gói mở rộng khác nhau, điều này khiến việc quản lý cài đặt Python trở thành một việc vặt.

Zz002zz dành cho các tiện ích phân phối do Greg Ward quản lý đã tạo ra Distutils, một hệ thống giúp việc cài đặt gói dễ dàng hơn nhiều. Chúng tạo thành gói distutils, một phần mới của thư viện chuẩn của Python. Trong trường hợp tốt nhất, việc cài đặt mô-đun Python từ nguồn sẽ yêu cầu các bước tương tự: đầu tiên bạn chỉ cần giải nén tệp lưu trữ tarball hoặc zip và chạy "python setup.py install". Nền tảng sẽ được tự động phát hiện, trình biên dịch sẽ được nhận dạng, các mô-đun mở rộng C sẽ được biên dịch và bản phân phối sẽ được cài đặt vào thư mục thích hợp. Các đối số dòng lệnh tùy chọn cung cấp nhiều quyền kiểm soát hơn đối với quá trình cài đặt, gói distutils cung cấp nhiều vị trí để ghi đè các giá trị mặc định -- tách bản dựng khỏi bản cài đặt, bản dựng hoặc cài đặt trong các thư mục không mặc định, v.v.

Để sử dụng Distutils, bạn cần viết tập lệnh setup.py. Đối với trường hợp đơn giản, khi phần mềm chỉ chứa các tệp .py, một setup.py tối thiểu có thể chỉ dài vài dòng

từ thiết lập nhập distutils.core
thiết lập (tên = "foo", phiên bản = "1.0",
       py_modules = ["module1", "module2"])

Tệp setup.py không phức tạp hơn nhiều nếu phần mềm bao gồm một vài gói

từ thiết lập nhập distutils.core
thiết lập (tên = "foo", phiên bản = "1.0",
       gói = ["gói", "gói.subpackage"])

Phần mở rộng C có thể là trường hợp phức tạp nhất; đây là một ví dụ được lấy từ gói PyXML

từ thiết lập nhập distutils.core, Tiện ích mở rộng

expat_extension = Tiện ích mở rộng('xml.parsers.pyexpat',
     xác định_macros = [('XML_NS', Không )],
     include_dirs = [ 'tiện ích mở rộng/expat/xmltok',
                      'tiện ích mở rộng/người nước ngoài/xmlparse'],
     nguồn = [ 'extensions/pyexpat.c',
                 'tiện ích mở rộng/expat/xmltok/xmltok.c',
                 'tiện ích mở rộng/expat/xmltok/xmlrole.c', ]
       )
thiết lập (tên = "PyXML", phiên bản = "0.5.4",
       ext_modules =[expat_extension] )

Distutils cũng có thể đảm nhiệm việc tạo các bản phân phối nguồn và nhị phân. Lệnh "sdist", do "python setup.py sdist' chạy, xây dựng một bản phân phối nguồn chẳng hạn như foo-1.0.tar.gz. Việc thêm các lệnh mới không khó, các lệnh "bdist_rpm" và "bdist_wininst" đã được góp phần lần lượt tạo ra bản phân phối RPM và trình cài đặt Windows cho phần mềm. Các lệnh tạo các định dạng phân phối khác như gói Debian và tệp .pkg Solaris đang trong các giai đoạn phát triển khác nhau.

Tất cả điều này được ghi lại trong một hướng dẫn mới, Distributing Python Modules, tham gia vào bộ tài liệu Python cơ bản.

mô-đun XML

Python 1.5.2 bao gồm một trình phân tích cú pháp XML đơn giản dưới dạng mô-đun xmllib, do Sjoerd Mullender đóng góp. Kể từ khi phát hành 1.5.2, hai giao diện khác nhau để xử lý XML đã trở nên phổ biến: SAX2 (phiên bản 2 của API đơn giản cho XML) cung cấp giao diện hướng sự kiện với một số điểm tương đồng với xmllib và DOM (Mô hình đối tượng tài liệu) cung cấp giao diện dựa trên cây, chuyển đổi tài liệu XML thành một cây nút có thể được duyệt qua và sửa đổi. Python 2.0 bao gồm giao diện SAX2 và giao diện DOM rút gọn như một phần của gói xml. Ở đây chúng tôi sẽ cung cấp một cái nhìn tổng quan ngắn gọn về các giao diện mới này; tham khảo tài liệu Python hoặc mã nguồn để biết chi tiết đầy đủ. Python XML SIG cũng đang nghiên cứu tài liệu cải tiến.

Hỗ trợ SAX2

SAX xác định giao diện hướng sự kiện để phân tích cú pháp XML. Để sử dụng SAX, bạn phải viết lớp xử lý SAX. Các lớp trình xử lý kế thừa từ các lớp khác nhau do SAX cung cấp và ghi đè các phương thức khác nhau mà sau đó sẽ được trình phân tích cú pháp XML gọi. Ví dụ: phương thức startElement()endElement() được gọi cho mọi thẻ bắt đầu và kết thúc mà trình phân tích cú pháp gặp phải, phương thức characters() được gọi cho mọi đoạn dữ liệu ký tự, v.v.

Ưu điểm của cách tiếp cận hướng sự kiện là toàn bộ tài liệu không nhất thiết phải nằm trong bộ nhớ bất kỳ lúc nào, điều này rất quan trọng nếu bạn đang xử lý các tài liệu thực sự lớn. Tuy nhiên, việc viết lớp trình xử lý SAX có thể rất phức tạp nếu bạn đang cố gắng sửa đổi cấu trúc tài liệu theo một cách phức tạp nào đó.

Ví dụ: chương trình ví dụ nhỏ này xác định một trình xử lý in thông báo cho mỗi thẻ bắt đầu và kết thúc, sau đó phân tích cú pháp tệp hamlet.xml bằng cách sử dụng nó:

từ sax nhập xml

lớp SimpleHandler(sax.ContentHandler):
    def startElement(self, name, attrs):
        print 'Bắt đầu phần tử:', tên, attrs.keys()

    def endElement(self, name):
        print 'Cuối phần tử:', tên

# Create một đối tượng phân tích cú pháp
trình phân tích  pháp = sax.make_parser()

# Tell, nên sử dụng trình xử lý nào
xử  = SimpleHandler()
trình phân tích  pháp.setContentHandler(trình xử )

# Parse một tập tin!
trình phân tích  pháp.parse( 'hamlet.xml' )

Để biết thêm thông tin, hãy tham khảo tài liệu Python hoặc XML HOWTO tại https://pyxml.sourceforge.net/topics/howto/xml-howto.html.

Hỗ trợ DOM

Mô hình đối tượng tài liệu là một biểu diễn dựa trên cây cho tài liệu XML. Một phiên bản Document cấp cao nhất là gốc của cây và có một phiên bản con duy nhất là phiên bản Element cấp cao nhất. Zz002zz này có các nút con biểu thị dữ liệu ký tự và bất kỳ phần tử phụ nào, có thể có thêm các nút con của riêng chúng, v.v. Sử dụng DOM, bạn có thể duyệt cây kết quả theo bất kỳ cách nào bạn muốn, truy cập các giá trị phần tử và thuộc tính, chèn và xóa các nút cũng như chuyển đổi cây trở lại XML.

DOM rất hữu ích cho việc sửa đổi tài liệu XML, vì bạn có thể tạo cây DOM, sửa đổi nó bằng cách thêm các nút mới hoặc sắp xếp lại các cây con, sau đó tạo ra một tài liệu XML mới làm đầu ra. Bạn cũng có thể tạo cây DOM theo cách thủ công và chuyển đổi nó thành XML, đây có thể là cách linh hoạt hơn để tạo đầu ra XML thay vì chỉ ghi <tag1>...</tag1> vào một tệp.

Việc triển khai DOM đi kèm với Python tồn tại trong mô-đun xml.dom.minidom. Đây là một triển khai nhẹ của DOM Cấp 1 với sự hỗ trợ cho các không gian tên XML. Các hàm tiện lợi parse()parseString() được cung cấp để tạo cây DOM:

từ mức tối thiểu nhập khẩu xml.dom
doc = minidom.parse('hamlet.xml')

doc là một phiên bản Document. Document, giống như tất cả các lớp DOM khác như ElementText, là một lớp con của lớp cơ sở Node. Do đó, tất cả các nút trong cây DOM đều hỗ trợ một số phương thức phổ biến nhất định, chẳng hạn như toxml() trả về một chuỗi chứa biểu diễn XML của nút và các nút con của nó. Mỗi lớp cũng có các phương thức đặc biệt của riêng nó; ví dụ: các phiên bản ElementDocument có một phương pháp để tìm tất cả các phần tử con có tên thẻ nhất định. Tiếp tục từ ví dụ 2 dòng trước:

Perslist = doc.getElementsByTagName('PERSONA' )
in danh sách liên tục [0].toxml()
in danh sách liên tục [1].toxml()

Đối với tệp Hamlet XML, một vài dòng trên xuất ra

<PERSONA>CLAUDIUS, vua Đan Mạch. </PERSONA>
<PERSONA>HAMLET, con trai quá cố  cháu trai của vị vua hiện tại.</PERSONA>

Phần tử gốc của tài liệu có sẵn dưới dạng doc.documentElement và các phần tử con của nó có thể dễ dàng sửa đổi bằng cách xóa, thêm hoặc xóa các nút:

root = doc.documentElement

# Remove đứa con đầu lòng
root.removeChild( root.childNodes[0] )

# Move đứa con đầu tiên mới đến hồi kết
root.appendChild( root.childNodes[0] )

# Insert đứa con đầu lòng mới (ban đầu,
# the con thứ ba) trước con thứ 20.
root.insertBefore( root.childNodes[0], root.childNodes[20] )

Một lần nữa, tôi sẽ giới thiệu cho bạn tài liệu Python để có danh sách đầy đủ các lớp Node khác nhau và các phương thức khác nhau của chúng.

Mối quan hệ với PyXML

Nhóm quan tâm đặc biệt XML đã nghiên cứu mã Python liên quan đến XML được một thời gian. Phân phối mã của nó, được gọi là PyXML, có sẵn từ các trang web của SIG tại https://www.python.org/community/sigs/current/xml-sig.. Bản phân phối PyXML cũng sử dụng tên gói xml. Nếu bạn đã viết các chương trình sử dụng PyXML, có thể bạn đang thắc mắc về khả năng tương thích của nó với gói 2.0 xml.

Câu trả lời là gói xml của Python 2.0 không tương thích với PyXML nhưng có thể tương thích bằng cách cài đặt phiên bản PyXML gần đây. Nhiều ứng dụng có thể hoạt động nhờ hỗ trợ XML đi kèm với Python 2.0, nhưng các ứng dụng phức tạp hơn sẽ yêu cầu cài đặt gói PyXML đầy đủ. Khi được cài đặt, PyXML phiên bản 0.6.0 trở lên sẽ thay thế gói xml đi kèm với Python và sẽ là một tập hợp siêu nghiêm ngặt của gói tiêu chuẩn, bổ sung thêm một loạt tính năng bổ sung. Một số tính năng bổ sung trong PyXML bao gồm:

  • 4DOM, triển khai DOM đầy đủ từ FourThought, Inc.

  • Trình phân tích cú pháp xác thực xmlproc, được viết bởi Lars Marius Garshol.

  • Mô-đun tăng tốc trình phân tích cú pháp sgmlop, được viết bởi Fredrik Lundh.

Thay đổi mô-đun

Rất nhiều cải tiến và sửa lỗi đã được thực hiện đối với thư viện tiêu chuẩn mở rộng của Python; một số mô-đun bị ảnh hưởng bao gồm readline, ConfigParser, cgi, calendar, posix, readline, xmllib, aifc, chunk, wave, random, shelvenntplib. Tham khảo nhật ký CVS để biết chi tiết chính xác từng bản vá.

Brian Gallew đã đóng góp hỗ trợ OpenSSL cho mô-đun socket. OpenSSL là triển khai Lớp cổng bảo mật, mã hóa dữ liệu được gửi qua ổ cắm. Khi biên dịch Python, bạn có thể chỉnh sửa Modules/Setup để bao gồm hỗ trợ SSL, bổ sung thêm một chức năng cho mô-đun socket: socket.ssl(socket, keyfile, certfile), lấy một đối tượng socket và trả về một socket SSL. Các mô-đun httpliburllib cũng được thay đổi để hỗ trợ URL https://, mặc dù chưa có ai triển khai FTP hoặc SMTP trên SSL.

Mô-đun httplib đã được Greg Stein viết lại để hỗ trợ HTTP/1.1.

Khả năng tương thích ngược với phiên bản 1.5 của httplib được cung cấp, mặc dù việc sử dụng các tính năng của HTTP/1.1 như pipeline sẽ yêu cầu viết lại mã để sử dụng một bộ giao diện khác.

Mô-đun Tkinter hiện hỗ trợ Tcl/Tk phiên bản 8.1, 8.2 hoặc 8.3 và đã ngừng hỗ trợ cho các phiên bản 7.x cũ hơn. Mô-đun Tkinter hiện hỗ trợ hiển thị chuỗi Unicode trong tiện ích Tk. Ngoài ra, Fredrik Lundh còn góp phần tối ưu hóa giúp các hoạt động như create_linecreate_polygon nhanh hơn nhiều, đặc biệt là khi sử dụng nhiều tọa độ.

Mô-đun curses đã được mở rộng đáng kể, bắt đầu từ phiên bản nâng cao của Oliver Andrich, để cung cấp nhiều chức năng bổ sung từ ncurses và lời nguyền SYSV, chẳng hạn như màu sắc, hỗ trợ bộ ký tự thay thế, miếng đệm và hỗ trợ chuột. Điều này có nghĩa là mô-đun này không còn tương thích với các hệ điều hành chỉ có lời nguyền BSD, nhưng dường như không có bất kỳ hệ điều hành nào hiện được duy trì thuộc loại này.

Như đã đề cập trong phần thảo luận trước đây về hỗ trợ Unicode của 2.0, việc triển khai cơ bản các biểu thức chính quy do mô-đun re cung cấp đã được thay đổi. SRE, một công cụ biểu thức chính quy mới được viết bởi Fredrik Lundh và được Hewlett Packard tài trợ một phần, hỗ trợ so khớp với cả chuỗi 8 bit và chuỗi Unicode.

Mô-đun mới

Một số mô-đun mới đã được thêm vào. Chúng tôi chỉ liệt kê chúng với những mô tả ngắn gọn; tham khảo tài liệu 2.0 để biết chi tiết về một mô-đun cụ thể.

  • atexit: Để đăng ký các hàm được gọi trước khi thoát trình thông dịch Python. Mã hiện đang đặt trực tiếp sys.exitfunc nên được thay đổi để sử dụng mô-đun atexit, nhập atexit và gọi atexit.register() với chức năng được gọi khi thoát. (Đóng góp bởi Skip Montanaro.)

  • codecs, encodings, unicodedata: Đã thêm như một phần của hỗ trợ Unicode mới.

  • filecmp: Thay thế các mô-đun cmp, cmpcachedircmp cũ, hiện không được dùng nữa. (Được đóng góp bởi Gordon MacMillan và Moshe Zadka.)

  • gettext: Mô-đun này cung cấp hỗ trợ quốc tế hóa (I18N) và bản địa hóa (L10N) cho các chương trình Python bằng cách cung cấp giao diện cho thư viện danh mục tin nhắn gettext GNU. (Được tích hợp bởi Barry Warsaw, từ những đóng góp riêng biệt của Martin von Löwis, Peter Funk và James Henstridge.)

  • linuxaudiodev: Hỗ trợ thiết bị /dev/audio trên Linux, một phiên bản song sinh của mô-đun sunaudiodev hiện có. (Được đóng góp bởi Peter Bosch, với các bản sửa lỗi của Jeremy Hylton.)

  • mmap: Giao diện cho các tệp được ánh xạ bộ nhớ trên cả Windows và Unix. Nội dung của tệp có thể được ánh xạ trực tiếp vào bộ nhớ, tại thời điểm đó, tệp hoạt động giống như một chuỗi có thể thay đổi, do đó nội dung của tệp có thể được đọc và sửa đổi. Chúng thậm chí có thể được chuyển đến các hàm yêu cầu các chuỗi thông thường, chẳng hạn như mô-đun re. (Được đóng góp bởi Sam Rushing, với một số phần mở rộng của A.M. Kuchling.)

  • pyexpat: Giao diện cho trình phân tích cú pháp XML của Expat. (Được đóng góp bởi Paul Prescod.)

  • robotparser: Phân tích tệp robots.txt, được sử dụng để viết các trình thu thập dữ liệu web tránh các khu vực nhất định của trang web một cách lịch sự. Trình phân tích cú pháp chấp nhận nội dung của tệp robots.txt, xây dựng một bộ quy tắc từ tệp đó và sau đó có thể trả lời các câu hỏi về khả năng tìm nạp của một URL nhất định. (Đóng góp bởi Skip Montanaro.)

  • tabnanny: Một mô-đun/tập lệnh để kiểm tra mã nguồn Python xem có vết lõm không rõ ràng hay không. (Được đóng góp bởi Tim Peters.)

  • UserString: Một lớp cơ sở hữu ích để tạo ra các đối tượng hoạt động giống như chuỗi.

  • webbrowser: Một mô-đun cung cấp một cách độc lập với nền tảng để khởi chạy trình duyệt web trên một URL cụ thể. Đối với mỗi nền tảng, các trình duyệt khác nhau sẽ được thử theo một thứ tự cụ thể. Người dùng có thể thay đổi trình duyệt nào được khởi chạy bằng cách đặt biến môi trường BROWSER. (Ban đầu lấy cảm hứng từ bản vá của Eric S. Raymond cho urllib đã bổ sung chức năng tương tự, nhưng mô-đun cuối cùng xuất phát từ mã do Fred Drake triển khai ban đầu là Tools/idle/BrowserControl.py và được Fred điều chỉnh cho phù hợp với thư viện tiêu chuẩn.)

  • _winreg: Giao diện cho sổ đăng ký Windows. _winreg là phiên bản chuyển thể của các hàm đã là một phần của PythonWin từ năm 1995 nhưng hiện đã được thêm vào bản phân phối cốt lõi và được cải tiến để hỗ trợ Unicode. _winreg được viết bởi Bill Tutt và Mark Hammond.

  • zipfile: Một mô-đun để đọc và ghi các kho lưu trữ có định dạng ZIP. Đây là các kho lưu trữ được tạo bởi PKZIP trên DOS/Windows hoặc zip trên Unix, đừng nhầm lẫn với các tệp gzip-format (được hỗ trợ bởi mô-đun gzip) (Được đóng góp bởi James C. Ahlstrom.)

  • imputil: Một mô-đun cung cấp một cách đơn giản hơn để viết các hook nhập tùy chỉnh, so với mô-đun ihooks hiện có. (Được thực hiện bởi Greg Stein, với nhiều cuộc thảo luận về python-dev trong quá trình thực hiện.)

IDLE Cải tiến

IDLE là IDE đa nền tảng Python chính thức, được viết bằng Tkinter. Python 2.0 bao gồm IDLE 0.6, bổ sung một số tính năng và cải tiến mới. Một phần danh sách:

  • Cải thiện và tối ưu hóa giao diện người dùng, đặc biệt là trong lĩnh vực đánh dấu cú pháp và tự động thụt lề.

  • Trình duyệt lớp hiện hiển thị nhiều thông tin hơn, chẳng hạn như các hàm cấp cao nhất trong một mô-đun.

  • Chiều rộng tab hiện là tùy chọn người dùng có thể cài đặt. Khi mở tệp Python hiện có, IDLE sẽ tự động phát hiện các quy ước thụt lề và điều chỉnh.

  • Hiện đã có hỗ trợ gọi trình duyệt trên nhiều nền tảng khác nhau, được sử dụng để mở tài liệu Python trong trình duyệt.

  • IDLE hiện có một dòng lệnh, phần lớn tương tự như trình thông dịch Python thông thường.

  • Lời khuyên cuộc gọi đã được thêm vào ở nhiều nơi.

  • IDLE hiện có thể được cài đặt dưới dạng gói.

  • Trong cửa sổ soạn thảo hiện có thanh dòng/cột ở dưới cùng.

  • Ba lệnh gõ phím mới: Kiểm tra mô-đun (Alt-F5), Nhập mô-đun (F5) và Chạy tập lệnh (Ctrl-F5).

Các mô-đun đã bị xóa và không được dùng nữa

Một số mô-đun đã bị loại bỏ vì chúng lỗi thời hoặc vì hiện tại có những cách tốt hơn để thực hiện điều tương tự. Mô-đun stdwin đã biến mất; nó dành cho bộ công cụ cửa sổ độc lập với nền tảng không còn được phát triển nữa.

Một số mô-đun đã được chuyển sang thư mục con lib-old: cmp, cmpcache, dircmp, dump, find, grep, packmail, poly, util, whatsound, zmod. Nếu bạn có mã dựa trên mô-đun đã được chuyển sang lib-old, bạn chỉ cần thêm thư mục đó vào sys.path để lấy lại chúng, nhưng bạn nên cập nhật bất kỳ mã nào sử dụng các mô-đun này.

Lời cảm ơn

Các tác giả xin cảm ơn những người sau đây đã đưa ra gợi ý về các bản thảo khác nhau của bài viết này: David Bolen, Mark Hammond, Gregg Hauser, Jeremy Hylton, Fredrik Lundh, Detlef Lannert, Aahz Maruch, Skip Montanaro, Vladimir Marangozov, Tobias Polzin, Guido van Rossum, Neil Schenauer và Russ Schmidt.