فصل دوم
حلقه ها و جملات شرطی
گذشته از جمله while که همان لحظه معرفی شد پایتون میتونه از کنترل های کنترلی دیگه زبان ها هم استفاده کنه با مقداری پیچ و تاب
if جمله شرطی
شاید شناخته ترین نوع جمله – if – باشه برای مثال:
مثال :
در این جا میتونه ۰ باشه یا در قسمت elif بیشتر- و قسمت else هم اختیاریه کلمه کلیدی elif کوتاه شده ی عبارت else if هستش و برای اجتناب از تو رفتگی های بیش از اندازه مفید هستش یه elif… elif .if جانشین جملات case و switch در دیگر زبان ها ست
forحلقه
حلقه for داخل پایتون یکم متفاوت از آنچه هست که داخل زبان سی یا پاسکال استفاده کرده اید حلقه می تونه تمام اشیای درون دنباله ها( رشته ها و لیست ها) تکرار کنه در این نظم بخشیدن این تکرار ها داخل خود دنباله انجام میگیره
مثال :
>>> #اندازه گیری رشته ها ... a = ['cat', 'window', 'defenestrate'] >>> for x in a: ... print(x, len(x)) ... cat 3 window 6 defenestrate 12
در حلقه زیر به متغییر ایکس تمام اشیای درون لیست داده میشود و چک می شود که اگر شئ ورودی اندازه اش از ۶ بزرگتر است آن شئ را به کمک تابع insert درون ایندکس ۰ از لیست a[:] قرار دهد
مثال :
>>> for x in a[:]: # یه بخش از لیست رو کپی می گیره درون لیست می ذاره ... if len(x) > 6: a.insert(0, x) ... >>> a ['defenestrate', 'cat', 'window', 'defenestrate']
range() تابع
اگه احتیاج دارید که یه سری عدد پشت سر هم رو تکرار کنید این تابع range()به سهولت این کارو انجام میده اون تصاعد های حسابی رو تولید میکنه
>>> for i in range(5): ... print(i) ... 0 1 2 3 4
تابعrange(5, 10)اعداد ۵ تا ۹ را تولید می کند تابعrange(0, 10, 3) اعداد از ۰ به قدر نسبت ۳ تا ۹ را تولید می کند تابع range(-10, -100, -30) اعداد از ۱۰- به قدر نسبت ۳۰- تا ۷۰- را تولید می کند. مثال:
مثال :
range(5, 10) 5 through 9 range(0, 10, 3) 0, 3, 6, 9 range(-10, -100, -30) -10, -40, -70برای تکرار کردن ایندکس یا زیر نویس دنباله ها (رشته ها یا لیست ها) می شه از ترکیب len() و range()مثل زیر استفاده کرد
مثال :
>>> a = ['Mary', 'had', 'a', 'little', 'lamb'] >>> for i in range(len(a)): ... print(i, a[i]) ... 0 Mary 1 had 2 a 3 little 4 lamb
داخل بیشتر موارد هنوز اون راه درست برای استفاده تابع هستش enumerate()
نگاهی بکنید Looping Techniques. فصل بعدی این قسمت رو
یه چیز عجیب اتفاق می افته اگه تنها تابع range رو پرینت بگیریم :
>>> print(range(10)) range(0, 10) #[0,1,2,3,4,5,6,7,8,9]
در بسیاری از موارد اشیای بازگردانده از تابع range()همچون یک لیست رفتار می کنند در حالیکه در حقیقت این طور نیست با کمک تابع list() می تونیم از شمارشی ها یک لیست درست کنیم
>>> list(range(5)) [0, 1, 2, 3, 4]
بعدن تابع های بیشتری رو می بینیم که تکرار کردنی هارو بر میگردانند یا مثل آرگومان آن هارو دریافت می کنند
تابع های break و continue و جزء else در حلقه ها
تابع break نیز مثل زبان سی از درونی ترین حلقه های for یا while خارج میشه
تابعcontinue نیز از زبان سی قرض گرفته شده حلقه رو با شماره بعدی ادامه میده
در زیر یه مثال هست که نشان میدهد حلقه های تو در تو چگونه کار می کنند مثال زیر اعداد اول بین۲ تا ۹ را پیدا می کنه در حلقه های تو در تو ابتدا حلقه انتهایی تا آخر اجرا میشه و بعد حلقه بیرونی آغاز می شود
مثال :
>>> for n in range(2, 10): ... for x in range(2, n): ... if n % x == 0: ... print(n, 'equals', x, '*', n//x) ... break ... else: ... # loop fell through without finding a factor ... print(n, 'is a prime number') ... 2 is a prime number 3 is a prime number 4 equals 2 * 2 5 is a prime number 6 equals 2 * 3 7 is a prime number 8 equals 2 * 4 9 equals 3 * 3
pass عبارت
عبارت pass کار خاصی انجام نمیده این عبارت وقتی که به قواعد نحوی نیاز پیدا بشه و برنامه به حرکت اضافی نیاز نداشته باشه ازش استفاده می کیم
مثال :
>>> while True: ... pass # Busy-wait for keyboard interrupt (Ctrl+C) ...
این یه کاربرد معمولی ان برای درست کردن کلاس های کوچیکه :
مثال :
>>> class MyEmptyClass: ... pass ...
چای دیگه که عبارت pass می تونه استفاده بشه در داخل بدنه توابع یا بدنه شروط است و به شما اجازه میده که به فکر کردن ادامه بدید عبارت pass یک ignore بی صداست
مثال :
>>> def initlog(*args): ... pass # بیاد داشته باشید که این رو انجام بدین ...
تعریف توابع
تابع یک مجموعه یا قطعه کدی می باشد که با یک نام خاص یا محدوده اسمی منحصر بفرد شناخته می شوند . بنابراین به دفعات زیادی می توانند اجرا گردند . از طریق توابع می توان پارامتر هایی را به کد مورد نظر ارسال کرده و نتایج حاصله از آن ورودی را دریافت کرد . در نتیجه تابع می تواند در دفعات مختلف اجرا متفاوت باشد . برای تعریف و استفاده از توابع در پایتون از عبارت های ویژه و از پیش تعیین شده def , return و global استفاده می گردد که در ادامه درس به توضیح هر یک پرداخته می شود .
مانند بسیاری از زبانهای دیگر توابع در پایتون آسانترین روش برای قطعه بندی منطقی کد برای استفاده مجدد در زمان و مکان دیگر می باشد . تمام کد های نوشته شد بصورت پشت سر هم و سریع اجرا می گردند . با استفاده از توابع می توان بخشی از کد را به دفعات متعددی استفاده کرد .
همیچنین با استفاده از توابع کل برنامه به رویه ها و بخش هایی کوچک و مشخص تجزیه می گردد . در این حالت برنامه نویس می تواند بصورت جداگانه بر روی بخش های مختلفی کار کند . هر یک از این بخش ها یا توابع کار مشخصی انجام می دهند . پس یک مسله پیچیده و بزرگ به مسایل کوچک و روشنی تقسیم شده و هر یک جداگانه پیاده سازی می شود .
با استفاده از عبارت def یک تابع ایجاد شده و نامی به آن تعلق می گیرد . در زبان پایتون برای تعریف یک تابع جدید از دستور def استفاده می گردد . با استفاده از def یک تابع جدید ایجاد شده و اسمی به آن تعلق می گیرد . نام تابع درست بعد از عبارت def ظاهر می گردد در مقابل نام تابع و در داخل پارانتز تنها نام پارامتر های قابل دریافت توسط تابع نوشته می شود و در نهایت همانند سایر قطعه کد های موجود با کاراکتر دو نقطه (:) ختم می گردد . در ادامه و در سطر های بعدی و با رعایت کردن تورفتگی کد های داخل تابع نوشته می شود
مثال :
>>> def fib(n): # جمله های فیبوناچی رو تا مقدار داده شدن چاپ میکند
… “”"Print a Fibonacci series up to n.”"”
… a, b = 0, 1
… while b < n:
… print(b, end=’ ‘)
… a, b = b, a+b
… print()
…
>>> # حالا تابعی را که تعریف کردیم فراخوانی میکنیم
… fib(2000)
1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597
اجرای تابع یه جدول نمادی جدید رو برای به کار بردن متغیر های محلی تابع فراهم می سازه تمام متغیر ها ی انتسابی تابع ارزش هاشون رو داخل جدول نمادی محلی ذخیره می کنند در حقیقت ارجاع های متغیر نخست داخل جدول نمادی محلی دیده می شوند بنابراین جدول های نمادی محلی تابع ها رو این طوری در بر می گیرند و بدین گونه به متغیرهای سراسری درون تابع نمی توان به طور مستقیم ارزش گذاری شوند اگر چه ممکن است که ارجاع داده شوند
پارامتر های واقعی ( آرگومان ها) که به یه تابع فرا خوانده میشوند داخل جدول نمادی محلی ای معرفی شده اند که در اون تابع هستش- بنابراین آرگومان ها برای فراخوانی ارزش ها مجاز هستند(همیشه مقدار یک مرجع هدف است ) وقتی یه تابع داخل تابع دیگه فراخوانی میشه یه جدول نمادی محلی برای فراخوانی اون تشکیل میشه
تعریف یه تابع اسم تابع رو داخل جدول نمادی در حال اجرا معرفی میکنه ما می تونیم اسم تابعی رو که تعریف کردیم به یک متغیر نسبت بدیم که اون متغیر از این پس نقش اسم تابع رو بازی می کنه
>>> fib
<function fib at 10042ed0> # آدرس خانه ای از حافظه که تابع در انجا ذخیره است
>>> f = fib
>>> f(100)
1 1 2 3 5 8 13 21 34 55 89
وقتی که به تابع فیب آرگومان صفر می دهید مقداری برای ان در تابع وجود ندارد بنابراین تابع مقدار None را بر می گرداند که اگر از تابع fib(0) پرینت بگریم واضح واشکار می شود
>>> fib(0)
>>> print(fib(0))
None
مثال زیر یک تابع هستش که لیستی از اعداد فیبوناچی رو بر میگردونه بجای اینکه اونارو تو یه سطر چاپ کنه
>>> def fib2(n): # return Fibonacci series up to n ... """Return a list containing the Fibonacci series up to n.""" ... result = [] ... a, b = 0, 1 ... while b < n: ... result.append(b) # see below ... a, b = b, a+b ... return result ... >>> f100 = fib2(100) # call it >>> f100 #نتیجه تابع را بر می گرداند [۱, ۱, ۲, ۳, ۵, ۸, ۱۳, ۲۱, ۳۴, ۵۵, ۸۹]
این مثال بعضی از ویژگی های جدید پایتون رو نشون میده:
عبارت return یک مقدار را از تابع بر می گرداند return بدون شناسه عبارت none را بر می گرداند
result یک لیست خالی است تا وقتی که b کوچکتر از n است تابع append ارگومان b را به درون لیست اضافه میکند و در اخر تابع return لیست ایجاد شده را بر می گرداند
مقدار دهی پیش فرض پارامتر ها
در هنگام فراخوانی تابع باید به هر پارامتر آن یه مقدار نسبت داد (فرستاد). و در صورتی که مقداری به یک پارامتر فرستاده نشود باعث بروز خطای برنامه نویسی خواهد شد . برای جلوگیری از این حالت پایتون شما را قادر می سازد تا برای پارمترها تابع خود مقدار پیش فرض نسبت دهید . این کار را می توانید در هنگام تعریف تابع و با نسبت دادن مقدار پیش فرض به نام پارامتر انجام دهید .
مثال :
def ask_ok(prompt, retries=4, complaint='Yes or no, please!'):
while True:
ok = input(prompt)
if ok in ('y', 'ye', 'yes'):
return True
if ok in ('n', 'no', 'nop', 'nope'):
return False
retries = retries - 1
if retries < 0:
raise IOError('refusenik user')
print(complaint)
این تابع از چندین راه می تونه فرا خوانی شه :
- دادن فقط یه آرگومان اجباری: ask_ok('Do you really want to quit?')
- گذاشتن حداقل یه آرگومان اختیاری: ask_ok('OK to overwrite the file?', 2)
- یا حتی دادن تمام آرگومان ها: ask_ok('OK to overwrite the file?', 2, 'Come on, only yes or no!')
این مثال همچنین کلمه کلیدی in را معرفی می کنه این کلمه تست میکنه که ایا زیر دنباله ورودی (لیست-مجموعه-رشته) شامل رشته های داده شده هست یا نه
مقدار های پیش فرض در دامنه تعریفی توابع پیش فرض تعریف می شوند بنا براین :
i = 5
def f(arg=i):
print(arg)
i = 6 # به آی مقدار ۶ داده شد
f() # 5 چاپ خواهد شد
هشدار مهم : مقدار پیش فرض فقط یک بار ارزشیابی میشود فرق میکنه با وقتی که پیش فرض یه شئ متغیر مثل لیست یا دیکشنری یا اغلب نمونه کلاس ها باشه- برای مثال تابع زیر آرگومان ها رو جمع میکنه و به لیستی که فرا خوانی شده اضافه می کند
def f(a, L=[]):
L.append(a)
return L
print(f(1))
print(f(2))
print(f(3))
چاپ خواهد شد :
[۱] [۱, ۲] [۱, ۲, ۳]
تابع زیر هم با تابع بالایی مساوی است
def f(a, L=None):
if L is None:
L = []
L.append(a)
return L
استفاده از متغییر های سراسری
در حالت معمول تمام متغییر های تعریف شده در داخل تابع محلی می باشند . یعنی طول عمر آنها تا زمان اجرا تابع بوده و فقط از داخل تابع امکان دسترسی را دارند . در صورتی که بخواهیم از نام یا متغییری در خارج از تابع نیز استفاده کنیم از دستور global استفاده می کنیم . این دستور متغییر های محلی را به متغییر های سراسری تبدیل می نماید .
ارسال پارامتر به تابع
در پایتون ارسال متغییر به توابع از طریق مقدار دهی صورت می گیرد . این مقدار دهی و ارسال آرگومان ها به توابع بر خلاف زبانهای C و ++C به صورت مرجع (reference) می باشد . یعنی همان متغییر و نه یک کپی از آن به تابع ارسال می گردد . پس هر گونه دستکاری آن در داخل تابع باعث تغییر متغییر اصلی خواهد شد . حتی با تغییر نام و مقدار دهی دوباره به متغییری جدید باعث ایجاد متغییر جدیدی در حافظه نخواهد شد ! و فقط داده موجود یک نام (اشاره گر) جدید خواهد داشت .
حال شما می توانید این تابع را با ورودی یا بدون ورودی فراخوانی کنید .
تابع در داخل تابع
شما می توانید تابعی را درون تابع تعریف کنید . زمانی که می خواهید تابعی بزگتر و پیچیده را به بخش هایی کوچکتر تقسیم کنید می توانید هر یک از این بخش ها را به عنوان یک تابع درون تابع اصلی تعریف کنید . که در این حالت نحوه تعریف همانند تعریف تابع معمولی می باشد . با این تفاوت که از بیرون تابع اصلی نمی توان به توابع عضو دسترسی داشت .
آرگومان های کلمات کلیدی
توابع می تونند بااستفاده از = یک ارزش یا مقدار رو به آرگومان های تابع معرفی کنند به مثال زیر توجه کنید :
مثال :
def parrot(voltage, state='a stiff', action='voom', type='Norwegian Blue'): کلید واژه
print("-- This parrot wouldn't", action, end=' ')
print("if you put", voltage, "volts through it.")
print("-- Lovely plumage, the", type)
print("-- It's", state, "!")
از تمام روش های زیر تابع میتونه فراخوانی بشه :
parrot(1000)
parrot(action = 'VOOOOOM', voltage = 1000000)
parrot('a thousand', state = 'pushing up the daisies')
parrot('a million', 'bereft of life', 'jump')
اما فراخوانی های زیر مشکل دارند:
parrot() # به یک آرگومان نیاز دارد parrot(voltage=5.0, 'dead') # به این آرگومان مثل کلید واژه مقدار داده شده است parrot(110, voltage=220) # 2 مقدار به آرگومان اولی داده شد است parrot(actor='John Cleese') # کلید واژه ناشناخته
اسامی پارامتر های قالب بندی شده با موقعیت آرگومانها مطابقت دارند و نمی توانند مانند کلید واژه ها آن ها را فرا خواند در مثال زیر آرگومان آ برابر صفر مشکل ساز است
مثال :
>>> def function(a): ... pass ... >>> function(0, a=0) Traceback (most recent call last): File "<stdin>", line 1, in ? TypeError: function() got multiple values for keyword argument 'a'
name** : یک لیست درست میکند و کلمات کلیدی همه آرگومان ها به جز آن آرگومان هایی که با پارامتر های محلی مطابقت می کنند را دریافت می کند
name*: آرگومان هایی را که در پارامتر های خاصی نیستند را به صورت یه چند تایی (ماتریس-رشته-دنباله هم) دریافت میکند
نکته : name* قبل از name** اتفاق می افتد
مثال ۱ :
>>> def z (a ,*b ,**d ) : # تعریف یک تابع می بنیید که ترتیب پارامتر های ستاره دار رعایت شده
print (a)
print (b)
print (d)
>>> z (1, ‘iraj’ , ‘jelodari’ , arg1 = ‘hello’, arg2 = ‘world’ )
1
(‘iraj’ , ‘jelodari’ )
{‘arg1′ : ‘hello’ , ‘arg2 ‘: ‘world’ }
مثال ۲ :
def cheeseshop(kind, *arguments, **keywords):
print("-- Do you have any", kind, "?")
print("-- I'm sorry, we're all out of", kind)
for arg in arguments: print(arg)
print("-" * 40)
keys = sorted(keywords.keys())
for kw in keys: print(kw, ":", keywords[kw])
این تابع می تونه این جوری فراخوانی شه :
cheeseshop("Limburger", "It's very runny, sir.",
"It's really very, VERY runny, sir.",
shopkeeper="Michael Palin",
client="John Cleese",
sketch="Cheese Shop Sketch")
و البته این تابع چاپ می کنه :
-- Do you have any Limburger ? -- I'm sorry, we're all out of Limburger It's very runny, sir. It's really very, VERY runny, sir. ---------------------------------------- client : John Cleese shopkeeper : Michael Palin sketch : Cheese Shop Sketch
توجه کنید که لیست اسامی آرگومان هایی( به رنگ —–) که درست شده است قبل از چاپ محتویات توسط متد keys() مرتب شده است اگه این کار را انجام ندیم نظم آرگومان ها به هم می خوره و معلوم نیست که چی به چیه
لیست های آرگومان اختیاری
برای تابعی استفاده می شود که می خواهد نعداد اختیاری ای از ارگومان ها را بپذیرد. این آرگومان ها درون یک تاپل (چند تایی) بندی می شوند.
def write_multiple_items(file, separator, *args):
file.write(separator.join(args))
در مثال زیر پارامتر *args تمام آرگومان های (“earth”, “mars”, “venus”) دریافتی را دریافت می کند و سپس با استفاده از متد join( ) آرگومان هارا به هم متصل میکند
مثال :
>>> def concat(*args, sep="/"):
... return sep.join(args)
...
>>> concat("earth", "mars", "venus")
'earth/mars/venus'
>>> concat("earth", "mars", "venus", sep=".") در اینجا مقدار پیش فرض sep="/" را باsep="." جایگزین کردیم #
'earth.mars.venus'
گشودن لیست های آرگومانی
وضع عکسی روی میده اگر آرگومانها بصورت فشرده توی تاپلی بسته بندی شوند و بعد برای در اختیار گذاشتن یک تابع بست باز بشن و موقعیت آرگومان ها تمییز داده شود. برای مثال تابع توکار range() آرگومان های آغاز و پایان رو نیاز داره اگر جدا کننده ای نباشد، تایعی با عملگر * بمنظور باز کردن آرگومان های درون لیست یا تاپل بنویسید
مثال :
>>> list(range(3, 6)) # یک فراخوانی نرمال با استفاده از آرگومان های جدا از هم [۳, ۴, ۵] >>> args = [3, 6] # این یک لیست است >>> list(range(*args)) # پارامتر *args تمام لیست را یکباره مثل یک آرگومان دریافت می کند [۳, ۴, ۵] # اگر علامت * را نگذاریم دستور نوشته شده خطا می دهد چون تابع نمی دونه که چی دریافت بکنه
مثل توابع – دیکشنری ها نیز می توانند با استفاده از (عملگر -**) آرگومان های کلیدی را ارسال کنند
>>> def parrot(voltage, state='a stiff', action='voom'):
... print("-- This parrot wouldn't", action, end=' ')
... print("if you put", voltage, "volts through it.", end=' ')
... print("E's", state, "!")
...
این دستور باعث میشه تا نوشته های درون تابع پرینت بعدی ادامه ی نوشته های پرینت فعلی چاپ بشن
>>> d = {"voltage": "four million", "state": "bleedin' demised", "action": "VOOM" # یک دیکشنری هستش
>>> parrot(**d)
-- This parrot wouldn't VOOM if you put four million volts through it. E's bleedin' demised !
لازم هستش اینچا تابع بالا رو توضیح بدم چون که تو متن انگلیسی چیزی ننوشته : تابع در بالا با پارامتر هایی ( پیش فرض و غیر پیش فرض) تعریف شده است در زیر تابع یک دیکشنری تعریف شده است که با استفاده از عملگر ** به تابع فرستاده می شود و باعث می شود به جای پارامتر های (پیش فرض و غیر پیش فرض) مقادیری که درون تابع هست جایگزین بشه- همین خیلی ساده و راحت
Lambda فرم های
به درخواست عموم کاربران-چند ویژگی معمولی در این تابع یافت می شه
زبان های برنامه نویسی سطح بالا که تمایل به پردازش لیست ها دارند به پایتون مجهز شده اند با کلمه کلیدی لامبدا توابع کوچک بدون نامی میشه ایجاد کرد اینجا یک تابع داریم که مجموع ۲ آرگومان را بر می گرداند : lambda a, b: a+b. شکل های مختلف لامبدا هر جایی که اشیا ئه توابع نیاز داشته باشند می توانند مورد استفاده قرار بگیرند .لامبدا قواعد نحوی محدودی دارند که فقط می توانند یک عبارت تنها را دریافت کنند به این معنی که اونا فقط شیرین کننده نحوی برای تعریف یک تابع نرمال هستند مثل تعریف توابع تو در تو – شکل های لامبدا می توانند به متغییر هایی از دامنه مشمول رجوع کنند
مثال :
>>> def make_incrementor(n): ... return lambda x: x + n ... >>> f = make_incrementor(42) # 42 در پارامتر n گذاشته می شود >>> f(0) # در اینجا حالا x=42 است و n=0 می باشد پس جمع دوتا میشه ۴۲ ۴۲ >>> f(1) # در اینجا x=42 است و n=1 می باشد پس جمع هر دو میشه۴۳ ۴۳
مستند سازی رشته ها
در اینجا بعضی از پیمان های مرتبط با مستند سازی رشته ها برای محتوا و قالب بندی کد را آورده ام:
اولین خط همیشه باید کوتاه باشد ، کوته نوشتی از هدف شی رو انتخاب کنید. برای کوتاهی اون نباید بطور صریح نام شی یا نوعش رو بیان کند زیرا اینها توسط چیزهای دیگه در دسترس هستند ( بجز انکه نام فعلی باشد که عملکرد تابع را توضیح می دهد) . این خط با حرف بزرگ آغاز و با یک نقطه پایان می یابد.
اگر مستند سازی رشته چندین خط باشد، خط دوم باید خال باشد، جدایی بصری کوته نوشت از کل توضیح.
مفسر پایتون فرو رفتگی های چندین خط از لیترال های رشته را درون پایتون نمایش نمی دهد پس ابزاری که مستندات را پردازش می کنند مجبور هستند که آن فرو رفتگی ها را نشان بدهند. این با توجه به پیمان زیر انجام می گیرد.
اولین خط غیر تهی پس از اولین خط رشته، مقدار کل فرو رفتی گی های رشته ی توضیحی را مشخص می کند ( ما نمی توانیم اولین خط رشته را برای این کار انتخاب کنیم چونکه این خط نزدیکه کوتشین های باز شده برای آغاز رشته است و در نتیجه فرو رفتگی های آن ظاهر نمی شوند) فضای سفید ۸ اسپیسی برای این کار کافی است
اینجا مثالی از یک سند چند خطی است:
مثال :
>>> def my_function():
... """Do nothing, but document it.
...
... No, really, it doesn't do anything.
... """
... pass
...
>>> print(my_function.__doc__)
Do nothing, but document it.
No, really, it doesn't do anything.
very good