Skip to content

Latest commit

 

History

History
1090 lines (760 loc) · 34.8 KB

File metadata and controls

1090 lines (760 loc) · 34.8 KB
jupytext
text_representation
extension format_name
.md
myst
kernelspec
display_name language name
Python 3
python
python3
translation
title headings
اصول برنامه‌نویسی پایتون
Overview Data Types Data Types::Primitive Data Types Data Types::Primitive Data Types::Boolean Values Data Types::Primitive Data Types::Numeric Types Data Types::Containers Data Types::Containers::Slice Notation Data Types::Containers::Sets and Dictionaries Input and Output Input and Output::Paths Iterating Iterating::Looping over Different Objects Iterating::Looping without Indices Iterating::List Comprehensions Comparisons and Logical Operators Comparisons and Logical Operators::Comparisons Comparisons and Logical Operators::Combining Expressions Coding Style and Documentation Coding Style and Documentation::Python Style Guidelines: PEP8 Coding Style and Documentation::Docstrings Exercises
مقدمه
انواع داده
انواع داده اولیه (Primitive Data Types)
مقادیر بولی (Boolean)
انواع داده‌های عددی
ظرف ها یا ساختارهای نگه دارنده(Containers)
نشانه گذاری برش (Slice Notation)
مجموعه‌ها و دیکشنری‌ها (Sets and Dictionaries)
ورودی و خروجی (Input and Output)
مسیرها (Paths)
تکرار(Iteration)
حلقه تکرار روی اشیاء مختلف
حلقه تکرار بدون ایندکس‌ها
‌خلاصه لیست (List Comprehension)
عملگرهای مقایسه‌ای و منطقی
عملگرهای مقایسه‌ای
ترکیب عبارات
سبک کدنویسی و مستندسازی
راهنمای سبک کدنویسی پایتون: PEP8
داک‌استرینگ‌ها (docstring)
تمرین‌ها

(python_done_right)=

<div id="qe-notebook-header" align="right" style="text-align:right;">
        <a href="https://quantecon.org/" title="quantecon.org">
                <img style="width:250px;display:inline;" width="250px" src="https://assets.quantecon.org/img/qe-menubar-logo.svg" alt="QuantEcon">
        </a>
</div>

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

مقدمه

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

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

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

انواع داده

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

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

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

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

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

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

(boolean)=

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

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

x = True
x

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

type(x)

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

y = 100 < 10
y
type(y)

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

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

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

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

sum(bools)

انواع داده‌های عددی

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

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

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

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

type(x)

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

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

پییش‌تر با {ref}لیست‌ها <lists_ref> آشنا شدیم.

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

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

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

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

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

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

---
tags: [raises-exception]
---
x = (1, 2)
x[0] = 10

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

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

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

در واقع شما قبلاً {ref}نمونه‌ای از این را دیده‌اید <tuple_unpacking_example>.

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

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

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

برای مثال:

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

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

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

a[-2:]  # Last two elements of the list

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

a[::2]

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

a[-2::-1] # Walk backwards from the second last element to the first element

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

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

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

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

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

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

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

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

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

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

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

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

ورودی و خروجی (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

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

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

f = open('newfile.txt', 'r')
out = f.read()
out
print(out)

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

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

این نوع بلوک به طور رسمی به عنوان یک [*context*](https://realpython.com/python-with-statement/#the-with-statement-approach) شناخته می‌شود.

بیایید سعی کنیم دو مثال بالا را به عبارت 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)

اکنون فرض کنید که می‌خواهیم ورودی را از یک فایل بخوانیم و خروجی را در فایل دیگری بنویسیم. در اینجا نحوه انجام این کار در حالی که به درستی منابع را دریافت و به سیستم عامل بازمی‌گردانیم با استفاده از عبارات 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())

می‌توانیم مثال بالا را ساده کنیم با این کار که دو دستور 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())

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

می‌توانیم حالت را به 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())
توجه کنید که ما فقط حالت‌های `r`، `w` و `a` را در اینجا پوشش دادیم که رایج‌ترین حالت‌های مورد استفاده هستند.
پایتون [حالت‌های متنوعی](https://www.geeksforgeeks.org/python/reading-writing-text-files-python/) ارائه می‌دهد که می‌توانید با آن‌ها آزمایش کنید.

مسیرها (Paths)

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

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

:class: no-execute

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

(iterating_version_1)=

تکرار(Iteration)

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

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

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

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

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

(us_cities_data)=

%%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

در اینجا 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()

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

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

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

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

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

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

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

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

برای مثال:

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

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

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

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

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

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

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

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

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

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

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

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

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

(list_comprehensions)=

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

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

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

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

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

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

range(8)
doubles = [2 * x for x in range(8)]
doubles

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

عملگرهای مقایسه‌ای

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

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

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

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

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

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

x = 1    # Assignment
x == 2   # Comparison

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

1 != 2

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

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

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

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

  • عباراتی که به صفر، دنباله‌ها یا ظرف های خالی (رشته‌ها، لیست‌ها و غیره) و None تشخیص داده می‌شوند، همه معادل False هستند.
    • به عنوان مثال، [] و () در یک شرط if معادل False هستند
  • همه مقادیر دیگر معادل True هستند.
    • به عنوان مثال، 42 در یک شرط if معادل True است

ترکیب عبارات

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

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

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

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

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

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

all([1 <= 2 <= 3, 5 <= 6 <= 7])
all([1 <= 2 <= 3, "a" in "letter"])
any([1 <= 2 <= 3, "a" in "letter"])
* `()all` زمانی `True` برمی‌گرداند که *همه* مقادیر/عبارات boolean در دنباله `True` باشند
* `()any` زمانی `True` برمی‌گرداند که *هر* مقدار/عبارت boolean در دنباله `True` باشد

سبک کدنویسی و مستندسازی

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

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

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

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

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

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

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

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

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

(Docstrings)=

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

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

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

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

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

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

f?
:class: no-execute

Type:       function
String Form:<function f at 0x2223320>
File:       /home/john/temp/temp.py
Definition: f(x)
Docstring:  This function squares its argument
f??
:class: no-execute

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 پیدا کنید.

تمرین‌ها

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

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

:label: pyess_ex1

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

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

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

:class: dropdown

`x % 2` اگر `x` زوج باشد 0 برمی‌گرداند، در غیر این صورت 1.

:class: dropdown

راه‌حل قسمت 1:

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

x_vals = [1, 2, 3]
y_vals = [1, 1, 1]
sum([x * y for x, y in zip(x_vals, y_vals)])

این روش هم برای این سوال کار می‌کند:

sum(x * y for x, y in zip(x_vals, y_vals))

راه‌حل قسمت 2:

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

sum([x % 2 == 0 for x in range(100)])

این روش هم برای این سوال کار می‌کند:

sum(x % 2 == 0 for x in range(100))

برخی از جایگزین‌های کمتر طبیعی که با این حال به نشان دادن انعطاف‌پذیری خلاصه لیست‌ها کمک می‌کنند عبارتند از:

len([x for x in range(100) if x % 2 == 0])

و

sum([1 for x in range(100) if x % 2 == 0])

راه‌حل قسمت 3:

یک راه حل این است:

pairs = ((2, 5), (4, 2), (9, 8), (12, 10))
sum([x % 2 == 0 and y % 2 == 0 for x, y in pairs])
:label: pyess_ex2

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

:label: polynom0

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) مقدار را در {eq}polynom0 با توجه به نقطه x و لیست ضرایب coeff ($a_1, a_2, \cdots a_n$) محاسبه کند.

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

:class: dropdown

یک راه‌حل این است:

def p(x, coeff):
    return sum(a * x**i for i, a in enumerate(coeff))
p(1, (2, 4))
:label: pyess_ex3

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

:class: dropdown

`'foo'.upper()` مقدار `'FOO'` را برمی‌گرداند.

:class: dropdown

یک راه‌حل این است:

def f(string):
    count = 0
    for letter in string:
        if letter == letter.upper() and letter.isalpha():
            count += 1
    return count

f('The Rain in Spain')

یک راه حل جایگزین‌ و پایتونی تر:

def count_uppercase_chars(s):
    return sum([c.isupper() for c in s])

count_uppercase_chars('The Rain in Spain')
:label: pyess_ex4

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

* منظور از "دنباله" یک لیست، یک tuple یا یک رشته است.
* تمرین را بدون استفاده از [set‌ها](https://docs.python.org/3/tutorial/datastructures.html#sets) و متدهای set انجام دهید.
:class: dropdown

یک راه‌حل این است:

def f(seq_a, seq_b):
    for a in seq_a:
        if a not in seq_b:
            return False
    return True

# == test == #
print(f("ab", "cadb"))
print(f("ab", "cjdb"))
print(f([1, 2], [1, 2, 3]))
print(f([1, 2, 3], [1, 2]))

یک راه حل جایگزین، پایتونی‌تر با استفاده از ()all:

def f(seq_a, seq_b):
  return all([i in seq_b for i in seq_a])

# == test == #
print(f("ab", "cadb"))
print(f("ab", "cjdb"))
print(f([1, 2], [1, 2, 3]))
print(f([1, 2, 3], [1, 2]))

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

def f(seq_a, seq_b):
    return set(seq_a).issubset(set(seq_b))
:label: pyess_ex5

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

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

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

* یک تابع `f` که بازه‌ای $[a, b]$ را به $\mathbb R$ نگاشت می‌کند.
* دو اسکالر `a` و `b` که حدود این بازه را مشخص می‌کنند.
* یک عدد صحیح `n` که تعداد نقاط شبکه را تعیین می‌کند.
* یک عدد `x` که `a <= x <= b` را برآورده می‌کند.

و [درونیابی خطی تکه‌ای](https://en.wikipedia.org/wiki/Linear_interpolation) `f` را در `x`، بر اساس `n` نقطه شبکه با فاصله یکسان `a = point[0] < point[1] < ... < point[n-1] = b` برمی‌گرداند.

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

یک راه‌حل این است:

def linapprox(f, a, b, n, x):
    """
    Evaluates the piecewise linear interpolant of f at x on the interval
    [a, b], with n evenly spaced grid points.

    Parameters
    ==========
        f : function
            The function to approximate

        x, a, b : scalars (floats or integers)
            Evaluation point and endpoints, with a <= x <= b

        n : integer
            Number of grid points

    Returns
    =======
        A float. The interpolant evaluated at x

    """
    length_of_interval = b - a
    num_subintervals = n - 1
    step = length_of_interval / num_subintervals

    # === find first grid point larger than x === #
    point = a
    while point <= x:
        point += step

    # === x must lie between the gridpoints (point - step) and point === #
    u, v = point - step, point

    return f(u) + (x - u) * (f(v) - f(u)) / (v - u)
:label: pyess_ex6

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

import numpy as np

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

یک راه‌حل این است:

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