درونگری در پایتون

May 2020

ویدیویی وجود ندارد

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

 

بدیهی ترین ویژگی فانکشن‌ها که قابل بازرسی است نام آن است. همچنین یکی از ساده ترین ویژگی‌هایی است که در  ‌__name__ وجود دارد. مقدار بازگشتی رشته‌ای است که فانکشن با آن تعریف شده است. برای فانکشن‌های لامبدا که نام ندارند، مقدار __name__ برابر با رشته '<lambda>' خواهد بود:

>>> def example():
...     pass
...
>>> example.__name__
'example'
>>> (lambda: None).__name__
'<lambda>'

تشخیص نوع آبجکت‌ها

ماهیت پویا پایتون میتواند فهمیدن اینکه آیا شما دارید مقدار درست را میگیرید یا فهمیدن نوع درست مقدار را دشوار کند. پایتون برای اینکه بتوانید به این اطلاعات دسترسی داشته باشید راه حل‌هایی را پیشنهاد میدهد اما باید بدانید که اینها دو کار متفاوت هستند و پایتون دو روش متفاوت را برای شما قرار داده است.

 

بدیهی‌ترین نیاز شما این است که بتوانید تشخیص دهید که چه نوع آبجکتی دریافت میکنید. برای اینکار پایتون فانکشن type را در اختیار شما قرار داده تا بتوانید نوع آبجکت را مشخص کنید. مقدار برگشتی این فانکشن کلاسی است که آبجکت از آن ساخته شده است حتی اگر این آبجکت به صورت ضمنی ساخته شده باشد:

>>> type('example')
<type 'str'>
>>> class Test:
...     pass
...
>>> type(Test)
<type 'classobj'>
>>> type(Test())
<type 'instance'>

روش‌های دیگری نیز وجود دارند. مثلا با استفاده از فانکشن isinstance میتوانید آبجکت خود را با نوع کلاسی که انتظار دارید مطابقت دهید:

>>> def test(value):
...     if isinstance(value, int):
...         print('Found an integer!')
...
>>> test('0')
>>> test(0)
Found an integer!

برای اطلاعات بیشتر در رابطه با isinstance به این لینک مراجعه کنید.

 

ماژول‌ها و پکیج‌ها

فانکشن‌ها و کلاس‌هایی که در پایتون ایجاد میشوند در داخل یک ماژول قرار میگیرند. همچنین به طور معمول تمام ماژول‌ها در یک پکیج قرار دارند. برای دسترسی به به ساختار هر پکیج به سادگی میتوانید از مستندات کمک بگیرید یا نگاهی به سورس برنامه بیندازید. به هر حال نوشتن یک قطعه کد برای اینکه مشخص کند هر جزء در کجای قرار دارد میتواند مفید باشد.

 

به همین خاطر معمولا در فانکشن‌ها و کلاس‌ها از یک attribute به نام __module__ استفاده میشود که محل ایجاد شدن آنها را مشخص میکند. البته در زمان کار کردن با مفسر پایتون باید دقت کنید که هیچ نامی برای ماژول قرار نخواهد گرفت. در صورت فراخوانی مقدار __main__ را نشان خواهد داد:

>>> def example():
...     pass
...
>>> example
<function example at 0x...>
>>> example.__module__
'__main__'

Docstring

از آنجایی که شما میتوانید با استفاده از docstring در داخل کد اقدام به مستندسازی برنامه‌هایتان کنید، پایتون docstring را به عنوان بخشی از آبجکت فانکشن در نظر میگیرد. شما میتوانید با استفاده از __doc__ به docstringهای کدهایتان دسترسی داشته باشید. به مثال زیر دقت کنید که در آن به docstring دسترسی پیدا کرده‌ایم:

def example():
    """This is just an example to illustrate docstring access."""
    pass
print(example.__doc__)

>>>This is just an example to illustrate docstring access.

و حالا این کد را داخل مفسر پایتون امتحان کنید:

>>> def divide(x, y):
...     """
...     divide(integer, integer) -> floating point
...
...     This is a more complex example, with more comprehensive documentation.
...     """
...     return float(x) / y # Use float()for compatibility prior to 3.0
...
>>> divide.__doc__
'\n    divide(integer, integer) -> floating point\n\n    This is a more
complex example, with more comprehensive documentation.\n    '
>>> print(divide.__doc__)
    divide(integer, integer) -> floating point

    This is a more complex example, with more comprehensive documentation.

همانطور که دیدید docstringهای ساده میتوانند بسیار مفیده باشند و شما به هر شکل که مایل هستید میتوانید از آنها استفاده کنید. اما متاسفانه docstringهای پیچیده کار را مشکل میکنند چون فاصله‌ها و خطوط خالی را نیز چاپ میکنند. مشکل بزرگتر اینست که کد شما بدون گشتن دنبال کاراکتری خاص نمیتواند تشخیص دهد که دنبال چه نوع docstring هستید. حتی اگر در مفسر پایتون docstring را print کنید هنوز هم خطوط خالی اضافی قبل و بعد از docstring و تورفتگی های اضافی که در فایل بودند را خواهید داشت.

برای خواندن docstringهای پیچیده، مثل چیزی که در مثال بالا دیدید، ماژول inspect یک فانکشن به نام getdoc دارد که docstringها را خوانده و به شکل مرتب به شما نشان میدهد. هر خط اضافی قبل و بعد از docstring و تورفتگی‌های اضافی را حذف خواهد کرد. در مثال زیر همان docstring را با ماژول inspect میبینید:

>>> import inspect
>>> print(inspect.getdoc(divide))

divide(integer, integer) -> floating point
This is a more complex example, with more comprehensive documentation.

البته ما هنوز هم باید از print استفاده کنیم چون کاراکتر n\ هنوز در رشته وجود خواهد داشت. تمام کاری که inspect.getdoc انجام داد این بود که خطوط اضافی که برای هماهنگ بودن docstring با کد نوشته شده بودند را حذف کرد. این هم یک مثال پیچیده‌تر برای اینکه ببینید inspect.getdoc چطور کار میکند:

>>> def clone(obj, count=1):
...     """
...    clone(obj, count=1) -> list of cloned objects
...
...    Clone an object a specified number of times, returning the cloned
...    objects as a list. This is just a shallow copy only.
...
...    obj
...        Any Python object
...    count
...        Number of times the object will be cloned
...
...      >>> clone(object(), 2)
...      [<object object at 0x12345678>, <object object at 0x87654321>]
...    """
...    import copy
...    return [copy.copy(obj) for x in count]
...
>>> print(inspect.getdoc(clone))
clone(obj, count=1) -> list of cloned objects
Clone an object a specified number of times, returning the cloned
objects as a list. This is just a shallow copy only.
obj
    Any Python object
count
    Number of times the object will be cloned
  >>> clone(object(), 2)
  [<object object at 0x12345678>, <object object at 0x87654321>]

دقت کنید که چطور هنوز توضیحاتی که مربوط به آرگومان‌ها بود هنوز با 4 تورفتگی نشان داده میشود درست مثل سورس کد.

ارسال نظر

اگر قراره سوالی بپرسید که داخلش کد هست، بهتره از کدتون عکس بگیرید و به ایمیلی که پایین نوشتم بفرستید

kamelyosof@iran.ir

June 2020

سلام میخواستم بگم برای تک قسمتی ها همون ویدئو ضبظ کنید بهتره
گیراییش بالاتره

پاسخ به نظر


امیرحسین بیگدلو

June 2020

سلام
با نظرتون موافقم. اما یکسری از موارد رو اگر بخوام ویدیو کنم، کوتاه خواهد بود. مثلا همین موضوع اگر ویدیو بود کلا ۵۰ ثانیه بیشتر نمیشد.
به علاوه این مقالات رو من نمینویسم، کس دیگه‌ای مینویسه.
برای موارد مهم حتما ویدیو ضبط میکنم.