python`with .. as ..`语句和多个返回值

时间:2014-10-11 09:32:42

标签: python sockets with-statement contextmanager

我正在尝试使用python with-statement(a.k.a。一个上下文管理器)来确保server_socket.accept()创建的TCP连接套接字始终关闭。然而,显而易见的形式不起作用,因为accept()返回多个值。

对于具有多个返回值的函数,有没有办法使用with语句?

下面是一个最小的例子,我想用注释代码之类的东西代替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()

使用带注释的with-statement时的错误消息是:

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

大概这是因为元组没有__exit__所需的with属性。

1 个答案:

答案 0 :(得分:7)

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

P.S。没有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)