5. اصول برنامه‌نویسی پایتون#

5.1. مقدمه#

ما تاکنون مطالب زیادی را با تمرکز بر مثال‌ها، به‌سرعت پوشش داده‌ایم.

اکنون بیایید برخی ویژگی‌های اصلی پایتون را به‌صورت نظام‌مندتر بررسی کنیم.

این روش شاید کمتر هیجان‌انگیز باشد، اما به درک بهتر جزئیات کمک می‌کند.

5.2. انواع داده#

برنامه‌های کامپیوتری مجموعه‌ای از انواع داده را مدیریت می‌کنند.
برای مثال، 1.5 یک عدد اعشاری (float) است، در حالی که 1 یک عدد صحیح (integer) محسوب می‌شود. در برنامه نویسی باید بین این دو نوع تفاوت قائل شوید، زیرا در حافظه به شکل متفاوتی ذخیره می‌شوند و عملیات ریاضی روی آنها فرق دارد.

  • به عنوان نمونه، در بیشتر رایانه‌ها محاسبات اعشاری توسط یک واحد خاص به نام واحد ممیز شناور (FPU) انجام می‌شود.

به طور کلی، اعداد اعشاری اطلاعات بیشتری دارند اما عملیات ریاضی روی اعداد صحیح سریع‌تر و دقیق‌تر است.

  • پایتون چندین نوع دادهٔ داخلی دیگر نیز دارد، مثل رشته‌ها (strings) و فهرست‌ها (lists).

بیایید بیشتر با آن‌ها آشنا شویم.

5.2.1. انواع داده اولیه (Primitive Data Types)#

5.2.1.1. مقادیر بولی (Boolean)#

یک نوع از داده های اولیه، مقادیر بولی هستند که می‌توانند فقط True (درست) یا False (نادرست) باشند.

x = True
x
True

می‌توانیم نوع هر شیء در حافظه را با استفاده از تابع ()type بررسی کنیم:

type(x)
bool

مثلاً در مثال زیر، پایتون مقدار عبارت سمت راست را محاسبه کرده و آن را به متغیر y نسبت می‌دهد:

y = 100 < 10
y
False
type(y)
bool

در عبارات ریاضی، True معادل 1 و False معادل 0 در نظر گرفته می‌شود.

این ویژگی را محاسبات بولی (Boolean arithmetic) می‌نامند و اغلب در برنامه نویسی کاربردی است.

مثال های زیر نمونه هایی از این دست هستند:

x + y
1
x * y
0
True + True
2
bools = [True, True, False, True]  # List of Boolean values

sum(bools)
3

5.2.1.2. انواع داده‌های عددی#

داده های عددی از دیگر داده‌های اولیه هستند.

همانطور که پیشتر با داده های صحیح (integer) و اعشاری (float) یا به اختصار intو float آشنا شدید؛ نوعی دیگر از داده های عددی اعداد مختلط (complex) نام دارند.

همانند مثال زیر:

x = complex(1, 2)
y = complex(2, 1)
print(x * y)

type(x)
5j
complex

5.2.2. ظرف ها یا ساختارهای نگه دارنده(Containers)#

پایتون چندین نوع پایه برای ذخیره سازی مجموعه هایی از داده ها(احتمالا ناهمگن) دارد.

پییش‌تر با لیست‌ها آشنا شدیم.

نوعی دیگر از آن، تاپل (tuple) است که «غیرقابل تغییر» (immutable) می باشد.

x = ('a', 'b')  # Parentheses instead of the square brackets
x = 'a', 'b'    # Or no brackets --- the meaning is identical
x
('a', 'b')
type(x)
tuple

اگر شیئی پس از ساخته شدن قابل تغییر نباشد، immutable است. در مقابل، اگر قابل ویرایش باشد، mutable نام دارد.

در پایتون لیست ها قابل تغییر هستند:

x = [1, 2]
x[0] = 10
x
[10, 2]

اما tuple‌ها قابل تغییر نیستند:

x = (1, 2)
x[0] = 10
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
Cell In[13], line 2
      1 x = (1, 2)
----> 2 x[0] = 10

TypeError: 'tuple' object does not support item assignment

کمی بعدتر درباره نقش داده‌های قابل تغییر و تغییرناپذیر بیشتر صحبت خواهیم کرد.

می‌توان تاپل‌ها (و لیست‌ها) را به صورت زیر «بازکرد» (unpack) کرد:

integers = (10, 20, 30)
x, y, z = integers
x
10
y
20

در واقع شما قبلاً نمونه‌ای از این را دیده‌اید.

باز کردن tuple راحت است و ما اغلب از آن استفاده خواهیم کرد.

5.2.2.1. نشانه گذاری برش (Slice Notation)#

برای دسترسی به چندین عنصر از یک دنباله (یک لیست، یک tuple یا یک رشته)، می‌توانید از نشانه‌گذاری slice در پایتون استفاده کنید.

برای مثال:

a = ["a", "b", "c", "d", "e"]
a[1:]
['b', 'c', 'd', 'e']
a[1:3]
['b', 'c']

قانون کلی این است که a[m:n] تعداد n - m عنصر را بازمی‌گرداند، که از a[m] شروع می‌شود.

اعداد منفی نیز مجاز هستند:

a[-2:]  # Last two elements of the list
['d', 'e']

همچنین می‌توانید از قالب [start:end:step] برای مشخص کردن گام برش استفاده کنید:

a[::2]
['a', 'c', 'e']

با استفاده از یک گام منفی، می‌توانید دنباله را به صورت معکوس برگردانید:

a[-2::-1] # Walk backwards from the second last element to the first element
['d', 'c', 'b', 'a']

همان نشانه‌گذاری slice روی tuple‌ها و رشته‌ها نیز کار می‌کند:

s = 'foobar'
s[-3:]  # Select the last three elements
'bar'

5.2.2.2. مجموعه‌ها و دیکشنری‌ها (Sets and Dictionaries)#

دو نوع دیگر از ظرف ها در پایتون،مجموعه ها (sets) و دیکشنری ها (dictionaries) هستند.

دیکشنری‌ها بسیار شبیه لیست‌ها هستند، با این تفاوت که آیتم‌ها به جای شماره‌گذاری، نام‌گذاری می‌شوند:

d = {'name': 'Frodo', 'age': 33}
type(d)
dict
d['age']
33

در این مثال از دیکشنری، نام های 'name' و'age' کلیدها(keys) نامیده می‌شوند.

اشیایی که کلیدها به آن‌ها ارجاع می‌دهند ('Frodo' و 33) مقادیر(Values) نامیده می‌شوند.

مجموعه‌ها(sets) دسته های بدون ترتیب و بدون عنصر تکراری هستند و متدهای مجموعه،عملیات معمول مربوط به مجموعه ها را فراهم می کنند:

s1 = {'a', 'b'}
type(s1)
set
s2 = {'b', 'c'}
s1.issubset(s2)
False
s1.intersection(s2)
{'b'}

تابع ()set مجموعه ها را از دنباله ها می سازد:

s3 = set(('foo', 'bar', 'foo'))
s3
{'bar', 'foo'}

5.3. ورودی و خروجی (Input and Output)#

بیایید به طور خلاصه خواندن و نوشتن در فایل‌های متنی را مرور کنیم.

با نوشتن شروع می‌کنیم:

f = open('newfile.txt', 'w')   # Open 'newfile.txt' for writing
f.write('Testing\n')           # Here '\n' means new line
f.write('Testing again')
f.close()

در اینجا:

  • تابع سازنده داخلی ()openیک فایل برای نوشتن ایجاد می کند.

  • هم ()write و هم ()close متدهای فایل های ایجادشده هستند.

این فایلی که ساخته‌ایم کجاست؟

به خاطر داشته باشید که پایتون مفهوم دایرکتوری فعلی (present working directory یا pwd) شما را حفظ می کند، که در Jupyter یا IPython می توان آن را از طریق دستور زیر پیدا کرد:

%pwd
'/home/runner/work/lecture-python-programming.fa/lecture-python-programming.fa/lectures'

اگر مسیری مشخص نشده باشد، پایتون در همین مکان(پوشه جاری)فایل را می نویسد.

ما همچنین میتوانیم با پایتون محتوای فایل newline.txt را به صورت زیر بخوانیم:

f = open('newfile.txt', 'r')
out = f.read()
out
'Testing\nTesting again'
print(out)
Testing
Testing again

در واقع، رویکرد پیشنهادی در پایتون مدرن، استفاده از یک دستور with است تا اطمینان حاصل شود که فایل‌ها به درستی دریافت و منتشر می‌شوند.

محدود کردن عملیات در همان بلوک نیز وضوح کد شما را بهبود می‌بخشد.

Note

این نوع بلوک به طور رسمی به عنوان یک context شناخته می‌شود.

بیایید سعی کنیم دو مثال بالا را به عبارت with تبدیل کنیم.

ابتدا مثال نوشتن را تغییر می‌دهیم:

with open('newfile.txt', 'w') as f:  
    f.write('Testing\n')         
    f.write('Testing again')

توجه کنید که نیازی به فراخوانی متد ()close نداریم زیرا بلوک with تضمین می‌کند که جریان(stream) در پایان بلوک بسته شود.

با کمی تغییر، می‌توانیم فایل‌ها را با استفاده از دستور with نیز بخوانیم:

with open('newfile.txt', 'r') as fo:
    out = fo.read()
    print(out)
Testing
Testing again

اکنون فرض کنید که می‌خواهیم ورودی را از یک فایل بخوانیم و خروجی را در فایل دیگری بنویسیم. در اینجا نحوه انجام این کار در حالی که به درستی منابع را دریافت و به سیستم عامل بازمی‌گردانیم با استفاده از عبارات with آمده است:

with open("newfile.txt", "r") as f:
    file = f.readlines()
    with open("output.txt", "w") as fo:
        for i, line in enumerate(file):
            fo.write(f'Line {i}: {line} \n')

فایل خروجی به صورت زیر خواهد بود:

with open('output.txt', 'r') as fo:
    print(fo.read())
Line 0: Testing
 
Line 1: Testing again

می‌توانیم مثال بالا را ساده کنیم با این کار که دو دستور with را در یک خط قرار دهیم:

with open("newfile.txt", "r") as f, open("output2.txt", "w") as fo:
        for i, line in enumerate(f):
            fo.write(f'Line {i}: {line} \n')

فایل خروجی یکسان خواهد بود:

with open('output2.txt', 'r') as fo:
    print(fo.read())
Line 0: Testing
 
Line 1: Testing again

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

می‌توانیم حالت را به a تغییر دهیم که مخفف حالت append است:

with open('output2.txt', 'a') as fo:
    fo.write('\nThis is the end of the file')
with open('output2.txt', 'r') as fo:
    print(fo.read())
Line 0: Testing
 
Line 1: Testing again 

This is the end of the file

Note

توجه کنید که ما فقط حالت‌های r، w و a را در اینجا پوشش دادیم که رایج‌ترین حالت‌های مورد استفاده هستند. پایتون حالت‌های متنوعی ارائه می‌دهد که می‌توانید با آن‌ها آزمایش کنید.

5.3.1. مسیرها (Paths)#

توجه کنید که اگر newfile.txt در دایرکتوری کاری فعلی نباشد، فراخوانی ()open با خطا مواجه می‌شود.

در این حالت، می‌توانید فایل را به pwd منتقل کنید یا مسیر کامل فایل را مشخص کنید:

f = open('insert_full_path_to_file/newfile.txt', 'r')

5.4. تکرار(Iteration)#

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

یکی از نقاط قوت پایتون رابط کاربری ساده و انعطاف‌پذیر آن برای این نوع تکرار از طریق حلقه for است.

5.4.1. حلقه تکرار روی اشیاء مختلف#

بسیاری از اشیاء پایتون “قابل تکرار” هستند، به این معنی که می‌توان روی آن‌ها حلقه(loop) زد.

برای ارائه یک مثال، بیایید فایل us_cities.txt را بنویسیم که شهرهای ایالات متحده و جمعیت آن‌ها را فهرست می‌کندو در پوشه کاری فعلی ذخیره می گردد:

%%writefile us_cities.txt
new york: 8244910
los angeles: 3819702
chicago: 2707120
houston: 2145146
philadelphia: 1536471
phoenix: 1469471
san antonio: 1359758
san diego: 1326179
dallas: 1223229
Overwriting us_cities.txt

در اینجا writefile%% یک IPython cell magic است.

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

برنامه زیر داده‌ها را می‌خواند و تبدیل را انجام می‌دهد:

data_file = open('us_cities.txt', 'r')
for line in data_file:
    city, population = line.split(':')         # Tuple unpacking
    city = city.title()                        # Capitalize city names
    population = f'{int(population):,}'        # Add commas to numbers
    print(city.ljust(15) + population)
data_file.close()
New York       8,244,910
Los Angeles    3,819,702
Chicago        2,707,120
Houston        2,145,146
Philadelphia   1,536,471
Phoenix        1,469,471
San Antonio    1,359,758
San Diego      1,326,179
Dallas         1,223,229

در اینجا 'f یک f-string است که برای درج متغیرها در رشته‌ها استفاده می‌شود.

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

بخش جالب این برنامه برای ما خط 2 است که نشان می‌دهد:

  1. شیء فایل data_file قابل تکرار است، به این معنی که می‌توان آن را در سمت راست in در یک حلقه for قرار داد.

  2. تکرار به صورت خط به خط از فایل عبور می‌کند.

این همان چیزی است که منجر به سینتکس ساده و کاربردی نمایش داده شده در برنامه ما می شود.

بسیاری از انواع دیگر اشیاء قابل تکرار هستند و ما بعداً درباره برخی از آن‌ها بحث خواهیم کرد.

5.4.2. حلقه تکرار بدون ایندکس‌ها#

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

برای مثال:

x_values = [1, 2, 3]  # Some iterable x
for x in x_values:
    print(x * x)
1
4
9

به این ترجیح داده می‌شود:

for i in range(len(x_values)):
    print(x_values[i] * x_values[i])
1
4
9

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

پایتون امکاناتی دارد که کار با حلقه ها را بدون ایندکس ساده می‌کند.

یکی از این امکانات ()zip است که به شما اجازه می‌دهد دو لیست را هم زمان با هم طی کنید و هر بار یک جفت از عناصر آنها را بگیرید.

برای مثال، کد زیر را اجرا کنید:

countries = ('Japan', 'Korea', 'China')
cities = ('Tokyo', 'Seoul', 'Beijing')
for country, city in zip(countries, cities):
    print(f'The capital of {country} is {city}')
The capital of Japan is Tokyo
The capital of Korea is Seoul
The capital of China is Beijing

تابع ()zip همچنین برای ایجاد دیکشنری‌ها کاربردی است، برای مثال:

names = ['Tom', 'John']
marks = ['E', 'F']
dict(zip(names, marks))
{'Tom': 'E', 'John': 'F'}

اگر واقعاً به شماره موقعیت یا ایندکس از یک لیست نیاز داریم، یکی از گزینه‌ها استفاده از ()enumerate است.

برای درک اینکه ()enumerate چه کاری انجام می‌دهد، مثال زیر را در نظر بگیرید:

letter_list = ['a', 'b', 'c']
for index, letter in enumerate(letter_list):
    print(f"letter_list[{index}] = '{letter}'")
letter_list[0] = 'a'
letter_list[1] = 'b'
letter_list[2] = 'c'

5.4.3. ‌خلاصه لیست (List Comprehension)#

همچنین می‌توانیم کد تولید لیست مقادیر تصادفی را با استفاده از چیزی به نام خلاصه لیست(list comprehension) به طور قابل توجهی ساده کنیم.

خلاصه لیست‌ها یک ابزار زیبای پایتون برای ایجاد لیست هستند.

مثال زیر را در نظر بگیرید، که خلاصه لیست در سمت راست خط دوم است:

animals = ['dog', 'cat', 'bird']
plurals = [animal + 's' for animal in animals]
plurals
['dogs', 'cats', 'birds']

این یک مثال دیگر است:

range(8)
range(0, 8)
doubles = [2 * x for x in range(8)]
doubles
[0, 2, 4, 6, 8, 10, 12, 14]

5.5. عملگرهای مقایسه‌ای و منطقی#

5.5.1. عملگرهای مقایسه‌ای#

بسیاری از انواع مختلف عبارات وجود دارند که نتیجه‌شان با یکی از مقادیر Boolean (یعنی True یا False) نشان داده می‌شوند.

یکی از رایج ترین این عبارت ها، عملگرهای مقایسه‌ای هستند، مانند:

x, y = 1, 2
x < y
True
x > y
False

یکی از ویژگی‌های خوب پایتون این است که می‌توان نامساوی‌ها را به صورت پشت سر هم (Chain) در یک عبارت نوشت.

1 < 2 < 3
True
1 <= 2 <= 3
True

همانطور که قبلاً دیدیم، هنگام آزمایش برابری از == استفاده می‌کنیم:

x = 1    # Assignment
x == 2   # Comparison
False

برای “نابرابر” از != استفاده کنید:

1 != 2
True

توجه کنید که هنگام آزمایش شرایط، می‌توانیم از هر عبارت معتبر پایتون استفاده کنیم:

x = 'yes' if 42 else 'no'
x
'yes'
x = 'yes' if [] else 'no'
x
'no'

اینجا چه اتفاقی می‌افتد؟

قانون این است:

  • عباراتی که به صفر، دنباله‌ها یا ظرف های خالی (رشته‌ها، لیست‌ها و غیره) و None تشخیص داده می‌شوند، همه معادل False هستند.

    • به عنوان مثال، [] و () در یک شرط if معادل False هستند

  • همه مقادیر دیگر معادل True هستند.

    • به عنوان مثال، 42 در یک شرط if معادل True است

5.5.2. ترکیب عبارات#

می‌توانیم عبارات را با استفاده از and، or و not ترکیب کنیم.

این‌ها ربط‌دهنده‌های منطقی استاندارد (عطف، فصل و نفی) هستند.

1 < 2 and 'f' in 'foo'
True
1 < 2 and 'g' in 'foo'
False
1 < 2 or 'g' in 'foo'
True
not True
False
not not True
True

به خاطر بسپارید:

  • P and Q زمانی True است که هر دو True باشند، در غیر این صورت False

  • P or Q زمانی False است که هر دو False باشند، در غیر این صورت True

همچنین می‌توانیم از ()all و ()any برای آزمایش دنباله‌ای از عبارات استفاده کنیم:

all([1 <= 2 <= 3, 5 <= 6 <= 7])
True
all([1 <= 2 <= 3, "a" in "letter"])
False
any([1 <= 2 <= 3, "a" in "letter"])
True

Note

  • ()all زمانی True برمی‌گرداند که همه مقادیر/عبارات boolean در دنباله True باشند

  • ()any زمانی True برمی‌گرداند که هر مقدار/عبارت boolean در دنباله True باشد

5.6. سبک کدنویسی و مستندسازی#

داشتن یک سبک کدنویسی سازگار و استفاده از مستندسازی می‌تواند کد را خواناتر و نگهداری و توسعه آن را آسان تر کند.

5.6.1. راهنمای سبک کدنویسی پایتون: PEP8#

می‌توانید با تایپ کردن import this در خط فرمان با فلسفه برنامه‌نویسی پایتون آشنا شوید.

پایتون در کنار موارد دیگر، تأکید زیادی بر یکدست بودن و ثبات در سبک برنامه نویسی دارد.

همه ما ضرب‌المثل « سازگاری و ذهن‌های کوچک » را شنیده‌ایم.

در برنامه‌نویسی، همانند ریاضیات، کاملا برعکس است.

  • یک مقاله ریاضی که در آن نمادهای \(\cup\) و \(\cap\) معکوس شده باشند، خواندن آن بسیار سخت خواهد بود، حتی اگر نویسنده در صفحه اول به شما بگوید.

در پایتون، سبک استاندارد در PEP8 بیان شده است.

(گاهی اوقات در این درس ها از PEP8 منحرف خواهیم شد تا نمادگذاری ریاضی را بهتر رعایت کنیم)

5.6.2. داک‌استرینگ‌ها (docstring)#

پایتون سیستمی برای اضافه کردن توضیحات به ماژول‌ها، کلاس‌ها، توابع و غیره دارد که داک‌استرینگ‌(docstring) نامیده می‌شود.

نکته خوب درباره داک‌استرینگ‌‌ها این است که در زمان اجرا در دسترس هستند.

اجرای این کد را امتحان کنید:

def f(x):
    """
    This function squares its argument
    """
    return x**2

پس از اجرای این کد، داک‌استرینگ‌ در دسترس است:

f?
Type:       function
String Form:<function f at 0x2223320>
File:       /home/john/temp/temp.py
Definition: f(x)
Docstring:  This function squares its argument
f??
Type:       function
String Form:<function f at 0x2223320>
File:       /home/john/temp/temp.py
Definition: f(x)
Source:
def f(x):
    """
    This function squares its argument
    """
    return x**2

با یک علامت سوال داک‌استرینگ‌ را می‌آوریم، و با دو علامت سوال کد منبع را نیز دریافت می‌کنیم.

می‌توانید قراردادهای داک‌استرینگ‌ را در PEP257 پیدا کنید.

5.7. تمرین‌ها#

تمرین‌های زیر را حل کنید.

(برای برخی از تمرینات، تابع داخلی ()sum کاربردی است).

Exercise 5.1

قسمت 1: با توجه به دو لیست یا tuple عددی x_vals و y_vals با طول برابر، ضرب داخلی آن‌ها را با استفاده از ()zip محاسبه کنید.

قسمت 2: در یک خط، تعداد اعداد زوج در بازه 0,…,99 را بشمارید.

قسمت 3: با توجه به pairs = ((2, 5), (4, 2), (9, 8), (12, 10))، تعداد جفت‌های (a, b) را بشمارید که در آن هم a و هم b زوج باشند.

Exercise 5.2

چندجمله‌ای زیر را در نظر بگیرید:

(5.1)#\[p(x) = a_0 + a_1 x + a_2 x^2 + \cdots a_n x^n = \sum_{i=0}^n a_i x^i\]

تابع p را بنویسید به طوری که p(x, coeff) مقدار را در (5.1) با توجه به نقطه x و لیست ضرایب coeff (\(a_1, a_2, \cdots a_n\)) محاسبه کند.

سعی کنید از ()enumerate در حلقه خود استفاده کنید.

Exercise 5.3

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

Exercise 5.4

تابعی بنویسید که دو دنباله seq_a و seq_b را به عنوان آرگومان می‌گیرد و True را برمی‌گرداند اگر هر عنصر در seq_a همچنین عنصری از seq_b باشد، در غیر این صورت False.

  • منظور از “دنباله” یک لیست، یک tuple یا یک رشته است.

  • تمرین را بدون استفاده از set‌ها و متدهای set انجام دهید.

Exercise 5.5

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

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

به طور خاص، بدون استفاده از هیچ import، تابعی linapprox بنویسید که به عنوان آرگومان‌ها می‌گیرد

  • یک تابع f که بازه‌ای \([a, b]\) را به \(\mathbb R\) نگاشت می‌کند.

  • دو اسکالر a و b که حدود این بازه را مشخص می‌کنند.

  • یک عدد صحیح n که تعداد نقاط شبکه را تعیین می‌کند.

  • یک عدد x که a <= x <= b را برآورده می‌کند.

و درونیابی خطی تکه‌ای f را در x، بر اساس n نقطه شبکه با فاصله یکسان a = point[0] < point[1] < ... < point[n-1] = b برمی‌گرداند.

برای وضوح تلاش کنید، نه کارایی.

Exercise 5.6

با استفاده از سینتکس خلاصه لیست، می‌توانیم حلقه در کد زیر را ساده کنیم.

import numpy as np

rng = np.random.default_rng()
n = 100
ϵ_values = []
for i in range(n):
    e = rng.standard_normal()
    ϵ_values.append(e)