str metodo español def __str__ __repr__ __init__ python python-3.x python-asyncio

python - metodo - Cómo establecer el atributo de clase con await en__init__



metodo str python (4)

¿Cómo puedo definir una clase con await en el constructor o cuerpo de clase?

Por ejemplo lo que quiero:

import asyncio # some code class Foo(object): async def __init__(self, settings): self.settings = settings self.pool = await create_pool(dsn) foo = Foo(settings) # it raises: # TypeError: __init__() should return None, not ''coroutine''

o ejemplo con atributo de cuerpo de clase:

class Foo(object): self.pool = await create_pool(dsn) # Sure it raises syntax Error def __init__(self, settings): self.settings = settings foo = Foo(settings)

Mi solución (pero me gustaría ver una forma más elegante)

class Foo(object): def __init__(self, settings): self.settings = settings async def init(self): self.pool = await create_pool(dsn) foo = Foo(settings) await foo.init()


La mayoría de los métodos mágicos no están diseñados para funcionar con async def / async def ; en general, solo debe usar await dentro de los métodos mágicos asincrónicos dedicados: __aiter__ , __anext__ , __aenter__ y __aexit__ . Usarlo dentro de otros métodos mágicos no funcionará en absoluto (como es el caso con __init__ ), o lo obligará a usar siempre cualquier desencadenante que llame al método mágico en un contexto asincrónico.

Las bibliotecas asyncio existentes tienden a tratar esto de una de dos maneras: Primero, he visto el patrón de fábrica utilizado ( asyncio-redis , por ejemplo):

import asyncio dsn = "..." class Foo(object): @classmethod async def create(cls, settings): self = Foo() self.settings = settings self.pool = await create_pool(dsn) return self async def main(settings): settings = "..." foo = await Foo.create(settings)

Otras bibliotecas usan una función de rutina de nivel superior que crea el objeto, en lugar de un método de fábrica:

import asyncio dsn = "..." async def create_foo(settings): foo = Foo(settings) await foo._init() return foo class Foo(object): def __init__(self, settings): self.settings = settings async def _init(self): self.pool = await create_pool(dsn) async def main(): settings = "..." foo = await create_foo(settings)

La función create_pool de aiopg que desea llamar __init__ está usando este patrón exacto.

Esto al menos soluciona el problema __init__ . No he visto variables de clase que hagan llamadas asíncronas en la naturaleza que pueda recordar, así que no sé si han surgido patrones bien establecidos.


Otra forma de hacer esto, por diversión:

class aobject(object): """Inheriting this class allows you to define an async __init__. So you can create objects by doing something like `await MyClass(params)` """ async def __new__(cls, *a, **kw): instance = super().__new__(cls) await instance.__init__(*a, **kw) return instance async def __init__(self): pass #With non async super classes class A: def __init__(self): self.a = 1 class B(A): def __init__(self): self.b = 2 super().__init__() class C(B, aobject): async def __init__(self): super().__init__() self.c=3 #With async super classes class D(aobject): async def __init__(self, a): self.a = a class E(D): async def __init__(self): self.b = 2 await super().__init__(1) # Overriding __new__ class F(aobject): async def __new__(cls): print(cls) return await super().__new__(cls) async def __init__(self): await asyncio.sleep(1) self.f = 6 loop = asyncio.get_event_loop() e = loop.run_until_complete(E()) e.b # 2 e.a # 1 c = loop.run_until_complete(C()) c.a # 1 c.b # 2 c.c # 3 f = loop.run_until_complete(F()) # Prints F class f.f # 6


Recomendaría un método de fábrica separado. Es seguro y sencillo. Sin embargo, si insiste en una versión async de __init__() , aquí hay un ejemplo:

def asyncinit(cls): __new__ = cls.__new__ async def init(obj, *arg, **kwarg): await obj.__init__(*arg, **kwarg) return obj def new(cls, *arg, **kwarg): obj = __new__(cls, *arg, **kwarg) coro = init(obj, *arg, **kwarg) #coro.__init__ = lambda *_1, **_2: None return coro cls.__new__ = new return cls

Uso:

@asyncinit class Foo(object): def __new__(cls): ''''''Do nothing. Just for test purpose.'''''' print(cls) return super().__new__(cls) async def __init__(self): self.initialized = True

async def f(): print((await Foo()).initialized) loop = asyncio.get_event_loop() loop.run_until_complete(f())

Salida:

<class ''__main__.Foo''> True

Explicación:

La construcción de su clase debe devolver un objeto de coroutine lugar de su propia instancia.


Si está utilizando Python3.7 o posterior, puede usar asyncio.run :

import asyncio # some code class Foo(object): async def __init(self): self.pool = await create_pool(dsn) def __init__(self, settings): self.settings = settings asyncio.run(self.__init) foo = Foo(settings)

Tenga en cuenta que esto no funcionará si está creando instancias de Foo en una función asincrónica que ya se está ejecutando. Vea esta publicación de blog para una discusión sobre cómo manejar este escenario y una buena discusión sobre programación asincrónica en Python.