Có gì mới trong Python 2.2¶
- tác giả:
A.M. Kuchling
Giới thiệu¶
Bài viết này giải thích các tính năng mới trong Python 2.2.2, được phát hành vào ngày 14 tháng 10 năm 2002. Python 2.2.2 là bản phát hành sửa lỗi của Python 2.2, được phát hành lần đầu vào ngày 21 tháng 12 năm 2001.
Python 2.2 có thể được coi là "bản phát hành dọn dẹp". Có một số tính năng như trình tạo và trình vòng lặp hoàn toàn mới, nhưng hầu hết các thay đổi, dù có ý nghĩa và sâu rộng, đều nhằm mục đích làm sạch những bất thường và góc tối của thiết kế ngôn ngữ.
Bài viết này không cố gắng cung cấp thông số kỹ thuật đầy đủ về các tính năng mới mà thay vào đó cung cấp một cái nhìn tổng quan thuận tiện. Để biết chi tiết đầy đủ, bạn nên tham khảo tài liệu dành cho Python 2.2, chẳng hạn như Python Library Reference và Python Reference Manual. Nếu bạn muốn hiểu lý do thiết kế và triển khai đầy đủ cho một thay đổi, hãy tham khảo PEP để biết một tính năng mới cụ thể.
PEP 252 và 253: Thay đổi loại và lớp¶
Những thay đổi lớn nhất và sâu rộng nhất trong Python 2.2 là đối với mô hình đối tượng và lớp của Python. Những thay đổi này phải tương thích ngược, do đó, có thể mã của bạn sẽ tiếp tục chạy không thay đổi nhưng những thay đổi này cung cấp một số khả năng mới đáng kinh ngạc. Trước khi bắt đầu phần này, phần dài nhất và phức tạp nhất của bài viết này, tôi sẽ cung cấp cái nhìn tổng quan về những thay đổi và đưa ra một số nhận xét.
Cách đây rất lâu tôi đã viết một trang web liệt kê các lỗi trong thiết kế của Python. Một trong những sai sót quan trọng nhất là không thể phân lớp các kiểu Python được triển khai trong C. Đặc biệt, không thể phân lớp các kiểu dựng sẵn, vì vậy bạn không thể chỉ phân lớp các danh sách để thêm một phương thức hữu ích duy nhất cho chúng. Mô-đun UserList cung cấp một lớp hỗ trợ tất cả các phương thức của danh sách và có thể được phân lớp thêm, nhưng có rất nhiều mã C yêu cầu danh sách Python thông thường và sẽ không chấp nhận phiên bản UserList.
Python 2.2 sửa lỗi này và trong quá trình này bổ sung thêm một số khả năng mới thú vị. Một bản tóm tắt ngắn gọn:
Bạn có thể phân lớp các loại có sẵn như danh sách và thậm chí cả số nguyên, đồng thời các lớp con của bạn sẽ hoạt động ở mọi nơi yêu cầu loại gốc.
Giờ đây, bạn có thể định nghĩa các phương thức tĩnh và lớp, ngoài các phương thức phiên bản có sẵn trong các phiên bản Python trước đó.
Cũng có thể tự động gọi các phương thức truy cập hoặc thiết lập một thuộc tính phiên bản bằng cách sử dụng cơ chế mới có tên properties. Thay vào đó, nhiều cách sử dụng
__getattr__()có thể được viết lại để sử dụng các thuộc tính, làm cho mã kết quả trở nên đơn giản và nhanh hơn. Là một lợi ích phụ nhỏ, giờ đây các thuộc tính cũng có thể có chuỗi tài liệu.Danh sách các thuộc tính pháp lý cho một phiên bản có thể được giới hạn ở một tập hợp cụ thể bằng cách sử dụng slots, giúp bảo vệ khỏi lỗi chính tả và có thể thực hiện nhiều tối ưu hóa hơn trong các phiên bản Python trong tương lai.
Một số người dùng đã bày tỏ lo ngại về tất cả những thay đổi này. Họ nói, chắc chắn, các tính năng mới rất gọn gàng và có đủ loại thủ thuật không thể có trong các phiên bản Python trước đó, nhưng chúng cũng làm cho ngôn ngữ trở nên phức tạp hơn. Một số người đã nói rằng họ luôn khuyên dùng Python vì tính đơn giản của nó và cảm thấy rằng tính đơn giản của nó đang bị mất đi.
Cá nhân tôi nghĩ không cần phải lo lắng. Nhiều tính năng mới khá bí truyền và bạn có thể viết nhiều mã Python mà không cần phải biết về chúng. Viết một lớp đơn giản không còn khó khăn hơn bao giờ hết, vì vậy bạn không cần phải bận tâm đến việc học hoặc dạy chúng trừ khi chúng thực sự cần thiết. Một số tác vụ rất phức tạp mà trước đây chỉ có thể thực hiện được từ C giờ đây sẽ có thể thực hiện được bằng Python thuần túy và theo tôi thì điều đó sẽ tốt hơn.
Tôi sẽ không cố gắng đề cập đến mọi trường hợp góc cạnh và thay đổi nhỏ cần thiết để các tính năng mới hoạt động. Thay vào đó phần này sẽ chỉ vẽ những nét rộng. Xem phần Liên kết liên quan, "Các liên kết liên quan", để biết thêm nguồn thông tin về mô hình đối tượng mới của Python 2.2.
Lớp cũ và lớp mới¶
Đầu tiên, bạn nên biết rằng Python 2.2 thực sự có hai loại lớp: lớp cổ điển hoặc lớp kiểu cũ và lớp kiểu mới. Mô hình lớp kiểu cũ hoàn toàn giống với mô hình lớp trong các phiên bản Python trước đó. Tất cả các tính năng mới được mô tả trong phần này chỉ áp dụng cho các lớp kiểu mới. Sự khác biệt này không có ý định kéo dài mãi mãi; cuối cùng các lớp kiểu cũ sẽ bị loại bỏ, có thể là trong Python 3.0.
Vậy làm thế nào để bạn xác định một lớp kiểu mới? Bạn làm điều đó bằng cách phân lớp một lớp kiểu mới hiện có. Hầu hết các kiểu dựng sẵn của Python, chẳng hạn như số nguyên, danh sách, từ điển và thậm chí cả tệp, hiện nay đều là các lớp kiểu mới. Một lớp kiểu mới có tên object, lớp cơ sở cho tất cả các kiểu dựng sẵn, cũng đã được thêm vào nên nếu không có kiểu dựng sẵn nào phù hợp, bạn chỉ có thể phân lớp object:
lớp C (đối tượng):
def __init__ (tự):
...
...
Điều này có nghĩa là các câu lệnh class không có bất kỳ lớp cơ sở nào luôn là các lớp cổ điển trong Python 2.2. (Trên thực tế, bạn cũng có thể thay đổi điều này bằng cách đặt một biến cấp mô-đun có tên __metaclass__ --- xem PEP 253 để biết chi tiết --- nhưng chỉ phân lớp object sẽ dễ dàng hơn.)
Các đối tượng kiểu cho các kiểu dựng sẵn có sẵn dưới dạng dựng sẵn, được đặt tên bằng một thủ thuật thông minh. Python luôn có sẵn các hàm có tên int(), float() và str(). Trong 2.2, chúng không còn hoạt động nữa mà nhập các đối tượng hoạt động như nhà máy khi được gọi.
>>> int
<gõ 'int'>
>>> int('123')
123
Để làm cho tập hợp các loại hoàn chỉnh, các đối tượng loại mới như dict() và file() đã được thêm vào. Đây là một ví dụ thú vị hơn, thêm phương thức lock() vào tệp đối tượng:
lớp LockableFile(tập tin):
khóa def (tự, hoạt động, độ dài = 0, bắt đầu = 0, từ đó = 0):
nhập khẩu fcntl
trả về fcntl.lockf(self.fileno(), thao tác,
độ dài, bắt đầu, từ đâu)
Mô-đun posixfile hiện đã lỗi thời chứa một lớp mô phỏng tất cả các phương thức của đối tượng tệp và cũng đã thêm phương thức lock(), nhưng lớp này không thể được chuyển đến các hàm nội bộ mong đợi một tệp tích hợp, điều này có thể thực hiện được với LockableFile mới của chúng tôi.
Mô tả¶
Trong các phiên bản trước của Python, không có cách nhất quán để khám phá những thuộc tính và phương thức nào được một đối tượng hỗ trợ. Có một số quy ước không chính thức, chẳng hạn như xác định các thuộc tính __members__ và __methods__ là danh sách tên, nhưng thường thì tác giả của loại tiện ích mở rộng hoặc lớp sẽ không bận tâm đến việc định nghĩa chúng. Bạn có thể quay lại kiểm tra __dict__ của một đối tượng, nhưng khi sử dụng kế thừa lớp hoặc móc __getattr__() tùy ý, điều này vẫn có thể không chính xác.
Một ý tưởng lớn đằng sau mô hình lớp mới là API để mô tả các thuộc tính của một đối tượng bằng descriptors đã được chính thức hóa. Bộ mô tả chỉ định giá trị của một thuộc tính, cho biết đó là phương thức hay trường. Với bộ mô tả API, các phương thức tĩnh và phương thức lớp cũng như các cấu trúc kỳ lạ hơn đều có thể thực hiện được.
Bộ mô tả thuộc tính là các đối tượng sống bên trong các đối tượng lớp và có một số thuộc tính riêng:
__name__là tên của thuộc tính.__doc__là chuỗi tài liệu của thuộc tính.__get__(object)là phương thức lấy giá trị thuộc tính từ object.__set__(object, value)đặt thuộc tính trên object thành value.__delete__(object, value)xóa thuộc tính value của object.
Ví dụ: khi bạn viết obj.x, các bước mà Python thực sự thực hiện là:
bộ mô tả = obj.__class__.x
bộ mô tả.__get__(obj)
Đối với các phương thức, descriptor.__get__ trả về một đối tượng tạm thời có thể gọi được và gói gọn thể hiện cũng như phương thức được gọi trên đó. Đây cũng là lý do tại sao hiện nay có thể sử dụng được các phương thức tĩnh và phương thức lớp; chúng có các bộ mô tả chỉ gói gọn phương thức hoặc phương thức và lớp. Như một lời giải thích ngắn gọn về các loại phương thức mới này, các phương thức tĩnh không được truyền qua phiên bản và do đó giống với các hàm thông thường. Các phương thức lớp được truyền vào lớp của đối tượng chứ không phải chính đối tượng đó. Các phương thức tĩnh và lớp được định nghĩa như thế này
lớp C (đối tượng):
định nghĩa f(arg1, arg2):
...
f = phương thức tĩnh(f)
def g(cls, arg1, arg2):
...
g = phương thức lớp (g)
Hàm staticmethod() lấy hàm f() và trả về nó được gói trong một bộ mô tả để nó có thể được lưu trữ trong đối tượng lớp. Bạn có thể mong đợi có cú pháp đặc biệt để tạo các phương thức như vậy (def static f, defstatic f() hoặc tương tự) nhưng chưa có cú pháp nào như vậy được xác định; phần đó được để lại cho các phiên bản Python trong tương lai.
Nhiều tính năng mới hơn, chẳng hạn như vị trí và thuộc tính, cũng được triển khai dưới dạng các loại bộ mô tả mới và không khó để viết một lớp mô tả thực hiện điều gì đó mới lạ. Ví dụ, có thể viết một lớp mô tả để có thể viết các điều kiện tiên quyết và hậu điều kiện kiểu Eiffel cho một phương thức. Một lớp sử dụng tính năng này có thể được định nghĩa như sau:
từ eiffel nhập eiffelmethod
lớp C (đối tượng):
def f(tự, arg1, arg2):
chức năng thực tế của # The
...
def pre_f(tự):
điều kiện tiên quyết của # Check
...
def post_f(tự):
điều kiện sau # Check
...
f = phương thức eiffel(f, pre_f, post_f)
Lưu ý rằng người sử dụng eiffelmethod() mới không cần phải hiểu gì về bộ mô tả. Đây là lý do tại sao tôi nghĩ các tính năng mới không làm tăng độ phức tạp cơ bản của ngôn ngữ. Sẽ có một số trình hướng dẫn cần biết về nó để viết eiffelmethod() hoặc ZODB hoặc bất cứ thứ gì, nhưng hầu hết người dùng sẽ chỉ viết mã lên trên các thư viện kết quả và bỏ qua các chi tiết triển khai.
Đa kế thừa: Quy tắc kim cương¶
Tính đa kế thừa cũng trở nên hữu ích hơn thông qua việc thay đổi các quy tắc giải quyết các tên. Hãy xem xét tập hợp các lớp này (sơ đồ lấy từ PEP 253 của Guido van Rossum):
lớp A:
^ ^ def lưu (tự): ...
/ \
/ \
/ \
/ \
lớp B lớp C:
^ ^ def lưu (tự): ...
\ /
\ /
\ /
\ /
lớp D
Quy tắc tra cứu cho các lớp cổ điển rất đơn giản nhưng không thông minh lắm; các lớp cơ sở được tìm kiếm theo chiều sâu, từ trái sang phải. Tham chiếu đến D.save() sẽ tìm kiếm các lớp D, B, và sau đó là A, trong đó save() sẽ được tìm thấy và trả về. C.save() sẽ không bao giờ được tìm thấy. Điều này thật tệ, vì nếu phương thức save() của C đang lưu một số trạng thái nội bộ cụ thể cho C, thì việc không gọi nó sẽ dẫn đến trạng thái đó không bao giờ được lưu.
Các lớp kiểu mới tuân theo một thuật toán khác phức tạp hơn một chút để giải thích, nhưng thực hiện đúng trong tình huống này. (Lưu ý rằng Python 2.3 thay đổi thuật toán này thành thuật toán tạo ra kết quả tương tự trong hầu hết các trường hợp, nhưng tạo ra kết quả hữu ích hơn cho các biểu đồ kế thừa thực sự phức tạp.)
Liệt kê tất cả các lớp cơ sở, tuân theo quy tắc tra cứu cổ điển và bao gồm một lớp nhiều lần nếu nó được truy cập nhiều lần. Trong ví dụ trên, danh sách các lớp đã truy cập là [
D,B,A,C,A].Quét danh sách các lớp trùng lặp. Nếu tìm thấy bất kỳ lỗi nào, hãy xóa tất cả trừ một lần xuất hiện, để lại một lần xuất hiện last trong danh sách. Trong ví dụ trên, danh sách trở thành [
D,B,C,A] sau khi loại bỏ các bản sao.
Tuân theo quy tắc này, việc tham chiếu đến D.save() sẽ trả về C.save(), đây là hành vi mà chúng tôi đang theo đuổi. Quy tắc tra cứu này giống với quy tắc theo sau của Common Lisp. Một hàm tích hợp mới, super(), cung cấp một cách để truy cập vào các siêu lớp của một lớp mà không cần phải triển khai lại thuật toán của Python. Dạng được sử dụng phổ biến nhất sẽ là super(class, obj), trả về một đối tượng siêu lớp bị ràng buộc (không phải đối tượng lớp thực tế). Biểu mẫu này sẽ được sử dụng trong các phương thức để gọi một phương thức trong siêu lớp; ví dụ: phương thức save() của D sẽ trông như thế này
lớp D (B, C):
def lưu (tự):
siêu lớp # Call .save()
super(D, self).save()
Thông tin riêng của # Save D tại đây
...
super() cũng có thể trả về các đối tượng siêu lớp không liên kết khi được gọi là super(class) hoặc super(class1, class2), nhưng điều này có thể không thường hữu ích.
Quyền truy cập thuộc tính¶
Một số lượng khá lớn các lớp Python phức tạp xác định các hook để truy cập thuộc tính bằng __getattr__(); Thông thường nhất, điều này được thực hiện để thuận tiện, giúp mã dễ đọc hơn bằng cách tự động ánh xạ quyền truy cập thuộc tính, chẳng hạn như obj.parent vào lệnh gọi phương thức, chẳng hạn như obj.get_parent. Python 2.2 bổ sung thêm một số cách mới để kiểm soát quyền truy cập thuộc tính.
Đầu tiên, __getattr__(attr_name) vẫn được hỗ trợ bởi các lớp kiểu mới và không có gì thay đổi. Như trước đây, nó sẽ được gọi khi cố gắng truy cập obj.foo và không tìm thấy thuộc tính nào có tên foo trong từ điển của phiên bản.
Các lớp kiểu mới cũng hỗ trợ một phương thức mới, __getattribute__(attr_name). Sự khác biệt giữa hai phương thức là __getattribute__() được gọi always bất cứ khi nào bất kỳ thuộc tính nào được truy cập, trong khi __getattr__() cũ chỉ được gọi nếu foo không được tìm thấy trong từ điển của phiên bản.
Tuy nhiên, sự hỗ trợ của Python 2.2 cho properties thường sẽ là cách đơn giản hơn để bẫy các tham chiếu thuộc tính. Viết một phương thức __getattr__() rất phức tạp vì để tránh đệ quy, bạn không thể sử dụng các truy cập thuộc tính thông thường bên trong chúng mà thay vào đó phải loay hoay với nội dung của __dict__. Các phương thức __getattr__() cuối cùng cũng được Python gọi khi nó kiểm tra các phương thức khác như __repr__() hoặc __coerce__(), và do đó phải được ghi nhớ điều này. Cuối cùng, việc gọi một hàm trên mọi quyền truy cập thuộc tính sẽ dẫn đến giảm hiệu suất đáng kể.
property là loại tích hợp mới, đóng gói ba chức năng nhận, đặt hoặc xóa một thuộc tính và một chuỗi tài liệu. Ví dụ: nếu bạn muốn xác định thuộc tính size được tính toán nhưng cũng có thể cài đặt được, bạn có thể viết:
lớp C (đối tượng):
def get_size (tự):
kết quả = ... tính toán ...
kết quả trả về
def set_size (tự, kích thước):
... tính toán thứ gì đó dựa trên kích thước
và thiết lập trạng thái nội bộ một cách thích hợp ...
# Define một tài sản. 'Xóa thuộc tính này'
# method được định nghĩa là Không có, do đó thuộc tính
# can không bị xóa.
kích thước = thuộc tính (get_size, set_size,
không,
"Kích thước lưu trữ của phiên bản này")
Điều đó chắc chắn rõ ràng và dễ viết hơn một cặp phương thức __getattr__()/__setattr__() kiểm tra thuộc tính size và xử lý nó một cách đặc biệt trong khi truy xuất tất cả các thuộc tính khác từ __dict__ của cá thể. Các quyền truy cập vào size cũng là những quyền duy nhất phải thực hiện công việc gọi một hàm, do đó các tham chiếu đến các thuộc tính khác chạy ở tốc độ thông thường.
Cuối cùng, có thể hạn chế danh sách các thuộc tính có thể được tham chiếu trên một đối tượng bằng thuộc tính lớp __slots__ mới. Các đối tượng Python thường rất năng động; bất cứ lúc nào bạn cũng có thể xác định thuộc tính mới trên một phiên bản chỉ bằng cách thực hiện obj.new_attr=1. Lớp kiểu mới có thể xác định thuộc tính lớp có tên __slots__ để giới hạn các thuộc tính pháp lý cho một tập hợp tên cụ thể. Một ví dụ sẽ làm rõ điều này:
>>> lớp C(đối tượng):
... __slots__ = ('mẫu', 'tên')
...
>>> obj = C()
>>> in obj.template
không có
>>> obj.template = 'Kiểm tra'
>>> in obj.template
kiểm tra
>>> obj.newatr = Không có
Traceback (cuộc gọi gần đây nhất):
Tệp "<stdin>", dòng 1, ở định dạng ?
AttributionError: Đối tượng 'C' không có thuộc tính 'newattr'
Lưu ý cách bạn nhận được AttributeError khi cố gắng gán cho một thuộc tính không được liệt kê trong __slots__.
PEP 234: Trình vòng lặp¶
Một bổ sung quan trọng khác cho 2.2 là giao diện lặp ở cả cấp độ C và Python. Các đối tượng có thể xác định cách chúng có thể được người gọi lặp lại.
Trong các phiên bản Python lên tới 2.1, cách thông thường để làm cho for item in obj hoạt động là xác định một phương thức __getitem__() trông giống như thế này:
def __getitem__(tự, chỉ mục):
trả lại <mục tiếp theo>
__getitem__() được sử dụng chính xác hơn để xác định thao tác lập chỉ mục trên một đối tượng để bạn có thể viết obj[5] để truy xuất phần tử thứ sáu. Sẽ hơi sai lầm một chút khi bạn chỉ sử dụng tính năng này để hỗ trợ các vòng lặp for. Hãy xem xét một số đối tượng giống như tệp muốn được lặp lại; tham số index về cơ bản là vô nghĩa, vì lớp có thể giả định rằng một loạt lệnh gọi __getitem__() sẽ được thực hiện với index tăng dần mỗi lần một. Nói cách khác, sự hiện diện của phương thức __getitem__() không có nghĩa là việc sử dụng file[5] để truy cập ngẫu nhiên phần tử thứ sáu sẽ hoạt động, mặc dù điều đó thực sự nên làm.
Trong Python 2.2, việc lặp có thể được triển khai riêng biệt và các phương thức __getitem__() có thể được giới hạn ở các lớp thực sự hỗ trợ truy cập ngẫu nhiên. Ý tưởng cơ bản của iterator rất đơn giản. Một hàm tích hợp mới, iter(obj) hoặc iter(C, sentinel), được sử dụng để lấy một trình vòng lặp. iter(obj) trả về một trình vòng lặp cho đối tượng obj, trong khi iter(C, sentinel) trả về một trình vòng lặp sẽ gọi đối tượng có thể gọi được C cho đến khi nó trả về sentinel để báo hiệu rằng trình vòng lặp đã hoàn thành.
Các lớp Python có thể định nghĩa một phương thức __iter__(), phương thức này sẽ tạo và trả về một trình vòng lặp mới cho đối tượng; nếu đối tượng là trình lặp của chính nó, phương thức này chỉ có thể trả về self. Đặc biệt, các trình vòng lặp thường sẽ là các trình vòng lặp của riêng chúng. Các loại tiện ích mở rộng được triển khai trong C có thể triển khai hàm tp_iter để trả về một trình vòng lặp và các loại tiện ích mở rộng muốn hoạt động như các trình vòng lặp có thể xác định hàm tp_iternext.
Vì vậy, sau tất cả những điều này, các trình vòng lặp thực sự làm gì? Họ có một phương thức bắt buộc, next(), không có đối số và trả về giá trị tiếp theo. Khi không còn giá trị nào được trả về, việc gọi next() sẽ đưa ra ngoại lệ StopIteration.
>>> L = [1,2,3]
>>> i = iter(L)
>>> in tôi
<đối tượng iterator tại 0x8116870>
>>> i.next()
1
>>> i.next()
2
>>> i.next()
3
>>> i.next()
Traceback (cuộc gọi gần đây nhất):
Tệp "<stdin>", dòng 1, ở định dạng ?
Dừng lại
>>>
Trong 2.2, câu lệnh for của Python không còn mong đợi một chuỗi nữa; nó mong đợi thứ gì đó mà iter() sẽ trả về một trình vòng lặp. Để tương thích ngược và thuận tiện, một trình vòng lặp được tạo tự động cho các chuỗi không triển khai __iter__() hoặc khe tp_iter, vì vậy for i in [1,2,3] sẽ vẫn hoạt động. Bất cứ khi nào trình thông dịch Python lặp lại một chuỗi, nó sẽ được thay đổi để sử dụng giao thức lặp. Điều này có nghĩa là bạn có thể làm những việc như thế này:
>>> L = [1,2,3]
>>> i = iter(L)
>>> a,b,c = i
>>> a,b,c
(1, 2, 3)
Hỗ trợ Iterator đã được thêm vào một số loại cơ bản của Python. Gọi iter() trên từ điển sẽ trả về một trình vòng lặp lặp qua các khóa của nó
>>> m = {'Tháng 1': 1, 'Tháng 2': 2, 'Tháng 3': 3, 'Tháng 4': 4, 'Tháng 5': 5, 'Tháng 6': 6,
... 'Tháng 7': 7, 'Tháng 8': 8, 'Tháng 9': 9, 'Tháng 10': 10, 'Tháng 11': 11, 'Tháng 12': 12}
>>> for key in m: print key, m[key]
...
3 tháng 3
ngày 2 tháng 2
ngày 8 tháng 8
ngày 9 tháng 9
ngày 5 tháng 5
ngày 6 tháng 6
ngày 7 tháng 7
ngày 1 tháng 1
ngày 4 tháng 4
ngày 11 tháng 11
ngày 12 tháng 12
ngày 10 tháng 10
Đó chỉ là hành vi mặc định. Nếu bạn muốn lặp lại các khóa, giá trị hoặc cặp khóa/giá trị, bạn có thể gọi các phương thức iterkeys(), itervalues() hoặc iteritems() một cách rõ ràng để có được một trình vòng lặp thích hợp. Trong một thay đổi nhỏ có liên quan, toán tử in hiện hoạt động trên từ điển, vì vậy key in dict hiện tương đương với dict.has_key(key).
Các tệp cũng cung cấp một trình lặp, gọi phương thức readline() cho đến khi không còn dòng nào trong tệp. Điều này có nghĩa là bây giờ bạn có thể đọc từng dòng của tệp bằng mã như thế này
cho dòng trong tập tin:
# do một cái gì đó cho mỗi dòng
...
Lưu ý rằng bạn chỉ có thể tiếp tục trong một trình vòng lặp; không có cách nào để lấy phần tử trước đó, đặt lại trình vòng lặp hoặc tạo bản sao của phần tử đó. Đối tượng iterator có thể cung cấp các khả năng bổ sung như vậy, nhưng giao thức iterator chỉ yêu cầu phương thức next().
Xem thêm
- PEP 234 - Trình vòng lặp
Viết bởi Ka-Ping Yee và GvR; được triển khai bởi nhóm Python Labs, chủ yếu là bởi GvR và Tim Peters.
PEP 255: Máy phát điện đơn giản¶
Trình tạo là một tính năng mới khác, một tính năng tương tác với việc giới thiệu các trình vòng lặp.
Bạn chắc chắn đã quen với cách hoạt động của các lệnh gọi hàm trong Python hoặc C. Khi bạn gọi một hàm, nó sẽ có một vùng tên riêng nơi các biến cục bộ của nó được tạo. Khi hàm đạt đến câu lệnh return, các biến cục bộ sẽ bị hủy và giá trị kết quả được trả về cho người gọi. Lệnh gọi sau đó tới hàm tương tự sẽ nhận được một tập hợp biến cục bộ mới. Tuy nhiên, điều gì sẽ xảy ra nếu các biến cục bộ không bị loại bỏ khi thoát khỏi hàm? Điều gì sẽ xảy ra nếu sau này bạn có thể tiếp tục lại chức năng mà nó đã dừng lại? Đây là những gì máy phát điện cung cấp; chúng có thể được coi là các chức năng có thể tiếp tục.
Đây là ví dụ đơn giản nhất về hàm tạo:
def tạo_ints (N):
cho tôi trong phạm vi (N):
nhường tôi
Một từ khóa mới, yield, đã được giới thiệu cho máy phát điện. Bất kỳ hàm nào chứa câu lệnh yield đều là hàm tạo; điều này được phát hiện bởi trình biên dịch mã byte của Python để biên dịch hàm một cách đặc biệt. Vì một từ khóa mới đã được giới thiệu nên trình tạo phải được kích hoạt rõ ràng trong mô-đun bằng cách đưa vào câu lệnh from __future__ import generators ở gần đầu mã nguồn của mô-đun. Trong Python 2.3, câu lệnh này sẽ trở nên không cần thiết.
Khi bạn gọi một hàm tạo, nó không trả về một giá trị nào; thay vào đó nó trả về một đối tượng trình tạo hỗ trợ giao thức vòng lặp. Khi thực thi câu lệnh yield, trình tạo sẽ xuất ra giá trị của i, tương tự như câu lệnh return. Sự khác biệt lớn giữa câu lệnh yield và câu lệnh return là khi đạt tới yield, trạng thái thực thi của trình tạo bị tạm dừng và các biến cục bộ được giữ nguyên. Trong lệnh gọi tiếp theo tới phương thức next() của trình tạo, hàm sẽ tiếp tục thực thi ngay sau câu lệnh yield. (Vì những lý do phức tạp, câu lệnh yield không được phép bên trong khối try của câu lệnh try...finally; hãy đọc PEP 255 để biết giải thích đầy đủ về sự tương tác giữa yield và các ngoại lệ.)
Đây là cách sử dụng mẫu của trình tạo generate_ints():
>>> gen = generate_ints(3)
>>> thế hệ
<đối tượng máy phát điện ở 0x8117f90>
>>> gen.next()
0
>>> gen.next()
1
>>> gen.next()
2
>>> gen.next()
Traceback (cuộc gọi gần đây nhất):
Tệp "<stdin>", dòng 1, ở định dạng ?
Tệp "<stdin>", dòng 2, trong generate_ints
Dừng lại
Bạn cũng có thể viết for i in generate_ints(5) hoặc a,b,c = generate_ints(3).
Bên trong hàm tạo, câu lệnh return chỉ có thể được sử dụng mà không có giá trị và báo hiệu sự kết thúc của quá trình xử lý các giá trị; sau đó trình tạo không thể trả về bất kỳ giá trị nào nữa. return có giá trị, chẳng hạn như return 5, là lỗi cú pháp bên trong hàm tạo. Sự kết thúc của kết quả của trình tạo cũng có thể được biểu thị bằng cách tăng StopIteration theo cách thủ công hoặc chỉ bằng cách để luồng thực thi rơi ra khỏi phần dưới cùng của hàm.
Bạn có thể đạt được hiệu quả của trình tạo theo cách thủ công bằng cách viết lớp của riêng bạn và lưu trữ tất cả các biến cục bộ của trình tạo dưới dạng biến thể hiện. Ví dụ: việc trả về một danh sách các số nguyên có thể được thực hiện bằng cách đặt self.count thành 0 và để phương thức next() tăng self.count rồi trả về nó. Tuy nhiên, đối với một trình tạo có độ phức tạp vừa phải, việc viết một lớp tương ứng sẽ rắc rối hơn nhiều. Lib/test/test_generators.py chứa một số ví dụ thú vị hơn. Cách đơn giản nhất là thực hiện duyệt cây theo thứ tự bằng cách sử dụng các trình tạo đệ quy.
Trình tạo đệ quy # A tạo ra các lá Cây theo thứ tự.
thứ tự def (t):
nếu t:
cho x theo thứ tự (t.left):
năng suất x
năng suất t.label
cho x theo thứ tự (t.right):
năng suất x
Hai ví dụ khác trong Lib/test/test_generators.py đưa ra giải pháp cho bài toán N-Queens (đặt các quân hậu $N$ trên bàn cờ $NxN$ để không có quân hậu nào đe dọa quân hậu khác) và Chuyến tham quan của Hiệp sĩ (một lộ trình đưa một quân mã đến mọi ô của bàn cờ $NxN$ mà không ghé thăm bất kỳ ô nào hai lần).
Ý tưởng về trình tạo xuất phát từ các ngôn ngữ lập trình khác, đặc biệt là Icon (https://www2.cs.arizona.edu/icon/), trong đó ý tưởng về trình tạo là trung tâm. Trong Icon, mọi lệnh gọi biểu thức và hàm hoạt động giống như một trình tạo. Một ví dụ từ "Tổng quan về Ngôn ngữ lập trình biểu tượng" tại https://www2.cs.arizona.edu/icon/docs/ipd266.htm đưa ra ý tưởng về giao diện này:
câu := "Cất nó ở bến cảng lân cận"
if (i := find("hoặc", câu)) > 5 thì viết(i)
Trong Icon, hàm find() trả về các chỉ mục tại đó tìm thấy chuỗi con "hoặc": 3, 23, 33. Trong câu lệnh if, i trước tiên được gán giá trị là 3, nhưng 3 nhỏ hơn 5, do đó so sánh không thành công và Icon thử lại với giá trị thứ hai là 23. 23 lớn hơn 5, do đó, phép so sánh bây giờ thành công và mã in giá trị 23 thành giá trị 23. màn hình.
Python không tiến xa được như Icon trong việc áp dụng máy phát điện làm khái niệm trung tâm. Trình tạo được coi là một phần mới của ngôn ngữ Python cốt lõi, nhưng việc học hoặc sử dụng chúng không bắt buộc; nếu chúng không giải quyết được bất kỳ vấn đề nào bạn gặp phải, hãy bỏ qua chúng. Một tính năng mới của giao diện Python so với Icon là trạng thái của trình tạo được biểu diễn dưới dạng một đối tượng cụ thể (trình vòng lặp) có thể được chuyển sang các hàm khác hoặc được lưu trữ trong cấu trúc dữ liệu.
Xem thêm
- PEP 255 - Máy phát điện đơn giản
Viết bởi Neil Schenauer, Tim Peters, Magnus Lie Hetland. Được triển khai chủ yếu bởi Neil Schenauer và Tim Peters, cùng với các bản sửa lỗi khác từ nhóm Python Labs.
PEP 237: Thống nhất số nguyên dài và số nguyên¶
Trong các phiên bản gần đây, sự khác biệt giữa số nguyên thông thường, là giá trị 32 bit trên hầu hết các máy và số nguyên dài, có thể có kích thước tùy ý, đã trở thành một vấn đề khó chịu. Ví dụ: trên nền tảng hỗ trợ các tệp lớn hơn 2**32 byte, phương thức tell() của các đối tượng tệp phải trả về một số nguyên dài. Tuy nhiên, có nhiều bit khác nhau của Python yêu cầu số nguyên đơn giản và sẽ gây ra lỗi nếu thay vào đó cung cấp số nguyên dài. Ví dụ: trong Python 1.5, chỉ các số nguyên thông thường mới có thể được sử dụng làm chỉ mục lát cắt và 'abc'[1L:] sẽ đưa ra một ngoại lệ TypeError với thông báo 'chỉ mục lát cắt phải là int'.
Python 2.2 sẽ chuyển giá trị từ số nguyên ngắn sang số nguyên dài theo yêu cầu. Hậu tố 'L' không còn cần thiết để biểu thị một số nguyên dài, vì bây giờ trình biên dịch sẽ chọn loại thích hợp. (Việc sử dụng hậu tố 'L' sẽ không được khuyến khích trong các phiên bản Python 2.x trong tương lai, gây ra cảnh báo trong Python 2.4 và có thể bị loại bỏ trong Python 3.0.) Nhiều thao tác từng dùng để tăng OverflowError giờ đây sẽ trả về một số nguyên dài làm kết quả. Ví dụ:
>>> 1234567890123
1234567890123L
>>> 2 ** 64
18446744073709551616L
Trong hầu hết các trường hợp, số nguyên và số nguyên dài bây giờ sẽ được xử lý giống hệt nhau. Bạn vẫn có thể phân biệt chúng bằng chức năng tích hợp sẵn type(), nhưng điều đó hiếm khi cần thiết.
Xem thêm
- PEP 237 - Hợp nhất số nguyên dài và số nguyên
Viết bởi Moshe Zadka và Guido van Rossum. Được thực hiện chủ yếu bởi Guido van Rossum.
PEP 238: Thay đổi toán tử chia¶
Thay đổi gây tranh cãi nhất trong Python 2.2 báo trước sự khởi đầu của nỗ lực sửa lỗi thiết kế cũ đã có trong Python ngay từ đầu. Hiện tại, toán tử chia của Python, /, hoạt động giống như toán tử chia của C khi được trình bày với hai đối số nguyên: nó trả về một kết quả số nguyên bị cắt bớt khi có phần phân số. Ví dụ: 3/2 là 1, không phải 1,5 và (-1)/2 là -1, không phải -0,5. Điều này có nghĩa là kết quả của phép chia có thể thay đổi bất ngờ tùy thuộc vào loại của hai toán hạng và vì Python được gõ động nên có thể khó xác định các loại toán hạng có thể có.
(Cuộc tranh cãi xoay quanh việc liệu đây có phải là một lỗi thiết kế của really hay không và liệu có đáng để phá mã hiện có để sửa lỗi này hay không. Nó đã gây ra các cuộc thảo luận bất tận về python-dev và vào tháng 7 năm 2001 đã nổ ra một cơn bão các bài đăng mang tính châm biếm gay gắt trên comp.lang.python. Tôi sẽ không tranh luận cho bên nào ở đây và sẽ tập trung vào việc mô tả những gì được triển khai trong 2.2. Đọc PEP 238 để biết tóm tắt các lập luận và lập luận phản biện.)
Vì thay đổi này có thể phá vỡ mã nên nó được áp dụng dần dần. Python 2.2 bắt đầu quá trình chuyển đổi, nhưng quá trình chuyển đổi sẽ không hoàn tất cho đến Python 3.0.
Đầu tiên, tôi sẽ mượn một số thuật ngữ từ PEP 238. "Chia thực sự" là phép chia mà hầu hết những người không phải lập trình viên đều quen thuộc: 3/2 là 1,5, 1/4 là 0,25, v.v. "Chia tầng" là điều mà toán tử / của Python hiện thực hiện khi cho các toán hạng số nguyên; kết quả là giá trị sàn được trả về bằng phép chia thực. "Phân chia cổ điển" là hành vi hỗn hợp hiện tại của /; nó trả về kết quả của phép chia sàn khi toán hạng là số nguyên và trả về kết quả của phép chia thực khi một trong các toán hạng là số dấu phẩy động.
Dưới đây là những thay đổi 2.2 giới thiệu:
Toán tử mới,
//, là toán tử phân chia tầng. (Đúng, chúng tôi biết nó trông giống biểu tượng nhận xét của C++.)//always thực hiện phép chia tầng bất kể loại toán hạng của nó là gì, vì vậy1 // 2là 0 và1.0 // 2.0cũng là 0,0.//luôn có sẵn trong Python 2.2; bạn không cần kích hoạt nó bằng câu lệnh__future__.Bằng cách đưa
from __future__ import divisionvào mô-đun, toán tử/sẽ được thay đổi để trả về kết quả của phép chia thực, vì vậy1/2là 0,5. Nếu không có câu lệnh__future__,/vẫn có nghĩa là phép chia cổ điển. Ý nghĩa mặc định của/sẽ không thay đổi cho đến khi Python 3.0.Các lớp có thể định nghĩa các phương thức được gọi là
__truediv__()và__floordiv__()để làm quá tải hai toán tử chia. Ở cấp độ C, cũng có các vị trí trong cấu trúcPyNumberMethodsđể các loại tiện ích mở rộng có thể xác định hai toán tử.Python 2.2 hỗ trợ một số đối số dòng lệnh để kiểm tra xem mã có hoạt động với ngữ nghĩa phân chia đã thay đổi hay không. Chạy python với
-Q warnsẽ gây ra cảnh báo mỗi khi áp dụng phép chia cho hai số nguyên. Bạn có thể sử dụng tính năng này để tìm mã bị ảnh hưởng bởi thay đổi và khắc phục mã đó. Theo mặc định, Python 2.2 sẽ chỉ thực hiện phép chia cổ điển mà không có cảnh báo; cảnh báo sẽ được bật theo mặc định trong Python 2.3.
Xem thêm
- PEP 238 - Thay đổi toán tử chia
Viết bởi Moshe Zadka và Guido van Rossum. Được thực hiện bởi Guido van Rossum..
Thay đổi Unicode¶
Hỗ trợ Unicode của Python đã được cải thiện một chút trong phiên bản 2.2. Chuỗi Unicode thường được lưu trữ dưới dạng UCS-2, dưới dạng số nguyên không dấu 16 bit. Python 2.2 cũng có thể được biên dịch để sử dụng UCS-4, số nguyên không dấu 32 bit, làm mã hóa nội bộ bằng cách cung cấp --enable-unicode=ucs4 cho tập lệnh cấu hình. (Cũng có thể chỉ định --disable-unicode để tắt hoàn toàn hỗ trợ Unicode.)
Khi được xây dựng để sử dụng UCS-4 ("Python rộng"), trình thông dịch có thể xử lý nguyên bản các ký tự Unicode từ U+000000 đến U+110000, do đó phạm vi giá trị pháp lý cho hàm unichr() được mở rộng tương ứng. Sử dụng trình thông dịch được biên dịch để sử dụng UCS-2 ("Python hẹp"), các giá trị lớn hơn 65535 vẫn sẽ khiến unichr() đưa ra ngoại lệ ValueError. Tất cả điều này được mô tả trong PEP 261, "Hỗ trợ các ký tự Unicode 'rộng'"; tham khảo ý kiến nó để biết thêm chi tiết.
Một thay đổi khác dễ giải thích hơn. Kể từ khi được giới thiệu, chuỗi Unicode đã hỗ trợ phương thức encode() để chuyển đổi chuỗi thành mã hóa đã chọn, chẳng hạn như UTF-8 hoặc Latin-1. Phương thức decode([*encoding*]) đối xứng đã được thêm vào chuỗi 8 bit (mặc dù không phải chuỗi Unicode) trong 2.2. decode() giả định rằng chuỗi đó nằm trong mã hóa được chỉ định và giải mã nó, trả về bất cứ thứ gì được codec trả về.
Sử dụng tính năng mới này, codec đã được thêm vào cho các tác vụ không liên quan trực tiếp đến Unicode. Ví dụ: codec đã được thêm vào để mã hóa uu, mã hóa base64 của MIME và nén bằng mô-đun zlib:
>>> s = """Đây là một đoạn dài dòng dư thừa, quá dài dòng,
... và văn bản lặp đi lặp lại.
... """
>>> data = s.encode('zlib')
>>> dữ liệu
'x\x9c\r\xc9\xc1\r\x80 \x10\x04\xc0?Ul...'
>>> data.decode('zlib')
'Đây là một đoạn văn bản dài dòng, thừa thãi, quá dài dòng và lặp đi lặp lại.\n'
>>> in s.encode('uu')
bắt đầu 666 <dữ liệu>
M2&5R92!I<R!A(&QE;F=T:'D@<&EE8V4@;V8@<F5D=6YD86YT+"!O=F5R;'D@
>=F5R8F]S92P*86YD(')E<&5T:71I=F4@=&5X="X*
kết thúc
>>> "sheesh".encode('rot-13')
'furrfu'
Để chuyển đổi một thể hiện của lớp thành Unicode, phương thức __unicode__() có thể được định nghĩa bởi một lớp, tương tự như __str__().
encode(), decode() và __unicode__() được triển khai bởi Marc-André Lemburg. Những thay đổi nhằm hỗ trợ việc sử dụng UCS-4 nội bộ được thực hiện bởi Fredrik Lundh và Martin von Löwis.
Xem thêm
- PEP 261 - Hỗ trợ các ký tự Unicode 'rộng'
Viết bởi Paul Prescod.
PEP 227: Phạm vi lồng nhau¶
Trong Python 2.1, các phạm vi lồng nhau tĩnh đã được thêm dưới dạng tính năng tùy chọn, được kích hoạt bằng lệnh from __future__ import nested_scopes. Trong 2.2 phạm vi lồng nhau không còn cần phải được kích hoạt đặc biệt nữa và hiện luôn có sẵn. Phần còn lại của phần này là bản sao mô tả về các phạm vi lồng nhau từ tài liệu "Có gì mới trong Python 2.1" của tôi; nếu bạn đọc nó khi 2.1 xuất hiện, bạn có thể bỏ qua phần còn lại của phần này.
Thay đổi lớn nhất được giới thiệu trong Python 2.1 và được hoàn thiện trong 2.2 là các quy tắc phạm vi của Python. Trong Python 2.0, tại bất kỳ thời điểm nào cũng có tối đa ba không gian tên được sử dụng để tra cứu tên biến: cục bộ, cấp độ mô-đun và không gian tên tích hợp. Điều này thường khiến mọi người ngạc nhiên vì nó không phù hợp với mong đợi trực quan của họ. Ví dụ: định nghĩa hàm đệ quy lồng nhau không hoạt động:
chắc chắn f():
...
def g(giá trị):
...
trả về g(giá trị-1) + 1
...
Hàm g() sẽ luôn đưa ra một ngoại lệ NameError, vì ràng buộc của tên g không nằm trong không gian tên cục bộ hoặc trong không gian tên cấp mô-đun. Đây không phải là vấn đề lớn trong thực tế (bạn có thường xuyên xác định đệ quy các hàm bên trong như thế này không?), nhưng điều này cũng khiến việc sử dụng biểu thức lambda trở nên vụng về hơn và đây là một vấn đề trong thực tế. Trong mã sử dụng lambda, bạn thường có thể thấy các biến cục bộ đang được sao chép bằng cách chuyển chúng làm giá trị mặc định của đối số.
def find(bản thân, tên):
"Trả về danh sách của bất kỳ mục nào bằng 'tên'"
L = filter(lambda x, name=name: x == name,
self.list_attribute)
trả lại L
Kết quả là khả năng đọc mã Python được viết theo phong cách chức năng mạnh mẽ bị ảnh hưởng rất nhiều.
Thay đổi quan trọng nhất đối với Python 2.2 là phạm vi tĩnh đã được thêm vào ngôn ngữ để khắc phục sự cố này. Hiệu ứng đầu tiên là đối số mặc định name=name hiện không còn cần thiết trong ví dụ trên. Nói một cách đơn giản, khi một tên biến nhất định không được gán giá trị trong một hàm (bằng một phép gán hoặc các câu lệnh def, class hoặc import), các tham chiếu đến biến đó sẽ được tra cứu trong không gian tên cục bộ của phạm vi kèm theo. Bạn có thể tìm thấy giải thích chi tiết hơn về các quy tắc và phân tích cách triển khai trong PEP.
Thay đổi này có thể gây ra một số vấn đề về tính tương thích đối với mã trong đó tên biến giống nhau được sử dụng cả ở cấp mô-đun và dưới dạng biến cục bộ trong hàm chứa các định nghĩa hàm khác. Tuy nhiên, điều này có vẻ khó xảy ra vì mã như vậy sẽ khá khó hiểu ngay từ đầu.
Một tác dụng phụ của sự thay đổi này là các câu lệnh from module import * và exec đã bị coi là bất hợp pháp trong phạm vi chức năng trong một số điều kiện nhất định. Hướng dẫn tham khảo Python đã nói rõ rằng from module import * chỉ hợp pháp ở cấp cao nhất của mô-đun, nhưng trình thông dịch CPython chưa bao giờ thực thi điều này trước đây. Là một phần của việc triển khai các phạm vi lồng nhau, trình biên dịch biến nguồn Python thành mã byte phải tạo mã khác nhau để truy cập các biến trong phạm vi chứa. from module import * và exec khiến trình biên dịch không thể tìm ra điều này vì chúng thêm tên vào không gian tên cục bộ mà không thể biết được tại thời điểm biên dịch. Do đó, nếu một hàm chứa các định nghĩa hàm hoặc biểu thức lambda với các biến tự do, trình biên dịch sẽ gắn cờ hàm này bằng cách đưa ra một ngoại lệ SyntaxError.
Để làm cho lời giải thích trước đó rõ ràng hơn một chút, đây là một ví dụ:
x = 1
chắc chắn f():
# The dòng tiếp theo là lỗi cú pháp
thực thi 'x=2'
chắc chắn g():
trả lại x
Dòng 4 chứa câu lệnh exec là một lỗi cú pháp, vì exec sẽ xác định một biến cục bộ mới có tên là x mà giá trị của nó phải được truy cập bởi g().
Đây không phải là một hạn chế lớn, vì exec hiếm khi được sử dụng trong hầu hết các mã Python (và khi nó được sử dụng, đó thường là dấu hiệu của một thiết kế kém).
Xem thêm
- PEP 227 - Phạm vi lồng nhau tĩnh
Được viết và thực hiện bởi Jeremy Hylton.
Các mô-đun mới và cải tiến¶
Mô-đun
xmlrpclibđược Fredrik Lundh đóng góp vào thư viện tiêu chuẩn, cung cấp hỗ trợ cho việc viết ứng dụng khách XML-RPC. XML-RPC là một giao thức gọi thủ tục từ xa đơn giản được xây dựng dựa trên HTTP và XML. Ví dụ: đoạn mã sau truy xuất danh sách các kênh RSS từ Mạng O'Reilly và sau đó liệt kê các tiêu đề gần đây cho một kênh:nhập xmlrpclib s = xmlrpclib.Server( 'http://www.oreillynet.com/meerkat/xml-rpc/server.php') kênh = s.meerkat.getChannels() # channels là danh sách các từ điển, như thế này: # [{'id': 4, 'title': 'Tin tức hàng ngày về thịt tươi'} # {'id': 190, 'title': '32Bits Online'}, # {'id': 4549, 'title': '3DGamers'}, ... ] # Get các mục cho một kênh items = s.meerkat.getItems( {'channel': 4} ) # 'items' là một danh sách từ điển khác, như thế này: # [{'liên kết': 'http://freshmeat.net/releases/52719/', # 'description': 'Một tiện ích chuyển đổi HTML thành XSL FO.', # 'tiêu đề': 'html2fo 0.3 (Mặc định)'}, ... ]
Mô-đun
SimpleXMLRPCServergiúp dễ dàng tạo các máy chủ XML-RPC đơn giản. Xem http://xmlrpc.scripting.com/ để biết thêm thông tin về XML-RPC.Mô-đun
hmacmới triển khai thuật toán HMAC được mô tả bởi RFC 2104. (Được đóng góp bởi Gerhard Häring.)Một số hàm ban đầu trả về các bộ dữ liệu dài giờ đây trả về các chuỗi giả vẫn hoạt động giống như các bộ dữ liệu nhưng cũng có các thuộc tính ghi nhớ như
memberst_mtimehoặctm_year. Các chức năng nâng cao bao gồmstat(),fstat(),statvfs()vàfstatvfs()trong mô-đunosvàlocaltime(),gmtime()vàstrptime()trong mô-đuntime.Ví dụ: để có được kích thước của tệp bằng cách sử dụng các bộ dữ liệu cũ, cuối cùng bạn sẽ viết một cái gì đó như
file_size = os.stat(filename)[stat.ST_SIZE], nhưng bây giờ điều này có thể được viết rõ ràng hơn làfile_size = os.stat(filename).st_size.Bản vá ban đầu cho tính năng này được đóng góp bởi Nick Mathewson.
Trình lược tả Python đã được làm lại một cách rộng rãi và nhiều lỗi trong kết quả đầu ra của nó đã được sửa chữa. (Được đóng góp bởi Fred L. Drake, Jr. và Tim Peters.)
Mô-đun
socketcó thể được biên dịch để hỗ trợ IPv6; chỉ định tùy chọn--enable-ipv6cho tập lệnh cấu hình của Python. (Được đóng góp bởi Jun-ichiro "itojun" Hagino.)Hai ký tự định dạng mới đã được thêm vào mô-đun
structcho số nguyên 64-bit trên nền tảng hỗ trợ loại C long long.qdành cho số nguyên 64 bit có dấu vàQdành cho số nguyên không dấu. Giá trị được trả về ở dạng số nguyên dài của Python. (Được đóng góp bởi Tim Peters.)Trong chế độ tương tác của trình thông dịch, có một hàm
help()tích hợp mới sử dụng mô-đunpydocđược giới thiệu trong Python 2.1 để cung cấp trợ giúp tương tác.help(object)hiển thị mọi văn bản trợ giúp có sẵn về object.help()không có đối số sẽ đưa bạn vào một tiện ích trợ giúp trực tuyến, nơi bạn có thể nhập tên của các hàm, lớp hoặc mô-đun để đọc văn bản trợ giúp của chúng. (Được đóng góp bởi Guido van Rossum, sử dụng mô-đunpydoccủa Ka-Ping Yee.)Nhiều bản sửa lỗi và cải tiến hiệu suất đã được thực hiện cho công cụ SRE nằm bên dưới mô-đun
re. Ví dụ: các hàmre.sub()vàre.split()đã được viết lại bằng C. Một bản vá đóng góp khác tăng tốc các phạm vi ký tự Unicode nhất định theo hệ số hai và một phương thứcfinditer()mới trả về một trình vòng lặp trên tất cả các kết quả khớp không chồng chéo trong một chuỗi nhất định. (SRE được duy trì bởi Fredrik Lundh. Bản vá BIGCHARSET được đóng góp bởi Martin von Löwis.)Mô-đun
smtplibhiện hỗ trợ RFC 2487, "Bảo mật SMTP trên TLS", vì vậy giờ đây có thể mã hóa lưu lượng SMTP giữa chương trình Python và tác nhân vận chuyển thư được chuyển một tin nhắn.smtplibcũng hỗ trợ xác thực SMTP. (Được đóng góp bởi Gerhard Häring.)Mô-đun
imaplib, do Piers Lauder duy trì, có hỗ trợ cho một số tiện ích mở rộng mới: tiện ích mở rộng NAMESPACE được xác định trong RFC 2342, SORT, GETACL và SETACL. (Được đóng góp bởi Anthony Baxter và Michel Pelletier.)Việc phân tích cú pháp địa chỉ email của mô-đun
rfc822hiện đã tuân thủ RFC 2822, một bản cập nhật cho RFC 822. (Tên mô-đun là not sẽ được đổi thànhrfc2822.) Một gói mới,email, cũng đã được thêm vào để phân tích cú pháp và tạo thư e-mail. (Được đóng góp bởi Barry Warsaw và phát sinh từ tác phẩm của ông về Người đưa thư.)Mô-đun
difflibhiện chứa một lớpDiffermới để tạo danh sách thay đổi mà con người có thể đọc được ("delta") giữa hai chuỗi dòng văn bản. Ngoài ra còn có hai hàm tạo,ndiff()vàrestore(), lần lượt trả về một delta từ hai chuỗi hoặc một trong các chuỗi ban đầu từ một delta. (Công việc của Grunt do David Goodger đóng góp, từ mã ndiff.py của Tim Peters, người sau đó đã thực hiện quá trình tạo.)Các hằng số mới
ascii_letters,ascii_lowercasevàascii_uppercaseđã được thêm vào mô-đunstring. Có một số mô-đun trong thư viện tiêu chuẩn sử dụngstring.lettersđể chỉ các phạm vi A-Za-z, nhưng giả định đó không chính xác khi sử dụng ngôn ngữ, vìstring.lettersthay đổi tùy thuộc vào tập hợp các ký tự pháp lý được xác định bởi ngôn ngữ hiện tại. Các mô-đun lỗi đều đã được sửa để sử dụngascii_lettersthay thế. (Báo cáo bởi một người không rõ danh tính; sửa bởi Fred L. Drake, Jr.)Mô-đun
mimetypesgiờ đây giúp sử dụng cơ sở dữ liệu loại MIME thay thế dễ dàng hơn bằng cách bổ sung lớpMimeTypes, lớp này nhận danh sách tên tệp sẽ được phân tích cú pháp. (Đóng góp bởi Fred L. Drake, Jr.)Một lớp
Timerđã được thêm vào mô-đunthreadingcho phép lên lịch cho một hoạt động diễn ra vào một thời điểm nào đó trong tương lai. (Được đóng góp bởi Itamar Shtull-Trauring.)
Thay đổi và sửa lỗi thông dịch viên¶
Một số thay đổi chỉ ảnh hưởng đến những người làm việc với trình thông dịch Python ở cấp độ C vì họ đang viết các mô-đun mở rộng Python, nhúng trình thông dịch hoặc chỉ hack vào chính trình thông dịch. Nếu bạn chỉ viết mã Python thì không có thay đổi nào được mô tả ở đây sẽ ảnh hưởng nhiều đến bạn.
Các chức năng lập hồ sơ và theo dõi hiện có thể được triển khai trong C, có thể hoạt động ở tốc độ cao hơn nhiều so với các hàm dựa trên Python và sẽ giảm chi phí cho việc lập hồ sơ và theo dõi. Điều này sẽ được các tác giả của môi trường phát triển cho Python quan tâm. Hai hàm C mới đã được thêm vào API,
PyEval_SetProfile()vàPyEval_SetTrace()của Python. Các chức năngsys.setprofile()vàsys.settrace()hiện tại vẫn tồn tại và chỉ được thay đổi để sử dụng giao diện cấp C mới. (Đóng góp bởi Fred L. Drake, Jr.)Một API cấp thấp khác, chủ yếu được những người triển khai trình gỡ lỗi và công cụ phát triển Python quan tâm, đã được thêm vào.
PyInterpreterState_Head()vàPyInterpreterState_Next()cho phép người gọi duyệt qua tất cả các đối tượng thông dịch hiện có;PyInterpreterState_ThreadHead()vàPyThreadState_Next()cho phép lặp qua tất cả các trạng thái luồng cho một trình thông dịch nhất định. (Được đóng góp bởi David Beazley.)Giao diện cấp C cho trình thu gom rác đã được thay đổi để giúp viết các loại tiện ích mở rộng hỗ trợ thu thập rác dễ dàng hơn và gỡ lỗi các trường hợp sử dụng sai chức năng. Các hàm khác nhau có ngữ nghĩa hơi khác nhau, vì vậy một loạt hàm đã phải được đổi tên. Các tiện ích mở rộng sử dụng API cũ vẫn sẽ biên dịch nhưng not sẽ tham gia vào quá trình thu gom rác, vì vậy việc cập nhật chúng lên phiên bản 2.2 nên được coi là có mức độ ưu tiên khá cao.
Để nâng cấp mô-đun mở rộng lên API mới, hãy thực hiện các bước sau:
Đổi tên
Py_TPFLAGS_GCthànhPy_TPFLAGS_HAVE_GC.- Sử dụng
PyObject_GC_New()hoặcPyObject_GC_NewVar()để phân bổ các đối tượng và
PyObject_GC_Del()để phân bổ chúng.
- Sử dụng
Đổi tên
PyObject_GC_Init()thànhPyObject_GC_Track()vàPyObject_GC_Fini()thànhPyObject_GC_UnTrack().Xóa
PyGC_HEAD_SIZEkhỏi tính toán kích thước đối tượng.Xóa cuộc gọi đến
PyObject_AS_GC()vàPyObject_FROM_GC().Một chuỗi định dạng
etmới đã được thêm vàoPyArg_ParseTuple();etnhận cả tham số và tên mã hóa, đồng thời chuyển đổi tham số đó thành mã hóa đã cho nếu tham số đó trở thành chuỗi Unicode hoặc để nguyên tham số đó nếu đó là chuỗi 8 bit, giả sử tham số đó đã ở dạng mã hóa mong muốn. Điều này khác với ký tự định dạnges, giả định rằng các chuỗi 8 bit nằm trong mã hóa ASCII mặc định của Python và chuyển đổi chúng sang mã hóa mới được chỉ định. (Được đóng góp bởi M.-A. Lemburg và được sử dụng để hỗ trợ MBCS trên Windows được mô tả trong phần sau.)Một chức năng phân tích đối số khác,
PyArg_UnpackTuple(), đã được thêm vào để đơn giản hơn và có lẽ nhanh hơn. Thay vì chỉ định một chuỗi định dạng, trình gọi chỉ cần đưa ra số lượng đối số tối thiểu và tối đa dự kiến cũng như một tập hợp các con trỏ tới các biến PyObject* sẽ được điền vào các giá trị đối số.Hai cờ mới
METH_NOARGSvàMETH_Ocó sẵn trong bảng định nghĩa phương thức để đơn giản hóa việc triển khai các phương thức không có đối số hoặc một đối số chưa được gõ. Việc gọi các phương thức như vậy hiệu quả hơn việc gọi một phương thức tương ứng sử dụngMETH_VARARGS. Ngoài ra, phong cách viết phương thức C cũ củaMETH_OLDARGShiện không còn được dùng nữa.Hai hàm bao bọc mới,
PyOS_snprintf()vàPyOS_vsnprintf()đã được thêm vào để cung cấp khả năng triển khai đa nền tảng cho các API libsnprintf()vàvsnprintf()C tương đối mới. Ngược lại với các hàmsprintf()vàvsprintf()tiêu chuẩn, các phiên bản Python kiểm tra giới hạn của bộ đệm được sử dụng để bảo vệ khỏi lỗi tràn bộ đệm. (Được đóng góp bởi M.-A. Lemburg.)Hàm
_PyTuple_Resize()đã mất một tham số không được sử dụng nên hiện tại hàm này nhận 2 tham số thay vì 3. Đối số thứ ba chưa bao giờ được sử dụng và có thể bị loại bỏ khi chuyển mã từ các phiên bản trước sang Python 2.2.
Những thay đổi và sửa lỗi khác¶
Như thường lệ, có một loạt các cải tiến và sửa lỗi khác nằm rải rác trong cây nguồn. Tìm kiếm trong nhật ký thay đổi CVS cho thấy đã có 527 bản vá được áp dụng và 683 lỗi đã được sửa giữa Python 2.1 và 2.2; 2.2.1 áp dụng 139 bản vá và sửa 143 lỗi; 2.2.2 áp dụng 106 bản vá và sửa 82 lỗi. Những con số này có thể được đánh giá thấp.
Một số thay đổi đáng chú ý hơn là:
Mã cho cổng MacOS dành cho Python, do Jack Jansen duy trì, hiện được lưu giữ trong cây Python CVS chính và nhiều thay đổi đã được thực hiện để hỗ trợ MacOS X.
Thay đổi đáng kể nhất là khả năng xây dựng Python dưới dạng khung, được kích hoạt bằng cách cung cấp tùy chọn
--enable-frameworkcho tập lệnh cấu hình khi biên dịch Python. Theo Jack Jansen, "Điều này cài đặt một bản cài đặt Python độc lập cộng với "keo" khung OS X vào/Library/Frameworks/Python.framework(hoặc một vị trí khác được lựa chọn). Hiện tại, có rất ít lợi ích được bổ sung ngay lập tức cho việc này (thực ra, có một nhược điểm là bạn phải thay đổi PATH của mình để có thể tìm thấy Python), nhưng nó là cơ sở để tạo một ứng dụng Python toàn diện, chuyển MacPython IDE, có thể sử dụng Python làm tiêu chuẩn ngôn ngữ kịch bản OSA và nhiều hơn nữa."Hầu hết các mô-đun hộp công cụ MacPython, giao tiếp với các API MacOS như windowing, QuickTime, scripting, v.v. đã được chuyển sang OS X, nhưng chúng không được nhận xét trong
setup.py. Những người muốn thử nghiệm các mô-đun này có thể bỏ ghi chú chúng theo cách thủ công.Đối số từ khóa được chuyển đến các hàm tích hợp không nhận chúng hiện gây ra ngoại lệ
TypeError, với thông báo "function không nhận đối số từ khóa".Các tham chiếu yếu, được thêm vào Python 2.1 dưới dạng mô-đun mở rộng, hiện là một phần cốt lõi vì chúng được sử dụng để triển khai các lớp kiểu mới. Do đó, ngoại lệ
ReferenceErrorđã được chuyển từ mô-đunweakrefđể trở thành một ngoại lệ tích hợp.Một tập lệnh mới,
Tools/scripts/cleanfuture.pycủa Tim Peters, tự động xóa các câu lệnh__future__lỗi thời khỏi mã nguồn Python.Một đối số flags bổ sung đã được thêm vào hàm tích hợp
compile(), do đó, hành vi của các câu lệnh__future__hiện có thể được quan sát chính xác trong các shell mô phỏng, chẳng hạn như các shell được trình bày bởi IDLE và các môi trường phát triển khác. Điều này được mô tả trong PEP 264. (Được đóng góp bởi Michael Hudson.)Giấy phép mới được giới thiệu với Python 1.6 không tương thích với GPL. Điều này được khắc phục bằng một số thay đổi văn bản nhỏ đối với giấy phép 2.2, vì vậy giờ đây việc nhúng lại Python vào chương trình GPLed là hợp pháp. Lưu ý rằng bản thân Python không được GPLed mà thay vào đó là theo một giấy phép về cơ bản tương đương với giấy phép BSD, giống như mọi khi. Những thay đổi về giấy phép cũng được áp dụng cho các bản phát hành Python 2.0.1 và 2.1.1.
Khi được cung cấp tên tệp Unicode trên Windows, giờ đây Python sẽ chuyển đổi nó thành chuỗi được mã hóa MBCS, như được sử dụng bởi API tệp của Microsoft. Vì MBCS được các API tệp sử dụng rõ ràng nên việc Python chọn ASCII làm mã hóa mặc định hóa ra lại gây khó chịu. Trên Unix, bộ ký tự của miền địa phương được sử dụng nếu
locale.nl_langinfo(CODESET)có sẵn. (Hỗ trợ Windows được đóng góp bởi Mark Hammond với sự hỗ trợ từ Marc-André Lemburg. Hỗ trợ Unix được thêm bởi Martin von Löwis.)Hỗ trợ tệp lớn hiện đã được bật trên Windows. (Được đóng góp bởi Tim Peters.)
Tập lệnh
Tools/scripts/ftpmirror.pyhiện phân tích tệp.netrc, nếu bạn có. (Được đóng góp bởi Mike Romberg.)Một số tính năng của đối tượng được hàm
xrange()trả về hiện không được dùng nữa và kích hoạt cảnh báo khi chúng được truy cập; chúng sẽ biến mất trong Python 2.3. Các đối tượngxrangeđã cố gắng giả vờ chúng là các loại chuỗi đầy đủ bằng cách hỗ trợ cắt, nhân chuỗi và toán tửin, nhưng các tính năng này hiếm khi được sử dụng và do đó có lỗi. Phương thứctolist()và các thuộc tínhstart,stopvàstepcũng không được dùng nữa. Ở cấp độ C, đối số thứ tư của hàmPyRange_New(),repeat, cũng không được dùng nữa.Có một loạt bản vá cho quá trình triển khai từ điển, chủ yếu là để khắc phục các kết xuất lõi tiềm ẩn nếu từ điển chứa các đối tượng lén lút thay đổi giá trị băm của chúng hoặc làm biến đổi từ điển mà chúng chứa trong đó. Trong một thời gian, python-dev rơi vào nhịp điệu nhẹ nhàng khi Michael Hudson tìm ra một trường hợp loại bỏ lõi, Tim Peters sửa lỗi, Michael tìm một trường hợp khác, và mọi chuyện cứ thế diễn ra vòng tròn.
Trên Windows, giờ đây Python có thể được biên dịch bằng Borland C nhờ một số bản vá do Stephen Hansen đóng góp, mặc dù kết quả vẫn chưa hoạt động đầy đủ. (Nhưng tiến trình is này...)
Một cải tiến khác của Windows: Wise Solutions đã hào phóng cung cấp cho PythonLabs quyền sử dụng hệ thống InstallerMaster 8.1 của họ. Các trình cài đặt PythonLabs Windows trước đó đã sử dụng Wise 5.0a, phiên bản này đã bắt đầu có dấu hiệu cũ kỹ. (Được đóng gói bởi Tim Peters.)
Các tệp kết thúc bằng
.pywhiện có thể được nhập vào Windows..pywlà một thứ chỉ dành cho Windows, được sử dụng để chỉ ra rằng tập lệnh cần được chạy bằng PYTHONW.EXE thay vì PYTHON.EXE để ngăn bảng điều khiển DOS bật lên để hiển thị đầu ra. Bản vá này cho phép nhập các tập lệnh như vậy, trong trường hợp chúng cũng có thể sử dụng được dưới dạng mô-đun. (Được thực hiện bởi David Bolen.)Trên các nền tảng mà Python sử dụng hàm C
dlopen()để tải các mô-đun mở rộng, giờ đây có thể đặt cờ đượcdlopen()sử dụng bằng cách sử dụng các hàmsys.getdlopenflags()vàsys.setdlopenflags(). (Được đóng góp bởi Bram Stolk.)Hàm tích hợp
pow()không còn hỗ trợ 3 đối số khi cung cấp số dấu phẩy động.pow(x, y, z)trả về(x**y) % z, nhưng điều này không bao giờ hữu ích đối với các số có dấu phẩy động và kết quả cuối cùng thay đổi khó lường tùy thuộc vào nền tảng. Lệnh gọi nhưpow(2.0, 8.0, 7.0)giờ đây sẽ đưa ra ngoại lệTypeError.
Lời cảm ơn¶
Tác giả xin cảm ơn những người sau đây đã đưa ra gợi ý, chỉnh sửa và hỗ trợ các bản thảo khác nhau của bài viết này: Fred Bremmer, Keith Briggs, Andrew Dalke, Fred L. Drake, Jr., Carel Fellinger, David Goodger, Mark Hammond, Stephen Hansen, Michael Hudson, Jack Jansen, Marc-André Lemburg, Martin von Löwis, Fredrik Lundh, Michael McLay, Nick Mathewson, Paul Moore, Gustavo Niemeyer, Don O'Donnell, Joonas Paalasma, Tim Peters, Jens Quade, Tom Reinhardt, Neil Schenauer, Guido van Rossum, Greg Ward, Edward Welbourne.