امکان تعریف توابع با تعداد متغیر آرگومانها نیز وجود دارد. سه شکل وجود دارد که میتوانند با هم ترکیب شوند.
مفیدترین شکل، مشخص کردن یک مقدار پیشفرض برای یک یا چند آرگومان است. این یک تابع ایجاد میکند که میتوان آن را با آرگومانهای کمتری نسبت به آنچه تعریف شده فراخوانی کرد. به عنوان مثال:
def ask_ok(prompt, retries=4, reminder='Please try again!'):
while True:
reply = input(prompt)
if reply in {'y', 'ye', 'yes'}:
return True
if reply in {'n', 'no', 'nop', 'nope'}:
return False
retries = retries - 1
if retries < 0:
raise ValueError('invalid user response')
print(reminder)
این تابع به چندین شکل قابل فراخوانی است:
ask_ok('Do you really want to quit?')ask_ok('OK to overwrite the file?', 2)ask_ok('OK to overwrite the file?', 2, 'Come on, only yes or no!')این مثال همچنین کلمه کلیدی in را معرفی میکند. این بررسی میکند آیا یک توالی حاوی یک مقدار خاص هست یا نه.
مقدارهای پیشفرض در نقطه تعریف تابع در دامنه تعریف ارزیابی میشوند، بنابراین:
i = 5
def f(arg=i):
print(arg)
i = 6
f()
عدد 5 را چاپ میکند.
هشدار مهم: مقدار پیشفرض فقط یک بار ارزیابی میشود. این وقتی تفاوت ایجاد میکند که مقدار پیشفرض یک شیء تغییرپذیر مانند لیست، دیکشنری یا نمونههای اکثر کلاسها باشد. برای مثال، تابع زیر آرگومانهای منتقل شده در فراخوانیهای بعدی را انباشت میکند:
def f(a, L=[]):
L.append(a)
return L
print(f(1))
print(f(2))
print(f(3))
این خروجی میدهد:
[1] [1, 2] [1, 2, 3]
اگر نمیخواهید مقدار پیشفرض بین فراخوانیهای بعدی مشترک باشد، تابع را به این شکل بنویسید:
def f(a, L=None):
if L is None:
L = []
L.append(a)
return L
توابع همچنین میتوانند با آرگومانهای کلیدی به شکل kwarg=value فراخوانی شوند. مثلاً، تابع زیر:
def parrot(voltage, state='a stiff', action='voom', type='Norwegian Blue'):
print("-- This parrot wouldn't", action, end=' ')
print("if you put", voltage, "volts through it.")
print("-- Lovely plumage, the", type)
print("-- It's", state, "!")
یک آرگومان اجباری (voltage) و سه آرگومان اختیاری (state، action و type) میپذیرد. این تابع به هر یک از روشهای زیر قابل فراخوانی است:
parrot(1000) # ۱ آرگومان موقعیتی
parrot(voltage=1000) # ۱ آرگومان کلیدی
parrot(voltage=1000000, action='VOOOOOM') # ۲ آرگومان کلیدی
parrot(action='VOOOOOM', voltage=1000000) # ۲ آرگومان کلیدی
parrot('a million', 'bereft of life', 'jump') # ۳ آرگومان موقعیتی
parrot('a thousand', state='pushing up the daisies') # ۱ موقعیتی، ۱ کلیدی
اما فراخوانیهای زیر همگی نامعتبر خواهند بود:
parrot() # آرگومان اجباری فراموش شده parrot(voltage=5.0, 'dead') # آرگومان غیرکلیدی بعد از آرگومان کلیدی parrot(110, voltage=220) # مقدار تکراری برای همان آرگومان parrot(actor='John Cleese') # آرگومان کلیدی ناشناخته
در فراخوانی تابع، آرگومانهای کلیدی باید بعد از آرگومانهای موقعیتی باشند. تمام آرگومانهای کلیدی منتقل شده باید با یکی از آرگومانهای پذیرفته شده توسط تابع مطابقت داشته باشند و ترتیبشان مهم نیست. هیچ آرگومانی نمیتواند بیش از یک بار مقدار دریافت کند.
وقتی پارامتر نهایی رسمی به شکل **name وجود داشته باشد، یک دیکشنری حاوی تمام آرگومانهای کلیدی به جز آنهایی که متناظر با پارامترهای رسمی هستند دریافت میکند. این میتواند با پارامتر رسمی به شکل *name ترکیب شود که حاوی یک تاپل از آرگومانهای موقعیتی فراتر از لیست پارامترهای رسمی است. (*name باید قبل از **name بیاید.) به عنوان مثال:
def cheeseshop(kind, *arguments, **keywords):
print("-- Do you have any", kind, "?")
print("-- I'm sorry, we're all out of", kind)
for arg in arguments:
print(arg)
print("-" * 40)
for kw in keywords:
print(kw, ":", keywords[kw])
میتواند اینطور فراخوانی شود:
cheeseshop("Limburger", "It's very runny, sir.",
"It's really very, VERY runny, sir.",
shopkeeper="Michael Palin",
client="John Cleese",
sketch="Cheese Shop Sketch")
و خروجی چنین خواهد بود:
-- Do you have any Limburger ? -- I'm sorry, we're all out of Limburger It's very runny, sir. It's really very, VERY runny, sir. ---------------------------------------- shopkeeper : Michael Palin client : John Cleese sketch : Cheese Shop Sketch
به طور پیشفرض، آرگومانها میتوانند به تابع پایتون یا از طریق موقعیت یا به طور صریح از طریق کلیدی منتقل شوند. برای خوانایی و عملکرد، منطقی است نحوه انتقال آرگومانها را محدود کنیم تا توسعهدهنده فقط نیاز به نگاه کردن به تعریف تابع داشته باشد تا تعیین کند آیا آیتمها از طریق موقعیت، موقعیت یا کلیدی، یا فقط کلیدی منتقل میشوند.
تعریف تابع ممکن است این شکل را داشته باشد:
def f(pos1, pos2, /, pos_or_kwd, *, kwd1, kwd2):
----------- ---------- ----------
| | |
| Positional or keyword |
| - Keyword only
-- Positional only
که در آن / و * اختیاری هستند. اگر استفاده شوند، این نمادها نوع پارامتر را با نحوه انتقال آرگومانها به تابع نشان میدهند: فقط موقعیتی، موقعیتی یا کلیدی، و فقط کلیدی.
اگر / و * در تعریف تابع وجود نداشته باشند، آرگومانها میتوانند از طریق موقعیت یا کلیدی منتقل شوند.
امکان علامتگذاری پارامترهای خاص به عنوان فقط موقعیتی وجود دارد. اگر فقط موقعیتی باشند، ترتیب پارامترها مهم است و پارامترها نمیتوانند از طریق کلیدی منتقل شوند. پارامترهای فقط موقعیتی قبل از / قرار میگیرند. / برای جداسازی منطقی پارامترهای فقط موقعیتی از بقیه پارامترها استفاده میشود. پارامترهای بعد از / میتوانند موقعیتی یا کلیدی، یا فقط کلیدی باشند.
برای علامتگذاری پارامترها به عنوان فقط کلیدی، نشان دهید که پارامترها باید از طریق آرگومان کلیدی منتقل شوند، * را در لیست آرگومانها فقط قبل از اولین پارامتر فقط کلیدی قرار دهید.
تعریفهای تابع زیر را با دقت در نظر بگیرید و به نشانگرهای / و * توجه کنید:
>>> def standard_arg(arg): ... print(arg) >>> def pos_only_arg(arg, /): ... print(arg) >>> def kwd_only_arg(*, arg): ... print(arg) >>> def combined_example(pos_only, /, standard, *, kwd_only): ... print(pos_only, standard, kwd_only)
اولین تعریف تابع، standard_arg، شکل آشناتر است و هیچ محدودیتی در قرارداد فراخوانی ایجاد نمیکند و آرگومانها میتوانند از طریق موقعیت یا کلیدی منتقل شوند:
>>> standard_arg(2) 2 >>> standard_arg(arg=2) 2
دومین تابع pos_only_arg محدود شده فقط به استفاده از پارامترهای موقعیتی است زیرا / در تعریف تابع وجود دارد:
>>> pos_only_arg(1) 1 >>> pos_only_arg(arg=1) Traceback (most recent call last): File "", line 1, in TypeError: pos_only_arg() got some positional-only arguments passed as keyword arguments: 'arg'
سومین تابع kwd_only_arg فقط آرگومانهای کلیدی را مجاز میداند:
>>> kwd_only_arg(3) Traceback (most recent call last): File "", line 1, in TypeError: kwd_only_arg() takes 0 positional arguments but 1 was given >>> kwd_only_arg(arg=3) 3
و آخری هر سه قرارداد فراخوانی را در یک تعریف تابع واحد استفاده میکند:
>>> combined_example(1, 2, 3) Traceback (most recent call last): File "", line 1, in TypeError: combined_example() takes 2 positional arguments but 3 were given >>> combined_example(1, 2, kwd_only=3) 1 2 3 >>> combined_example(1, standard=2, kwd_only=3) 1 2 3 >>> combined_example(pos_only=1, standard=2, kwd_only=3) Traceback (most recent call last): File " ", line 1, in TypeError: combined_example() got some positional-only arguments passed as keyword arguments: 'pos_only'
در نهایت، این تعریف تابع را در نظر بگیرید که تداخل بالقوهای بین پارامتر موقعیتی name و **kwds که name به عنوان کلید دارد ایجاد میکند:
def foo(name, **kwds):
return 'name' in kwds
هیچ فراخوانی ممکنی وجود ندارد که آن را True برگرداند زیرا کلمه کلیدی 'name' همیشه به پارامتر اول متصل خواهد شد. اما با استفاده از / (آرگومانهای فقط موقعیتی)، این امکانپذیر است:
>>> def foo(name, /, **kwds):
... return 'name' in kwds
>>> foo(1, **{'name': 2})
True
موارد استفاده تعیین میکند از کدام پارامترها در تعریف تابع استفاده شود:
def f(pos1, pos2, /, pos_or_kwd, *, kwd1, kwd2):
به عنوان راهنما:
در نهایت، کمترین گزینه پرکاربرد مشخص کردن این است که تابعی میتواند با تعداد دلخواه آرگومان فراخوانی شود. این آرگومانها در یک تاپل بستهبندی میشوند. قبل از تعداد متغیر آرگومانها، صفر یا آرگومان معمولی دیگر میتواند وجود داشته باشد.
def write_multiple_items(file, separator, *args):
file.write(separator.join(args))
معمولاً این آرگومانهای variadic آخر در لیست پارامترهای رسمی هستند. هر پارامتر رسمی که بعد از پارامتر *args بیاید، آرگومانهای «فقط کلیدی» هستند.
>>> def concat(*args, sep="/"):
... return sep.join(args)
>>> concat("earth", "mars", "venus")
'earth/mars/venus'
>>> concat("earth", "mars", "venus", sep=".")
'earth.mars.venus'
حالت عکس وقتی اتفاق میافتد که آرگومانها قبلاً در یک لیست یا تاپل هستند اما باید برای فراخوانی تابع که آرگومانهای موقعیتی جداگانه نیاز دارد بازپاکسازی شوند. برای مثال، تابع داخلی range() آرگومانهای جداگانه start و stop را انتظار دارد. اگر جداگانه موجود نیستند، فراخوانی تابع را با عملگر * بنویسید تا آرگومانها از لیست یا تاپل بازپاکسازی شوند:
>>> list(range(3, 6)) # فراخوانی معمولی با آرگومانهای جداگانه [3, 4, 5] >>> args = [3, 6] >>> list(range(*args)) # فراخوانی با آرگومانهای بازپاکسازی شده از لیست [3, 4, 5]
به همین ترتیب، دیکشنریها میتوانند آرگومانهای کلیدی را با عملگر ** تحویل دهند:
>>> def parrot(voltage, state='a stiff', action='voom'):
... print("-- This parrot wouldn't", action, end=' ')
... print("if you put", voltage, "volts through it.", end=' ')
... print("E's", state, "!")
...
>>> d = {"voltage": "four million", "state": "bleedin' demised", "action": "VOOM"}
>>> parrot(**d)
-- This parrot wouldn't VOOM if you put four million volts through it. E's bleedin' demised !
توابع ناشناس کوچک میتوانند با کلمه کلیدی lambda ایجاد شوند. این تابع مجموع دو آرگومان خود را برمیگرداند: lambda a, b: a+b. توابع lambda میتوانند هرجا که اشیاء تابع مورد نیاز هستند استفاده شوند. از نظر نحوی به یک عبارت واحد محدود هستند. از نظر معنایی، فقط قند نحوی برای تعریف تابع معمولی هستند. مانند تعریفهای تابع تودرتو، توابع lambda میتوانند به متغیرها از دامنه محصورکننده مراجعه کنند:
>>> def make_incrementor(n): ... return lambda x: x + n >>> f = make_incrementor(42) >>> f(0) 42 >>> f(1) 43
مثال بالا از یک عبارت lambda برای برگرداندن یک تابع استفاده میکند. استفاده دیگر انتقال یک تابع کوچک به عنوان آرگومان است. مثلاً list.sort() یک تابع کلید مرتبسازی key میگیرد که میتواند تابع lambda باشد:
>>> pairs = [(1, 'one'), (2, 'two'), (3, 'three'), (4, 'four')] >>> pairs.sort(key=lambda pair: pair[1]) >>> pairs [(4, 'four'), (1, 'one'), (3, 'three'), (2, 'two')]
در اینجا چندین قرارداد درباره محتوا و قالببندی رشتههای مستندات آورده شده است.
خط اول همیشه باید خلاصهای کوتاه و مختصر از هدف شیء باشد. برای مختصر بودن، نباید نام یا نوع شیء را به طور صریح بیان کند (مگر اینکه نام اتفاقاً فعلی باشد که عملکرد تابع را توصیف میکند). این خط باید با حرف بزرگ شروع شود و با نقطه تمام شود.
اگر خطوط بیشتری در رشته مستندات وجود داشته باشد، خط دوم باید خالی باشد و خلاصه را از بقیه توضیحات جدا کند. خطوط بعدی باید یک یا چند پاراگراف توصیف قراردادهای فراخوانی شیء، عوارض جانبی و غیره باشند.
تجزیهکننده پایتون تورفتگی را از لیترالهای رشتهای چندخطی وقتی به عنوان رشته مستندات ماژول، کلاس یا تابع عمل میکنند حذف میکند.
مثالی از یک رشته مستندات چندخطی:
>>> def my_function():
... """Do nothing, but document it.
...
... No, really, it doesn't do anything:
...
... >>> my_function()
... >>>
... """
... pass
>>> print(my_function.__doc__)
Do nothing, but document it.
No, really, it doesn't do anything:
>>> my_function()
>>>
حاشیهنویسی توابع (Function annotations) اطلاعات فراداده کاملاً اختیاری درباره انواع استفاده شده توسط توابع تعریفشده توسط کاربر هستند (برای اطلاعات بیشتر به PEP 3107 و PEP 484 مراجعه کنید).
حاشیهنویسیها در ویژگی __annotations__ تابع به صورت دیکشنری ذخیره میشوند و تأثیری بر بخش دیگری از تابع ندارند. حاشیهنویسی پارامترها با دونقطه بعد از نام پارامتر تعریف میشود، و سپس عبارتی که مقدار حاشیهنویسی را ارزیابی میکند. حاشیهنویسی مقدار برگشتی با لیترال -> تعریف میشود. مثال زیر دارد:
>>> def f(ham: str, eggs: str = 'eggs') -> str:
... print("Annotations:", f.__annotations__)
... print("Arguments:", ham, eggs)
... return ham + ' and ' + eggs
>>> f('spam')
Annotations: {'ham': , 'return': , 'eggs': }
Arguments: spam eggs
'spam and eggs' این محتوا کاملا رایگان توسط تیم کدلپر ترجمه شده و در اختیار شما کاربران عزیز قرار گرفته است، هر گونه کپی برداری برای مقاصد غیر رایگان و بدون ذکر منبع، مورد پیگیری قانونی قرار میگیرد.
ترجمه شده از منبع: https://docs.python.org/3/tutorial