كيفية تزيين أسينسيو.كوروتين للاحتفاظ بها _ _ اسم__?
-
21-12-2019 - |
سؤال
لقد حاولت أن أكتب وظيفة الديكور الذي يلتف 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()
.