在python3.5中异步设置描述符

时间:2016-09-11 16:18:48

标签: python python-3.5 python-asyncio descriptor

我可以编写一个描述符,返回一个可以等待的未来。

class AsyncDescriptor:
    def __get__(self, obj, cls=None):
         # generate some async future here
         return future

    def __set__(self, obj, value):
         # generate some async future here
         return future

class Device:
    attr=AsyncDescriptor()

device=Device()

现在我可以使用value=await device.attr在协程中获取值。

我该如何设置此属性?

  • await device.attr=5 - > SyntaxError:无法分配给等待表达式
  • await setattr(device, 'attr', 5) - > TypeError:object NoneType不能用于'await'表达式
  • device.attr=5 - >运行时警告:从未等待过'__set__'协程

3 个答案:

答案 0 :(得分:5)

您尝试做的事情是不可能的(使用Python 3.5)。

虽然__get__返回Future可能是明智的,但Python 3.5根本不支持__set__异步。 Python忽略__set__的返回值,因为没有"返回值"作业。对__set__的调用始终是同步的。像a = (b.c = 5)这样的东西实际上会引发SyntaxError,就像你已经注意到的那样。

如果允许像await device.attr = 5这样的异步分配,那么异步描述符可能会有一个单独的协议,即使用协同程序__aget____aset__作为与异步上下文管理器类似的特殊方法( async with / __aenter__)和异步迭代(async for / __aiter__)。有关async / await支持背后的设计决策,请参见PEP 492

另请注意,__get__返回未来不会使__get__成为协程。

如果没有进一步的上下文,看起来你想要隐藏描述符协议提供的属性访问抽象背后的东西,这应该更好地明确地完成,但当然这取决于你。

答案 1 :(得分:0)

等待setattr(device,'attr',5)构造也是可能的,实际上比device.attr = 5更体面,但是当然不要过载真正的setattr。

这个实际上对异步无线程很有用,同时保持易于阅读的状态。设置值而无需等待的运行代码将引发一个不错的“ RuntimeWarning:从未等待协程attr.__set__

import asyncio, sys

async def main():

    obj.attr = "not async value"
    print(obj.attr)

    print()
    print("now give set an async value")

    #DO NOT DO THAT use aio.asetattr(obj,'attr',5)
    setattr = aio.asetattr
    # ============== yes i can, thanks python ! ===========
    await setattr(obj,'attr',5)
    # ======================================

    print(obj.attr)

    print("bye")
    flush_io()

def flush_io():
    sys.stdout.flush()
    sys.stderr.flush()

class attr:
    def __init__(self, value):
        self.value = value

    def __get__(self, obj, objtype):
        if obj is None:
            return self
        return self.value

    async def __set__(self, obj, value):
        if value is not obj.__class__:
            print("    async updating", self, end=" ")
            for i in range(value):
                await asyncio.sleep(1)
                print(".", end="")
                flush_io()
            print()
            self.value = value
            print("set", obj, value)
            return
        print("__set__", obj, value)


    def __repr__(self):
        return "<async attr>"

class aobj:

    attr = attr("empty")

    def __repr__(self):
        return "<aobj>"

class aio:

    async def asetattr(self, obj, attr, value):
        await asyncio.sleep(1)
        a_attr = getattr( type(obj), attr)
        await a_attr.__set__(obj, value)
        print("done!")

aio = aio()
obj = aobj()

loop = asyncio.get_event_loop()
loop.run_until_complete(main())

答案 2 :(得分:-1)

但是python很棒,因此如果您编写device.attr = 5但等待设置 操作,您可以借助上下文块来做到这一点(尽管在无线程python场景中可能有用,但我没有说这是个好主意)

import asyncio, sys

async def main():

    obj.attr = "not async value"
    print(obj.attr)

    print()
    print("now set with an async value")

    async with aio(obj):
        # ============== yes i can, thanks python ! ===
        obj.attr = 5
        # =============================================

    print(obj.attr)

    print("bye")
    flush_io()

def flush_io():
    sys.stdout.flush()
    sys.stderr.flush()

class attr:
    def __init__(self, value):
        self.value = value

    def __get__(self, obj, objtype):
        if obj is None:
            print("__get__", obj, objtype)
            return self
        print("get", obj, objtype)
        return self.value

    def __set__(self, obj, value):
        if value is not obj.__class__:
            if obj in aio.updating:
                aio.setlist.append([self, value])
                print("    future set", self, value)
                return

            self.value = value
            print("set", obj, value)
            return
        print("__set__", obj, value)

    async def setter(self, value):
        print("    async updating", self, end=" ")
        for i in range(value):
            await asyncio.sleep(1)
            print(".", end="")
            flush_io()
        print()
        self.value = value

    def __repr__(self):
        return "<async attr>"

class aobj:

    attr = attr("empty")

    def __repr__(self):
        return "<aobj>"

class aio:
    updating = []
    setlist = []

    def __call__(self, obj):
        self.updating.append(obj)
        return self

    async def __aenter__(self):
        print("aenter", self.updating)

    async def __aexit__(self, *tb):
        self.updating.pop()
        while len(self.setlist):
            obj, value = self.setlist.pop()
            await obj.setter(value)
        print("aexit")


aio = aio()
obj = aobj()

loop = asyncio.get_event_loop()
loop.run_until_complete(main())