Как использовать сокет в Python в качестве менеджера контекста?
Похоже, что было бы естественно делать что-то вроде:
with socket(socket.AF_INET, socket.SOCK_DGRAM) as s:
но Python не реализует диспетчер контекста для сокета. Могу ли я легко использовать его в качестве менеджера контекста, и если да, то как?
Ответы
Ответ 1
Модуль socket
довольно низкоуровневый, что дает вам почти прямой доступ к функциям библиотеки C.
Вы всегда можете использовать contextlib.contextmanager
decorator, чтобы создать свой собственный:
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.x делает socket()
менеджером контекста, хотя документация не упоминает об этом. См. socket
class в исходном коде, который добавляет методы __enter__
и __exit__
.
Ответ 2
Модуль сокетов - это всего лишь оболочка интерфейса сокетов BSD. Он низкоуровневый и на самом деле не пытается предоставить вам удобный или простой в использовании API Pythonic. Вы можете использовать что-то более высокоуровневое.
Тем не менее, на самом деле он реализует диспетчер контекстов:
>>> 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
Ответ 3
Обратите внимание на следующие фрагменты: для сокетов 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 в именах методов.