Skip to content

6. توابع

Image

6.1. اسکریپت [fonc_01]: دامنه متغیر

اسکریپت [fonc_01] مثال‌هایی از دامنه متغیرها بین توابع ارائه می‌دهد:


# دامنه متغیر
def f1():
    # استفاده از متغیرهای سراسری باید اجتناب شود
    # متغیر سراسری i
    global i
    i += 1
    # متغیر محلی j
    j = 10
    print(f"f1[i,j]=[{i},{j}]")


def f2():
    # استفاده از متغیرهای سراسری باید اجتناب شود
    # متغیر سراسری i
    global i
    i += 1
    # متغیر محلی j
    j = 20
    print(f"f2[i,j]=[{i},{j}]")


def f3():
    # متغیر محلی i
    i = 1
    # متغیر محلی j
    j = 30
    print(f"f3[i,j]=[{i},{j}]")


# برنامهٔ اصلی
i = 0
j = 0
# این دو متغیر تنها برای تابع f قابل دسترسی خواهند بود
# مگر اینکه تابع صراحتاً با استفاده از دستور `global` اعلام کند که می‌خواهد از آن‌ها استفاده کند
# یا اگر تابع متغیر سراسری را فقط برای خواندن استفاده کند
f1()
f2()
f3()
# j تغییر نکرده است، اما i تغییر کرده است
print(f"[i,j]=[{i},{j}]")

نتایج


C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\venv\Scripts\python.exe C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/fonctions/fonc_01.py
f1[i,j]=[1,10]
f2[i,j]=[2,20]
f3[i,j]=[1,30]
[i,j]=[2,0]

Process finished with exit code 0

یادداشت‌ها:

  • اسکریپت کاربرد متغیر i را که در توابع f1 و f2 به‌صورت سراسری (global) اعلام شده است، نشان می‌دهد. در این مورد، برنامهٔ اصلی و توابع f1 و f2 از یک متغیر i مشترک استفاده می‌کنند.

6.2. اسکریپت [fonc_02]: دامنه متغیر

اسکریپت [fonc_03] بر اساس اسکریپت [fonc_02] ساخته شده است و نشان می‌دهد که چگونه می‌توان از استفاده از متغیرهای سراسری اجتناب کرد:


# دامنهٔ متغیرها
def f1(i):
    # متغیر محلی i
    i += 1
    # متغیر محلی j
    j = 10
    print(f"f1[i,j]=[{i},{j}]")
    # ما مقدار اصلاح‌شده را بازمی‌گردانیم
    return i


def f2(i):
    # متغیر محلی i
    i += 1
    # متغیر محلی j
    j = 20
    print(f"f2[i,j]=[{i},{j}]")
    # بازگرداندن مقدار اصلاح‌شده
    return i


def f3():
    # متغیر محلی i
    i = 1
    # متغیر محلی j
    j = 30
    print(f"f3[i,j]=[{i},{j}]")


# برنامهٔ اصلی
i = 0
j = 0
# این دو متغیر تنها برای تابع f قابل دسترسی خواهند بود
# مگر اینکه با استفاده از دستور `global` صراحتاً اعلام کند که می‌خواهد از آن‌ها استفاده کند
# یا اگر تابع متغیر سراسری را فقط برای خواندن استفاده کند
i = f1(i)
i = f2(i)
f3()
# j تغییر نکرده است، اما i تغییر کرده است
print(f"[i,j]=[{i},{j}]")

توضیحات:

  • خطوط ۲ و ۱۲: به جای اینکه متغیر [i] به صورت سراسری (global) اعلام شود، به عنوان پارامتر به توابع f1 و f2 پاس داده می‌شود؛
  • خطوط 9 و 19: توابع f1 و f2 متغیر اصلاح‌شده [i] را به برنامه اصلی بازمی‌گردانند. برنامه اصلی آن را در خطوط 36 و 37 بازیابی می‌کند؛

نتایج


C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\venv\Scripts\python.exe C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/fonctions/fonc_02.py
f1[i,j]=[1,10]
f2[i,j]=[2,20]
f3[i,j]=[1,30]
[i,j]=[2,0]

Process finished with exit code 0

6.3. اسکریپت [fonc_03]: دامنه متغیرها

اسکریپت [fonc_03] ویژگی خاصی از متغیرها را نشان می‌دهد که هم در داخل یک تابع و هم در کدی که آن تابع را فراخوانی می‌کند، بسته به اینکه آیا متغیر برای مقاصد فقط-خواندن در داخل تابع استفاده می‌شود یا خیر، اعمال می‌شود.


def f1():
    # در اینجا، متغیر سراسری i شناخته‌شده است
    print(f"[f1] i={i}")
    # در اینجا متغیر سراسری j شناخته شده است
    print(f"[f1] j={j}")


def f2():
    # در اینجا متغیر سراسری `i` ناشناخته است
    # زیرا تابع f2 یک متغیر محلی با همان نام تعریف می‌کند
    # بنابراین این متغیر است که اولویت دارد
    try:
        # تلاش برای نمایش متغیر محلی `i` که در ادامه تعریف شده است
        print(f"[f2] i={i}")
        # دستور زیر `i` را به یک متغیر محلی در تابع `f2` تبدیل می‌کند
        i = 7
    except BaseException as erreur:
        print(f"[f2] erreur={erreur}")


def f3():
    # در اینجا متغیر سراسری i شناسایی نمی‌شود
    # زیرا تابع f3 یک متغیر محلی با همان نام تعریف می‌کند
    # بنابراین این متغیر اولویت دارد

    # بیانیه زیر i را به‌عنوان یک متغیر محلی اعلام می‌کند
    i = 7
    # display – در اینجا i شناخته‌شده است
    print(f"[f3] i={i}")


# main -----------
# متغیرهای سراسری برای توابع
i = 10
j = 20
# فراخوانی f1
f1()
print(f"[main] i={i}, j={j}")
# فراخوانی f2
f2()
print(f"[main] i={i}")
# فراخوانی f3
f3()
print(f"[main] i={i}")

یادداشت‌ها

  • خط ۳۴: کد اصلی یک متغیر [i] را تعریف می‌کند؛
  • خطوط ۱–۵: تابع f1 نیز از متغیری به نام [i] بدون تخصیص مقدار استفاده می‌کند. این یک عملیات خواندن بر روی متغیر [i] است. در این مورد، متغیر مورد استفاده [i] همان متغیر کد فراخوانی‌کننده، خط ۳۴، است؛
  • خطوط ۸–۱۸: تابع f2 نیز از متغیر [i] استفاده می‌کند اما در خط ۱۶ برای آن مقداری تعیین می‌کند. واگذاری مقدار به متغیر [i] در f2 به‌طور خودکار [i] را به یک متغیر محلی برای تابع [f2] تبدیل می‌کند. بنابراین این متغیر [i] را از کد فراخوانی‌کننده «پنهان» می‌کند؛
  • خط ۱۴: عملیات نوشتن روی متغیر محلی [i] با شکست مواجه می‌شود زیرا وقتی به خط ۱۴ می‌رسیم، این متغیر هیچ مقداری ندارد. مقدار آن در خط ۱۶ تعیین می‌شود. یک استثنا پرتاب خواهد شد. به همین دلیل، خط ۱۴ در داخل یک بلوک try/catch قرار داده شده است؛
  • خطوط 21–29: تابع f3 همانند تابع f2 عمل می‌کند اما متغیر محلی خود [i] را زودتر تعریف می‌کند؛

نتایج


C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\venv\Scripts\python.exe C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/fonctions/fonc_03.py
[f1] i=10
[f1] j=20
[main] i=10, j=20
[f2] erreur=local variable 'i' referenced before assignment
[main] i=10
[f3] i=7
[main] i=10

Process finished with exit code 0

6.4. اسکریپت [fonc_04]: حالت ارسال پارامتر

اسکریپت به شرح زیر است:


# تابع f1
def f1(a):
    a = 2


# تابع f2
def f2(a, b):
    a = 2
    b = 3
    return a, b


# ------------------------ main
x = 1
f1(x)
print(f"x={x}")
(x, y) = (-1, -1)
(x, y) = f2(x, y)
print(f"x={x}, y={y}")

نتایج

1
2
3
4
5
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\venv\Scripts\python.exe C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/fonctions/fonc_04.py
x=1
x=2, y=3

Process finished with exit code 0

یادداشت‌ها:

  • در پایتون، همه چیز یک شیء است. برخی از اشیاء به عنوان «غیرقابل تغییر» (immutable) شناخته می‌شوند: آنها قابل تغییر نیستند. این موضوع در مورد اعداد، رشته‌ها و تپل‌ها صدق می‌کند. هنگامی که اشیاء پایتون به عنوان آرگومان به توابع پاس داده می‌شوند، ارجاع آنها منتقل می‌شود، مگر اینکه این اشیاء «غیرقابل تغییر» باشند، که در این صورت خودِ مقدار شیء منتقل می‌شود؛
  • توابع f1 (خط ۲) و f2 (خط ۷) برای نشان دادن ارسال یک پارامتر خروجی در نظر گرفته شده‌اند. هدف این است که پارامتر حقیقی یک تابع توسط خود تابع اصلاح شود؛
  • خطوط ۲–۳: تابع f1 پارامتر صوری خود a را تغییر می‌دهد. می‌خواهیم بدانیم که آیا پارامتر حقیقی نیز تغییر خواهد کرد یا خیر؛
  • خطوط 14–15: پارامتر واقعی x=1 است. خط 2 نتایج نشان می‌دهد که پارامتر واقعی بدون تغییر باقی می‌ماند. بنابراین، پارامتر واقعی x و پارامتر صوری a دو شیء متفاوت هستند؛
  • خطوط ۸–۱۰: تابع f2 پارامترهای صوری a و b را تغییر می‌دهد و آن‌ها را به‌عنوان نتایج بازمی‌گرداند؛
  • خطوط 17–18: پارامترهای واقعی (x, y) به f2 ارسال می‌شوند و نتیجه f2 به (x, y) تخصیص داده می‌شود. خط 3 نتایج نشان می‌دهد که پارامترهای واقعی (x, y) تغییر کرده‌اند.

بنابراین می‌توان نتیجه گرفت که وقتی اشیاء «غیرقابل تغییر» پارامترهای خروجی هستند، باید در نتایج بازگردانده‌شده توسط تابع گنجانده شوند.

6.5. اسکریپت [fonc_05]: ترتیب توابع در یک اسکریپت

اسکریپت [fonc_05] نشان می‌دهد که اگر یک تابع قبلاً در کد با آن مواجه نشده باشد، نمی‌توان آن را فراخوانی کرد:


# ------------------------ main
print(f2(100, 200))

# تابع f1
def f1(a):
    return a + 10


# تابع f2
def f2(a, b):
    return f1(a + b)

یادداشت‌ها

  • خط ۲ باعث خطا خواهد شد زیرا از تابع f2 استفاده می‌کند که هنوز در اسکریپت تعریف نشده است؛

نتایج

1
2
3
4
5
6
7
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\venv\Scripts\python.exe C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/fonctions/fonc_05.py
Traceback (most recent call last):
  File "C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/fonctions/fonc_05.py", line 2, in <module>
    print(f2(100, 200))
NameError: name 'f2' is not defined

Process finished with exit code 1

6.6. اسکریپت [fonc_06]: ترتیبی که توابع در یک اسکریپت نوشته می‌شوند

اسکریپت [fonc_06] نشان می‌دهد که آنچه برای کد فراخوانی صدق می‌کند، برای توابع صدق نمی‌کند:


# تابع f2
def f2(a, b):
    return f1(a + b)


# تابع f1
def f1(a):
    return a + 10


# ------------------------ main
print(f2(100, 200))

یادداشت‌ها

  • خط ۳: تابع [f2] از تابع [f1] که بعداً در اسکریپت تعریف شده است، استفاده می‌کند. با این حال، این امر باعث ایجاد خطا نمی‌شود. بنابراین می‌توانیم نتیجه بگیریم که ترتیبی که توابع در یک اسکریپت پایتون با آن تعریف می‌شوند، بی‌اهمیت است؛

نتایج

1
2
3
4
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\venv\Scripts\python.exe C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/fonctions/fonc_06.py
310

Process finished with exit code 0

6.7. اسکریپت [fonc_07]: استفاده از ماژول‌ها

اسکریپت [fonc_07] نشان می‌دهد که چگونه توابع را در یک ماژول محصور کرد.

Image

ما توابع قابل استفاده مجدد را در یک ماژول جدا می‌کنیم. به جای کپی کردن آن‌ها از یک اسکریپت به اسکریپت دیگر:

  • ما آن‌ها را در یک فایل جداگانه قرار می‌دهیم که به شیوه‌ای خاص آن را اعلام می‌کنیم؛
  • اسکریپت‌هایی که به این توابع نیاز دارند، ماژولی را که حاوی آن‌هاست «وارد» می‌کنند؛

اسکریپت [fonctions_module_01] به شرح زیر است:


# تابع f2
def f2(a, b):
    return f1(a + b)

# تابع f1
def f1(a):
    return a + 10

راه‌های مختلفی برای اطمینان از اینکه توابع موجود در اسکریپت [fonctions_module_01] توسط اسکریپت‌های دیگر قابل ارجاع باشند، وجود دارد. این راه‌ها بسته به اینکه آیا اسکریپت در داخل [PyCharm] اجرا می‌شود یا خیر، متفاوت هستند.

در [PyCharm ماژول‌های واردشده در پوشه‌های مشخصی به نام [Sources Root] جستجو می‌شوند. دو روش برای تبدیل یک پوشه به یک [Sources Root] وجود دارد:

Image

  • در [4]، رنگ پوشه تغییر کرده است؛

پس از این عملیات، پوشه [fonctions/modules] به‌عنوان پوشهٔ منبع شناخته می‌شود. سپس می‌توانید در یک اسکریپت موارد زیر را بنویسید:

from fonctions_module_01 import f2

برای وارد کردن/استفاده از تابع f2 تعریف‌شده در ماژول [fonctions_module_01.py].

روش دیگر استفاده از خواص پروژه است:

  • در بالا، توالی [1-6] اجازه می‌دهد تا پوشه [shared] به عنوان پوشه‌ای برای ذخیره ماژول‌های وارد شده استفاده شود؛

فعلاً، ما هیچ پوشه‌ای مانند [Sources Root] را به جز ریشه پروژه تعریف نخواهیم کرد:

Image

با انجام این کار، می‌توانیم اسکریپت زیر را بنویسیم، [fonc-07]:


# استفاده از ماژول‌ها
import sys

# ------------------------ main
print(f"Python path={sys.path}")
from fonctions.shared.fonctions_module_01 import f2

print(f2(100, 200))
  • خط ۲: ما شیء [sys] را وارد می‌کنیم تا بتوانیم از خط ۵، ویژگی آن [path]، که آنچه به نام [Python Path] شناخته می‌شود را فراهم می‌کند، استفاده کنیم: فهرستی از پوشه‌هایی که برای ماژول‌های واردشده جستجو خواهند شد؛
  • خط ۶: ما تابع f2 را از ماژول [fonctions_module_01] وارد می‌کنیم. برای مشخص کردن این ماژول، از مسیری که از ریشه پروژه به این ماژول منتهی می‌شود، استفاده می‌کنیم. در PyCharm، ریشه پروژه همیشه در میان پوشه‌هایی که هنگام جستجوی یک ماژول واردشده (imported) در یک اسکریپت جستجو می‌شوند، گنجانده می‌شود. بنابراین این پوشه بخشی از [Python Path] پروژه است. این همان چیزی است که خط ۵ به ما امکان بررسی آن را می‌دهد؛
  • خط ۶: اگر بخواهیم مسیری را که از ریشه پروژه به پوشه [fonctions_module_01] منتهی می‌شود، توصیف کنیم، خواهیم نوشت [fonctions/shared/fonctions_module_01]. در مسیر یک ماژول، اسلش (/) با یک نقطه (.) جایگزین می‌شود. بنابراین می‌نویسیم [fonctions.modules.fonctions_module_01];
  • پس از خط ۶، تابع f2 شناخته شده است. از آن در خط ۸ استفاده می‌شود؛

نتایج


C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\venv\Scripts\python.exe C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/fonctions/fonc_07.py
Python path=['C:\\Data\\st-2020\\dev\\python\\cours-2020\\python3-flask-2020\\fonctions', 'C:\\Data\\st-2020\\dev\\python\\cours-2020\\python3-flask-2020', 'C:\\Data\\st-2020\\dev\\python\\cours-2020\\python3-flask-2020\\fonctions\\shared', 'C:\\myprograms\\Python38\\python38.zip', 'C:\\myprograms\\Python38\\DLLs', 'C:\\myprograms\\Python38\\lib', 'C:\\myprograms\\Python38', 'C:\\Data\\st-2020\\dev\\python\\cours-2020\\python3-flask-2020\\venv', 'C:\\Data\\st-2020\\dev\\python\\cours-2020\\python3-flask-2020\\venv\\lib\\site-packages']
310

Process finished with exit code 0

بالا:

  • همانطور که به رنگ سبز برجسته شده است، می‌توانیم ببینیم که ریشه پروژه نیز بخشی از [Python Path] است؛
  • با رنگ زرد برجسته شده، می‌بینیم که پوشه‌ای که حاوی اسکریپت اجرا شده است نیز بخشی از [Python Path] است؛
  • سایر عناصر [Python Path] مستقیماً از پوشه نصب پایتون آمده‌اند؛

اگر برای اجرای [fonc-07] از PyCharm استفاده نکنید چه اتفاقی می‌افتد؟

Image

Image

  • در [1]، اسکریپت [fonc-07] اجرا می‌شود. شما در پوشه [fonctions] قرار دارید؛
  • در [2]، می‌توانیم ببینیم که دایرکتوری اجرایی بخشی از [Python Path] است. این همیشه صادق است. همچنین می‌توانیم ببینیم که دایرکتوری ریشه [C:\\Data\\st-2020\\dev\\python\\cours-2020\\python3-flask-2020] بخشی از [Python Path] نیست؛
  • در [3]، تفسیرگر پایتون گزارش می‌دهد که ماژول [fonctions] را پیدا نمی‌کند؛

برای یافتن ماژول واردشده [fonctions.shared.fonctions_module_01]، تفسیرگر پایتون در پوشه‌های داخل [Python Path] به دنبال زیرپوشه‌ای به نام [fonctions] می‌گردد. این زیرپوشه را در هیچ کجا نمی‌تواند پیدا کند. دلیل آن این است که زیرپوشه [fonctions] در داخل پوشه [C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020] قرار دارد که بخشی از [Python Path] نیست.

اسکریپت [fonc-08] راه‌حل احتمالی برای این مشکل ارائه می‌دهد.

6.8. اسکریپت [fonc_08]: افزودن پوشه‌ها به [Python Path]

امکان تغییر [Python Path] به‌صورت برنامه‌ای وجود دارد، همان‌طور که در اسکریپت [fonc-08] نشان داده شده است:


# استفاده از ماژول‌ها
import os
import sys

# دایرکتوری اسکریپت
script_dir = os.path.dirname(os.path.abspath(__file__))
# مسیر پایتون قبل از اصلاح
print(f"Python path avant={sys.path}")
# افزودن پوشه [shared] به مسیر پایتون
sys.path.append(f"{script_dir}/shared")
# مسیر پایتون پس از اصلاح
print(f"Python path après={sys.path}")

# import f2
from fonctions_module_01 import f2

# ------------------------ main
print(f2(100, 200))

یادداشت‌ها

  • خط ۴: متغیر ویژه [__file__] نام اسکریپت در حال اجرا است. بسته به زمینه اجرا، این نام ممکن است مطلق (PyCharm) یا نسبی (کنسول) باشد. تابع [os.path.abspath] مسیر مطلق فایلی را که نام آن به تابع ارسال می‌شود، بازمی‌گرداند. تابع [os.path.dirname] مسیر مطلق پوشه‌ای را که حاوی فایلی است که نام آن به تابع ارسال می‌شود، بازمی‌گرداند؛
  • خط ۱۰: [sys.path] آرایه‌ای است که حاوی نام پوشه‌هایی است که هنگام جستجوی یک ماژول باید جستجو شوند. ریشه پروژه که در خط ۴ تعریف شده است به این آرایه اضافه می‌شود؛
  • آرایه [Python Path] قبل (خط ۸) و بعد (خط ۱۲) از اصلاح نمایش داده می‌شود؛
  • خط ۱۵: ماژول [fonctions_module_01]، که شامل تابع f2 است، وارد می‌شود؛

اجرای PyCharm نتایج زیر را تولید می‌کند:


C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\venv\Scripts\python.exe C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/fonctions/fonc_08.py
Python path avant=['C:\\Data\\st-2020\\dev\\python\\cours-2020\\python3-flask-2020\\fonctions', 'C:\\Data\\st-2020\\dev\\python\\cours-2020\\python3-flask-2020', 'C:\\Data\\st-2020\\dev\\python\\cours-2020\\python3-flask-2020\\fonctions\\shared', 'C:\\myprograms\\Python38\\python38.zip', 'C:\\myprograms\\Python38\\DLLs', 'C:\\myprograms\\Python38\\lib', 'C:\\myprograms\\Python38', 'C:\\Data\\st-2020\\dev\\python\\cours-2020\\python3-flask-2020\\venv', 'C:\\Data\\st-2020\\dev\\python\\cours-2020\\python3-flask-2020\\venv\\lib\\site-packages']
Python path après=['C:\\Data\\st-2020\\dev\\python\\cours-2020\\python3-flask-2020\\fonctions', 'C:\\Data\\st-2020\\dev\\python\\cours-2020\\python3-flask-2020', 'C:\\Data\\st-2020\\dev\\python\\cours-2020\\python3-flask-2020\\fonctions\\shared', 'C:\\myprograms\\Python38\\python38.zip', 'C:\\myprograms\\Python38\\DLLs', 'C:\\myprograms\\Python38\\lib', 'C:\\myprograms\\Python38', 'C:\\Data\\st-2020\\dev\\python\\cours-2020\\python3-flask-2020\\venv', 'C:\\Data\\st-2020\\dev\\python\\cours-2020\\python3-flask-2020\\venv\\lib\\site-packages', 'C:\\Data\\st-2020\\dev\\python\\cours-2020\\python3-flask-2020\\fonctions/shared']
310

Process finished with exit code 0
  • خط ۳: می‌بینیم که پوشه [shared] دو بار در [Python Path] ظاهر می‌شود. می‌توان از این امر جلوگیری کرد، اما در این مورد مشکلی نیست؛
  • خط ۴: تابع f2 در واقع اجرا شده است؛

حالا بیایید [fonc-08] را در یک ترمینال اجرا کنیم:


(venv) C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\fonctions>python fonc_08.py
Python path avant=['C:\\Data\\st-2020\\dev\\python\\cours-2020\\python3-flask-2020\\fonctions', 'C:\\myprograms\\Python38\\python38.zip', 'C:\\myprograms\\Python38\\DLLs', 'C:\\myprograms\\Python38\\lib', 'C:\\myprograms\\Python38', 'C:\\Data\\st-2020\\dev\\python\\cours-2020\\python3-flask-2020\\venv', 'C:\\Data\\st-2020\\dev\\python\\cours-2020\\python3-flask-2020\\venv\\lib\\site-packages']
Python path après=['C:\\Data\\st-2020\\dev\\python\\cours-2020\\python3-flask-2020\\fonctions', 'C:\\myprograms\\Python38\\python38.zip', 'C:\\myprograms\\Python38\\DLLs', 'C:\\myprograms\\Python38\\lib', 'C:\\myprograms\\Python38', 'C:\\Data\\st-2020\\dev\\python\\cours-2020\\python3-flask-2020\\venv', 'C:\\Data\\st-2020\\dev\\python\\cours-2020\\python3-flask-2020\\venv\\lib\\site-packages', 'C:\\Data\\st-2020\\dev\\python\\cours-2020\\python3-flask-2020\\fonctions/shared']
310
  • خط ۲: همانند قبل، پوشه [shared] در [Python Path] وجود ندارد؛
  • خط ۳: اکنون وجود دارد؛
  • خط ۴: تابع f2 یافت شده است؛

6.9. اسکریپت [fonc_09]: اعلام انواع پارامترها

اسکریپت [fonc_09] نشان می‌دهد که شما می‌توانید نوع پارامترهای یک تابع و همچنین نوع مقدار بازگشتی آن را اعلام کنید. با این حال، این اعلام تنها برای مستندسازی تابع مفید است. تفسیرگر پایتون بررسی نمی‌کند که پارامترهای واقعی تابع واقعاً از نوع مورد انتظار باشند. با این حال، PyCharm ناسازگاری‌های نوع بین پارامترهای واقعی و رسمی را نشان می‌دهد. همین دلیل به تنهایی برای ضروری کردن اعلان‌های نوع کافی است.

اسکریپت به شرح زیر است:


# یک تابع با مشخصات نوع پارامتر
# این فقط برای اهداف مستندسازی است، زیرا تفسیرگر پایتون آن را نادیده می‌گیرد


def show(param: int) -> int:
    print(f"param={param}, type(param)={type(param)}")
    return param + 1


# main -------------------------
print(show(4))
show("xyz")

نکات:

  • خط ۵: ما اعلام می‌کنیم که پارامتر اسمی [param] از نوع [int] است و مقدار بازگشتی تابع نیز از نوع [int] است؛
  • خط ۱۱: پارامتر واقعی تابع [show] از نوع صحیح است؛
  • خط ۱۲: پارامتر واقعی تابع [show] از نوع صحیح نیست؛

نتایج

C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\venv\Scripts\python.exe C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/fonctions/fonc_09.py
param=4, type(param)=<class 'int'>
5
param=xyz, type(param)=<class 'str'>
Traceback (most recent call last):
  File "C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/fonctions/fonc_09.py", line 11, in <module>
    show("xyz")
  File "C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/fonctions/fonc_09.py", line 6, in show
    return param + 1
TypeError: can only concatenate str (not "int") to str

Process finished with exit code 1
  • خط ۱۰: نوع پارامتر [param]، [str] است. هنگامی که این پیام ظاهر می‌شود، ما قبلاً کد تابع [show] را وارد کرده‌ایم. بنابراین تفسیرگر پایتون پذیرفته است که پارامتر واقعی تابع [show] از نوع [str] است؛
  • خط ۷ کد، استثنای نمایش‌داده‌شده در خطوط ۴–۱۰ نتایج را فعال می‌کند؛

با این حال PyCharm نشان می‌دهد که یک ناهنجاری وجود دارد:

Image

در [1]، PyCharm فراخوانی نادرست را برجسته کرده است.

6.10. اسکریپت [fonc_10]: پارامترهای نام‌گذاری‌شده

برای ارسال پارامترها به یک تابع، می‌توانید از نام‌های پارامترهای رسمی آن استفاده کنید. در این حالت، ملزم به رعایت ترتیب پارامترهای رسمی نیستید:


# پارامترهای واقعی را می‌توان با نام‌های رسمی‌شان ارجاع داد
def f(x, y):
    return x + y


# main
print(f(y=10, x=3))

یادداشت‌ها

  • خط ۲: تابع f دو پارامتر رسمی x و y دارد؛
  • خط ۷: هنگام فراخوانی تابع f، می‌توانید از نام‌های پارامترهای شکلی استفاده کنید. این روش دست‌کم در دو مورد می‌تواند مفید باشد:
    • تابع پارامترهای زیادی دارد که بیشتر آن‌ها مقدار پیش‌فرض دارند. هنگام فراخوانی تابع، تکنیک توصیف‌شده در بالا به شما امکان می‌دهد تنها آن پارامترهایی را که نمی‌خواهید از مقدار پیش‌فرضشان استفاده کنید، مقداردهی اولیه نمایید؛
    • اگر پارامترهای شکلی نام‌های معناداری داشته باشند، استفاده از پارامترهای نام‌گذاری‌شده هنگام فراخوانی تابع خوانایی کد را بهبود می‌بخشد؛

نتایج

1
2
3
4
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\venv\Scripts\python.exe C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/fonctions/fonc_10.py
13

Process finished with exit code 0

6.11. اسکریپت [fonc_11]: تابع بازگشتی

اسکریپت [fonc_11] نمونه‌ای از یک تابع بازگشتی (که خود را فراخوانی می‌کند) است:


# تابع بازگشتی
def fact(i: int) -> int:
    #factorial(1) برابر با 1 است
    # یک تابع بازگشتی باید در مقطعی خاتمه یابد
    if i == 1:
        return 1
    else:
        # فاکتوریل(i) = i * فاکتوریل(i-1)
        return i * fact(i - 1)


# ---------- main
print(f"fact(8)={fact(8)}")

توضیحات

  • خطوط ۱–۹: تابع فاکتوریل؛
  • خط ۹: تابع [factorielle] خود را فراخوانی می‌کند؛
  • خطوط ۵–۶: یک تابع بازگشتی باید همیشه وقتی شرط برقرار شد، خاتمه یابد؛ در غیر این صورت، بازگشتی بی‌نهایت رخ می‌دهد؛

نتایج

1
2
3
4
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\venv\Scripts\python.exe C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/fonctions/fonc_11.py
fact(8)=40320

Process finished with exit code 0

6.12. اسکریپت [fonc_12]: تابع بازگشتی

تابع [fonc_12] جزئیات بیشتری در مورد نحوه کارکرد بازگشتی ارائه می‌دهد:


# تابع بازگشتی
# رفتار پارامتر j


def fact(i: int, j: int) -> int:
    #خاتمه‌ی تابع بازگشتی
    if i == 1:
        print(f"j={j}")
        return 1
    else:
        # دستکاری j
        j += 1
        print(f"avant fact j={j}")
        #بازگشتی
        f = fact(i - 1, j)
        print(f"après fact j={j}")
        # نتیجه
        return i * f


# ---------- اصلی
print(f"fact(8)={fact(8, 0)}")

نظرات

  • خط ۵: ما همچنان در حال بررسی تابع فاکتوریل هستیم. ما پارامتر [j] را به آن اضافه می‌کنیم؛
  • خط ۱۲: متغیر j پس از هر محاسبه فاکتوریل افزایش می‌یابد. ما مقدار آن را قبل (خط ۱۲) و بعد (خط ۱۶) از بازگشتی (خط ۱۵) نمایش می‌دهیم؛

نتایج

C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\venv\Scripts\python.exe C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/fonctions/fonc_12.py
avant fact j=1
avant fact j=2
avant fact j=3
avant fact j=4
avant fact j=5
avant fact j=6
avant fact j=7
j=7
après fact j=7
après fact j=6
après fact j=5
après fact j=4
après fact j=3
après fact j=2
après fact j=1
fact(8)=40320

Process finished with exit code 0
  • خطوط ۲–۸: می‌بینیم که مقدار [j] تا زمانی که بازگشتی ادامه یابد، افزایش می‌یابد، تا زمانی که شرطی برقرار شود که باعث توقف بازگشتی می‌شود. از این نقطه به بعد، بازگشت از فراخوانی‌های تابع [fact] به ترتیب معکوس فراخوانی‌ها انجام می‌شود؛
  • خطوط ۱۰–۱۶: این خروجی‌ها بازگردشت‌های متوالی از فراخوانی factorial را منعکس می‌کنند. متغیر [j] به مقدار اولیه خود یعنی ۱ بازمی‌گردد؛