异步设置 python3.5 中的描述符
Setting a descriptor in python3.5 asynchronously
我可以写一个描述符返回一个可以等待的未来。
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
-> 语法错误:无法分配给等待表达式
await setattr(device, 'attr', 5)
-> TypeError: object NoneType can't be used in 'await' expression
device.attr=5
-> 运行时警告:从未等待协程“__set__”
你试图做的事情是不可能的(Python 3.5)。
虽然 __get__
到 return 一个 Future 可能是明智的,Python 3.5 根本不支持 __set__
异步。 __set__
的 return 值被 Python 忽略,因为没有 "return value" 赋值。并且对 __set__
的调用始终是同步的。正如您已经注意到的,像 a = (b.c = 5)
这样的东西实际上会引发 SyntaxError
。
如果允许像 await device.attr = 5
这样的异步赋值,那么可能会有一个单独的异步描述符协议,即协程 __aget__
和 __aset__
作为类比异步上下文的特殊方法管理器 (async with
/ __aenter__
) 和异步迭代 (async for
/ __aiter__
)。有关 async
/ await
支持背后的设计决策,请参阅 PEP 492。
另请注意,__get__
return未来不会使 __get__
成为协程。
在没有进一步上下文的情况下,您似乎想在描述符协议提供的属性访问抽象背后隐藏一些东西,最好明确地完成,但这当然取决于您。
但是 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())
await setattr(device,'attr',5) 构造也是可能的,实际上比 device.attr = 5 更体面,但当然不要超载真正的 setattr。
这个实际上对异步无线程很有用,同时保持易读性。 运行 不等待设置值的代码将引发一个很好的 "RuntimeWarning: coroutine attr.__set__
was never awaited"
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())
我可以写一个描述符返回一个可以等待的未来。
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
-> 语法错误:无法分配给等待表达式await setattr(device, 'attr', 5)
-> TypeError: object NoneType can't be used in 'await' expressiondevice.attr=5
-> 运行时警告:从未等待协程“__set__”
你试图做的事情是不可能的(Python 3.5)。
虽然 __get__
到 return 一个 Future 可能是明智的,Python 3.5 根本不支持 __set__
异步。 __set__
的 return 值被 Python 忽略,因为没有 "return value" 赋值。并且对 __set__
的调用始终是同步的。正如您已经注意到的,像 a = (b.c = 5)
这样的东西实际上会引发 SyntaxError
。
如果允许像 await device.attr = 5
这样的异步赋值,那么可能会有一个单独的异步描述符协议,即协程 __aget__
和 __aset__
作为类比异步上下文的特殊方法管理器 (async with
/ __aenter__
) 和异步迭代 (async for
/ __aiter__
)。有关 async
/ await
支持背后的设计决策,请参阅 PEP 492。
另请注意,__get__
return未来不会使 __get__
成为协程。
在没有进一步上下文的情况下,您似乎想在描述符协议提供的属性访问抽象背后隐藏一些东西,最好明确地完成,但这当然取决于您。
但是 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())
await setattr(device,'attr',5) 构造也是可能的,实际上比 device.attr = 5 更体面,但当然不要超载真正的 setattr。
这个实际上对异步无线程很有用,同时保持易读性。 运行 不等待设置值的代码将引发一个很好的 "RuntimeWarning: coroutine attr.__set__
was never awaited"
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())