بسیاری از مواقع در پایتون میبینید که از عملگرهای *
و **
استفاده میشود. این دو عملگر گاهی برای برنامهنویسان تازهکار و حتی کسانی که از زبانهای برنامهنویسی دیگر به پایتون مهاجرت کردهاند، کمی گیج کننده هستند. در این مقاله، قصد دارم توضیح دهم این عملگرها چه هستند و چگونه میتوان از آنها بهره برد.
عملگرهای *
و **
طی سالها قابلیتهای خود را گسترش دادهاند. من تمام روشهایی که استفاده از این عملگرها را بررسی خواهم کرد و همچنین توضیح میدهم کدام یک فقط در نسخههای جدید پایتون قابل استفاده هستند. اگر در زمان پایتون 2 با این عملگرها آشنا شدهاید، پیشنهاد میکنم حتماً این مقاله را مطالعه کنید زیرا پایتون 3 کاربردهای جدید زیادی برای این عملگرها اضافه کرده است.
چیزی که در مورد آن صحبت نمیکنم
وقتی در این مقاله از * و ** صحبت میکنم، در مورد عملگرهای prefix * و ** صحبت میکنم، نه عملگرهای infix. بنابراین من در مورد ضرب و توان صحبت نمیکنم:
>>> 2 * 5
10
>>> 2 ** 5
32
پیشنهاد مطالعه: ارتقاء امنیت وبسایت با استفاده از کانفیگهای امنیتی Nginx
استفاده از * و ** در پایتون
ارسال آرگومانها به یک تابع
در پایتون، از *
و **
برای ارسال آرگومانها به یک تابع استفاده میشود. به عنوان مثال، args
*لیستی از آرگومانهای نامحدود را به تابع ارسال میکند و kwargs
**دیکشنریای از آرگومانهای کلیدی را میپذیرد.
گرفتن آرگومانهای ارسال شده به یک تابع
شما میتوانید از args
*برای گرفتن آرگومانهای نامحدود و از kwargs
**برای گرفتن آرگومانهای کلیدی استفاده کنید. این باعث انعطاف پذیری بیشتری در توابع شما میشود.
پذیرش آرگومانهای فقط کلیدی
با استفاده از *
در امضای تابع، میتوانید مطمئن شوید که تمامی آرگومانهای بعد از آن به عنوان آرگومانهای کلیدی پذیرش شوند.
پیشنهاد مطالعه: مراقب امنیت وبسایت خودباشید! ۱۵ خطر امنیتی ۲۰۲۴
ستاره برای باز کردن بسته بندی در فراخوانی تابع
>>> fruits = ['lemon', 'pear', 'watermelon', 'tomato']
>>> print(fruits[0], fruits[1], fruits[2], fruits[3])
lemon pear watermelon tomato
# همین عملکرد با رویکرد جدید
>>> print(*fruits)
lemon pear watermelon tomato
print(*fruits)
همه آیتمهای موجود در لیست fruits
را به عنوان آرگومانهای جداگانه به تابع print
ارسال میکند، بدون نیاز به دانستن تعداد آرگومانهای موجود در لیست. توانایی ارسال همه آیتمهای موجود در یک تکرار بهعنوان آرگومانهای جداگانه بدون *
امکانپذیر نیست، مگر اینکه لیست طول ثابتی داشته باشد. اینجا یک مثال دیگر داریم:
def example(list_of_lists):
return [
list(row) for row in zip(*list_of_lists)
]
در اینجا ما یک لیست از لیست ها را میپذیریم و یک لیست “تغییر یافته” از لیست ها را بر میگردانیم.
>>> example([[1, 4, 7], [2, 5, 8], [3, 6, 9]])
[[1, 2, 3], [4, 5, 6], [7, 8, 9]]
عملگر ** نیز کاری مشابه انجام میدهد، اما با آرگومان های keyword . عملگر ** به ما اجازه میدهد تا یک دیکشنری از جفت های کلید-مقدار را برداریم و آن را در آرگومان های کلمه کلیدی در یک فراخوانی تابع باز کنیم.
>>> date_info = {'year': "2020", 'month': "01", 'day': "01"}
>>> filename = "{year}-{month}-{day}.txt".format(**date_info)
>>> filename
'2020-01-01.txt'
یا مثالی دیگر:
>>> fruits = ['lemon', 'pear', 'watermelon', 'tomato']
>>> numbers = [2, 1, 3, 4, 7]
>>> print(*numbers, *fruits)
2 1 3 4 7 lemon pear watermelon tomato
با توجه به تجربه من، استفاده از **
برای باز کردن آرگومانهای کلیدواژه در فراخوانی تابع چندان رایج نیست. بیشتر این استفاده را هنگام تمرین وراثت میبینیم: فراخوانی به super()
اغلب شامل هر دو *
و **
میشود. از پایتون 3.5 به بعد، هر دو *
و **
میتوانند چندین بار در فراخوانی تابع استفاده شوند. استفاده از چند بار *
گاهی اوقات میتواند مفید باشد.
class Base:
def __init__(self, **kwargs):
self.base_attr = kwargs.get('base_attr', 'default value')
class Derived(Base):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.derived_attr = kwargs.get('derived_attr', 'default value')
# نمونهسازی
obj = Derived(base_attr='value1', derived_attr='value2')
print(obj.base_attr) # خروجی: value1
print(obj.derived_attr) # خروجی: value2
>>> date_info = {'year': "2020", 'month': "01", 'day': "01"}
>>> track_info = {'artist': "Beethoven", 'title': 'Symphony No 5'}
>>> filename = "{year}-{month}-{day}-{artist}-{title}.txt".format(
... **date_info,
... **track_info,
... )
>>> filename
'2020-01-01-Beethoven-Symphony No 5.txt'
**
باید مراقب باشید. توابع در پایتون نمیتوانند آرگومان کلیدواژه یکسانی داشته باشند که چندین بار مشخص شده باشد، بنابراین کلیدهای هر دیکشنری مورد استفاده با **
باید متمایز باشند، در غیر این صورت یک استثنا ایجاد میشود.
def func(a, b, c):
print(a, b, c)
dict1 = {'a': 1, 'b': 2}
dict2 = {'c': 3, 'b': 4} # کلید 'b' تکراری است
# این فراخوانی خطا ایجاد میکند
try:
func(**dict1, **dict2)
except TypeError as e:
print(e) # خروجی: func() got multiple values for keyword argument 'b'
پیشنهاد مطالعه: نکات امنیتی در Django: مقابله با ۱۲ مدل حمله سایبری در جنگو
ستاره برای بسته بندی آرگومان های داده شده به تابع
هنگام تعریف یک تابع، عملگر *
میتواند برای گرفتن تعداد نامحدود از آرگومانهای موقعیتی که به تابع داده میشود، استفاده شود. این آرگومانها به صورت یک تاپل (tuple) در میآیند.
در مثال زیر، *args
تمامی آرگومانهای موقعیتی که به تابع example_func
داده میشود را به عنوان یک تاپل جمعآوری میکند.
def example_func(*args):
print(args)
example_func(1, 2, 3)
# خروجی: (1, 2, 3)
# مثال بالا با خروجی آنپک شده
def example_func(*args):
print(*args)
>>example_func(1, 2, 3)
# >> 1,2,3
def tag(tag_name, **attributes):
attribute_list = [
f'{name}="{value}"'
for name, value in attributes.items()
]
return f"<{tag_name} {' '.join(attribute_list)}>"
>>> tag('a', href="https://tecpro.ir")
''
>>> tag('img', height=20, width=40, src="face.jpg")
''
پیشنهاد مطالعه: Sniffing: استراق سمع در دنیای دیجیتال
پذیرش آرگومانهای فقط کلیدواژه در پایتون
از پایتون 3 به بعد، میتوانیم از یک نحو ویژه برای پذیرش آرگومانهای فقط کلیدواژه استفاده کنیم. آرگومانهای فقط کلیدواژه، آرگومانهای تابعی هستند که تنها با استفاده از نحو کلیدواژه قابل تعیین هستند، به این معنی که نمیتوان آنها را به صورت موقعیتی مشخص کرد. برای پذیرش این نوع آرگومانها، میتوانیم آرگومانهای نامگذاری شده را پس از استفاده از *
در هنگام تعریف تابع خود قرار دهیم.
def get_multiple(dictionary, *keys, default=None):
return [
dictionary.get(key, default) for key in keys
]
>>> fruits = {'lemon': 'yellow', 'orange': 'orange', 'tomato': 'red'}
>>> get_multiple(dictionary=fruits,'lemon', 'tomato', 'squash', default='unknown')
['yellow', 'red', 'unknown']
پیشنهاد مطالعه: ارتقاء امنیت وبسایت با استفاده از کانفیگهای امنیتی Nginx
آرگومان های فقط کلیدواژه بدون آرگومان های موقعیتی
*
-on-its-own فراهم می کند:
def with_previous(iterable, *, fillvalue=None):
"""Yield each iterable item along with the item before it."""
....previous = fillvalue
....for item in iterable:
........yield previous, item
........previous = item
این تابع یک آرگومان iterable را میپذیرد، که میتواند به صورت موقعیت (به عنوان اولین آرگومان) یا با نام آن و یک آرگومان fillvalue که یک آرگومان فقط کلمه کلیدی است مشخص شود. به این معنی است که ما میتوانیم with_previous را به این صورت فراخوانی کنیم:
>>> list(with_previous([2, 1, 3], fillvalue=0))
[(0, 2), (2, 1), (1, 3)]
>>> list(with_previous([2, 1, 3], 0))
Traceback (most recent call last):
File "", line 1, in
TypeError: with_previous() takes 1 positional argument but 2 were given
>>> help(sorted)
Help on built-in function sorted in module builtins:
sorted(iterable, /, *, key=None, reverse=False)
Return a new list containing all items from the iterable in ascending order.
A custom key function can be supplied to customize the sort order, and the
reverse flag can be set to request the result in descending order.
پیشنهاد مطالعه: راز موفقیت وبسایتها: هاست چیست و چرا انتخابش مهم است؟
ستاره در باز کردن بسته بندی تاپل
پایتون 3 همچنین روش جدیدی برای استفاده از عملگر * اضافه کرده که فقط تا حدودی به ویژگی های *-when-defining-a-function و *-when-calling-a-function مرتبط است. اپراتور * هم اکنون میتواند در باز کردن بسته بندی تاپل استفاده شود:
>>> fruits = ['lemon', 'pear', 'watermelon', 'tomato']
>>> first, second, *remaining = fruits
>>> remaining
['watermelon', 'tomato']
>>> first, *remaining = fruits
>>> remaining
['pear', 'watermelon', 'tomato']
>>> first, *middle, last = fruits
>>> middle
['pear', 'watermelon']
اگر میپرسید «کجا میتوانم از این در کد خودم استفاده کنم»، به مثال زیر دقت کنید که چطور میتوانید به راحتی از این اپراتور برای آنپک کردن استفاده کنید:
>>> fruits = ['lemon', 'pear', 'watermelon', 'tomato']
>>> ((first_letter, *remaining), *other_fruits) = fruits>>
>>> first_letter
['l']
>>> remaining
['e', 'm', 'o', 'n']
>>> other_fruits
['pear', 'watermelon', 'tomato']
پیشنهاد مطالعه: آموزش جامع تنظیمات HAProxy
کاربرد ستاره در لیستها
پایتون 3.5 تعداد زیادی ویژگی جدید مرتبط با * را از طریق PEP 448 معرفی کرد. یکی از بزرگترین این ویژگیها، امکان استفاده از * برای ریختن یک تکرار در لیست جدید است. فرض کنید تابعی دارید که هر دنباله ای را میگیرد و لیستی را با دنباله و عکس آن دنباله به هم پیوسته برمیگرداند:
def example(sequence):
return list(sequence) + list(reversed(sequence))
def example(sequence):
....return [*sequence, *reversed(sequence)]
مثالی دیگر:
def rotate_first_item(sequence):
....return [*sequence[1:], sequence[0]]
>>> fruits = ['lemon', 'pear', 'watermelon', 'tomato']
>>> (*fruits[1:], fruits[0])
('pear', 'watermelon', 'tomato', 'lemon')
>>> uppercase_fruits = (f.upper() for f in fruits)
>>> {*fruits, *uppercase_fruits}
{'lemon', 'watermelon', 'TOMATO', 'LEMON', 'PEAR', 'WATERMELON', 'tomato', 'pear'}
>>> set().union(fruits, uppercase_fruits)
{'lemon', 'watermelon', 'TOMATO', 'LEMON', 'PEAR', 'WATERMELON', 'tomato', 'pear'}
پیشنهاد مطالعه: dns چیست؟ راهنمای جامع DNS برای کاربران
دو ستاره در دیکشنری:
>>> date_info = {'year': "2020", 'month': "01", 'day': "01"}
>>> track_info = {'artist': "Beethoven", 'title': 'Symphony No 5'}
>>> all_info = {**date_info, **track_info}
>>> all_info
{'year': '2020', 'month': '01', 'day': '01', 'artist': 'Beethoven', 'title': 'Symphony No 5'}
>>> date_info = {'year': '2020', 'month': '01', 'day': '7'}
>>> event_info = {**date_info, 'group': "Python Meetup"}
>>> event_info
{'year': '2020', 'month': '01', 'day': '7', 'group': 'Python Meetup'}
>>> event_info = {'year': '2020', 'month': '01', 'day': '7', 'group': 'Python Meetup'}
>>> new_info = {**event_info, 'day': "14"}
>>> new_info
{'year': '2020', 'month': '01', 'day': '14', 'group': 'Python Meetup'}
پیشنهاد مطالعه: 11 کاری که پس از نصب وردپرس باید انجام دهید !!
ستاره های پایتون قدرتمند هستند
عملگرهای * و ** پایتون فقط سینتکس شیرین نیستند. برخی از کارهایی که آنها به شما اجازه میدهند انجام دهید را میتوان از راه های دیگر به دست آورد، اما جایگزین های * و ** معمولاً دست و پا گیرتر و دارای منابع یادگیری کمتری هستند. و دستیابی به برخی از ویژگی هایی که آنها ارائه میدهند، بدون آنها به سادگی غیرممکن است: برای مثال هیچ راهی برای پذیرش تعدادی آرگومان موقعیتی برای یک تابع بدون * وجود ندارد. پس از خواندن تمام ویژگی های * و **، ممکن است تعجب کنید که نام این عملگرهای عجیب و غریب چیست. متأسفانه، آنها واقعاً اسامی مختصر ندارند. شنیدهام به اپراتور * «بستهبندی» و «باز کردن بستهبندی» میگویند. من همچنین شنیده ام که آن را “splat” (از دنیای روبی) مینامند و در پایتون به سادگی “ستاره” نامیده شده است. من تمایل دارم این عملگرها را “ستاره” و “ستاره دوگانه” یا “ستاره ستاره” بنامم. این آنها را از خویشاوندان پسوندشان متمایز نمیکند (ضرب و توان)، اما موضوع معمولاً مشخص میکند که در مورد عملگرهای prefix یا infix صحبت میکنیم. اگر * و ** را نمیفهمید یا نگران به خاطر سپردن تمام کاربردهای آنها هستید، این کار را نکنید! این اپراتورها کاربردهای زیادی دارند و به خاطر سپردن کاربرد خاص هر یک از آنها به اندازه درک این که چه زمانی بتوانید به این اپراتورها دسترسی پیدا کنید مهم نیست. من پیشنهاد میکنم از این مقاله به عنوان یک راهنما یا ساختن برگه راهنمای خود استفاده کنید تا به شما در استفاده از * و ** در پایتون کمک کند.
پیشنهاد مطالعه: حمله Man-in-the-Middle (MITM) چیست؟ چگونه انجام میشود؟