آرگومانها و حوزه متغیر
در فصل قبل تابع ساختیم. حالا دقیقتر میبینیم که مقدارها چطور به تابع میرسند، متغیرها کجا زندگی میکنند، و شکل استاندارد یک برنامه پایتون چیست.
۱. مقدار پیشفرض
پارامتر میتواند مقدار پیشفرض داشته باشد. آن وقت اگر صداکننده آن آرگومان را ندهد، مقدار پیشفرض به کار میرود.
# A parameter can have a default value, used when the caller omits it.
def welcome(name='Omid', lastname='Shabani'):
print('Welcome', name, lastname)
welcome() # both defaults
welcome('Alireza') # only 'lastname' keeps its default
welcome('Alireza', 'Ghasemi') # no default used
# Parameters WITH a default must come after the ones without.
def calculate_price(price, tax=0.0):
return price * (1 + tax)
print(calculate_price(100000)) # 100000.0
print(calculate_price(100000, 0.2)) # 120000.0
مقدار پیشفرض: نمونه 01_default_values.py
Welcome Omid Shabani Welcome Alireza Shabani Welcome Alireza Ghasemi 100000.0 120000.0
شرح مثال
- مقدار پیشفرض با علامت مساوی در خود
defنوشته میشود. - پارامترهای دارای پیشفرض باید بعد از پارامترهای بدون پیشفرض بیایند. عکسش خطای نگارشی است، چون پایتون نمیداند مقدار دادهشده مال کدام است.
- پیشفرض کار را برای صداکننده راحت میکند: حالت معمول را ساده بنویسد و فقط وقتی لازم شد جزئیات بدهد.
۲. آرگومان کلیدواژهای
به جای اینکه به ترتیب تکیه کنید، میتوانید آرگومانها را با نام بدهید. آن وقت ترتیب اهمیتی ندارد و مهمتر از آن، کد بسیار خواناتر میشود.
# Arguments can be given BY NAME. Then the order does not matter any more,
# and the call becomes much easier to read.
def student_info(name, age, city):
print(name, age, city)
student_info('Ali', 20, 'Rasht') # positional
student_info(age=20, name='Ali', city='Rasht') # keyword, any order
# A long call is often written over several lines.
student_info(
name='Ali',
age=20,
city='Rasht',
)
آرگومان با نام: نمونه 02_keyword_arguments.py
کدام یک را راحتتر میفهمید؟
create_user('ali', True, False, True) یا
create_user('ali', active=True, admin=False, verified=True)؟
نسخه دوم شش ماه بعد هم قابل فهم است. هر جا آرگومانی از جنس
True و False یا عدد بینام دارید، حتماً
نامش را بنویسید.
۳. ترکیب دو نوع آرگومان
def welcome(name='Omid', lastname='Shabani'):
print('Welcome', name, lastname)
welcome(lastname='Ahmadi') # skip the first parameter
welcome(lastname='Raeisi', name='Ebrahim') # both by name
# Positional arguments must always come BEFORE keyword arguments.
# This line would be a SyntaxError:
# welcome(name='Ali', 'Ahmadi')
def add(num1=0.0, num2=0.0):
return num1 + num2
num1 = float(input('Enter a number: '))
num2 = float(input('Enter a number: '))
print(add(num2=num2, num1=num1))
ترکیب: نمونه 03_mixing_arguments.py
قانون یکخطی: آرگومانهای بینام همیشه اول، آرگومانهای نامدار همیشه بعد. نقض این قانون خطای نگارشی است، نه خطای اجرا، پس بلافاصله متوجه میشوید.
۴. رد کردن یک پارامتر میانی
# When a function has several optional parameters, keyword arguments let us
# set only the one we care about.
def register(name, role='student', active=True):
print(name, role, active)
register('Ali', active=False) # 'role' keeps its default
register('Saeed') # everything default
register('Sara', 'teacher') # 'role' given positionally
# Division with a safe default divisor.
def div(num1, num2=1.0):
if num2 == 0:
print('Cannot divide by zero!')
return None
return num1 / num2
number = float(input('Enter a number: '))
print(div(num1=number)) # divided by the default 1.0
print(div(number, 0)) # the guard protects us
رد کردن پارامتر میانی: نمونه 04_default_and_keyword.py
این کاربرد اصلی آرگومان نامدار است: وقتی تابعی پنج پارامتر اختیاری دارد و شما فقط میخواهید چهارمی را عوض کنید، نمیشود سه تای اول را «خالی گذاشت». باید نامش را بنویسید.
۵. حوزه محلی
متغیری که داخل تابع ساخته میشود، فقط داخل همان تابع وجود دارد و به محض تمام شدن تابع از بین میرود. به این حوزه محلی میگویند.
# A variable created inside a function only exists inside that function.
def calculate():
result = 42 # local variable
print('inside: ', result)
calculate()
# This line would raise NameError: name 'result' is not defined
# print('outside:', result)
# Two functions may use the same name without disturbing each other.
def first():
value = 1
print('first:', value)
def second():
value = 2
print('second:', value)
first()
second()
حوزه محلی: نمونه 05_local_scope.py
این محدودیت در عمل یک مزیت بزرگ است. به لطف آن، وقتی تابعی مینویسید ضرورتی ندارد نگران باشید که نام متغیرتان جایی دیگر در برنامه به کار رفته باشد. دو تابع میتوانند هر دو متغیری به نام i یا result داشته باشند بیآنکه به هم کاری داشته باشند.
۶. حوزه سراسری و کلمه global
# A variable created outside every function is "global": functions can READ it.
counter = 0
def show():
print('counter is', counter) # reading works without anything special
show()
# But ASSIGNING to it inside a function creates a NEW local variable,
# unless we declare it as global.
def increase_wrong():
counter = 100 # this is a local variable!
print('local copy:', counter)
increase_wrong()
print('still:', counter) # 0
def increase_right():
global counter # now we really touch the global one
counter += 1
increase_right()
increase_right()
print('after global:', counter) # 2
# 'global' makes a program hard to follow. Prefer parameters and return values:
def increase(value):
return value + 1
counter = increase(counter)
print('better style:', counter)
حوزه سراسری: نمونه 06_global_scope.py
شرح مثال
- خواندن متغیر سراسری از داخل تابع آزاد است و نیاز به هیچ کاری ندارد.
- نوشتن فرق میکند: خط
counter = 100داخل تابع، یک متغیر محلی تازه با همان نام میسازد و متغیر بیرونی دستنخورده میماند. - کلمه
globalبه پایتون میگوید «منظورم همان متغیر بیرونی است». - ولی توصیه جدی: از
globalاستفاده نکنید. برنامهای که متغیرهایش از هر جایی قابل تغییرند، عیبیابیاش کابوس است. راه درست، دادن مقدار به تابع و گرفتن نتیجه از آن است، مثل خط آخر مثال.
۷. فرستادن لیست به تابع
اینجا دام فصل نهم دوباره سر بلند میکند، این بار در قالب تابع.
# Numbers and strings are IMMUTABLE: the function works on a copy.
def try_to_change(number):
number += 1
print('inside: ', number)
value = 10
try_to_change(value)
print('outside:', value) # 10 -> unchanged
# Lists are MUTABLE: the function receives the SAME list, not a copy.
def add_item(items):
items.append('new')
my_list = ['a', 'b']
add_item(my_list)
print(my_list) # ['a', 'b', 'new'] -> changed!
# When you do not want that, pass a copy.
def add_item_safely(items):
items = items.copy()
items.append('new')
return items
original = ['a', 'b']
changed = add_item_safely(original)
print(original, changed)
لیست به عنوان آرگومان: نمونه 07_pass_a_list.py
| نوع آرگومان | رفتار | مثال |
|---|---|---|
| تغییرناپذیر | تابع روی کپی کار میکند، اصل دستنخورده | int float str bool |
| تغییرپذیر | تابع همان شیء را میگیرد، تغییر بیرون هم دیده میشود | list dict set |
این رفتار همیشه بهتر است. گاهی دقیقاً همان چیزی است که میخواهید: تابعی به نام sort_list(items) که لیست را مرتب کند. مهم این است که بدانید کدام حالت را دارید. اگر تابع لیست را عوض میکند، در نامش یا در مستندش بگویید.
۸. تابع main
از این فصل به بعد شکل استاندارد برنامههای ما این است: همه کد اصلی داخل
تابعی به نام main میرود و در انتهای فایل یک خط نگهبان
صدایش میزند.
# From now on we put the "story" of the program inside a function called main().
# It keeps every variable local and makes the program easy to read.
def show(text):
print(text)
def main():
show('Kadoos')
show('Rasht')
if __name__ == '__main__':
main()
الگوی main: نمونه 08_main_function.py
سه فایده دارد:
- هیچ متغیر سراسری ساخته نمیشود، پس دام بخش ششم پیش نمیآید.
- خواننده کد با یک نگاه به
mainمیفهمد برنامه چه میکند. - فایل میتواند هم برنامه باشد و هم ماژول قابل import، که موضوع بخش بعد است.
۹. متغیر __name__ چیست
# Python gives every file a variable called __name__.
# * when the file is RUN directly -> __name__ == '__main__'
# * when the file is IMPORTED elsewhere -> __name__ is the module name
#
# So the guard below means: "run this only when I am the program being started".
# It is what allows a file to be both a program and a reusable module
# (see session 17).
print('The value of __name__ here is:', __name__)
def main():
print('main() was called')
if __name__ == '__main__':
main()
معنی __name__: نمونه 09_name_main.py
The value of __name__ here is: __main__ main() was called
شرح مثال
- پایتون به هر فایل متغیری به نام
__name__میدهد. - اگر فایل را مستقیم اجرا کنید، مقدارش رشته
'__main__'است. - اگر فایل را از فایل دیگری
importکنید، مقدارش نام فایل است. - پس آن خط نگهبان یعنی «این کد فقط وقتی اجرا شود که من خودم برنامه اصلی باشم». به لطف آن، میتوانید توابع یک فایل را در فایل دیگر استفاده کنید بدون اینکه برنامهاش اجرا شود. در فصل هفدهم این را کامل میبینید.
- اگر شکل عجیب
__name__با دو زیرخط در دو طرف را نمیپسندید، در فصل بیست و چهارم میبینید که این نامگذاری در پایتون قاعدهای عمومی برای چیزهای «خاص» است.
۱۰. یک برنامه کامل، تکهتکه شده
# A complete program cut into small functions:
# one function reads, one computes, one displays, and main() puts them together.
def read_number(message):
'Ask the user for a number until the answer is really a number.'
while True:
user_input = input(message)
try:
return float(user_input)
except ValueError:
print('That is not a number, try again.')
def add(number1, number2):
return number1 + number2
def show_result(value):
print('Sum:', value)
def main():
num1 = read_number('Enter a number: ')
num2 = read_number('Enter a number: ')
show_result(add(num1, num2))
if __name__ == '__main__':
main()
برنامه تکهتکه: نمونه 10_program_with_functions.py
شرح مثال
- هر تابع یک کار میکند: یکی میخواند، یکی حساب میکند، یکی نمایش میدهد.
- تابع
mainفقط داستان برنامه را میگوید و خودش هیچ جزئیاتی ندارد. - ساختار
tryوexceptاینجا برای اولین بار آمده است: اگر تبدیل به عدد شکست بخورد، به جای خراب شدن برنامه، پیام میدهد و دوباره میپرسد. فصل نوزدهم کاملاً درباره همین است. - تابع
read_numberنمونه عالی یک تابع قابل استفاده مجدد است: در هر برنامهای که ورودی عددی میخواهد، همین را کپی کنید.
۱۱. توابعی که با لیست کار میکنند
# Functions that take a list as a parameter and return a value.
def total_of(numbers):
'Return the sum of a list of numbers.'
total = 0
for number in numbers:
total += number
return total
def average_of(numbers):
'Return the average, or 0 for an empty list.'
if len(numbers) == 0:
return 0
return total_of(numbers) / len(numbers)
def bigger_list(list1, list2):
'Return the list whose items add up to more.'
if total_of(list1) > total_of(list2):
return list1
return list2
def squares_of(numbers):
'Return a NEW list with the square of every item.'
result = []
for number in numbers:
result.append(number * number)
return result
def main():
l1 = [3, 2, 5, 6]
l2 = [10, 2]
print('total :', total_of(l1))
print('average:', average_of(l1))
print('bigger :', bigger_list(l1, l2))
print('squares:', squares_of(l1))
print('empty :', average_of([]))
if __name__ == '__main__':
main()
توابع لیستی: نمونه 11_list_functions.py
شرح مثال
average_ofازtotal_ofاستفاده میکند. توابع روی هم سوار میشوند، درست مثل فصل قبل.- حالت لیست خالی داخل خود تابع بررسی شده است. این بهتر از این است که هر بار در برنامه اصلی چک کنید.
squares_ofلیست ورودی را دست نمیزند و لیست تازه میسازد. این سبک امنتر است و در فصل بیست و پنجم راه یکخطی نوشتنش را میبینید.- هر تابع یک مستند تکخطی دارد که میگوید چه چیزی برمیگرداند. عادت خوبی است.
پوشه S12-functions-arguments-and-scope شامل نمونههای
01_default_values.py تا 11_list_functions.py.
خلاصه فصل
| ابزار | وظیفه آن |
|---|---|
def f(x=5) | مقدار پیشفرض. بعد از پارامترهای بدون پیشفرض |
f(name='Ali') | آرگومان کلیدواژهای. ترتیب آزاد و خواناتر |
| قانون ترتیب | بینامها اول، نامدارها بعد |
| حوزه محلی | متغیر داخل تابع، بیرون وجود ندارد |
| حوزه سراسری | خواندنش آزاد است، نوشتنش نیاز به global دارد |
global | کار میکند ولی از آن پرهیز کنید |
| آرگومان تغییرپذیر | لیست و دیکشنری در تابع واقعاً عوض میشوند |
main() | محل کد اصلی برنامه |
if __name__ == '__main__': | «فقط وقتی من برنامه اصلی هستم اجرا شو» |
تمرینهای فصل دوازده
- تابع
greet(name, greeting='Hello')را بنویسید و به سه شکل مختلف صدایش بزنید. - تابع
power(base, exponent=2)را بنویسید و با یک و با دو آرگومان امتحانش کنید. - توضیح دهید چرا در کد زیر، خروجی
0است:x = 0سپس تابعی که داخلشx = 5است، و بعد از صدا زدنشprint(x). - تابع
add_score(scores, value)را بنویسید که نمرهای به لیست اضافه کند، و نشان دهید لیست اصلی عوض میشود. - همان تمرین را طوری بازنویسی کنید که لیست اصلی عوض نشود.
- یکی از برنامههای فصل دهم را با
main()و چند تابع بازنویسی کنید. - تابع
count_words(sentence, min_length=3)را بنویسید که کلمههای حداقل به این طول را بشمارد. - یک برنامه منودار بنویسید که هر گزینه منو، تابع خودش را صدا بزند.