استفاده از نماد درک لیست پایتون

کسب و کار

در پایتون، هنگام ایجاد یک لیست جدید، استفاده از نماد درک لیست آسان است.(List comprehensions)

در این مقاله ابتدا به موارد زیر می پردازیم

  • نوع اصلی نماد درک لیست
  • نماد درک مطلب را با انشعاب شرطی توسط if فهرست کنید
  • ترکیب با عملگرهای سه تایی (اگر پردازش مشابه دیگری باشد)
  • zip()،enumerate()ترکیب با اینها
  • نماد گنجاندن لیست تو در تو

در ادامه مجموعه نمادهای درک لیست را با کد نمونه توضیح خواهیم داد.

  • نماد گنجاندن را تنظیم کنید(Set comprehensions)
  • نماد گنجاندن فرهنگ لغت(Dict comprehensions)
  • نوع ژنراتور(Generator expressions)

نوع اصلی نماد درک لیست

نماد درک لیست به صورت زیر نوشته می شود.

[Expression for Any Variable Name in Iterable Object]

هر عنصر از یک شیء تکرارپذیر مانند لیست، تاپل یا محدوده را با نام متغیر دلخواه می گیرد و آن را با یک عبارت ارزیابی می کند. یک لیست جدید با نتیجه ارزیابی به عنوان یک عنصر برگردانده می شود.

یک مثال همراه با یک معادل برای عبارت آورده شده است.

squares = [i**2 for i in range(5)]
print(squares)
# [0, 1, 4, 9, 16]
squares = []
for i in range(5):
    squares.append(i**2)

print(squares)
# [0, 1, 4, 9, 16]

همین فرآیند را می توان با map() انجام داد، اما نماد درک لیست به دلیل سادگی و وضوح ترجیح داده می شود.

نماد درک مطلب را با انشعاب شرطی توسط if فهرست کنید

انشعاب مشروط با if نیز امکان پذیر است. if را در پسوند به صورت زیر بنویسید.

[Expression for Any Variable Name in Iterable Object if Conditional Expression]

فقط عناصری از شی تکرارپذیر که عبارت شرطی آنها true است توسط عبارت مورد ارزیابی قرار می گیرند و یک لیست جدید که عناصر آن نتیجه هستند برگردانده می شود.

می توانید از هر نام متغیری در عبارت شرطی استفاده کنید.

یک مثال همراه با یک معادل برای عبارت آورده شده است.

odds = [i for i in range(10) if i % 2 == 1]
print(odds)
# [1, 3, 5, 7, 9]
odds = []
for i in range(10):
    if i % 2 == 1:
        odds.append(i)

print(odds)
# [1, 3, 5, 7, 9]

همین فرآیند را می توان با filter() انجام داد، اما نماد درک لیست به دلیل سادگی و وضوح ترجیح داده می شود.

ترکیب با عملگرهای سه تایی (اگر پردازش مشابه دیگری باشد)

در مثال بالا، تنها عناصری که معیارها را برآورده می‌کنند پردازش می‌شوند و آن‌هایی که معیارها را ندارند از لیست جدید حذف می‌شوند.

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

در پایتون عملگر سه تایی را می توان به صورت زیر نوشت

Value When True if Conditional Expression else Value When False

همانطور که در زیر نشان داده شده است در قسمت بیان نماد درک لیست استفاده می شود.

[Value When True if Conditional Expression else Value When False for Any Variable Name in Iterable Object]

یک مثال همراه با یک معادل برای عبارت آورده شده است.

odd_even = ['odd' if i % 2 == 1 else 'even' for i in range(10)]
print(odd_even)
# ['even', 'odd', 'even', 'odd', 'even', 'odd', 'even', 'odd', 'even', 'odd']
odd_even = []
for i in range(10):
    if i % 2 == 1:
        odd_even.append('odd')
    else:
        odd_even.append('even')

print(odd_even)
# ['even', 'odd', 'even', 'odd', 'even', 'odd', 'even', 'odd', 'even', 'odd']

همچنین امکان نوشتن عبارات با استفاده از نام متغیر دلخواه برای مقادیر true و false وجود دارد.

اگر شرط برآورده شود، مقداری پردازش انجام می شود، در غیر این صورت مقدار شیء تکرارپذیر اصلی بدون تغییر باقی می ماند.

odd10 = [i * 10 if i % 2 == 1 else i for i in range(10)]
print(odd10)
# [0, 10, 2, 30, 4, 50, 6, 70, 8, 90]

ترکیب با zip() و enumerate()

توابع مفیدی که اغلب در دستور for استفاده می شوند عبارتند از zip() که چندین تکرار را با هم ترکیب می کند و enumerate() که یک مقدار را همراه با شاخص آن برمی گرداند.

البته می توان از zip() و enumerate() با نماد درک لیست استفاده کرد. این یک نحو خاص نیست و اگر مطابقت با عبارت for را در نظر بگیرید دشوار نیست.

مثالی از zip().

l_str1 = ['a', 'b', 'c']
l_str2 = ['x', 'y', 'z']

l_zip = [(s1, s2) for s1, s2 in zip(l_str1, l_str2)]
print(l_zip)
# [('a', 'x'), ('b', 'y'), ('c', 'z')]
l_zip = []
for s1, s2 in zip(l_str1, l_str2):
    l_zip.append((s1, s2))

print(l_zip)
# [('a', 'x'), ('b', 'y'), ('c', 'z')]

مثال enumerate().

l_enu = [(i, s) for i, s in enumerate(l_str1)]
print(l_enu)
# [(0, 'a'), (1, 'b'), (2, 'c')]
l_enu = []
for i, s in enumerate(l_str1):
    l_enu.append((i, s))

print(l_enu)
# [(0, 'a'), (1, 'b'), (2, 'c')]

هنگام استفاده از if، ایده مانند قبل است.

l_zip_if = [(s1, s2) for s1, s2 in zip(l_str1, l_str2) if s1 != 'b']
print(l_zip_if)
# [('a', 'x'), ('c', 'z')]

هر عنصر همچنین می تواند برای محاسبه یک عنصر جدید استفاده شود.

l_int1 = [1, 2, 3]
l_int2 = [10, 20, 30]

l_sub = [i2 - i1 for i1, i2 in zip(l_int1, l_int2)]
print(l_sub)
# [9, 18, 27]

نماد گنجاندن لیست تو در تو

مانند تودرتو برای حلقه ها، نماد درک لیست نیز می تواند تودرتو باشد.

[Expression for Variable Name 1 in Iterable Object 1
    for Variable Name 2 in Iterable Object 2
        for Variable Name 3 in Iterable Object 3 ... ]

برای سهولت، شکستن خط و تورفتگی اضافه شده است، اما برای دستور زبان مورد نیاز نیست. آنها را می توان در یک خط ادامه داد.

یک مثال همراه با یک معادل برای عبارت آورده شده است.

matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]

flat = [x for row in matrix for x in row]
print(flat)
# [1, 2, 3, 4, 5, 6, 7, 8, 9]
flat = []
for row in matrix:
    for x in row:
        flat.append(x)

print(flat)
# [1, 2, 3, 4, 5, 6, 7, 8, 9]

همچنین امکان استفاده از چندین متغیر وجود دارد.

cells = [(row, col) for row in range(3) for col in range(2)]
print(cells)
# [(0, 0), (0, 1), (1, 0), (1, 1), (2, 0), (2, 1)]

همچنین می توانید انشعاب شرطی را انجام دهید.

cells = [(row, col) for row in range(3)
         for col in range(2) if col == row]
print(cells)
# [(0, 0), (1, 1)]

همچنین می توان برای هر شیء تکرارشونده به صورت مشروط شاخه کرد.

cells = [(row, col) for row in range(3) if row % 2 == 0
         for col in range(2) if col % 2 == 0]
print(cells)
# [(0, 0), (2, 0)]

نماد گنجاندن را تنظیم کنید(Set comprehensions)

تغییر براکت های مربع [] در نماد درک لیست به براکت های مجعد {} یک مجموعه (شی از نوع مجموعه) ایجاد می کند.

{Expression for Any Variable Name in Iterable Object}
s = {i**2 for i in range(5)}

print(s)
# {0, 1, 4, 9, 16}

نماد گنجاندن فرهنگ لغت(Dict comprehensions)

دیکشنری ها (اشیاء نوع dict) نیز می توانند با نشانه گذاری درک مطلب تولید شوند.

{}، و کلید و مقدار را در قسمت عبارت به عنوان key: value مشخص کنید.

{Key: Value for Any Variable Name in Iterable Object}

هر عبارتی را می توان برای کلید و مقدار مشخص کرد.

l = ['Alice', 'Bob', 'Charlie']

d = {s: len(s) for s in l}
print(d)
# {'Alice': 5, 'Bob': 3, 'Charlie': 7}

برای ایجاد یک دیکشنری جدید از لیستی از کلیدها و مقادیر، از تابع zip() استفاده کنید.

keys = ['k1', 'k2', 'k3']
values = [1, 2, 3]

d = {k: v for k, v in zip(keys, values)}
print(d)
# {'k1': 1, 'k2': 2, 'k3': 3}

نوع ژنراتور(Generator expressions)

اگر از براکت‌های مربع [] در نماد درک فهرست به‌عنوان براکت‌های گرد () استفاده شود، به جای یک تاپل، یک مولد برگردانده می‌شود. به این اصطلاحات مولد می گویند.

نمونه ای از نشانه گذاری درک لیست.

l = [i**2 for i in range(5)]

print(l)
# [0, 1, 4, 9, 16]

print(type(l))
# <class 'list'>

نمونه ای از عبارت مولد. اگر ژنراتور را همانطور که هست چاپ کنید، محتویات آن را چاپ نمی کند، اما اگر آن را با دستور for اجرا کنید، می توانید محتویات را دریافت کنید.

g = (i**2 for i in range(5))

print(g)
# <generator object <genexpr> at 0x10af944f8>

print(type(g))
# <class 'generator'>

for i in g:
    print(i)
# 0
# 1
# 4
# 9
# 16

عبارات مولد همچنین امکان انشعاب شرطی و تودرتو با استفاده از if و همچنین نماد درک لیست را می دهد.

g_cells = ((row, col) for row in range(0, 3)
           for col in range(0, 2) if col == row)

print(type(g_cells))
# <class 'generator'>

for i in g_cells:
    print(i)
# (0, 0)
# (1, 1)

به عنوان مثال، اگر لیستی با تعداد زیادی عنصر با استفاده از نماد درک لیست ایجاد شود و سپس با یک عبارت for حلقه زده شود، در صورت استفاده از نماد درک لیست، لیستی حاوی تمام عناصر در ابتدا ایجاد می شود. از طرف دیگر، اگر از عبارت مولد استفاده می کنید، هر بار که حلقه تکرار می شود، عناصر یکی یکی تولید می شوند و در نتیجه میزان حافظه مصرفی کاهش می یابد.

اگر عبارت مولد تنها آرگومان تابع باشد، براکت های گرد () را می توان حذف کرد.

print(sum([i**2 for i in range(5)]))
# 30

print(sum((i**2 for i in range(5))))
# 30

print(sum(i**2 for i in range(5)))
# 30

در مورد سرعت پردازش، هنگام پردازش همه عناصر، نماد درک لیست اغلب سریعتر از نماد مولد است.

با این حال، برای مثال، هنگام قضاوت با all() یا any()، نتیجه زمانی مشخص می شود که false یا true وجود داشته باشد، بنابراین استفاده از عبارات مولد ممکن است سریعتر از استفاده از نماد درک لیست باشد.

هیچ نماد درک تاپلی وجود ندارد، اما اگر از یک عبارت مولد به عنوان آرگومان tuple() استفاده کنید، می توانید یک تاپل در نماد درک ایجاد کنید.

t = tuple(i**2 for i in range(5))

print(t)
# (0, 1, 4, 9, 16)

print(type(t))
# <class 'tuple'>