راه
۵ کیو· متوسط رو به بالامتوسطPYTHON۴ ثانیه · ۲۵۶ مگابایت

چیراکسی؟

برای متمرکز کردن مدیریت و نمایندگی اطلاعات مربوط به استثناها در یک ساختار یکپارچه، در این تمرین از یک کلاس به نام ExceptionProxy استفاده می‌کنیم. هدف این است که به‌ازای هر فراخوانی تابع، چه موفقیت‌آمیز باشد چه منجر به استثنا شود، یک شیء از این کلاس ایجاد کنیم که وضعیت آن فراخوانی را در خود نگه دارد.


کلاس ExceptionProxy نماینده‌ای (proxy) برای اطلاعات مربوط به یک استثنا یا نتیجه‌ی موفقیت‌آمیز یک فراخوانی تابع است. هر شیء از این کلاس دو ویژگی اصلی خواهد داشت:

  • message: متنی شامل پیام استثنا در صورت بروز خطا، یا پیامی مشخص (مانند 'ok!') در صورت اجرای موفقیت‌آمیز.
  • function: شیء تابع پایتونی که فراخوانی شده و منجر به این نتیجه (خطا یا موفقیت) شده‌است.
python
class ExceptionProxy:
    def __init__(self, message, function):
        self.message = message
        self.function = function

از شما می‌خواهیم تابعی به نام transform_exceptions بنویسید که:

  1. یک لیست از توابع را به‌عنوان ورودی می‌گیرد. فرض کنید تمام توابع در این لیست بدون آرگومان هستند.
  2. به ترتیب، هر تابع موجود در لیست ورودی را فراخوانی می‌کند.
  3. برای هر فراخوانی تابع، یک شیء جدید از کلاس ExceptionProxy ایجاد می‌کند.
  4. اگر فراخوانی تابع با موفقیت انجام شد، شیء ExceptionProxy ایجادشده را با message برابر با رشته 'ok!' و function برابر با همان تابعِ فراخوانی‌شده، مقداردهی می‌کند.
  5. اگر در حین فراخوانی تابع استثنایی رخ داد، شیء ExceptionProxy ایجادشده را با message برابر با پیام متنی استثنای رخ‌داده و function برابر با همان تابعِ فراخوانی‌شده، مقداردهی می‌کند.
  6. در نهایت، تابع transform_exceptions باید یک لیست شامل تمام اشیاء ExceptionProxy ایجادشده (به همان ترتیب توابع ورودی) را بازگرداند.

🎯 نمونه

python
def f():
    return 1 / 0

def g():
    pass

exceptions_info = transform_exceptions([f, g])

for item in exceptions_info:
    print(f"function: {item.function}")
    print(f"function name: {item.function.__name__}")
    print(f"message: {item.message}")
    print("-" * 10)

خروجی:

text
function: <function f at 0x...>
function name: f
message: division by zero
----------
function: <function g at 0x...>
function name: g
message: ok!
----------

همان‌طور که در خروجی مشاهده می‌شود، اجرای تابع f منجر به استثنای ZeroDivisionError با پیام division by zero شده است که در شیء ExceptionProxy مربوط به آن ذخیره شده‌است. از سوی دیگر، اجرای تابع g موفقیت‌آمیز بوده و پیام ok! برای آن ثبت شده‌است. در هر دو حالت، شیء تابع اصلی نیز در ویژگی function نگهداری شده‌است.


💡 چالش‌های حل تمرین

⚠️ کلاس ExceptionProxy را چگونه پیاده‌سازی کنم؟

کلاس ExceptionProxy تنها دارای یک تابع سازنده است که دو مقدار message و function را گرفته و self.message و self.function را با این مقادیر مقداردهی می‌کند.

⚠️ چگونه توابعی که داخل لیست func_ls قرار دارند را صدا بزنم؟

برای صدا زدن هر تابع از لیست func_ls باید با استفاده از for یک پیمایش روی اعضای این لیست انجام دهید. اگر هر عضو از این لیست func نام گیرد، به‌صورت func() می‌توان آن را فراخوانی کرد:

python
for func in func_ls:
    ...
    func()
    ...

⚠️ چگونه می‌توانم به متن پیام یک استثنا دسترسی داشته باشم؟

به‌صورت زیر می‌توانید به پیام یک استثنا دسترسی پیدا کنید (متغیر message حاوی متن خطا خواهد بود).

python
try:
    ...
except Exception as e:
    ...
    message = str(e)

⚠️ چگونه تابع را در ویژگی function قرار دهم؟

نیازی نیست نام تابع را در ویژگی function قرار دهید؛ بلکه باید خود تابع در آن قرار گیرد. اگر هنگام پیمایش روی func_ls نام func را روی هر تابع گذاشته باشید، باید همین تابع func را مستقیماً در ویژگی function شیئی که از کلاس ExceptionProxy ساخته‌اید، قرار دهید.

💡 نکته: استفاده از __name__ در مثال تنها جهت نمایش خروجی نمونه است که خارج از تابع transform_exceptions قرار گرفته‌است و در داخل کلاس ExceptionProxy یا تابع transform_exceptions نباید نام تابع را به جای خود شیء تابع ذخیره کنید.

⚠️ خروجی تابع transform_exceptions چیست؟

تابع transform_exceptions نباید چیزی را چاپ کند، بلکه باید لیست اشیاء ExceptionProxy را با استفاده از return بازگرداند.


💡 راهنمایی نهایی

ساختار کلی تابع transform_exceptions به این شکل است:

python
def transform_exceptions(func_ls):
    result = []
    for func in func_ls:
        try:
            func()
            proxy = ExceptionProxy("ok!", func)
        except Exception as e:
            proxy = ExceptionProxy(str(e), func)
        result.append(proxy)
    return result
  • یک لیست خالی به نام result برای ذخیره‌ی اشیاء ExceptionProxy ایجاد می‌کنیم.
  • با یک حلقه for روی توابع ورودی پیمایش می‌کنیم.
  • در بلوک try، تابع را فراخوانی می‌کنیم و اگر موفق بود، یک شیء ExceptionProxy با پیام 'ok!' و خود تابع ایجاد می‌کنیم.
  • اگر استثنا رخ داد، در بلوک except یک شیء ExceptionProxy با پیام استثنا و خود تابع ایجاد می‌کنیم.
  • هر شیء ایجادشده را به لیست result اضافه می‌کنیم.
  • در نهایت، لیست result را با return برمی‌گردانیم.

✅ این تمرین را در ویرایشگر خود پیاده‌سازی کنید. به یاد داشته باشید که تابع transform_exceptions باید یک لیست از اشیاء ExceptionProxy برگرداند و چیزی را چاپ نکند.

نمونه‌ها

ورودی نمونه ۱
def f():
    return 1 / 0

def g():
    pass

result = transform_exceptions([f, g])
[(item.function.__name__, item.message) for item in result]
خروجی نمونه ۱
[('f', 'division by zero'), ('g', 'ok!')]
ورودی نمونه ۲
def a():
    raise ValueError("bad value")

def b():
    return [][3]

def c():
    return {}["k"]

[item.message for item in transform_exceptions([a, b, c])]
خروجی نمونه ۲
['bad value', 'list index out of range', "'k'"]
ورودی نمونه ۳
transform_exceptions([])
خروجی نمونه ۳
[]
ورودی نمونه ۴
def h():
    return 5

result = transform_exceptions([h])
[result[0].function is h, result[0].message, type(result[0]).__name__]
خروجی نمونه ۴
[True, 'ok!', 'ExceptionProxy']
ورودی نمونه ۵
def parse():
    return int("x")

def fine():
    return "ignored"

[item.message for item in transform_exceptions([fine, parse, fine])]
خروجی نمونه ۵
['ok!', "invalid literal for int() with base 10: 'x'", 'ok!']

برای نوشتن پاسخ و دریافت امتیاز، ابتدا وارد حساب شوید.

ورود