【问题标题】:How to use socket in Python as a context manager?如何在 Python 中使用套接字作为上下文管理器?
【发布时间】:2013-05-22 06:26:19
【问题描述】:

看起来这样做是很自然的:

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

但是 Python 没有为套接字实现上下文管理器。我可以轻松地将它用作上下文管理器吗?如果可以,如何使用?

【问题讨论】:

  • “为什么”问题通常不是 SO 的好问题。也许您可以将其重写为“如何”? :-)
  • @msw:不。那只是变相的为什么问题。一个很好的问题是“如何使用套接字作为上下文管理器?”。我可以正确回答的当前问题。 “它确实”或“否”或“是”。不是很有帮助。
  • @msw:首先,这只是更多地证实了“为什么”问题是不好的,因为它们可以引发无限的“为什么不”下地狱。 :-) 其次,我认为您误解了上下文管理器是什么。在这种情况下,“套接字不像文件”的答案作为“为什么”问题的答案没有任何意义。它不像文件是完全不相关的。上下文管理器不仅仅用于文件。
  • @msw:我没有改变意思,我提出了一个很好的问题。
  • @msw:注意关闭其实是一种临时状态;改善问题,它可以再次重新打开。改进问题结束之前仍然是一个更好的主意。

标签: python sockets contextmanager


【解决方案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() 成为上下文管理器,但文档并未更新以反映此 until well into the Python 3.5 cycle, in 2016。查看源代码中的socket class,其中添加了__enter____exit__方法。

【讨论】:

  • Python3 with/as 似乎非常有用,我想不出任何理由将其从文档中删除,您是否知道它为什么不在其中?跨度>
  • @RyanHaining:patch submitter 只是忘记正确记录它。
  • @RyanHaining:补丁确实更新了socket.create_connection(),但它有点隐藏。
  • “首先”是什么意思,它说“让您有机会首先使用套接字做其他事情”?在我看来,自定义上下文管理器(即 socketcontext)允许人们做与 contextlib.closure 完全相同的事情。有什么区别??
  • @allyourcode: contextlib.closing() 只会在套接字上调用close。例如,自定义上下文管理器可以绑定到打开的端口,首先设置超时或其他套接字选项,或者在退出上下文时发送特定于协议的关闭数据包等。
【解决方案2】:

套接字模块只是 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

【讨论】:

    【解决方案3】:

    请查看以下 sn-ps,用于 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 之间的“连接”一词的行为和方法的差异。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-07-15
      • 1970-01-01
      • 2011-12-25
      • 2013-03-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-12-24
      相关资源
      最近更新 更多