Как использовать сокет в Python в качестве контекстного менеджера?

Кажется, что было бы вполне естественно сделать что-то вроде:

with socket(socket.AF_INET, socket.SOCK_DGRAM) as s:

но Python не реализует контекстный менеджер для сокета. Я могу легко использовать его в качестве контекста менеджера, и если да, то как?

3 ответов


на socket модуль довольно низком уровне, что дает почти прямой доступ к функциям библиотеки.

вы всегда можете использовать contextlib.contextmanager оформителя построить свой собственный:

import socket
from contextlib import contextmanager

@contextmanager
def socketcontext(*args, **kw):
    s = socket.socket(*args, **kw)
    try:
        yield s
    finally:
        s.close()

with socketcontext(socket.AF_INET, socket.SOCK_DGRAM) as s:

или использовать contextlib.closing() для достижения того же эффекта:

from contextlib import closing

with closing(socket.socket(socket.AF_INET, socket.SOCK_DGRAM)) as s:

но contextmanager() декоратор дает вам возможность делать другие вещи с гнездом первых.

Python 3.х делает socket() контекст менеджер, хотя в документации об этом не упоминается. Вижу socket класс в исходном коде, который добавляет __enter__ и __exit__ методы.


модуль сокета-это просто оболочка вокруг интерфейса сокета BSD. Это низкоуровневый, и на самом деле не пытается предоставить Вам удобный или простой в использовании Pythonic API. Вы можете использовать что-то более высокого уровня.

тем не менее, он фактически реализует контекстный менеджер:

>>> with socket.socket() as s:
...   print(s)
... 
<socket.socket object, fd=3, family=2, type=1, proto=0>

но вам нужно использовать Python 3.

для совместимости с Python 2 вы можете использовать contextlib.

from contextlib import closing
import socket

with closing(socket.socket()) as s:
    print s

пожалуйста, посмотрите на следующие фрагменты, для сокетов TCP и UDP

import socket
from contextlib import contextmanager


@contextmanager
def tcp_connection_to(*args, **kwargs):
    s = socket.create_connection(*args, **kwargs)
    yield s
    s.close()


@contextmanager
def udp_connection():
    s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    yield s
    s.close()

чтобы вы могли использовать их следующим образом:

MY_SERVER = ('localhost', 5000)   # Yes, we need tuple here
some_data = bytes("Hello.")

with tcp_connection_to(MY_SERVER) as conn:
    conn.send(some_data)

with udp_connection() as conn:
    conn.sendto(some_data, MY_SERVER)

Я также попытался подчеркнуть разницу в поведении и подходе к термину "соединение" между TCP и UDP в именах методов.