2014-10-28 14 views
6

Tôi đã đọc, tập tin đó mở như thế này được đóng tự động khi rời với khối:với và đóng cửa các tập tin bằng Python

with open("x.txt") as f: 
    data = f.read() 
    do something with data 

chưa khi mở từ web, tôi cần điều này:

from contextlib import closing 
from urllib.request import urlopen 

with closing(urlopen('http://www.python.org')) as page: 
    for line in page: 
     print(line) 

tại sao và sự khác biệt là gì? (Tôi đang sử dụng Python3)

+0

Tôi sắp đề nghị ai đó nên gửi một lỗi tài liệu trên 'contextlib' cho điều này, và nếu đó không phải là bạn nên tin bạn ... nhưng trước khi tôi có thể kết thúc, Martijn đã đệ trình lỗi, với liên kết quay lại đây. :) – abarnert

Trả lời

6

Các chi tiết được một chút kỹ thuật, vì vậy chúng ta hãy bắt đầu với phiên bản đơn giản:

Một số loại biết làm thế nào để được sử dụng trong một tuyên bố with. Đối tượng tệp, giống như những gì bạn nhận được từ open, là ví dụ về loại như vậy. Khi nó quay ra, các đối tượng mà bạn lấy lại từ urllib.request.urlopen, là cũng là ví dụ về loại đó, vì vậy ví dụ thứ hai của bạn có thể được viết giống như cách đầu tiên.

Nhưng một số loại không biết cách sử dụng trong tuyên bố with.Chức năng closing được thiết kế để bọc các loại đó - miễn là chúng có phương thức close, nó sẽ gọi phương thức close của chúng tôi khi bạn thoát khỏi câu lệnh with.

Tất nhiên một số loại không biết cách sử dụng trong tuyên bố with và cũng không thể sử dụng với closing vì phương pháp dọn dẹp của chúng không được đặt tên là close (hoặc vì làm sạch chúng phức tạp hơn đóng chúng). Trong trường hợp đó, bạn cần phải viết một trình quản lý ngữ cảnh tùy chỉnh. Nhưng ngay cả điều đó cũng không quá khó.


Về kỹ thuật:

Một tuyên bố with đòi hỏi một context manager, một đối tượng với __enter____exit__ phương pháp. Nó sẽ gọi phương thức __enter__ và cung cấp cho bạn giá trị trả về theo phương thức đó trong mệnh đề as và sau đó nó sẽ gọi phương thức __exit__ ở cuối câu lệnh with.

Đối tượng tệp được kế thừa từ io.IOBase, là trình quản lý ngữ cảnh có phương thức __enter__ tự trả về và có __exit__ cuộc gọi self.close().

Đối tượng được trả về bởi urlopen là (giả sử một http hoặc https URL) một HTTPResponse, trong đó, như các tài liệu nói, "có thể được sử dụng với một tuyên bố with".

Các closing chức năng:

Return một người quản lý bối cảnh đó đóng cửa điều sau khi hoàn thành khối. Đây là về cơ bản tương đương với:

@contextmanager 
def closing(thing): 
    try: 
     yield thing 
    finally: 
     thing.close() 

Nó không phải luôn luôn 100% rõ ràng trong các tài liệu mà loại là các nhà quản lý bối cảnh và những loại thì không. Đặc biệt vì đã có một ổ đĩa lớn kể từ 3.1 để làm mọi thứ có thể là một trình quản lý ngữ cảnh thành một (và, cho vấn đề đó, để làm mọi thứ giống như tập tin thành IOBase thực tế), nhưng nó vẫn không 100% hoàn thành là 3,4.

Bạn luôn có thể dùng thử và xem. Nếu bạn nhận được AttributeError: __exit__, thì đối tượng không thể sử dụng làm người quản lý ngữ cảnh. Nếu bạn nghĩ rằng nó nên được, nộp một lỗi cho thấy sự thay đổi. Nếu bạn không gặp lỗi đó, nhưng các tài liệu không đề cập đến nó là hợp pháp, hãy gửi một lỗi đề xuất tài liệu được cập nhật.

+0

cảm ơn lời giải thích chi tiết! – nekomimi

7

Bạn không. urlopen('http://www.python.org') lợi nhuận một người quản lý bối cảnh quá:

with urlopen('http://www.python.org') as page: 

Đây là tài liệu trên urllib.request.urlopen() page:

Đối với ftp, tập tin, và các url dữ liệu và yêu cầu explicity xử lý bởi di sản URLopenerFancyURLopener lớp, hàm này trả về một urllib.response.addinfourl đối tượng có thể hoạt động như trình quản lý ngữ cảnh [...].

Mỏ nhấn mạnh. Đối với phản ứng HTTP, http.client.HTTPResponse() object được trả lại, đó cũng là một người quản lý bối cảnh:

Câu trả lời là một đối tượng iterable và có thể được sử dụng trong một tuyên bố với.

Các Examples section cũng sử dụng các đối tượng như một người quản lý bối cảnh:

Là website python.org sử dụng utf-8 mã hóa theo quy định tại nó thẻ meta, chúng tôi sẽ sử dụng cùng để giải mã các byte vật.

>>> with urllib.request.urlopen('http://www.python.org/') as f: 
...  print(f.read(100).decode('utf-8')) 
... 
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
"http://www.w3.org/TR/xhtml1/DTD/xhtm 

Đối tượng được trả về bởi open()context managers quá; họ triển khai các phương pháp đặc biệt object.__enter__()object.__exit__().

contextlib.closing() documentation sử dụng ví dụ có urlopen() đã lỗi thời; trong Python 2, tiền thân của urllib.request.urlopen() không tạo ra trình quản lý ngữ cảnh và bạn cần sử dụng công cụ đó để tự động đóng kết nối bằng trình quản lý ngữ cảnh. Điều này đã được khắc phục với các sự cố 541812365, nhưng ví dụ đó không được cập nhật. Tôi đã tạo issue 22755 yêu cầu một ví dụ khác.

+0

Tôi vừa mới viết :-). điều quan trọng là các tài liệu nói rằng nó trả về một đối tượng "giống như tệp". Nếu nó không thể được sử dụng như một người quản lý ngữ cảnh, nó không thực sự giống như tập tin. – mgilson

+0

nhưng tại sao ví dụ này lại nằm trong tài liệu python? https://docs.python.org/3/library/contextlib.html – nekomimi

+1

@nekomimi: một sự lưu giữ từ Python 2 có thể, trong đó đối tượng không phải là người quản lý ngữ cảnh. –

Các vấn đề liên quan