asyncio create_connection 协议工厂
asyncio create_connection protocol factory
Python 3 的 asyncio 模块中的 create_connection 函数将协议工厂作为其第一个参数。该文档有以下注释:
Note protocol_factory can be any kind of callable, not necessarily a class. For example, if you want to use a pre-created protocol instance, you can pass lambda: my_protocol.
因此您可以使用 lambda
传递实例,如下所示:
create_connection(lambda: Protocol(a, b, c))
另一种方法是将 __call__
定义为 return self
,这样您就可以在不定义 lambda 的情况下传递实例。
protocol = Protocol(a, b, c)
create_connection(protocol)
是否有任何理由像文档建议的那样使用 lambda 在 class 上定义 __call__
?
简答:
应优先使用 lambda,因为它更具可读性 - 无需仔细检查协议 class 代码即可轻松理解。
解释:
BaseEventLoop.create_connection 从 BaseEventLoop._create_connection_transport ref 产生,它从协议 class 中实例化一个协议对象,如下所示:
protocol = protocol_factory()
我们可以在没有事件循环代码的情况下以简化的方式呈现问题,以演示如何实例化协议:
class Prococol:
pass
def create_connection(Protocol):
protocol = Protocol()
create_connection(Protocol)
因此,"protocol = Protocol()" 需要使用参数。这可以通过使用 lambda 来实现:
class Protocol:
def __init__(self, a):
self.a = a
def create_connection(Protocol):
protocol = Protocol()
create_connection(lambda: Protocol(1))
或者 OP 建议的替代建议是使对象可调用:
class Protocol:
def __init__(self, a):
self.a = a
def __call__(self):
return self
def create_connection(Protocol):
protocol = Protocol()
create_connection(Protocol(1))
在功能上两者都可以,因此这是一个更好的做法的问题。我认为 lambda 方法更好,因为查看最后一行 create_connection(lambda: Protocol(1))
清楚地表明我们正在传递给 create_connection 函数,调用时 returns 一个对象,而传递一个 a可调用对象使代码的可读性降低 - 因为需要仔细检查协议 class 以确定实例化对象也是可调用实体。
Udi 对这个问题的回答说,使用 def __call__(self): return self
,将不适用于 create_server
(顺便说一句,这不是问题所问的),因为它将重用实例化对象的一个实例.这个观察是正确的,但该答案中遗漏的是可以轻松调整可调用对象以与 create_server
一起使用。例如:
class Protocol:
def __init__(self, a):
self.a = a
def __call__(self):
return Protocol(self.a)
底线是使用 __call__
应该像 lambda 方法一样工作。应优先使用 lambda 的原因是出于可读性原因。
注意这两行的区别:
loop.create_connection(MyProtocol, '127.0.0.1', 8888) # Option #1
loop.create_connection(MyProtocol(), '127.0.0.1', 8888) # Option #2
这是来自 asyncio 文档的 echo 客户端示例,已修改为与选项 #1 一起使用:
class MyEchoClientProtocol(asyncio.Protocol):
def connection_made(self, transport):
message = "hello"
transport.write(message.encode())
print('Data sent: {!r}'.format(message))
def data_received(self, data):
print('Data received: {!r}'.format(data.decode()))
def connection_lost(self, exc):
print('The server closed the connection')
print('Stop the event loop')
loop.stop()
loop = asyncio.get_event_loop()
coro = loop.create_connection(MyEchoClientProtocol, '127.0.0.1', 8765)
loop.run_until_complete(coro)
loop.run_forever()
loop.close()
如果您选择使用选项 #2,则需要实施 MyProtocol.__call__(self)
,它适用于 MyProtocol
的 个实例 。
虽然这对于 create_connection
可能工作正常,因为您的 __call__
将只被调用一次,这对于 create_server
的 protocol_factory
参数不起作用:
...
# Each client connection will create a new protocol instance
coro = loop.create_server(EchoServerClientProtocol, '127.0.0.1', 8888)
...
此处 protocol_factory
被多次调用以创建新的 Protocol
实例。使用 EchoServerClientProtocol()
并定义 def __call__(self): return self
将仅重用 Protocol
!
的一个实例
Python 3 的 asyncio 模块中的 create_connection 函数将协议工厂作为其第一个参数。该文档有以下注释:
Note protocol_factory can be any kind of callable, not necessarily a class. For example, if you want to use a pre-created protocol instance, you can pass lambda: my_protocol.
因此您可以使用 lambda
传递实例,如下所示:
create_connection(lambda: Protocol(a, b, c))
另一种方法是将 __call__
定义为 return self
,这样您就可以在不定义 lambda 的情况下传递实例。
protocol = Protocol(a, b, c)
create_connection(protocol)
是否有任何理由像文档建议的那样使用 lambda 在 class 上定义 __call__
?
简答:
应优先使用 lambda,因为它更具可读性 - 无需仔细检查协议 class 代码即可轻松理解。
解释:
BaseEventLoop.create_connection 从 BaseEventLoop._create_connection_transport ref 产生,它从协议 class 中实例化一个协议对象,如下所示:
protocol = protocol_factory()
我们可以在没有事件循环代码的情况下以简化的方式呈现问题,以演示如何实例化协议:
class Prococol:
pass
def create_connection(Protocol):
protocol = Protocol()
create_connection(Protocol)
因此,"protocol = Protocol()" 需要使用参数。这可以通过使用 lambda 来实现:
class Protocol:
def __init__(self, a):
self.a = a
def create_connection(Protocol):
protocol = Protocol()
create_connection(lambda: Protocol(1))
或者 OP 建议的替代建议是使对象可调用:
class Protocol:
def __init__(self, a):
self.a = a
def __call__(self):
return self
def create_connection(Protocol):
protocol = Protocol()
create_connection(Protocol(1))
在功能上两者都可以,因此这是一个更好的做法的问题。我认为 lambda 方法更好,因为查看最后一行 create_connection(lambda: Protocol(1))
清楚地表明我们正在传递给 create_connection 函数,调用时 returns 一个对象,而传递一个 a可调用对象使代码的可读性降低 - 因为需要仔细检查协议 class 以确定实例化对象也是可调用实体。
Udi 对这个问题的回答说,使用 def __call__(self): return self
,将不适用于 create_server
(顺便说一句,这不是问题所问的),因为它将重用实例化对象的一个实例.这个观察是正确的,但该答案中遗漏的是可以轻松调整可调用对象以与 create_server
一起使用。例如:
class Protocol:
def __init__(self, a):
self.a = a
def __call__(self):
return Protocol(self.a)
底线是使用 __call__
应该像 lambda 方法一样工作。应优先使用 lambda 的原因是出于可读性原因。
注意这两行的区别:
loop.create_connection(MyProtocol, '127.0.0.1', 8888) # Option #1
loop.create_connection(MyProtocol(), '127.0.0.1', 8888) # Option #2
这是来自 asyncio 文档的 echo 客户端示例,已修改为与选项 #1 一起使用:
class MyEchoClientProtocol(asyncio.Protocol):
def connection_made(self, transport):
message = "hello"
transport.write(message.encode())
print('Data sent: {!r}'.format(message))
def data_received(self, data):
print('Data received: {!r}'.format(data.decode()))
def connection_lost(self, exc):
print('The server closed the connection')
print('Stop the event loop')
loop.stop()
loop = asyncio.get_event_loop()
coro = loop.create_connection(MyEchoClientProtocol, '127.0.0.1', 8765)
loop.run_until_complete(coro)
loop.run_forever()
loop.close()
如果您选择使用选项 #2,则需要实施 MyProtocol.__call__(self)
,它适用于 MyProtocol
的 个实例 。
虽然这对于 create_connection
可能工作正常,因为您的 __call__
将只被调用一次,这对于 create_server
的 protocol_factory
参数不起作用:
...
# Each client connection will create a new protocol instance
coro = loop.create_server(EchoServerClientProtocol, '127.0.0.1', 8888)
...
此处 protocol_factory
被多次调用以创建新的 Protocol
实例。使用 EchoServerClientProtocol()
并定义 def __call__(self): return self
将仅重用 Protocol
!