如何获得对生成器发送方法的弱引用?
How can I get a weak reference to the send method of a generator?
weakref
documentation 似乎没有提供创建对生成器的 send
方法的弱引用的方法:
import weakref
def gen(): yield
g=gen()
w_send=weakref.ref(g.send)
w_send() # <- this is None; the g.send object is ephemeral
我认为它行不通,但我确实尝试了 weakref.WeakMethod
以防万一:
>>> w_send=weakref.WeakMethod(g.send)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "C:\Users\ricky\AppData\Local\Programs\Python\Python37\lib\weakref.py", line 50, in __new__
.format(type(meth))) from None
TypeError: argument should be a bound method, not <class 'builtin_function_or_method'>
如何在不将生成器包装在自定义 class 中的情况下完成此操作?像这样:
import weakref
def gen(): yield
class MyGenerator:
def __init__(self):
self._generator = gen()
def send(self, arg):
return self._generator.send(arg)
g = MyGenerator()
ref = weakref.WeakMethod(g.send)
我不想这样做。有没有更好的方法?
我想这样做的原因是我正在研究一个想法,为我可能构建的应用程序创建一个简单的消息传递协议。消息看起来像这样:
# messaging module
from typing import Generator
from functools import wraps
from collections import NamedTuple
import weakref
class Message(NamedTuple):
channel: int
content: str
_REGISTRY = {}
def _do_register(channel, route):
# handle bound methods
if hasattr(route,"__self__") and hasattr(route,"__func__"):
route_ref = weakref.WeakMethod(route)
# handle generators
elif isinstance(route, Generator):
route_ref = weakref.ref(route.send) # get weak ref to route.send here
# all other callables
else:
route_ref = weakref.ref(route)
try:
_REGISTRY[channel].add(route_ref)
except KeyError:
_REGISTRY[channel] = {route_ref}
def register(obj=None, *, channel, route=None):
"""Decorator for registering callable objects for messaging."""
if obj is None:
def wrapper(callable):
@wraps(callable)
def wrapped(*args, **kwargs):
nonlocal route
obj_ = callable(*args, **kwargs)
route_ = obj_ if route is None else route
_do_register(channel, route_)
return obj_
return wrapped
return wrapper
else:
if route is None:
route = obj
_do_register(channel, route)
def manager():
msg_obj = None
while True:
msg_obj = yield _broadcast(msg_obj)
def _broadcast(msg_obj):
count = 0
if msg_obj:
for route_ref in _REGISTRY[msg_obj.channel]:
route = route_ref()
if route is not None:
count += 1
route(msg_obj)
return count
...这样使用:
@register(channel=1)
def listening_gen(name):
while True:
msg = yield
print(f"{name} received message {msg.content} on channel {msg.channel}")
a = listening_gen("a")
b = listening_gen("b")
next(a)
next(b)
register(a, channel=2)
register(b, channel=3)
msg1 = Message(channel=1, content="foo")
msg2 = Message(channel=2, content="bar")
msg3 = Message(channel=3, content="baz")
m = manager()
next(m)
m.send(msg1)
m.send(msg2)
m.send(msg3)
a
在频道 1 和 2 上收听消息,b
在频道 1 和 3 上收听消息。
来自the docs:
Not all objects can be weakly referenced; those objects which can
include class instances, functions written in Python (but not in C),
instance methods, sets, frozensets, some file objects, generators,
type objects, sockets, arrays, deques, regular expression pattern
objects, and code objects.
由于生成器是用 C 编写的内置类型,因此您不能创建对生成器的 send
方法的弱引用。正如您已经发现的那样,解决方法是将生成器包装在 python class.
中
weakref
documentation 似乎没有提供创建对生成器的 send
方法的弱引用的方法:
import weakref
def gen(): yield
g=gen()
w_send=weakref.ref(g.send)
w_send() # <- this is None; the g.send object is ephemeral
我认为它行不通,但我确实尝试了 weakref.WeakMethod
以防万一:
>>> w_send=weakref.WeakMethod(g.send)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "C:\Users\ricky\AppData\Local\Programs\Python\Python37\lib\weakref.py", line 50, in __new__
.format(type(meth))) from None
TypeError: argument should be a bound method, not <class 'builtin_function_or_method'>
如何在不将生成器包装在自定义 class 中的情况下完成此操作?像这样:
import weakref
def gen(): yield
class MyGenerator:
def __init__(self):
self._generator = gen()
def send(self, arg):
return self._generator.send(arg)
g = MyGenerator()
ref = weakref.WeakMethod(g.send)
我不想这样做。有没有更好的方法?
我想这样做的原因是我正在研究一个想法,为我可能构建的应用程序创建一个简单的消息传递协议。消息看起来像这样:
# messaging module
from typing import Generator
from functools import wraps
from collections import NamedTuple
import weakref
class Message(NamedTuple):
channel: int
content: str
_REGISTRY = {}
def _do_register(channel, route):
# handle bound methods
if hasattr(route,"__self__") and hasattr(route,"__func__"):
route_ref = weakref.WeakMethod(route)
# handle generators
elif isinstance(route, Generator):
route_ref = weakref.ref(route.send) # get weak ref to route.send here
# all other callables
else:
route_ref = weakref.ref(route)
try:
_REGISTRY[channel].add(route_ref)
except KeyError:
_REGISTRY[channel] = {route_ref}
def register(obj=None, *, channel, route=None):
"""Decorator for registering callable objects for messaging."""
if obj is None:
def wrapper(callable):
@wraps(callable)
def wrapped(*args, **kwargs):
nonlocal route
obj_ = callable(*args, **kwargs)
route_ = obj_ if route is None else route
_do_register(channel, route_)
return obj_
return wrapped
return wrapper
else:
if route is None:
route = obj
_do_register(channel, route)
def manager():
msg_obj = None
while True:
msg_obj = yield _broadcast(msg_obj)
def _broadcast(msg_obj):
count = 0
if msg_obj:
for route_ref in _REGISTRY[msg_obj.channel]:
route = route_ref()
if route is not None:
count += 1
route(msg_obj)
return count
...这样使用:
@register(channel=1)
def listening_gen(name):
while True:
msg = yield
print(f"{name} received message {msg.content} on channel {msg.channel}")
a = listening_gen("a")
b = listening_gen("b")
next(a)
next(b)
register(a, channel=2)
register(b, channel=3)
msg1 = Message(channel=1, content="foo")
msg2 = Message(channel=2, content="bar")
msg3 = Message(channel=3, content="baz")
m = manager()
next(m)
m.send(msg1)
m.send(msg2)
m.send(msg3)
a
在频道 1 和 2 上收听消息,b
在频道 1 和 3 上收听消息。
来自the docs:
Not all objects can be weakly referenced; those objects which can include class instances, functions written in Python (but not in C), instance methods, sets, frozensets, some file objects, generators, type objects, sockets, arrays, deques, regular expression pattern objects, and code objects.
由于生成器是用 C 编写的内置类型,因此您不能创建对生成器的 send
方法的弱引用。正如您已经发现的那样,解决方法是将生成器包装在 python class.