__Init__ এ অপেক্ষা সহ শ্রেণীর বৈশিষ্ট্য কীভাবে সেট করবেন


92

আমি কীভাবে awaitকনস্ট্রাক্টর বা ক্লাস বডি সহ কোনও শ্রেণি সংজ্ঞা দিতে পারি ?

উদাহরণস্বরূপ আমি যা চাই:

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'

বা ক্লাস বডি অ্যাট্রিবিউট সহ উদাহরণ:

class Foo(object):

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

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

foo = Foo(settings)

আমার সমাধান (তবে আমি আরও মার্জিত উপায় দেখতে চাই)

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()

4
আপনার সাথে কিছুটা __new__
ভাগ্যও

আমার ৩.৫ এর সাথে অভিজ্ঞতা নেই এবং অন্যান্য ভাষায় এটি অ্যাসিঙ্ক / অপেক্ষার ভাইরাল প্রকৃতির কারণে কাজ করবে না, তবে আপনি কি অ্যাসিঙ্ক ফাংশনটি সংজ্ঞায়িত করার _pool_init(dsn)এবং এরপরে কল করার চেষ্টা করেছেন __init__? এটি আর্ট-ইন-কনস্ট্রাক্টর উপস্থিতি সংরক্ষণ করবে।
এপি

4
আপনি যদি কুরিও
matsjoyce

4
ব্যবহার - @classmethodএটি একটি বিকল্প নির্মাণকারী। সেখানে async কাজ করা; তারপরে __init__, কেবলমাত্র selfবৈশিষ্ট্যগুলি নির্ধারণ করুন
গ্রিসাইটিস

উত্তর:


117

সর্বাধিক যাদু পদ্ধতি কাজ করার জন্য ডিজাইন করা হয় না async def/ awaitসাধারণভাবে, আপনি শুধুমাত্র ব্যবহার করা উচিত - awaitডেডিকেটেড অ্যাসিঙ্ক্রোনাস যাদু পদ্ধতি ভিতরে - __aiter__, __anext__, __aenter__, এবং __aexit__। এটি অন্যান্য যাদু পদ্ধতির অভ্যন্তরে এটি ব্যবহার করা একেবারেই কার্যকর হবে না __init__( যেমন আপনি এখানে অন্যান্য উত্তরে বর্ণিত কিছু কৌশল ব্যবহার না করেন) বা যিনি যাদুর পদ্ধতির কলটিকে অ্যাসিঙ্ক্রোনাস প্রসঙ্গে কল্পনা করতে সর্বদা ব্যবহার করতে বাধ্য করবেন।

বিদ্যমান 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)

অন্যান্য গ্রন্থাগারগুলি একটি শীর্ষ-স্তরের কর্টিন ফাংশন ব্যবহার করে যা কোনও কারখানার পদ্ধতির পরিবর্তে অবজেক্ট তৈরি করে:

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)

create_poolথেকে ফাংশন aiopgআপনি কল করতে চান যে __init__আসলে ঠিক এই প্যাটার্ন ব্যবহার করছে।

এটি কমপক্ষে __init__সমস্যাটির সমাধান করে। আমি শ্রেণীর ভেরিয়েবলগুলি দেখিনি যা বন্যে অ্যাসিক্রোনাস কল করে যা আমি স্মরণ করতে পারি, তাই আমি জানি না যে কোনও সু-প্রতিষ্ঠিত নিদর্শন উদ্ভূত হয়েছে।


35

মজা করার জন্য এটি করার আর একটি উপায়:

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

async def main():
    e = await E()
    print(e.b) # 2
    print(e.a) # 1

    c = await C()
    print(c.a) # 1
    print(c.b) # 2
    print(c.c) # 3

    f = await F() # Prints F class
    print(f.f) # 6

import asyncio
loop = asyncio.get_event_loop()
loop.run_until_complete(main())

4
এটি বর্তমানে আমার মতে সবচেয়ে স্পষ্ট এবং বোধগম্য বাস্তবায়ন implementation আমি এটি স্বজ্ঞাতভাবে কতটা এক্সটেনসিবল তা পছন্দ করি। আমি উদ্বিগ্ন ছিলাম मेटाটাগ্লাসে প্রবেশ করা প্রয়োজন হবে।
টানকোবট

4
__init__যদি super().__new__(cls)প্রাক-বিদ্যমান উদাহরণটি প্রদান করে তবে এর সঠিক শব্দার্থবিজ্ঞান নেই - সাধারণত, এটি এড়িয়ে যায় __init__, তবে আপনার কোডটি নয়।
এরিক

হুম, প্রতি object.__new__ডকুমেন্টেশন, __init__শুধুমাত্র যদি অনুরোধ করা উচিত isinstance(instance, cls)? এটি আমার কাছে কিছুটা অস্পষ্ট বলে মনে হচ্ছে ... তবে আপনি কোথাও দাবি করা শব্দার্থবিজ্ঞানটি দেখছি না ...
খাজিক

এ সম্পর্কে আরও চিন্তাভাবনা করে আপনি যদি __new__পূর্ব-বিদ্যমান বস্তুটি ফেরত দিতে ওভাররাইড করে থাকেন তবে সেই নতুনটি যে কোনওরকম বোঝার জন্য সবচেয়ে বাহ্যিক হওয়া দরকার কারণ যেহেতু __new__আপনি যদি নতুন অবিস্মরণীয় উদাহরণটি ফিরিয়ে দিচ্ছেন তবে এটির অন্যান্য বাস্তবায়নের কোনও সাধারণ উপায় থাকবে না বা না.
খাজিক

4
@ খাজহাইক ওয়েল, অবশ্যই আপনাকে সংজ্ঞা দিতে বাধা async def __init__(...)দিচ্ছে, যেমনটি ওপি দেখিয়েছে, এবং আমি বিশ্বাস করি যে TypeError: __init__() should return None, not 'coroutine'পাইথনের অভ্যন্তরে ব্যতিক্রমটি হার্ডকোডযুক্ত এবং এড়াতে পারবেন না। সুতরাং আমি বুঝতে চেষ্টা করেছি যে কীভাবে async def __new__(...)একটি পার্থক্য রয়েছে। এখন আমার বোঝাপড়াটি হ'ল, আপনার async def __new__(...)(আব) বৈশিষ্ট্যটি "যদি __new__()ক্লসের কোনও উদাহরণ না ফেরায়, তবে ডাকা __init__()হবে না " use আপনার নতুন __new__()একটি ক্লোটিন নয়, একটি কর্টিন ফেরত দেয় এই জন্য. চালাক হ্যাক!
রায়লুও

20

আমি একটি পৃথক কারখানা পদ্ধতি প্রস্তাব করব। এটি নিরাপদ এবং সোজা। তবে, যদি আপনি কোনও asyncসংস্করণে জোর দিয়ে থাকেন তবে __init__()এখানে একটি উদাহরণ দেওয়া আছে:

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

ব্যবহার:

@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())

আউটপুট:

<class '__main__.Foo'>
True

ব্যাখ্যা:

আপনার শ্রেণি নির্মাণ অবশ্যই coroutineতার নিজস্ব উদাহরণের পরিবর্তে কোনও জিনিস ফেরত দিতে হবে ।


আপনি কি নিজের নামটি new __new__ব্যবহার করতে পারবেন না এবং তার পরিবর্তে super(একইভাবে __init__অর্থাত্ ক্লায়েন্টকে ওভাররাইড করতে দিন)?
ম্যাথিয়াস উরলিচস

7

আরও ভাল আপনি এই জাতীয় কিছু করতে পারেন যা খুব সহজ:

import asyncio

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

    async def async_init(self):
        await create_pool(dsn)

    def __await__(self):
        return self.async_init().__await__()

loop = asyncio.get_event_loop()
foo = loop.run_until_complete(Foo(settings))

মূলত এখানে যা ঘটে তা __init__() যথারীতি প্রথম বলা হয় । তারপরে __await__()যা বলা হয় তার পরে অপেক্ষা করে async_init()


আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.