Can the __contains__
function by adapted to Python asyncio, where the method becomes a coroutine? For example
class A():
async def __contains__(self, a):
return True
async def main():
a = A()
print(2 in a)
Of course this generates the warning
RuntimeWarning: coroutine 'A.__contains__' was never awaited
I assume there needs to be some special version of in
for asyncio, though I've tried several permutations of using await
and in
with no luck.
My real use case of contains
is a wrapper around a database search to check if there is an entity conflict. One case is to check if a user's screen name already exists in a database table, such as 'mike' in Users
Of course I could make my own contains
and do something like User.contains('mike')
but I prefer the beauty of in
.
No - the built-in in
operator is naturally synchronous, and making it make use of an asynchronous iterator needs changes on the language side.
original answer
Just see, for an analogy, that when the language introduced support to asynchronous context managers and asynchronous iterators, there where syntax changes introducing the async with
and async for
versions.
If you want a hacky toy, not suitable for production anyway, you might play along calling (synchronously) loop._run_once()
inside your __contains__
code, where you would yield to the async loop with an await
. It is an internal structure, so if there is a custom event-loop running this will simply fail - and also, it is not designed to be re-entrant, so if it is called from an asynchronous function, _run_once
will be on the call stack already: you results might range from it doing nothing, to a lot of state information both on your async loop and tasks to be corrupted. As a hacky toy, it could be a valid experience though.
update
There almost is a hacky path to attain that, but it is blocked
While for some dunder special methods it is possible to either simply declare them as an async function or return an awaitable (in which case even a synchronous function can return a co-routine or other async-aware object that can be awaited), the in
operator will coerce whatever the special method __contains__
returns to a boolean. The ordinary workings of the in
operator will call __bool__
in whatever __contains__
returns to coerce it to a bool. But the __bool__
method itself must return either True
or False
and no other object, or a TypeError
will be raised - and so, it is not possible to have in
operating asynchronously, even if it returns an awaitable object. (but check the original answer, before this update)
To be clear, it is possible to have an async async def __add__(self, other): ...
method, for example, and do await (myinstance + operand)
- that will work (up to Python 3.13 at least), but no with the in
operator and __contains__
call.