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

Изглежда, че би било съвсем естествено да се направи нещо като:

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

но Python не имплементира контекстен мениджър за сокет. Мога ли лесно да го използвам като контекстен мениджър и ако да, как?


person ChaimKut    schedule 27.05.2013    source източник
comment
Защо въпросите като цяло не са добри въпроси за SO. Може би можете да пренапишете това в как? :-)   -  person Lennart Regebro    schedule 27.05.2013
comment
@msw: Не. Това е просто прикрит въпрос защо. Добър въпрос е Как да използвам сокет като контекстен мениджър?. Текущият въпрос, с който мога да отговоря правилно. Прави или Не, или Да. Не е много полезно.   -  person Lennart Regebro    schedule 27.05.2013
comment
@msw: Първо, това е само още потвърждение, че въпросите защо са лоши, тъй като те могат да предизвикат безкрайно защо не слизане в ада. :-) Второ, мисля, че не разбирате какво представляват контекстните мениджъри. Отговорът сокетът не е като файл няма никакъв смисъл като отговор на въпроса защо в този случай. Напълно без значение е, че не е като файл. Контекстните мениджъри не са само за файлове.   -  person Lennart Regebro    schedule 27.05.2013
comment
@msw: Не промених значението, направих го добър въпрос.   -  person Lennart Regebro    schedule 27.05.2013
comment
@msw: Имайте предвид, че затварянето наистина е временно състояние; подобрете въпроса и той може да бъде отворен отново. Подобряването на въпроса преди да бъде затворен е все още по-добра идея.   -  person Martijn Pieters    schedule 27.05.2013


Отговори (3)


Модулът socket е сравнително ниско ниво, което ви дава почти директен достъп до функционалността на C библиотеката.

Винаги можете да използвате 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.x прави socket() контекстен мениджър, но документацията не беше актуализирана, за да отрази това до добре в цикъла на Python 3.5, през 2016. Вижте socket клас в изходния код, който добавя __enter__ и __exit__ методи.

person Martijn Pieters    schedule 27.05.2013
comment
Python3 with/as изглежда много полезен, не мога да се сетя за никаква причина да оставя това извън документите, случайно да знаете защо го няма там? - person Ryan Haining; 01.04.2014
comment
@RyanHaining: подавателят на корекции просто е забравил да го документира правилно. - person Martijn Pieters; 01.04.2014
comment
@RyanHaining: Корекцията актуализира socket.create_connection(), но някак си е скрита . - person Martijn Pieters; 01.04.2014
comment
какво се има предвид под първо, когато се казва, че ви дава възможност първо да правите други неща с гнездото? Струва ми се, че потребителският контекстен мениджър (т.е. socketcontext) позволява да се правят точно същите неща като contextlib.closing. Каква е разликата?? - person allyourcode; 31.05.2016
comment
@allyourcode: contextlib.closing() ще извика само close на сокета. Персонализиран контекстен мениджър може, да речем, да се свърже с отворен порт, да зададе първо изчакване или други опции за сокет или да изпрати специфичен за протокола затварящ пакет или нещо подобно при излизане от контекста. - person Martijn Pieters; 31.05.2016
comment
Това трябва да е with closing(socket.socket(socket.AF_INET, socket.SOCK_DGRAM)) as s: - person Lunulata; 08.11.2017
comment
@Lunulata: наистина; Коригирах и другата препратка. Благодаря! - person Martijn Pieters; 09.11.2017
comment
документацията на python всъщност посочва изрична поддръжка за протокола на контекстния мениджър . Не съм сигурен дали е така през '13, но поне сега е така. - person MikeVe; 07.10.2020
comment
@MikeVe: не през 2013 г. Имайте предвид, че през 2014 г. обсъдихме това в коментарите тук, как изпращачът на корекцията е забравил да актуализира документите. В крайна сметка документите бяха коригирани през 2016 г.. - person Martijn Pieters; 07.10.2020

Модулът на гнездото е просто обвивка около интерфейса на гнездото на 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
person Lennart Regebro    schedule 27.05.2013

Моля, разгледайте следните фрагменти както за 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 в имената на методите.

person mieciu    schedule 16.07.2015