سؤال

لقد حاولت أن أكتب وظيفة الديكور الذي يلتف asyncio.coroutine ويعود الوقت الذي استغرقته للحصول على القيام به.تحتوي الوصفة أدناه على الكود الذي يعمل كما توقعت.مشكلتي الوحيدة مع ذلك بطريقة أو بأخرى أنا تفقد اسم وظيفة مزينة على الرغم من استخدام @functools.wraps.كيفية الاحتفاظ باسم كوروتين الأصلي?راجعت مصدر asyncio.

import asyncio
import functools
import random
import time

MULTIPLIER = 5

def time_resulted(coro):
    @functools.wraps(coro)
    @asyncio.coroutine
    def wrapper(*args, **kargs):
        time_before = time.time()
        result = yield from coro(*args, **kargs)
        if result is not None:
            raise TypeError('time resulted coroutine can '
                'only return None')
        return time_before, time.time()
    print('= wrapper.__name__: {!r} ='.format(wrapper.__name__))
    return wrapper

@time_resulted
@asyncio.coroutine
def random_sleep():
    sleep_time = random.random() * MULTIPLIER
    print('{} -> {}'.format(time.time(), sleep_time))
    yield from asyncio.sleep(sleep_time)

if __name__ == '__main__':
    loop = asyncio.get_event_loop()
    tasks = [asyncio.Task(random_sleep()) for i in range(5)]
    loop.run_until_complete(asyncio.wait(tasks))
    loop.close()
    for task in tasks:
        print(task, task.result()[1] - task.result()[0])
    print('= random_sleep.__name__: {!r} ='.format(
        random_sleep.__name__))
    print('= random_sleep().__name__: {!r} ='.format(
        random_sleep().__name__))

النتيجة:

= wrapper.__name__: 'random_sleep' =
1397226479.00875 -> 4.261069174838891
1397226479.00875 -> 0.6596335046471768
1397226479.00875 -> 3.83421163259601
1397226479.00875 -> 2.5514027672929713
1397226479.00875 -> 4.497471439365472
Task(<wrapper>)<result=(1397226479.00875, 1397226483.274884)> 4.266134023666382
Task(<wrapper>)<result=(1397226479.00875, 1397226479.6697)> 0.6609499454498291
Task(<wrapper>)<result=(1397226479.00875, 1397226482.844265)> 3.835515022277832
Task(<wrapper>)<result=(1397226479.00875, 1397226481.562422)> 2.5536720752716064
Task(<wrapper>)<result=(1397226479.00875, 1397226483.51523)> 4.506479978561401
= random_sleep.__name__: 'random_sleep' =
= random_sleep().__name__: 'wrapper' =

كما ترى random_sleep() إرجاع كائن مولد مع اسم مختلف.أود الاحتفاظ باسم كوروتين المزخرف.أنا لست على علم إذا كان هذا هو مشكلة محددة ل asyncio.coroutines أم لا.حاولت أيضا رمز مع أوامر الديكور مختلفة ، ولكن كل لديه نفس النتيجة.إذا كنت أعلق @functools.wraps(coro) ثم حتى random_sleep.__name__ يصبح wrapper كما توقعت.

تحرير:لقد نشرت هذه المسألة إلى بيثون قضية المقتفي وتلقى الجواب التالي من قبل ر.ديفيد موراي:"أعتقد أن هذه حالة محددة لحاجة أكثر عمومية لتحسين "الأغطية" التي تمت مناقشتها على بايثون ديف منذ وقت ليس ببعيد."

هل كانت مفيدة؟

المحلول

القضية هي أن functools.wraps التغييرات فقط wrapper.__name__ و wrapper().__name__ الإقامة wrapper. __name__ هي سمة مولد للقراءة فقط.يمكنك استخدام exec لتعيين الاسم المناسب:

import asyncio
import functools
import uuid
from textwrap import dedent

def wrap_coroutine(coro, name_prefix='__' + uuid.uuid4().hex):
    """Like functools.wraps but preserves coroutine names."""
    # attribute __name__ is not writable for a generator, set it dynamically
    namespace = {
        # use name_prefix to avoid an accidental name conflict
        name_prefix + 'coro': coro,
        name_prefix + 'functools': functools,
        name_prefix + 'asyncio': asyncio,
    }
    exec(dedent('''
        def {0}decorator({0}wrapper_coro):
            @{0}functools.wraps({0}coro)
            @{0}asyncio.coroutine
            def {wrapper_name}(*{0}args, **{0}kwargs):
                {0}result = yield from {0}wrapper_coro(*{0}args, **{0}kwargs)
                return {0}result
            return {wrapper_name}
        ''').format(name_prefix, wrapper_name=coro.__name__), namespace)
    return namespace[name_prefix + 'decorator']

الاستخدام:

def time_resulted(coro):
    @wrap_coroutine(coro)
    def wrapper(*args, **kargs):
        # ...
    return wrapper

إنه يعمل ولكن ربما تكون هناك طريقة أفضل من استخدام exec().

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top