개발/파이썬

init 안에서 await를 이용해 class attribute를 정의하기

맨날치킨 2022. 12. 22. 10:05
반응형

Stack Overflow에 자주 검색, 등록되는 문제들과 제가 개발 중 찾아 본 문제들 중에서 나중에도 찾아 볼 것 같은 문제들을 정리하고 있습니다.

Stack Overflow에서 가장 먼저 확인하게 되는 가장 높은 점수를 받은 Solution과 현 시점에 도움이 될 수 있는 가장 최근에 업데이트(최소 점수 확보)된 Solution을 각각 정리하였습니다.

 

아래 word cloud를 통해 이번 포스팅의 주요 키워드를 미리 확인하세요.

How to set class attribute with await in __init__

init 안에서 await를 이용해 class attribute를 어떻게 정의할 수 있을까?

 문제 내용 

How can I define a class with await in the constructor or class body?

생성자 또는 클래스 본문에서 대기가 있는 클래스를 정의하려면 어떻게 해야 합니까?

 

For example what I want:

예를 들어, 다음과 같은 것을 원한다면:
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'

 

or example with class body attribute:

또는 class body attribute 예시:
class Foo(object):

    self.pool = await create_pool(dsn)  # Sure it raises syntax Error

    def __init__(self, settings):
        self.settings = settings

foo = Foo(settings)

 

My solution (But I would like to see a more elegant way)

다음은 내 솔루션 (하지만 더 우아한 방법을 보고 싶다):
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()

 

 

 높은 점수를 받은 Solution 

Most magic methods aren't designed to work with async def/await - in general, you should only be using await inside the dedicated asynchronous magic methods - __aiter__, __anext__, __aenter__, and __aexit__. Using it inside other magic methods either won't work at all, as is the case with __init__ (unless you use some tricks described in other answers here), or will force you to always use whatever triggers the magic method call in an asynchronous context.

대부분의 magic method는 async def/await와 함께 작동하도록 설계되지 않았으며, 일반적으로 aiter, anext, aenter, __aexit__와 같은 전용 비동기 magic method 내에서만 await을 사용해야합니다. 다른 magic method 내에서 사용하면 (예 : __init__에서는 일부 다른 답변에서 설명하는 트릭을 사용하지 않는 한) 전혀 작동하지 않거나 magic method 호출을 트리거하는 것을 항상 비동기 콘텍스트에서 사용해야합니다.

 

Existing asyncio libraries tend to deal with this in one of two ways: First, I've seen the factory pattern used (asyncio-redis, for example):

기존 asyncio 라이브러리는 일반적으로 두 가지 방법 중 하나로 이 문제를 처리합니다. 첫째, 팩토리 패턴이 사용됩니다(asyncio-redis 예제) :
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)

 

Other libraries use a top-level coroutine function that creates the object, rather than a factory method:

다른 라이브러리는 팩토리 메소드 대신 객체를 생성하는 최상위 코루틴 함수를 사용합니다:
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)

 

The create_pool function from aiopg that you want to call in __init__ is actually using this exact pattern.

__init__에서 호출하려는 create_pool 함수는 실제로 이 정확한 패턴을 사용합니다.

 

This at least addresses the __init__ issue. I haven't seen class variables that make asynchronous calls in the wild that I can recall, so I don't know that any well-established patterns have emerged.

이렇게 해도 init 문제를 해결할 수 있습니다. 나는 기억하는 한 비동기 호출을 수행하는 class 변수를 실제로 본 적이 없기 때문에 잘 정립된 패턴이 존재하는지 알 수 없습니다.

 

 

 

 가장 최근 달린 Solution 

Vishnu shettigar's answer is so far the simplest, except that his async_init method doesn't return the object itself so foo isn't assigned a Foo instance. As for OP's purpose, the most elegant way to construct the class IMHO is

Vishnu shettigar의 답변은 지금까지 가장 간단합니다. 그러나 그의 async_init 메소드는 객체 자체를 반환하지 않으므로 foo는 Foo 인스턴스가 할당되지 않습니다. OP의 목적에 대해서는 내가 생각하기에 클래스를 구성하는 가장 우아한 방법은 다음과 같습니다.
import asyncio

class Foo:
    def __init__(self, settings):
        self.settings = settings

    def __await__(self):
        self.pool = asyncio.create_task(create_pool(dsn))
        yield from self.pool
        self.pool = self.pool.result()
        return self

 

To initialize the object, do the following

객체를 초기화하려면 다음을 수행하십시오.
def main():
    loop = asyncio.get_event_loop()
    foo = loop.run_until_complete(Foo(settings))

 

Or

또는
async def main():
    foo = await Foo(settings)

 

 

출처 : https://stackoverflow.com/questions/33128325/how-to-set-class-attribute-with-await-in-init

반응형