2014-10-11 15 views
1

我試圖使用python with-statement(a.k.a.上下文管理器)來確保由server_socket.accept()創建的TCP連接套接字始終關閉。然而,明顯的形式不起作用,因爲accept()返回多個值。python`with .. as ..`聲明和多個返回值

有沒有辦法使用帶有多個返回值的函數with-statement?

下面是一個簡單的例子,我想使用類似註釋的代碼來替換try/finally塊。

#!/usr/bin/env python3 

import socket 
from socket import socket as Socket 

with Socket(socket.AF_INET, socket.SOCK_STREAM) as server_socket: 

     server_socket.bind(('', 8011)) 
     server_socket.listen(1) 
     print("server ready") 

     while True: 

      # with server_socket.accept() as (connection_socket, _): 
      #  request = connection_socket.recv(1024).decode('ascii') 
      #  reply = request.upper() 
      #  connection_socket.send(reply.encode('ascii')) 

      try: 
       connection_socket, _ = server_socket.accept() 
       request = connection_socket.recv(1024).decode('ascii') 
       reply = request.upper() 
       connection_socket.send(reply.encode('ascii')) 

      finally: 
       connection_socket.close() 

使用時的錯誤消息與評論語句是:

Traceback (most recent call last): 
    File "./test.py", line 26, in <module> 
    with server_socket.accept() as (connection_socket, _): 
AttributeError: __exit__ 

據推測,這是因爲一個元組不具有with所需的__exit__屬性。

回答

6

socket.socket的返回值有一個內置的上下文管理器,它實現了__exit____enter__。通過accept返回的元組沒有,但有一個庫來添加:

import socket 
import contextlib 

@contextlib.contextmanager 
def accept(s): 
    c,a = s.accept() 
    print('client connected on',a) 
    yield c,a 
    print('client disconnected on',a) 
    c.close() 

with socket.socket() as s: 
    s.bind(('',8000)) 
    s.listen(1) 
    with accept(s) as (c,a): 
     while True: 
      data = c.recv(1024) 
      if not data: break 
      c.sendall(data) 

參見:

https://docs.python.org/3.3/library/contextlib.html

附:沒有contextlib

import socket 

with socket.socket() as s: 
    s.bind(('',8000)) 
    s.listen(1) 
    with s.accept()[0] as c: 
     a = c.getpeername() 
     print('client connected on',a) 
     while True: 
      data = c.recv(1024) 
      if not data: break 
      c.sendall(data) 
     print('client disconnected on',a) 
+0

難道這不能在socket類中實現嗎?是不是有一個原因? – dshepherd 2014-10-11 10:33:42

+1

它在'socket class'中實現。這就是爲什麼第一個「with」有效。 's'直接包含'__exit__'和'__enter__'方法的實現。事實證明,如果像這樣使用's.accept()[0]作爲c:',你可以直接使用'with'與's.accept()',但是你失去了客戶端地址。單獨的客戶端套接字具有上下文管理器功能,但由「accept」返回的元組沒有。 – 2014-10-11 17:44:22

+0

我沒有使用'contextlib'。請參閱編輯。 – 2014-10-11 17:53:27