برنامه‌نویسی پایتون فهرست مطالب
فصل ۱۲

آرگومان‌ها و حوزه متغیر

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

در این فصل می‌خوانید
  1. مقدار پیش‌فرض
  2. آرگومان کلیدواژه‌ای
  3. ترکیب دو نوع آرگومان
  4. رد کردن یک پارامتر میانی
  5. حوزه محلی
  6. حوزه سراسری و کلمه global
  7. فرستادن لیست به تابع
  8. تابع main
  9. متغیر __name__ چیست
  10. یک برنامه کامل، تکه‌تکه شده
  11. توابعی که با لیست کار می‌کنند

۱. مقدار پیش‌فرض

پارامتر می‌تواند مقدار پیش‌فرض داشته باشد. آن وقت اگر صداکننده آن آرگومان را ندهد، مقدار پیش‌فرض به کار می‌رود.

# 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

شرح مثال

۲. آرگومان کلیدواژه‌ای

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

# 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

شرح مثال

۷. فرستادن لیست به تابع

اینجا دام فصل نهم دوباره سر بلند می‌کند، این بار در قالب تابع.

# 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

سه فایده دارد:

۹. متغیر __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

شرح مثال

۱۰. یک برنامه کامل، تکه‌تکه شده

# 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

شرح مثال

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

# 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

شرح مثال

فایل‌های تمرینی این فصل

پوشه 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__':«فقط وقتی من برنامه اصلی هستم اجرا شو»

تمرین‌های فصل دوازده

  1. تابع greet(name, greeting='Hello') را بنویسید و به سه شکل مختلف صدایش بزنید.
  2. تابع power(base, exponent=2) را بنویسید و با یک و با دو آرگومان امتحانش کنید.
  3. توضیح دهید چرا در کد زیر، خروجی 0 است: x = 0 سپس تابعی که داخلش x = 5 است، و بعد از صدا زدنش print(x).
  4. تابع add_score(scores, value) را بنویسید که نمره‌ای به لیست اضافه کند، و نشان دهید لیست اصلی عوض می‌شود.
  5. همان تمرین را طوری بازنویسی کنید که لیست اصلی عوض نشود.
  6. یکی از برنامه‌های فصل دهم را با main() و چند تابع بازنویسی کنید.
  7. تابع count_words(sentence, min_length=3) را بنویسید که کلمه‌های حداقل به این طول را بشمارد.
  8. یک برنامه منودار بنویسید که هر گزینه منو، تابع خودش را صدا بزند.
فصل قبل: توابع فصل بعد: توابع پیشرفته