我可以编写一个描述符,返回一个可以等待的未来。
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__'协程答案 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())