python - 如何获得对生成器发送方法的弱引用?

标签 python python-3.x generator weak-references

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'>

如果不将生成器包装在自定义类中,如何做到这一点?像这样:

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)

我不想这样做。有没有更好的办法?


我想这样做的原因是我正在研究一个想法,为我可能构建的应用程序创建一个简单的消息传递协议(protocol)。消息看起来像这样:

# 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 在 channel 1 和 2 上收听消息,b 在 channel 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 类中。

关于python - 如何获得对生成器发送方法的弱引用?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50547179/

相关文章:

python - 将 apscheduler 从 3.0.1 升级到 3.1.0

python - 使用 Python Bottle、Multiprocessing 和 gevent 的流式连接

python - 如何为列表中的每个邻居调用函数?

Python 元素树 : write to the file it is parsing already

Python 猜谜游戏

python - `DummyExecutor` 用于 Python 's ` future `

python - Netgen.exe - 系统错误 python35.dll 未找到

python-3.x - Imageio 无法以正确的帧速率读取网络摄像头

python - 如何在文件夹中使用 opencv 更改速度后保存视频?

c++ - Rand() 是生成器,即使我调用了 srand(time(NULL))