Professional Documents
Culture Documents
مباحث پیشرفته در پایتون
مباحث پیشرفته در پایتون
Ahwaz_Hackerz
ﻣﺒﺎﺣﺚﭘﯿﺸﺮﻓﺘﻪدر
ﭘﺎﯾﺘﻮن
ﻣﺆﻟﻒ:
ﺳﯿﺎوش ﮔﻨﺠﯽ
Ahwaz_Hackerz
ﻓﻬﺮﺳﺖﻣﻄﺎﻟﺐ
ﺑﺨﺶ : 1وﯾﮋﮔﯽﻫﺎي ﺟﺪﯾﺪ در ﭘﺎﯾﺘﻮن 10 ..................................................................
ﻓﺼﻞ / 1ﭘﺎﯾﺘﻮن 11 ......................................................................................... 3.6
12 .......................................................................... Formatted String Literals
ﺟﺎﯾﮕﺬاري ﻣﺘﻐﯿﺮ در 12 ................................................................................. string
ﻋﺒﺎرات در 13 ......................................................................................... f-string
Type Annotationﺑﺮاي ﻣﺘﻐﯿﺮﻫﺎ 20 .......................................................................
اﺿﺎﻓﻪ ﺷﺪن ﻣﺎژول 21 ................................................................................ Secrets
ﻓﺼﻞ / 2ﭘﺎﯾﺘﻮن 24 ....................................................................................... 3.7
ﮐﻼسﻫﺎي داده 25 ...............................................................................................
ﺗﺮﺗﯿﺐ در دﯾﮑﺸﻨﺮي 29 ..........................................................................................
دﯾﮕﺮ ﺗﻐﯿﯿﺮات در ﭘﺎﯾﺘﻮن 29 ................................................................................ 3.7
ﻓﺼﻞ / 3ﭘﺎﯾﺘﻮن 30........................................................................................ 3.8
ﻋﺒﺎرت اﻧﺘﺴﺎﺑﯽ 31 ................................................................................................
آرﮔﻮﻣﺎنﻫﺎي ﻣﮑﺎﻧﯽ 32 ...........................................................................................
اﺳﺘﻔﺎده از ﻣﺴﺎوي در 34 ............................................................................. f-string
دﯾﮕﺮ ﺗﻐﯿﯿﺮات در ﭘﺎﯾﺘﻮن 34 ................................................................................ 3.8
ﻓﺼﻞ / 4ﭘﺎﯾﺘﻮن 35 ........................................................................................ 3.9
ﻋﻤﻠﮕﺮ ادﻏﺎم و ﺑﺮوزرﺳﺎن 36......................................................................................
ﺣﺬف ﭘﯿﺸﻮﻧﺪ و ﭘﺴﻮﻧﺪ رﺷﺘﻪﻫﺎ 37 ..............................................................................
37 ................................................................................................ Type Hint
ب.م.م و ك.م.م ﭼﻨﺪ ﻋﺪدي 38 ..................................................................................
دﯾﮕﺮ ﺗﻐﯿﯿﺮات در ﭘﺎﯾﺘﻮن 38 ................................................................................ 3.9
ﺑﺨﺶ : 2اﺷﺘﺒﺎﻫﺎت راﯾﺞ در ﮐﺪزدن و راهﺣﻞ ﺑﻬﺒﻮد آﻧﻬﺎ 39.........................................
ﻓﺼﻞ / 1ﺳﺒﮏﻫﺎي وﯾﮋة زﺑﺎن 40 ..............................................................................
اﺳﺘﻔﺎده از indexدر ﺣﻠﻘﻪﻫﺎي 41 ........................................................................ for
ﻋﻤﻠﮕﺮﻫﺎي ﻣﻘﺎﯾﺴﻪاي زﻧﺠﯿﺮﺷﺪه 42 ............................................................................
Ahwaz_Hackerz
ﻣﻘﺪﻣﻪ ﻧﺎﺷﺮ
9 3
ب د8ان 4ان،ﯾ
54ﯾ و
ﺣﻤﺪ و ﺳﭙﺎس اﻳﺰد ﻣﻨﺎن را ﻛﻪ ﺑﺎ اﻟﻄﺎف ﺑﻴﻜﺮان ﺧﻮد اﻳﻦ ﺗﻮﻓﻴﻖ را ﺑﻪ ﻣﺎ ارزاﻧﻲ داﺷﺖ ﺗﺎ ﺑﺘﻮاﻧﻴﻢ در راه
ارﺗﻘﺎي داﻧﺶ ﻋﻤﻮﻣﻲ و ﻓﺮﻫﻨﮕﻲ اﻳﻦ ﻣﺮز و ﺑﻮم در زﻣﻴﻨﻪ ﭼﺎپ و ﻧﺸﺮ ﻛﺘﺐ ﻋﻠﻤﻲ داﻧﺸﮕﺎﻫﻲ ،ﻋﻠﻮم ﭘﺎﻳﻪ و
ﺑﻪ وﻳﮋه ﻋﻠﻮم ﻛﺎﻣﭙﻴﻮﺗﺮ و اﻧﻔﻮرﻣﺎﺗﻴﻚ ﮔﺎمﻫﺎﻳﻲ ﻫﺮﭼﻨﺪ ﻛﻮﭼﻚ ﺑﺮداﺷﺘﻪ و در اﻧﺠﺎم رﺳﺎﻟﺘﻲ ﻛﻪ ﺑﺮ ﻋﻬﺪه
ﮔﺴﺘﺮدﮔﻲ ﻋﻠﻮم و ﺗﻮﺳﻌﻪ روزاﻓﺰون آن ،ﺷﺮاﻳﻄﻲ را ﺑﻪ وﺟﻮد آورده ﻛﻪ ﻫﺮ روز ﺷﺎﻫﺪ ﺗﺤﻮﻻت اﺳﺎﺳﻲ
ﭼﺸﻤﮕﻴﺮي در ﺳﻄﺢ ﺟﻬﺎن ﻫﺴﺘﻴﻢ .اﻳﻦ ﮔﺴﺘﺮش و ﺗﻮﺳﻌﻪ ﻧﻴﺎز ﺑﻪ ﻣﻨﺎﺑﻊ ﻣﺨﺘﻠﻒ از ﺟﻤﻠﻪ ﻛﺘﺎب را ﺑﻪ
ﻋﻨﻮان ﻗﺪﻳﻤﻲﺗﺮﻳﻦ و راﺣﺖﺗﺮﻳﻦ راه دﺳﺘﻴﺎﺑﻲ ﺑﻪ اﻃﻼﻋﺎت و اﻃﻼعرﺳﺎﻧﻲ ،ﺑﻴﺶ از ﭘﻴﺶ روﺷﻦ ﻣﻲﻧﻤﺎﻳﺪ.
در اﻳﻦ راﺳﺘﺎ ،واﺣﺪ اﻧﺘﺸﺎرات ﻣﺆﺳﺴﻪ ﻓﺮﻫﻨﮕﻲ ﻫﻨﺮي دﻳﺒﺎﮔﺮان ﺗﻬﺮان ﺑﺎ ﻫﻤﻜﺎري ﺟﻤﻌﻲ از اﺳﺎﺗﻴﺪ،
ﻣﺆﻟﻔﺎن ،ﻣﺘﺮﺟﻤﺎن ،ﻣﺘﺨﺼﺼﺎن ،ﭘﮋوﻫﺸﮕﺮان ،ﻣﺤﻘﻘﺎن و ﻧﻴﺰ ﭘﺮﺳﻨﻞ ورزﻳﺪه و ﻣﺎﻫﺮ در زﻣﻴﻨﻪ اﻣﻮر ﻧﺸﺮ
درﺻﺪد ﻫﺴﺘﻨﺪ ﺗﺎ ﺑﺎ ﺗﻼشﻫﺎي ﻣﺴﺘﻤﺮ ﺧﻮد ﺑﺮاي رﻓﻊ ﻛﻤﺒﻮدﻫﺎ و ﻧﻴﺎزﻫﺎي ﻣﻮﺟﻮد ،ﻣﻨﺎﺑﻌﻲ ﭘﺮﺑﺎر ،ﻣﻌﺘﺒﺮ و
و ﺗﻼش ﺟﻤﻌﻲ از ﻫﻤﻜﺎران اﻧﺘﺸﺎرات "ﻛﺘﺎﺑﻲ ﻛﻪ در دﺳﺖ دارﻳﺪ ﺑﺎ ﻫﻤﺖ "ﺟﻨﺎب آﻗﺎي ﺳﻴﺎوش ﮔﻨﺠﻲ
ﻣﻴﺴﺮ ﮔﺸﺘﻪ ﻛﻪ ﺷﺎﻳﺴﺘﻪ اﺳﺖ از ﻳﻜﺎﻳﻚ اﻳﻦ ﮔﺮاﻣﻴﺎن ﺗﺸﻜﺮ و ﻗﺪرداﻧﻲ ﻛﻨﻴﻢ.
در ﺧﺎﺗﻤﻪ ﺿﻤﻦ ﺳﭙﺎﺳﮕﺰاري از ﺷﻤﺎ داﻧﺶﭘﮋوه ﮔﺮاﻣﻲ درﺧﻮاﺳﺖ ﻣﻲﻧﻤﺎﻳﺪ ﺑﺎ ﻣﺮاﺟﻌﻪ ﺑﻪ آدرس
)ارﺗﺒﺎط ﺑﺎ ﻣﺸﺘﺮي( ﻓﺮم ﻧﻈﺮﺳﻨﺠﻲ را ﺑﺮاي ﻛﺘﺎﺑﻲ ﻛﻪ در دﺳﺖ دارﻳﺪ ﺗﻜﻤﻴﻞ و dibagaran.mft.info
ارﺳﺎل ﻧﻤﻮده ،اﻧﺘﺸﺎرات دﻳﺒﺎﮔﺮان ﺗﻬﺮان را ﻛﻪ ﺟﻠﺐ رﺿﺎﻳﺖ و وﻓﺎداري ﻣﺸﺘﺮﻳﺎن را ﻫﺪف ﺧﻮد ﻣﻲداﻧﺪ،
ﻳﺎري ﻓﺮﻣﺎﻳﻴﺪ.
اﻣﻴﺪوارﻳﻢ ﻫﻤﻮاره ﺑﻬﺘﺮ از ﮔﺬﺷﺘﻪ ﺧﺪﻣﺎت و ﻣﺤﺼﻮﻻت ﺧﻮد را ﺗﻘﺪﻳﻢ ﺣﻀﻮرﺗﺎن ﻧﻤﺎﻳﻴﻢ.
ﻣﺪﻳﺮ اﻧﺘﺸﺎرات
ﻣﻘﺪﻣﻪ ﻣﺆﻟﻒ
زﺑﺎن ﭘﺎﯾﺘﻮن ﺑﻪدﻟﯿﻞ ﺳﺎدﮔﯽ و اﻣﮑﺎﻧﺎت ﻓﺮاواﻧﯽ ﮐﻪ دارد ﺑﻪ ﯾﮑﯽ از زﺑﺎنﻫﺎي ﻣﺤﺒﻮب و ﭘﺮاﺳﺘﻔﺎده در ﺣﻮزهﻫﺎي
ﻣﺨﺘﻠﻒ ﺗﺒﺪﯾﻞ ﮔﺸﺘﻪ و اﻓﺮاد ﺑﺴﯿﺎري از رﺷﺘﻪﻫﺎي ﻣﺨﺘﻠﻒ ﺑﻪ ﻓﺮاﮔﯿﺮي اﯾﻦ زﺑﺎن ﻣﯽﭘﺮدازﻧﺪ .ﺑﺴﯿﺎري از
ﺑﺮﻧﺎﻣﻪﻧﻮﯾﺴﺎن ﺑﺎ داﺷﺘﻦ ﺗﺠﺮﺑﮥ ﮐﺎر ﺑﺎ زﺑﺎن ﺑﺮﻧﺎﻣﻪﻧﻮﯾﺴﯽ دﯾﮕﺮ ،ﺷﺮوع ﺑﻪ ﯾﺎدﮔﯿﺮي ﭘﺎﯾﺘﻮن ﻣﯽﮐﻨﻨﺪ .ﺑﺴﯿﺎري دﯾﮕﺮ ﻫﻢ
از ﻫﻤﺎن اﺑﺘﺪا ﺑﺎ دﺳﺘﻮرات اﺑﺘﺪاﯾﯽ ﭘﺎﯾﺘﻮن آﺷﻨﺎ ﺷﺪه و در ﺳﻄﺢ ﺑﺮﻧﺎﻣﻪﻧﻮﯾﺲ ﻣﺒﺘﺪي ﺑﺎﻗﯽﻣﺎﻧﺪهاﻧﺪ .در اﯾﻦ ﮐﺘﺎب
ﻗﺼﺪ دارم ﺑﻪ اﯾﻦ اﻓﺮاد ﻣﺒﺎﺣﺚ ﭘﯿﺸﺮﻓﺘﻪ و ﻻزم در زﺑﺎن ﭘﺎﯾﺘﻮن را ﮐﻪ ﺑﺮاي ﻣﻮﻗﻌﯿﺖﻫﺎي ﺷﻐﻠﯽ ﻻزم و ﺣﯿﺎﺗﯽ اﺳﺖ
را در ﺷﺶ ﺑﺨﺶ ﺑﻪﺻﻮرت ﮐﺎرﺑﺮدي آﻣﻮزش دﻫﻢ.
در ﺑﺨﺶ اول ﺑﻪ ﺷﺮح درﻣﻮرد وﯾﮋﮔﯽﻫﺎي ﺟﺪﯾﺪ در ﭼﻬﺎر ﻧﺴﺨﮥ آﺧﺮ ﭘﺎﯾﺘﻮن ﭘﺮداﺧﺘﻪام .وﯾﮋﮔﯽﻫﺎﯾﯽ ﮐﻪ در ﻫﺮ
ﻧﺴﺨﻪ از ﭘﺎﯾﺘﻮن ﺑﻪ آن اﺿﺎﻓﻪ ﻣﯽﺷﻮد ﺑﺴﯿﺎر زﯾﺎد ﻫﺴﺘﻨﺪ و ﻧﮕﺎرش ﻫﻤﮥ آﻧﻬﺎ ﺧﻮد ﮐﺘﺎﺑﯽ ﺟﺪا ﺧﻮاﻫﺪ ﺑﻮد؛ ﻟﺬا در اﯾﻦ
ﺑﺨﺶ ﺗﻨﻬﺎ ﺑﻪ ﻣﻮاردي ﮐﻪ ﻃﺒﻖ ﺗﺠﺮﺑﮥ اﯾﻨﺠﺎﻧﺐ ﺑﺴﯿﺎر ﮐﺎرﺑﺮدي ﻫﺴﺘﻨﺪ ،ﺑﻪ ﻣﻌﺮﻓﯽ ﺣﻀﻮر رﺳﯿﺪهاﻧﺪ.
در ﺑﺨﺶ دوم ،ﺑﺮاﺳﺎس ﺗﺠﺮﺑﮥ ﭼﻨﺪﯾﻦ ﺳﺎﻟﮥ ﺧﻮدم در اﻣﺮ آﻣﻮزش زﺑﺎن ﭘﺎﯾﺘﻮن ،ﮐﺪﻫﺎﯾﯽ دﯾﺪهام ﮐﻪ اﺷﺘﺒﺎﻫﺎت راﯾﺞ
ﺑﺴﯿﺎري از داﻧﺸﺠﻮﯾﺎﻧﻢ ﺑﻮده اﺳﺖ .ﺳﻌﯽ ﮐﺮدهام ﺗﻤﺎم اﯾﻦ اﺷﺘﺒﺎﻫﺎت را در اﯾﻦ ﮐﺘﺎب ﺟﻤﻊآوري ﮐﻨﻢ ﺗﺎ ﺑﻪ ﮔﻨﺠﯿﻨﮥ
ارزﺷﻤﻨﺪي از اﺷﺘﺒﺎﻫﺎت راﯾﺞ و روش ﺣﻞ آنﻫﺎ ﺗﺒﺪﯾﻞ ﮔﺮدد ﺗﺎ ﺑﺎ ﻣﻄﺎﻟﻌﻪ آن ،دﯾﮕﺮ ﺷﻤﺎ ﻫﻢ آن اﺷﺘﺒﺎﻫﺎت را ﺗﮑﺮار
ﻧﮑﻨﯿﺪ .در اﯾﻦ ﺑﺨﺶ ﺑﻪ ﻣﻘﺎﯾﺴﮥ ﮐﺎﻣﻞ ﺑﯿﻦ دو ﺑﺮﻧﺎﻣﻪﻧﻮﯾﺲ ﻣﺒﺘﺪي و ﺣﺮﻓﻪاي ﭘﺮداﺧﺘﻪام .از واژة ﺑﺮﻧﺎﻣﻪﻧﻮﯾﺲ ﺑﺪ در
اﯾﻦ ﻓﺼﻞ اﺳﺘﻔﺎده ﻧﮑﺮدهام ،زﯾﺮا اﻋﺘﻘﺎد دارم ﮐﻪ ﺑﺮﻧﺎﻣﻪﻧﻮﯾﺲ ﺑﺪ وﺟﻮد ﻧﺪارد .ﺑﺮﻧﺎﻣﻪﻧﻮﯾﺲ ﻣﺒﺘﺪي ﺑﺎﻋﺚ ﺑﻪ-
وﺟﻮدآﻣﺪن ﮐﺪﻫﺎي ﺑﺪ ﻣﯽﺷﻮد زﯾﺮا آﻣﻮزشﻫﺎ ،ﺑﻪﺻﻮرت درﺳﺖ و اﺻﻮﻟﯽ ﺑﻪ او داده ﻧﺸﺪه اﺳﺖ.
در ﺑﺨﺶ ﺳﻮم ،ﺑﻪ ﺑﺮرﺳﯽ و ﺗﻮﺿﯿﺢ ﻣﻔﺼﻞ ﯾﮑﯽ از ﭘﺎراداﯾﻢﻫﺎي ﭘﺮﮐﺎرﺑﺮد ﺑﺮﻧﺎﻣﻪﻧﻮﯾﺴﯽ ﯾﻌﻨﯽ ﺑﺮﻧﺎﻣﻪﻧﻮﯾﺴﯽ ﺗﺎﺑﻌﯽ
ﭘﺮداﺧﺘﻪام .از ﺗﻮاﺑﻊ ﮐﻮﭼﮏ ﮔﺮﻓﺘﻪ ﺗﺎ ﻣﻔﺎﻫﯿﻢ ﭘﯿﭽﯿﺪه ﻫﻤﭽﻮن ژﻧﺮاﺗﻮر را ﺑﺎ ﻣﺜﺎلﻫﺎﯾﯽ ﺳﺎده و ﮐﺎرﺑﺮدي ﺑﻪﻃﻮر ﮐﺎﻣﻞ
آﻣﻮزش دادهام.
در ﺑﺨﺶ ﭼﻬﺎرم ،ﯾﮑﯽ از ﺟﻨﺒﻪﻫﺎي ﻣﻬﻢ در ﻧﺮماﻓﺰار ﯾﻌﻨﯽ ﻻگ را ﺑﺮرﺳﯽ ﻣﯽﮐﻨﯿﻢ .ﻫﻨﻮز ﻫﻢ ﺗﻌﺪاد ﺑﺴﯿﺎر زﯾﺎدي از
ﺑﺮﻧﺎﻣﻪﻧﻮﯾﺴﺎن ﻫﺴﺘﻨﺪ ﮐﻪ ﺑﺎ دﺳﺘﻮر printﻻگ را ﻧﻤﺎﯾﺶ ﻣﯽدﻫﻨﺪ .در اﯾﻦ ﻓﺼﻞ ﺑﻪ ﺑﺮرﺳﯽ درﺳﺖ و ﮐﺎﻣﻞ
ﻧﺤﻮة اﺳﺘﻔﺎده از ﻻگ ﭘﺮداﺧﺘﻪام ﺗﺎ ﭘﺮوژهﻫﺎﯾﯽ ﮐﻪ اﻧﺠﺎم ﻣﯽدﻫﯿﺪ ﺣﺎوي ﻻگﻫﺎي دﺳﺘﻪﺑﻨﺪيﺷﺪه و ﮐﺎرﺑﺮدي ﺑﺎﺷﺪ.
در ﺑﺨﺶ ﭘﻨﺠﻢ ،آزﻣﻮن ﻧﺮماﻓﺰار را ﻣﻮرد ﺑﺤﺚ ﻗﺮار ﻣﯽدﻫﻢ .در اﺑﺘﺪاي راه ﺑﺮﻧﺎﻣﻪﻧﻮﯾﺴﯽ ﮐﻪ ﺑﻮدم ﻫﯿﭻ دﯾﺪي ﻧﺴﺒﺖ
ﺑﻪ ﺗﺴﺖﻫﺎي ﻧﺮماﻓﺰار ﻧﺪاﺷﺘﻢ و اﺻﻼً ﻧﻤﯽداﻧﺴﺘﻢ ﮐﻪ ﭼﻪ ﻟﺰوﻣﯽ در ﻧﻮﺷﺘﻦ و اﻧﺠﺎم ﺗﺴﺖ وﺟﻮد دارد .ﺷﺎﯾﺪ ﺷﻤﺎ ﻫﻢ
در ﺣﺎل ﺣﺎﺿﺮ ﻫﻤﭽﻮن ﻣﻦ ﻓﮑﺮ ﻣﯽﮐﻨﯿﺪ وﻟﯽ ﺑﺎ ﮔﺬﺷﺖ زﻣﺎن و ﮐﺴﺐ ﺗﺠﺮﺑﻪ و ﻣﻄﺎﻟﻌﻪ درﯾﺎﻓﺘﻢ ،ﺗﺴﺖ ﯾﮑﯽ از
زﯾﺒﺎﺗﺮﯾﻦ ﮐﺎرﻫﺎ در ﺑﺮﻧﺎﻣﻪﻧﻮﯾﺴﯽ و ﺳﺎﺧﺖ ﻧﺮماﻓﺰار اﺳﺖ .ﻧﻪﺗﻨﻬﺎ ﮐﺎري زﻣﺎنﮔﯿﺮ ﻧﯿﺴﺖ ﺑﻠﮑﻪ ﯾﮏ ﻫﻨﺮ اﺳﺖ ﺗﺎ در
زﻣﺎن ﺻﺮﻓﻪﺟﻮﯾﯽ ﮐﻨﺪ!
در ﺑﺨﺶ ﺷﺸﻢ ،ﻧﺤﻮة ﺳﺎﺧﺖ ﯾﮏ ﭘﮑﯿﺞ و اﻧﺘﺸﺎر آن را ﺗﻮﺿﯿﺢ دادهام .اﻧﺘﺸﺎر ﭘﮑﯿﺞﻫﺎ ﻣﯽﺗﻮاﻧﺪ ﺑﻪ دﯾﮕﺮ
ﺗﻮﺳﻌﻪدﻫﻨﺪﮔﺎن در ﺟﻠﻮﮔﯿﺮي از ﻧﻮﺷﺘﻦ دوﺑﺎره ﮐﺪﻫﺎ ﮐﻤﮏ ﮐﻨﺪ و ﺑﺮاي ﺷﻤﺎ اﻋﺘﺒﺎر ﺑﻪ ﻫﻤﺮاه داﺷﺘﻪ ﺑﺎﺷﺪ.
در اﻧﺘﻬﺎ ﻻزم ﺑﻪ ذﮐﺮ اﺳﺖ ﮐﻪ اﯾﻦ ﮐﺘﺎب ﯾﻘﯿﻨﺎً ﺧﺎﻟﯽ از اﺷﮑﺎل ﻧﯿﺴﺖ .از ﺷﻤﺎ ﺧﻮاﻧﻨﺪة ﻋﺰﯾﺰ و ﮔﺮاﻣﯽ ﺧﻮاﻫﺸﻤﻨﺪم
در ﺻﻮرت داﺷﺘﻦ ﭘﯿﺸﻨﻬﺎد و ﯾﺎ اﻧﺘﻘﺎد ﺣﺘﻤﺎً ﺑﺎ ﺑﻨﺪه در ﻣﯿﺎن ﺑﮕﺬارﯾﺪ.
ﺳﯿﺎوش ﮔﻨﺠﯽ
Contact@Siyanew.ir
Ahwaz_Hackerz
ﭘﯿﺸﮕﻔﺘﺎر
ﻧﺨﺴﺖ ،از اﯾﺰد ﯾﮑﺘﺎ ﺳﭙﺎﺳﮕﺰارم ﮐﻪ ﻣﺮا از داﻧﺶ ﺑﯽﮐﺮان ﺧﻮد ﺳﻬﻤﯽ داد و راﻫﯽ را ﺑﺮاﯾﻢ ﻣﺤﻘﻖ ﮐﺮد ﺗﺎ
ﺑﺘﻮاﻧﻢ داﻧﺶ اﻧﺪك ﺧﻮد را ﺑﻪ اﻧﺴﺎنﻫﺎي ﻓﺮﻫﯿﺨﺘﻪ و ﺟﻮﯾﺎي ﻋﻠﻢ ﻣﻨﺘﻘﻞ ﮐﻨﻢ و ﺳﻬﻤﯽ در ﭘﯿﺸﺮﻓﺖ ﻋﻠﻢ و
داﻧﺶ ﺑﺸﺮﯾﺖ داﺷﺘﻪ ﺑﺎﺷﻢ .از آﻗﺎي ﻓﺮﺳﺎﯾﯽ ،ﻣﺪﯾﺮ ﻣﺤﺘﺮم اﻧﺘﺸﺎرات دﯾﺒﺎﮔﺮان و ﻫﻤﭽﻨﯿﻦ ﺧﺎﻧﻢ ﻗﺰﻟﺒﺎش،
ﻣﺴﺌﻮل ﺗﺄﻟﯿﻒ و ﺗﺮﺟﻤﮥ اﻧﺘﺸﺎرات دﯾﺒﺎﮔﺮان ،ﺻﻤﯿﻤﺎﻧﻪ ﺗﺸﮑﺮ ﻣﯽﮐﻨﻢ؛ ﭼﺮاﮐﻪ ﺑﺪون ﮐﻤﮏﻫﺎي اﯾﺸﺎن
ﮔﺮدآوري و ﭼﺎپ اﯾﻦ ﮐﺘﺎب ﻏﯿﺮﻣﻤﮑﻦ ﺑﻮد.
ﻧﮕﺎرش اﯾﻦ ﮐﺘﺎب در ﺣﺪود 6ﻣﺎه زﻣﺎن ﺑﺮده اﺳﺖ ،اﻣﺎ ﻣﻄﺎﻟﺒﯽ ﮐﻪ در آن وﺟﻮد دارد ﺣﺎﺻﻞ ﺳﺎلﻫﺎ
ﺗﺠﺮﺑﻪ ،آﻣﻮزش ،ﺗﺤﻠﯿﻞ و ﻣﻄﺎﻟﻌﻪ ﻣﺒﺎﺣﺚ زﺑﺎن ﺷﯿﺮﯾﻦ ﭘﺎﯾﺘﻮن ﺑﻮده اﺳﺖ .ﭘﺲ از ﺑﺮﮔﺰاري ﭼﻨﺪ ﺻﺪ ﺳﺎﻋﺖ
ﮐﻼس ﺣﻀﻮري و ﻏﯿﺮﺣﻀﻮري ﺑﺮﻧﺎﻣﻪﻧﻮﯾﺴﯽ ،ﯾﮑﯽ از ﭼﺎﻟﺶﻫﺎي ﺗﺄﻟﯿﻒ اﯾﻦ ﮐﺘﺎب ﺑﺮاﯾﻢ اﻧﺘﻘﺎل ﻣﻔﺎﻫﯿﻢ
ﺗﻨﻬﺎ ﺑﺎ ﮐﻠﻤﺎت ﺑﻮد .ﺗﻤﺎم ﺳﻌﯽ ﺧﻮد را ﮐﺮدهام ﺗﺎ ﻣﺒﺎﺣﺜﯽ ﮐﺎرﺑﺮدي را ﮔﺮدآوري ﮐﻨﻢ ﺗﺎ ﺣﺘﯽ ﭘﺲ از
ﭼﻨﺪﯾﻦ ﺳﺎل ﮐﺎرآﻣﺪ ﺑﺎﺷﺪ و در ﺗﻤﺎم ﻣﺒﺎﺣﺚ ﻣﺜﺎلﻫﺎي ﮐﺎرﺑﺮدي را ﺑﺮاي ﺷﻤﺎ ﺧﻮاﻧﻨﺪة ﻋﺰﯾﺰ ﺑﺎ ﺻﺒﺮ و
ﺣﻮﺻﻠﻪ ﺗﺪوﯾﻦ ﮐﻨﻢ .اﻣﯿﺪوارم ﮐﻪ ﺑﻪ ﮐﻤﮏ اﯾﻦ ﮐﺘﺎب ﺑﺘﻮاﻧﯿﺪ ﺳﻄﺢ داﻧﺶ ﺧﻮد را در زﺑﺎن ﭘﺎﯾﺘﻮن ﺑﺎﻻﺗﺮ
ﺑﺮده و ﺑﺎ ﻣﻔﺎﻫﯿﻢ ﭘﯿﺸﺮﻓﺘﻪ ﭘﺎﯾﺘﻮن آﺷﻨﺎ ﺷﻮﯾﺪ.
در ﭘﺎﯾﺎن ،از ﭘﺪر ،ﻣﺎدر و ﺑﺮادرم ﮐﻪ ﺻﻤﯿﻤﺎﻧﻪ در ﺗﻤﺎم ﻃﻮل زﻧﺪﮔﯽام ﻣﺸﻮق و راﻫﻨﻤﺎي دﻟﺴﻮزم ﺑﻮدهاﻧﺪ،
ﮐﻤﺎل ﺗﺸﮑﺮ و ﻗﺪرداﻧﯽ را دارم و اﻣﯿﺪوارم ﻫﻤﯿﺸﻪ ﺑﺎﻋﺚ ﺳﺮﺑﻠﻨﺪي و ﺳﺮاﻓﺮازﯾﺸﺎن ﺑﺎﺷﻢ.
ﺳﯿﺎوش ﮔﻨﺠﯽ
Ahwaz_Hackerz
ﺑﺨﺶ1
ﻓﺼﻞ1
ﭘﺎﯾﺘﻮن 3.6
Ahwaz_Hackerz
اﺳﺘﻔﺎده از ﺗﻤﺎﻣﯽ اﻣﮑﺎﻧﺎت ﯾﮏ زﺑﺎن ﺑﻪ ﺷﻤﺎ ﮐﻤﮏ ﻣﯽﮐﻨﺪ ﺗﺎ ﮐﺪﻫﺎي ﺑﺎ ﮐﯿﻔﯿﺖ ﺑﺎﻻﺗﺮي ﺑﻨﻮﯾﺴﯿﺪ .در
ﻓﺼﻞﻫﺎي ﭘﯿﺶرو ،ﺑﻪ ﺑﺮرﺳﯽ ﺟﺎﻣﻊ وﯾﮋﮔﯽﻫﺎي ﺟﺪﯾﺪ و اﺿﺎﻓﻪ ﺷﺪه ﺑﻪ زﺑﺎن ﭘﺎﯾﺘﻮن از ﻧﺴﺨﻪ 3.6ﺑﻪ ﺑﻌﺪ
ﻣﯽﭘﺮدازﯾﻢ.
"name = "Siavash
text = "My name is " + name
)print(text
"name = "Siavash
)text = "My name is {}".format(name
)print(text
اﻣﺎ ﺑﺎ اﺳﺘﻔﺎده از وﯾﮋﮔﯽ ﺟﺪﯾﺪ در ﭘﺎﯾﺘﻮن 3.6اﯾﻦ ﻋﻤﻞ را ﻣﯽﺗﻮان ﺳﺎدهﺗﺮ اﻧﺠﺎم داد ،ﺗﻨﻬﺎ ﮐﺎﻓﯿﺴﺖ در
اﺑﺘﺪاي رﺷﺘﻪ ﺣﺮف fرا ﻗﺮار داده و ﻫﺮﮐﺠﺎ ﮐﻪ ﻧﯿﺎز ﺑﻪ ﭼﺎپ ﻣﺘﻐﯿﺮ وﺟﻮد دارد آن را ﺑﯿﻦ آﮐﻮﻻد ﻗﺮار داد.
"name = "Siavash
"}text = f"My name is {name
)print(text
ﻋﺒﺎرات در f-string
ﻋﻼوهﺑﺮ ﺟﺎﯾﮕﺬاري ﻣﺘﻐﯿﺮﻫﺎ در رﺷﺘﻪﻫﺎ ﻣﯽﺗﻮان از ﻋﺒﺎرات و ﻋﻤﻠﮕﺮﻫﺎ در ﻣﯿﺎن آﮐﻮﻻد اﺳﺘﻔﺎده ﮐﺮد .اﯾﻦ
ﻋﺒﺎرات در زﻣﺎن اﺟﺮا 1ﻣﺤﺎﺳﺒﻪﺷﺪه و در داﺧﻞ رﺷﺘﻪﻫﺎي ﻣﺘﻨﯽ ﺟﺎيﮔﺬاري ﻣﯽﺷﻮﻧﺪ.
a = 26
b = 21
"}text = f"A plus B is equal to {a + b
)print(text
ﻗﺎﺑﻠﯿﺖ ﻓﺮاﺧﻮاﻧﯽ ﺗﻮاﺑﻊ ﻧﯿﺰ در f-stringوﺟﻮد دارد .در ﻣﺜﺎل زﯾﺮ ﻣﺘﻐﯿﺮ nameرا ﺑﺎ اﺳﺘﻔﺎده از ﻓﺮاﺧﻮاﻧﯽ
ﻣﺘﺪ upperﺑﻪ ﺣﺮوف ﺑﺰرگ ﺗﺒﺪﯾﻞ ﻣﯽﮐﻨﯿﻢ.
"name = "python
"text = f"{name.upper()} is the best programming language
)print(text
در ﺗﮑﻪ ﮐﺪﻫﺎﯾﯽ ﮐﻪ ﻧﯿﺎز ﺑﻪ ﻓﺮاﺧﻮاﻧﯽ ﺗﻮاﺑﻊ ﺑﺎ ﭘﺎراﻣﺘﺮﻫﺎي رﺷﺘﻪاي اﺳﺖ ﺑﺎﯾﺪ از quotingﻣﺘﻔﺎوت اﺳﺘﻔﺎده
ﮐﺮد ،ﯾﻌﻨﯽ اﮔﺮ ﺑﺮاي ﮐﻞ رﺷﺘﻪ از single quoteاﺳﺘﻔﺎده ﮐﺮدهاﯾﺪ از double quoteﺑﺮاي ﻓﺮاﺧﻮاﻧﯽ
ﭘﺎراﻣﺘﺮﻫﺎ اﺳﺘﻔﺎده ﮐﻨﯿﺪ و ﺑﺮﻋﮑﺲ .اﯾﻦ ﻗﺎﻧﻮن ﺑﺮاي درﯾﺎﻓﺖ ﻣﻘﺎدﯾﺮ از ﯾﮏ دﯾﮑﺸﻨﺮي ﺑﺎ اﺳﺘﻔﺎده از ﮐﻠﯿﺪ
رﺷﺘﻪاي ﻧﯿﺰ ﺻﺎدق اﺳﺖ .در ﻣﺜﺎل اﺷﺘﺒﺎه زﯾﺮ از ﯾﮏ ﻧﻮع quotingاﺳﺘﻔﺎده ﺷﺪه اﺳﺖ.
"number = "1-000-000
"})"text = f"The number is {number.replace("-",",
)print(text
"number = "1-000-000
"})'text = f"The number is {number.replace('-',',
)print(text
1
Runtime
Ahwaz_Hackerz
"line_separated = "line1-line2
"})'text = f"{line_separated.replace('-','\n
)print(text
"line_separated = "line1-line2
'_ = '\n
"})_text = f"{line_separated.replace('-',
)print(text
>> line1
line2
ﺗﻮﺟﻪ ﮐﻨﯿﺪ ﮐﻪ ﮔﺬاﺷﺘﻦ ﺳﻪ ﻋﺪد آﮐﻮﻻد ﻫﻢ ﻣﺎﻧﻨﺪ دو آﮐﻮﻻد ﺗﻨﻬﺎ ﯾﮑﺒﺎر آن را ﭼﺎپ ﻣﯽﮐﻨﺪ و ﯾﮏ آﮐﻮﻻد
ﺑﺮاي ﺟﺎيﮔﺬاري ﻣﺘﻐﯿﺮ و ﯾﺎ اﻧﺠﺎم ﻣﺤﺎﺳﺒﺎت در آن ﺑﯿﻦ اﺳﺘﻔﺎده ﻣﯽ ﺷﻮد.
)"}}print(f"{{2+2
}>> {2+2
)"}}}print(f"{{{2+2
}>> {4
)"}}}}print(f"{{{{2+2
}}>> {{2+2
Ahwaz_Hackerz
ﺑﻪﻃﻮر ﭘﯿﺶﻓﺮض ﭘﺎﯾﺘﻮن ﺑﺮاي ﻓﺮﻣﺖﮐﺮدن از ﻫﻤﺎن ﺗﻌﺪاد ﮐﺎراﮐﺘﺮ در ﻣﺤﺘﻮا اﺳﺘﻔﺎده ﻣﯽﮐﻨﺪ وﻟﯽ ﺑﺎ
اﺳﺘﻔﺎده از Paddingﻣﯽﺗﻮان ﻓﺎﺻﻠﻪ و ﮐﺎراﮐﺘﺮﻫﺎﯾﯽ از ﻃﺮﻓﯿﻦ ﺑﻪ ﯾﮏ رﺷﺘﻪ اﺿﺎﻓﻪ ﮐﺮد .در ﻣﺜﺎلﻫﺎي زﯾﺮ
ﺳﻌﯽ ﺷﺪه ﺗﻤﺎم ﺣﺎﻟﺖﻫﺎي ﻣﻤﮑﻦ ﺑﺮرﺳﯽ ﺷﻮد.
ﺑﺮاي ﻣﺘﻐﯿﺮ رﺷﺘﻪاي ،ﺑﺎ ﮔﺬاﺷﺘﻦ ﻋﺪد در ﻗﺴﻤﺖ ﻓﺮﻣﺖ ﻣﯽﺗﻮان ﺑﻪ آن Paddingداد .ﺑﻪ اﻧﺪازة ﺗﻌﺪاد
ﮐﺎراﮐﺘﺮﻫﺎﯾﯽ ﮐﻪ ﻧﯿﺎز اﺳﺖ ﺗﺎ ﺑﻪ رﺷﺘﮥ اﺻﻠﯽ اﺿﺎﻓﻪ ﺷﺪه ﺗﺎ ﻣﺠﻤﻮع ﻃﻮل آﻧﻬﺎ ﺑﻪ ﻋﺪد درجﺷﺪه ﺑﺮﺳﺪ،
ﻓﺎﺻﻠﻪ ﮔﺬاﺷﺘﻪ ﻣﯽﺷﻮد .در ﻣﺜﺎل زﯾﺮ ﻋﺪد 10ﻧﻮﺷﺘﻪ ﺷﺪه ﯾﻌﻨﯽ ﻣﺘﻐﯿﺮ testﭼﺎپ ﺷﻮد و ﺗﻌﺪاد ﻣﮑﺎن
ﺑﺎﻗﯽﻣﺎﻧﺪه ﺑﺮاي رﺳﯿﺪن ﺑﻪ ﻋﺪد space ،10ﭼﺎپ ﺷﻮد .در اﯾﻨﺠﺎ testﭼﻬﺎر ﺣﺮف دارد و ﺑﺮاي رﺳﯿﺪن
ﺑﻪ 10ﮐﺎراﮐﺘﺮ 6 ،ﮐﺎراﮐﺘﺮ ﮐﻢ اﺳﺖ؛ ﺑﻨﺎﺑﺮاﯾﻦ 6ﮐﺎراﮐﺘﺮ از ﺳﻤﺖ راﺳﺖ ﺑﻪ آن اﺿﺎﻓﻪ ﻣﯽﺷﻮد.
"test = "test
)"print(f"this is {test:10} string.
ﺑﺮاي ﮔﺬاﺷﺘﻦ Paddingاز ﺳﻤﺖ ﭼﭗ ،ﮐﺎﻓﯿﺴﺖ ﮐﺎراﮐﺘﺮ > را ﻗﺒﻞ از ﻋﺪد ﻗﺮار دﻫﯿﻢ .ﻫﻤﭽﻨﯿﻦ ﻣﯽﺗﻮان
ﺑﺮاي Paddingاز ﺳﻤﺖ راﺳﺖ از ﮐﺎراﮐﺘﺮ < اﺳﺘﻔﺎده ﮐﺮد .دﻗﺖ ﮐﻨﯿﺪ ﮐﻪ ﺑﻪ ﺻﻮرت ﭘﯿﺶﻓﺮض اﮔﺮ
ﻫﯿﭻﮐﺪام از اﯾﻦ ﮐﺎراﮐﺘﺮﻫﺎ ﻧﻮﺷﺘﻪ ﻧﺸﻮد ،ﭘﺎﯾﺘﻮن از ﺳﻤﺖ راﺳﺖ ﻓﺎﺻﻠﻪﮔﺬاري ﻣﯽﮐﻨﺪ.
Ahwaz_Hackerz
"test = "test
)"print(f"this is {test:>10} string.
ﻣﯽﺗﻮان ﮐﺎراﮐﺘﺮ Paddingرا ﻧﯿﺰ ﺗﻐﯿﯿﺮ داد و از ﮐﺎراﮐﺘﺮ دﯾﮕﺮي ﺑﻪ ﻏﯿﺮ از ﻓﺎﺻﻠﻪ اﺳﺘﻔﺎده ﮐﺮد .ﺗﻨﻬﺎ
ﮐﺎﻓﯿﺴﺖ اﯾﻦ ﮐﺎراﮐﺘﺮ را ﺑﻼﻓﺎﺻﻠﻪ ﭘﺲ از دوﻧﻘﻄﻪ ﻗﺮار دﻫﯿﺪ .دﻗﺖ ﮐﻨﯿﺪ ﮐﻪ در اﯾﻦ ﺣﺎﻟﺖ ﺣﺘﻤﺎً ﺑﺎﯾﺪ
ﮐﺎراﮐﺘﺮﻫﺎي < ﯾﺎ > را ﺑﻨﻮﯾﺴﯿﺪ.
"test = "test
)"print(f"this is {test:+<10} string.
ﺑﺮاي آﻧﮑﻪ ﺑﺘﻮاﻧﯿﺪ ﻫﻢ از راﺳﺖ و ﻫﻢ از ﭼﭗ Paddingداﺷﺘﻪ ﺑﺎﺷﯿﺪ و رﺷﺘﮥ ﺣﺮﻓﯽ را در وﺳﻂ ﺑﻨﻮﯾﺴﯿﺪ
ﻣﯽﺗﻮاﻧﯿﺪ از ﮐﺎراﮐﺘﺮ ^ اﺳﺘﻔﺎده ﮐﻨﯿﺪ.
"test = "test
)"print(f"this is {test:-^10} string.
"test = "test
)"print(f"this is {test:-^7} string.
ﺑﺮﻋﮑﺲ Paddingﻣﯽﺗﻮان رﺷﺘﻪﻫﺎي ﺣﺮﻓﯽ ﻃﻮﻻﻧﯽ را ﮐﻮﺗﺎه ﮐﺮد .ﻋﺪد ﭘﺲ از ﮐﺎراﮐﺘﺮ ﻧﻘﻄﻪ ﺗﻌﺪاد
ﮐﺎراﮐﺘﺮ ﻣﺤﺪودﺷﺪه را ﺑﻪ ﻧﻤﺎﯾﺶ ﻣﯽﮔﺬارد.
"test = "test
)"print(f"this is {test:.2} string.
1
Truncate
Ahwaz_Hackerz
"test = "test
)"print(f"this is {test:+>8.2} string.
ﻣﺸﺎﺑﻪ Paddingدر رﺷﺘﻪﻫﺎي ﺣﺮﻓﯽ ،در اﻋﺪاد ﻫﻢ ﻣﻔﻬﻮم ﻣﺸﺎﺑﻬﯽ ﺑﺎ ﻧﺎم Widthوﺟﻮد دارد و اﯾﻦ
ﻋﺪد ﺑﻌﺪ از ﮐﺎراﮐﺘﺮ دو ﻧﻘﻄﻪ ﻧﻮﺷﺘﻪ ﻣﯽﺷﻮد .ﻓﻘﻂ ﺑﺎﯾﺪ ﺗﻮﺟﻪ داﺷﺖ ﮐﻪ ﺗﻨﻬﺎ ﺗﻔﺎوت آﻧﻬﺎ در ﺟﻬﺖ
ﭘﯿﺶﻓﺮض آن اﺳﺖ .در رﺷﺘﻪﻫﺎي ﻋﺪدي ﺑﻪ ﺻﻮرت ﭘﯿﺶﻓﺮض از ﺳﻤﺖ ﭼﭗ ﻓﺎﺻﻠﻪ ﮔﺬاﺷﺘﻪ ﻣﯽﺷﻮد.
number = 21
)"print(f"The number is {number:6}.
number = 21
)"print(f"The number is {number:<6}.
ﻫﻤﭽﻨﯿﻦ ﺑﺮاي ﺗﻐﯿﯿﺮ ﮐﺎراﮐﺘﺮ ﭘﯿﺶﻓﺮض ﺑﻪﺟﺎي ﻓﺎﺻﻠﻪ ،ﻣﯽﺗﻮان ﮐﺎراﮐﺘﺮ را ﻗﺒﻞ از Widthﻧﻮﺷﺖ.
ﻧﮑﺘﮥ ﻗﺎﺑﻞ ﺗﻮﺟﻪ آن اﺳﺖ ﮐﻪ ﺗﻨﻬﺎ ﺑﺮاي ﺟﺎﯾﮕﺰﯾﻨﯽ ﺻﻔﺮ ﺑﺎ ﮐﺎراﮐﺘﺮ ﻓﺎﺻﻠﻪ ﻧﯿﺎزي ﺑﻪ ﮐﺎراﮐﺘﺮﻫﺎي < و ﯾﺎ >
ﻧﯿﺴﺖ.
number = 21
)"print(f"The number is {number:06}.
number = 21
)"print(f"The number is {number:*6}.
number = 21
)"print(f"The number is {number:*>6}.
ﻣﯽﺗﻮان ﻗﺴﻤﺘﯽ از اﻋﺪاد اﻋﺸﺎري ) (Floatرا رﻧﺪ ﮐﺮد و ﺗﻨﻬﺎ ﻗﺴﻤﺘﯽ از اﻋﺸﺎر آن را ﮔﺰارش ﮐﺮد .ﺑﺮاي
اﺳﺘﻔﺎده ﺑﺎﯾﺪ ﺗﻌﺪاد ارﻗﺎﻣﯽ ﮐﻪ ﺑﻌﺪ از اﻋﺸﺎر ﻣﯽﺧﻮاﻫﯿﻢ ﺑﺎﻗﯽ ﺑﻤﺎﻧﺪ را ﺑﻌﺪ از ﮐﺎراﮐﺘﺮ ﻧﻘﻄﻪ ﻧﻮﺷﺖ ،ﺳﭙﺲ
ﺑﻌﺪ از آن از ﮐﺎراﮐﺘﺮ fاﺳﺘﻔﺎده ﮐﺮد .ﺑﺴﯿﺎر واﺿﺢ اﺳﺖ ﮐﻪ اﺳﺘﻔﺎده از اﯾﻦ ﻋﻤﻠﯿﺎت در اﻋﺪاد ﺻﺤﯿﺢ
) (Integerﻣﻌﻨﯽ ﻧﺪارد.
number = 3.1415926535
)"}print(f"The Pi number = {number:.2f
number = 3.1415926535
)"}print(f"The Pi number = {number:.3f
ﺗﺮﮐﯿﺐ ﻓﺮﻣﺖﻫﺎ
در اداﻣﻪ ﭼﻨﺪﯾﻦ ﻣﺜﺎل از ﺗﺮﮐﯿﺐ و ﻗﺎﺑﻠﯿﺖﻫﺎي دﯾﮕﺮ در ﺟﺪول 1ﺑﺮرﺳﯽ ﺷﺪه اﺳﺖ.
ﻓﺮﻣﺖ ﺗﺎرﯾﺦ
ﻓﺮﻣﺖدﻫﯽ در f-stringﺑﺎﻋﺚ ﻣﯽﺷﻮد از ﻓﺮﻣﺖﻫﺎي اﺷﯿﺎ ﻣﺨﺘﻠﻒ ﻧﯿﺰ ﺑﺘﻮان اﺳﺘﻔﺎده ﮐﺮد .ﺑﻪ ﻫﻤﯿﻦ
ﻣﻨﻈﻮر ﻣﯽﺗﻮان ﺑﺮاي datetimeﻧﯿﺰ ﻓﺮﻣﺖ ﺗﻌﯿﯿﻦ ﮐﺮد.
)date = datetime(2020,1,2,10,10
)"}print(f"{date:%Y/%m/%d%H:%M
1
در ﺟﺪول 2ﮐﺪﻫﺎي ﭘﺮ اﺳﺘﻔﺎده ﺑﺮاي ﻓﺮﻣﺖدﻫﯽ در ﺗﺎرﯾﺦ ﮔﺮدآوري ﺷﺪه اﺳﺖ .اﯾﻦ ﻣﺜﺎلﻫﺎ ﺑﺎ Locale
اﻧﮕﻠﯿﺴﯽ ﻣﯽﺑﺎﺷﺪ و ﺑﺮاي ﻫﺮ Localeﺧﺮوﺟﯽﻫﺎ ﻣﯽﺗﻮاﻧﺪ ﻣﺘﻔﺎوت ﺑﺎﺷﺪ.
اﺳﺘﻔﺎده از ﻓﺮﻣﺖﻫﺎ ﺑﻪ ﭼﺎپ رﺷﺘﻪﻫﺎ ﺑﻪ ﺻﻮرت ﻣﻨﻈﻢ و راﺣﺖﺗﺮ ﮐﻤﮏ ﻣﯽﮐﻨﺪ .در اداﻣﻪ اﻃﻼﻋﺎت اﻓﺮاد
ﺷﺎﻣﻞ ،ﻧﺎم ،ﺳﻦ و ﻗﺪ آﻧﻬﺎ در ﯾﮏ دﯾﮑﺸﻨﺮي ﻣﻮﺟﻮد اﺳﺖ و ﻣﺎ ﺑﺎ اﺳﺘﻔﺎده از ﻓﺮﻣﺖﻫﺎ ﻣﯽﺧﻮاﻫﯿﻢ اﯾﻦ
اﻃﻼﻋﺎت را ﺑﻪﺻﻮرت ﻣﻨﻈﻢ و ﺧﻮاﻧﺎ ﭼﺎپ ﮐﻨﯿﻢ.
[ = users
{"name": "Siavash", "age": 25, "height": 1.85},
{"name": "Soroush", "age": 16, "height": 1.72},
}{"name": "Guido", "age": 64, "height": 1.8
]
در ﻣﺜﺎل ﻗﺒﻞ ،ﺑﺮاي ﻧﺎم ،ﻓﻀﺎي 8و ﺑﺮاي ﺳﻦ ،ﻓﻀﺎي 3ﮐﺎراﮐﺘﺮي درﻧﻈﺮ ﮔﺮﻓﺘﻪ ﺷﺪه اﺳﺖ .ﻫﻤﭽﻨﯿﻦ ﻗﺪ
اﻓﺮاد ﺑﺎ دو رﻗﻢ اﻋﺸﺎر ،ﻓﺮﻣﺖدﻫﯽ ﺷﺪه اﺳﺖ .ﻧﮑﺘﮥ دﯾﮕﺮي ﮐﻪ در اﯾﻦ ﻣﺜﺎل وﺟﻮد دارد ﻗﺎﺑﻠﯿﺖ ﻧﻮﺷﺘﻦ
ﭼﻨﺪﺧﻄﯽ رﺷﺘﻪﻫﺎ ﻣﯽﺑﺎﺷﺪ .ﺑﻪ اﯾﻦ ﺻﻮرت ﮐﻪ رﺷﺘﻪﻫﺎ را ﺑﺮاي ﺧﻮاﻧﺎﯾﯽ ﺑﯿﺸﺘﺮ ﻣﯽﺗﻮان در ﻫﺮ ﺧﻂ ﻧﻮﺷﺖ
اﻣﺎ در اﺑﺘﺪاي ﻫﺮ ﺧﻂ ﺑﺎﯾﺪ ﮐﺎراﮐﺘﺮ fﮔﺬاﺷﺘﻪ ﺷﻮد.
اﻣﺎ در ﭘﺎﯾﺘﻮن 3.6ﺑﺮاي ﻣﺘﻐﯿﺮﻫﺎ ﻫﻢ اﯾﻦ اﻣﮑﺎن ﻓﺮاﻫﻢ ﺷﺪه اﺳﺖ .ﻣﯽﺗﻮان ﻧﻮع ﻣﺘﻐﯿﺮﻫﺎ را ﻧﯿﺰ ﺗﻌﺮﯾﻒ ﮐﺮد.
ﺑﺮاي اﺳﺘﻔﺎده از دﯾﮑﺸﻨﺮي و ﻟﯿﺴﺖ و ﻧﻮعﻫﺎي دﯾﮕﺮ ﺑﺎﯾﺪ از ﻣﺎژول typingاﺳﺘﻔﺎده ﮐﺮد.
ﻻزم ﺑﻪ ذﮐﺮ اﺳﺖ ﮐﻪ ﻣﻔﺴﺮ ﭘﺎﯾﺘﻮن Type Hintsرا ﺑﺮرﺳﯽ ﻧﻤﯽﮐﻨﺪ و ﺑﺮاي آن ﻣﻌﻨﯽ ﻧﺪارد و ﺑﺮاي
اﺳﺘﻔﺎده از اﺑﺰارﻫﺎ و ﮐﺘﺎﺑﺨﺎﻧﻪﻫﺎي دﯾﮕﺮ ﻫﻤﭽﻮن PyCharmو Mypyﻣﻔﯿﺪ اﺳﺖ.
ﺷﺎﯾﺪ اﯾﻦ ﺳﺆال ﭘﯿﺶ ﺑﯿﺎﯾﺪ ﮐﻪ ﭼﺮا از ﻫﻤﺎن randomاﺳﺘﻔﺎده ﻧﮑﻨﯿﻢ؟ ﺟﻮاب آن اﺳﺖ ﮐﻪ ﺑﺮاي ﮐﺎرﻫﺎي
ﻏﯿﺮﺿﺮوري )ﻣﺜﻼً در ﯾﮏ ﺑﺎزي ﺑﺨﻮاﻫﯿﻢ ﯾﮏ ﻋﺪد ﺗﺼﺎدﻓﯽ ﺗﻮﻟﯿﺪ ﮐﻨﯿﻢ( ﻣﯽﺗﻮان از randomاﺳﺘﻔﺎده
ﮐﺮد .در اﯾﻦ ﻣﺎژول از seedاﺳﺘﻔﺎده ﻣﯽﺷﻮد ﮐﻪ اﮔﺮ ﻣﻨﺒﻊ randomnessرا ﺳﯿﺴﺘﻢﻋﺎﻣﻞ ﻋﺮﺿﻪ ﻧﮑﻨﺪ،
از زﻣﺎن ﺳﯿﺴﺘﻢ اﺳﺘﻔﺎده ﻣﯽﺷﻮد .ﺑﺎ داﺷﺘﻦ اﻋﺪاد ﺗﺼﺎدﻓﯽ ﺗﻮﻟﯿﺪ ﺷﺪه ،ﻣﯽﺗﻮان seedرا ﺣﺪس زد و
رﺷﺘﻪﻫﺎي ﻣﺘﻮاﻟﯽ را در ﻃﻮل زﻣﺎن ﺗﻮﻟﯿﺪ ﮐﺮد .اﻣﺎ در ﻣﻮرد ﺗﻮﻟﯿﺪ رﻣﺰ ﻋﺒﻮر ﮐﻪ ﯾﮏ رﺷﺘﻪ ﺑﺴﯿﺎر ﻣﻬﻢ و
ﺣﯿﺎﺗﯽ اﺳﺖ ،ﻣﺴﺌﻠﻪ ﻓﺮق ﻣﯽﮐﻨﺪ و ﺑﺎﯾﺪ ﺣﺘﻤﺎً از ﺳﺨﺖﺑﻮدن رﺷﺘﻪ ﺗﻮﻟﯿﺪﺷﺪه ﻣﻄﻤﺌﻦ ﺑﻮد و اﻣﮑﺎن ﺣﺪس
و ﺗﻮﻟﯿﺪ آن وﺟﻮد ﻧﺪاﺷﺘﻪ ﺑﺎﺷﺪ.
1
Pseudo-random
Ahwaz_Hackerz
ﺗﺎﺑﻊ ﺗﻮﺿﯿﺤﺎت
ﯾﮏ اﻟﻤﺎن از ﯾﮏ دﻧﺒﺎﻟﮥ ﻏﯿﺮﺧﺎﻟﯽ را
)secrets.choice(sequence
ﺗﺼﺎدﻓﯽ ﺑﺮﻣﯽﮔﺮداﻧﺪ.
ﯾﮏ ﻋﺪد ﺻﺤﯿﺢ ﺗﺼﺎدﻓﯽ را در ﺑﺎزة
)secrets.randbelow(n
) [0,nﺑﺮﻣﯽﮔﺮداﻧﺪ.
ﯾﮏ رﺷﺘﻪ hexﺗﺼﺎدﻓﯽ ﺑﺮﻣﯽﮔﺮداﻧﺪ.
]secrets.token_hex([nbytes=None ﺗﻮﺟﻪ ﺷﻮد ﻫﺮ ﺑﺎﯾﺖ ﺑﻪ دو رﻗﻢ hex
ﺗﺒﺪﯾﻞ ﻣﯽﺷﻮد.
ﯾﮏ Tokenاﻣﻦ ﺑﺮاي اﺳﺘﻔﺎده در URL
ﺑﺮﻣﯽﮔﺮداﻧﺪ ﮐﻪ ﺑﺎ اﻟﮕﻮرﯾﺘﻢ Base64
)]secrets.token_urlsafe([nbytes=None رﻣﺰﮔﺬاري ﺷﺪه اﺳﺖ .ﺗﻘﺮﯾﺒﺎً ﻃﻮل آن
ﺑﻪﻃﻮر ﻣﯿﺎﻧﮕﯿﻦ 1٫3ﺑﺮاﺑﺮ nbytes
اﺳﺖ.
در ﻣﺜﺎل زﯾﺮ ﻣﯽﺧﻮاﻫﯿﻢ ﯾﮏ رﻣﺰ ﻋﺒﻮر 10ﮐﺎراﮐﺘﺮي ﺣﺎوي ﺣﺪاﻗﻞ 3ﻋﺪد ،ﺣﺮوف ﮐﻮﭼﮏ و ﺑﺰرگ ﺑﻪ
ﺻﻮرت درﻫﻢ ﺗﻮﻟﯿﺪ ﮐﻨﯿﻢ.
import string
import secrets
while True:
))password = ''.join(secrets.choice(alphabet) for i in range(10
)if (any(c.islower() for c in password
)and any(c.isupper() for c in password
and sum(c.isdigit() for c in password) >= 3):
break
)print(password
>> Mcltz0c1N3
در ﻣﺜﺎلﻫﺎي ﺑﻌﺪي ﻧﺤﻮة ﻋﻤﻠﮑﺮد ﺗﻮاﺑﻊ دﯾﮕﺮ ﻧﺸﺎن داده ﺷﺪه اﺳﺖ.
import secrets
)token = secrets.token_hex(16
)print(token
>> 5674cf331c32928bcd2c767531ccf883
Ahwaz_Hackerz
import secrets
token = secrets.token_urlsafe(16)
print(token)
>> bfaIv7qGAmjt9osV6UOFJA
Ahwaz_Hackerz
ﻓﺼﻞ2
ﭘﺎﯾﺘﻮن 3.7
Ahwaz_Hackerz
ﮐﻼسﻫﺎي داده
در ﭘﺎﯾﺘﻮن 3.7روش ﺟﺪﯾﺪي ﺑﺮاي ﺗﻮﻟﯿﺪ ﮐﻼسﻫﺎي داده ﻣﻌﺮﻓﯽ ﺷﺪ .در اﯾﻦ روش ﻣﺘﺪﻫﺎي وﯾﮋه 1از
ﺟﻤﻠﻪ)(__ .__eq__()،.__repr__()،.__initو)(__ .__hashﺑﻪﺻﻮرت ﺧﻮدﮐﺎر اﺿﺎﻓﻪ
ﻣﯽﺷﻮد و ﺗﻨﻬﺎ ﮐﺎري ﮐﻪ ﻧﯿﺎز اﺳﺖ اﻧﺠﺎم ﺷﻮد اﺳﺘﻔﺎده از ﯾﮏ Decoratorﺑﺎ ﻧﺎم @dataclass
ﻣﯽﺑﺎﺷﺪ .ﺑﺎ Decoratorدر ﺑﺨﺶﻫﺎي آﯾﻨﺪه آﺷﻨﺎ ﺧﻮاﻫﯿﺪ ﺷﺪ.
در ﻣﺜﺎل زﯾﺮ ﮐﻼس ﻣﺎﺷﯿﻦ را ﺑﺎ ﺧﺼﻮﺻﯿﺎﺗﺶ از ﻗﺒﯿﻞ ﻧﺎم ،ﻣﺪل ،ﺣﺪاﮐﺜﺮ ﺳﺮﻋﺖ و ﻗﯿﻤﺖ و ﯾﮏ ﻣﺘﺪ ﺑﺮاي
ﺗﺒﺪﯾﻞ ﺳﺮﻋﺖ از ﮐﯿﻠﻮﻣﺘﺮ در ﺳﺎﻋﺖ ﺑﻪ ﻣﺎﯾﻞ در ﺳﺎﻋﺖ ﭘﯿﺎدهﺳﺎزي ﻣﯽﮐﻨﯿﻢ.
)@dataclass(order=True
class Car:
name: str
model: str
max_speed: float
)price: int = field(repr=False, compare=False
def max_speed_in_mph(self):
""Return max speed in mile per hour
return self.max_speed * 0.62
ﺑﻌﺪ از ﺳﺎﺧﺖ ﮐﻼس ،ﻣﯽﺗﻮان ﺑﺎ آن ﻣﺜﻞ ﯾﮏ ﮐﻼس ﻣﻌﻤﻮﻟﯽ رﻓﺘﺎر ﮐﺮد .از آن ﻧﻤﻮﻧﻪﻫﺎﯾﯽ اﯾﺠﺎد ﮐﺮد،
ارثﺑﺮي ﮐﺮد و . ...درواﻗﻊ ،ﻫﺪف اﺻﻠﯽ ﮐﻼسﻫﺎي دادهاي ،اﯾﺠﺎد اﻣﮑﺎﻧﯽ ﺳﺮﯾﻊ و راﺣﺖ ﺑﺮاي ﻧﻮﺷﺘﻦ
ﮐﻼسﻫﺎي ذﺧﯿﺮهﮐﻨﻨﺪه داده ﻣﯽﺑﺎﺷﺪ .ﺑﺮاي ﻣﺜﺎل از ﮐﻼس ﻣﺎﺷﯿﻦ ﻣﺜﻞ ﺗﻤﺎم ﮐﻼسﻫﺎي دﯾﮕﺮ ﻣﯽﺗﻮان
اﺳﺘﻔﺎده ﮐﺮد.
>> BMW
))(print(bmw.max_speed_in_mph
>> 136.4
1در ﭘﺎﯾﺘﻮن اﺳﻢﻫﺎي ﻣﺘﻔﺎوﺗﯽ ﺑﺮاي ﻣﺘﺪﻫﺎي وﯾﮋه ،ﻣﺎﻧﻨﺪ ﻣﺘﺪﻫﺎي ﺟﺎدوﯾﯽ وﺟﻮد دارد! ﯾﮑﯽ از اﯾﻦ ﻧﺎمﻫﺎ
Dunder Methodﻫﺎ ﻫﺴﺘﻨﺪ .ﺟﺎﻟﺐ اﺳﺖ ﺑﺪاﻧﯿﺪ Dunderاز اﺑﺘﺪاي ﮐﻠﻤﺎت Double Underscoreﮐﻪ ﻧﺸﺎﻧﮕﺮ دو
ﻋﻼﻣﺖ زﯾﺮﯾﻦ ﺧﻂ اﺑﺘﺪا و اﻧﺘﻬﺎي اﯾﻦ ﻣﺘﺪﻫﺎﺳﺖ ،ﺗﺸﮑﯿﻞ ﺷﺪه اﺳﺖ.
Ahwaz_Hackerz
ﻫﻤﭽﻨﯿﻦ ﻧﻤﺎﯾﺶ ﯾﮏ ﺷﺊ از اﯾﻦ ﮐﻼس ﺑﻪ روش ﺧﻮب و آﺳﺎﻧﯽ ﻗﺎﺑﻞ ﺧﻮاﻧﺪن اﺳﺖ .ﻫﻤﺎنﻃﻮري ﮐﻪ در
ﻣﺜﺎل زﯾﺮ ﻣﺸﺎﻫﺪه ﻣﯽﮐﻨﯿﺪ ﻣﺒﻠﻎ ﻣﺎﺷﯿﻦ ﻧﻤﺎﯾﺶ داده ﻧﺸﺪه اﺳﺖ زﯾﺮا ﻣﻘﺪار repr=Falseﺑﺮاي آن
ﻓﯿﻠﺪ درﻧﻈﺮ ﮔﺮﻓﺘﻪ ﺷﺪه ﺑﻮد.
)print(bmw
ﻣﯽﺗﻮان ﺑﻪراﺣﺘﯽ ﺑﺮاﺑﺮي ﺑﯿﻦ دو ﮐﻼس دادهاي را ﺑﺮرﺳﯽ ﮐﺮد .در ﻣﺜﺎل زﯾﺮ ﻣﺒﻠﻎ ﻣﺎﺷﯿﻦ ﺑﻪدﻟﯿﻞ ﻧﻮﺷﺘﻦ
compare=Falseدر ﻓﯿﻠﺪ آن ،درﻧﺘﯿﺠﮥ ﺑﺮاﺑﺮي درﻧﻈﺮ ﮔﺮﻓﺘﻪ ﻧﻤﯽﺷﻮد.
)print(bmw == bmw2
>> True
[ >>
Car(name='BENZ', model='CLS 500', max_speed=200),
Car(name='BMW', model='328i', max_speed=220),
)Car(name='Pride', model='132', max_speed=180.0
]
@dataclass
class Email:
sender:str
title: str
body: str
def get_new_inbox():
])"return [Email("Admin", "Welcome", "Welcome to our website.
@dataclass
class UserInbox:
user_id: int = 1
)address: str = field(default="test@example.com", compare=False
)inbox: List[Email] = field(default_factory=get_new_inbox
ﺑﺮاي ﻣﻘﺪاردﻫﯽ user_idﺑﻪﺳﺎدﮔﯽ آن را ﺑﺮاﺑﺮ ﺑﺎ ﻣﻘﺪار ﭘﯿﺶﻓﺮض 1ﻗﺮار دادﯾﻢ .ﺑﺮاي ﻣﻘﺪاردﻫﯽ
addressﮐﻪ ﻣﯽﺧﻮاﻫﯿﻢ در ﺑﺮاﺑﺮي ﭼﮏ ﻧﺸﻮد ،ﺑﺮاي ﻣﻘﺪار ﭘﯿﺶﻓﺮض ﺑﺎﯾﺪ از ﮐﻠﻤﮥ ﮐﻠﯿﺪي
defaultاﺳﺘﻔﺎده ﮐﻨﯿﻢ .در ﻣﻮرد inboxﺗﻮﺟﻪ ﮐﻨﯿﺪ ﮐﻪ ﻣﺎ ﯾﮏ ﻟﯿﺴﺖ ﻧﺴﺒﺘﺎً ﭘﯿﭽﯿﺪه دارﯾﻢ ﮐﻪ در
اﯾﻨﺠﺎ ﺑﺮاي ﻣﻘﺪاردﻫﯽ ﭘﯿﺶﻓﺮض از default_factoryاﺳﺘﻔﺎده ﮐﺮدﯾﻢ .اﮔﺮ ﻣﯽﺧﻮاﺳﺘﯿﻢ ﻫﻤﺎﻧﻨﺪ
ﺗﮑﻪ ﮐﺪ زﯾﺮ ﺑﻪﻃﻮر ﻣﺴﺘﻘﯿﻢ اﯾﻦ ﻣﻘﺪاردﻫﯽ را اﻧﺠﺎم دﻫﯿﻢ ،ﯾﮏ ﭘﺎد اﻟﮕﻮ در ﭘﺎﯾﺘﻮن ﺑﻮد.
در اﯾﻦ ﭘﺎد اﻟﮕﻮ ،ﻣﺸﮑﻞ از آﻧﺠﺎ ﻧﺎﺷﯽ ﻣﯽﺷﻮد ﮐﻪ از ﯾﮏ mutableﺑﻪﻋﻨﻮان ﻣﻘﺪار ﭘﯿﺶﻓﺮض ﺑﺮاي ﯾﮏ
آرﮔﻮﻣﺎن ﻣﯽﺧﻮاﻫﯿﻢ اﺳﺘﻔﺎده ﮐﻨﯿﻢ .ﯾﻌﻨﯽ ﺑﺮاي ﺗﻤﺎم ﻧﻤﻮﻧﻪﻫﺎي ﮐﻼس ،UserInboxﯾﮏ ﻟﯿﺴﺖ
ﯾﮑﺴﺎن ﺑﺮاي ﻫﻤﮥ ﻧﻤﻮﻧﻪﻫﺎ ﺳﺎﺧﺘﻪ ﻣﯽﺷﻮد .ﺑﻪ زﺑﺎن دﯾﮕﺮ ،اﮔﺮ ﯾﮏ اﯾﻤﯿﻞ ﺑﻪ inboxﯾﮏ ﮐﺎرﺑﺮ اﺿﺎﻓﻪ ﺷﻮد
ﺑﺮاي ﺗﻤﺎﻣﯽ ﮐﺎرﺑﺮان ﻧﯿﺰ اﺿﺎﻓﻪ ﻣﯽﺷﻮد .ﺧﻮﺷﺒﺨﺘﺎﻧﻪ ﮐﻼس داده ﺷﻤﺎ را از ﻣﻘﺪاردﻫﯽ ﭘﯿﺶﻓﺮض
ﺑﺪﯾﻦﺻﻮرت ﻣﻨﻊ ﻣﯽﮐﻨﺪ.
)@dataclass(frozen=True
Ahwaz_Hackerz
class File:
name: str
size: float
در ﻣﺜﺎل ﺑﺎﻻ ،ﻣﻘﺪار sizeﻧﻤﯽﺗﻮاﻧﺪ ﺗﻐﯿﯿﺮ ﭘﯿﺪا ﮐﻨﺪ ،زﯾﺮا ﮐﻼس ﻓﺎﯾﻞ از ﻧﻮع read-onlyﻣﯽﺑﺎﺷﺪ و ﭘﺲ
از ﻣﻘﺪاردﻫﯽ اوﻟﯿﻪ دﯾﮕﺮ ﻧﻤﯽﺗﻮان ﻣﻘﺎدﯾﺮ ﻓﯿﻠﺪﻫﺎي آن را ﺗﻐﯿﯿﺮ داد.
@dataclass
class Object2D:
)}'width: float = field(metadata={'unit':'cm
)}'height: float= field(metadata={'unit':'cm
))print(fields(Object2D
>> (Field(name='width',
type=<class 'float'>,
metadata=mappingproxy({'unit':'cm'}),
Field(name='height',
type=<class 'float'>,
))}'metadata=mappingproxy({'unit':'cm
)]'print(fields(Object2D)[1].metadata['unit
>> cm
1
map
2
metadata
Ahwaz_Hackerz
@dataclass
class User:
]'__slots__ = ['username','password
username: str
password: str
ﺗﺮﺗﯿﺐ در دﯾﮑﺸﻨﺮي
اﯾﻦ ﮔﺎراﻧﺘﯽ در ﭘﺎﯾﺘﻮن 3.6اﺿﺎﻓﻪ ﺷﺪ وﻟﯽ ﺑﻪﻃﻮر رﺳﻤﯽ در ﭘﺎﯾﺘﻮن 3.7ﻣﻌﺮﻓﯽ ﺷﺪ .در ﭘﺎﯾﺘﻮن 3.7
ﺗﻀﻤﯿﻦ ﻣﯽﺷﻮد ﮐﻪ اﺟﺰاي واردﺷﺪه ﺑﻪ دﯾﮑﺸﻨﺮي در ﻫﻤﺎن ﺗﺮﺗﯿﺒﯽ ﮐﻪ اﺿﺎﻓﻪ ﺷﺪهاﻧﺪ iterateﻣﯽﺷﻮﻧﺪ.
}{=test_dictionary
for i in range(5,0,-1):
test_dictionary[i] = i
>> 5:5
4:4
3:3
2:2
1:1
https://docs.python.org/3/whatsnew/3.7.html
Ahwaz_Hackerz
ﻓﺼﻞ3
ﭘﺎﯾﺘﻮن 3.8
Ahwaz_Hackerz
ﻋﺒﺎرت اﻧﺘﺴﺎﺑﯽ
ﯾﮑﯽ از ﺗﻐﯿﯿﺮات ﻋﻤﺪه و ﮐﺎرآﻣﺪ در ﭘﺎﯾﺘﻮن 3.8ﻣﻌﺮﻓﯽ ﻋﺒﺎرات اﻧﺘﺴﺎﺑﯽ 1اﺳﺖ .اﯾﻦ ﻋﺒﺎرات ﺑﺎ ﻧﻮﺷﺘﻦ
ﻧﻤﺎد = :ﻗﺎﺑﻞ اﻧﺠﺎم اﺳﺖ .ﺑﻪ اﯾﻦ ﻋﻤﻠﮕﺮ Walrusﮔﻔﺘﻪ ﻣﯽﺷﻮد .2ﻋﺒﺎرات اﻧﺘﺴﺎﺑﯽ ﺑﻪ ﺷﻤﺎ ﮐﻤﮏ ﻣﯽﮐﻨﺪ
ﺗﺎ ﺑﻪ ﯾﮏ ﻣﺘﻐﯿﺮ ﻣﻘﺪاري اﻧﺘﺴﺎب ﮐﺮده و ﺳﭙﺲ آن ﻣﻘﺪار را ﺑﺮﮔﺮداﻧﯿﺪ .ﺑﺮاي ﻣﺜﺎل ﮐﺪ ﺳﺎدة زﯾﺮ را درﻧﻈﺮ
ﺑﮕﯿﺮﯾﺪ.
"name = "Siavash
)print(name
>> Siavash
)"print(name:= "Siavash
>> Siavash
در ﻣﺜﺎل ﺑﺎﻻ ﻣﺘﻐﯿﺮ nameاﺑﺘﺪا ﺑﺎ " "Siavashﻣﻘﺪار دﻫﯽ ﺷﺪ و ﺳﭙﺲ ﻣﻘﺪار آن ﺑﺮﮔﺮداﻧﺪه و در
اﺧﺘﯿﺎر ﺗﺎﺑﻊ printﻗﺮار ﮔﺮﻓﺖ .ﺑﺎﯾﺪ ﺗﻮﺟﻪ ﮐﻨﯿﺪ ﮐﻪ اﯾﻦ ﻋﺒﺎرات ﮐﺎر ﺟﺪﯾﺪي ﮐﻪ ﺷﻤﺎ ﻗﺒﻞ از آن
ﻣﯽﺗﻮاﻧﺴﺘﯿﺪ اﻧﺠﺎم دﻫﯿﺪ ،ﻧﻤﯽﮐﻨﺪ ﺑﻠﮑﻪ ﺗﻨﻬﺎ راه ﺗﻤﯿﺰ و ﮐﻮﺗﺎهﺗﺮي ﺑﺮاي ﮐﺪﻫﺎي ﺷﻤﺎ ﻓﺮاﻫﻢ ﻣﯽﮐﻨﺪ.
ﯾﮑﯽ از ﻣﻮارد اﺳﺘﻔﺎده اﯾﻦ ﻋﻤﻠﮕﺮ در ﺣﻠﻘﻪﻫﺎ ﻣﯽﺑﺎﺷﺪ .ﺑﺮاي ﻣﺜﺎل ﮐﺪي ﻣﯽﺧﻮاﻫﯿﻢ ﺑﻨﻮﯾﺴﯿﻢ ﮐﻪ ﻧﺎم
ﮐﺎرﺑﺮي اﻓﺮاد را ﺗﺎ ﮔﺮﻓﺘﻦ رﺷﺘﻪ " "endاداﻣﻪ دﻫﺪ .اﺣﺘﻤﺎﻻً ﺗﺎ ﻗﺒﻞ از آن ،اﯾﻦ ﮐﺪ را ﺑﻪ ﺻﻮرت زﯾﺮ
ﻣﯽﻧﻮﺷﺘﯿﻢ.
)(users = list
while True:
)(user = input
if user == "end":
break
)users.append(user
ﺣﺎل ﺑﺎ داﻧﺴﺘﻦ ﻋﻤﻠﮕﺮ Walrusﻣﯽﺗﻮان در ﻋﺒﺎرت whileﮐﺪ ﮐﻤﺘﺮ و ﺧﻮاﻧﺎﺗﺮي ﺑﻨﻮﯾﺴﯿﻢ و ﮐﺪ ﺑﺎﻻ را
ﺑﻪ اﯾﻦﺻﻮرت ﺗﻐﯿﯿﺮ دﻫﯿﻢ.
1
Assignment Expression
2 Walrusﺑﻪ ﻣﻌﻨﯽ ﮔﺮاز درﯾﺎﯾﯽ اﺳﺖ .دوﻧﻘﻄﻪ ﻣﺎﻧﻨﺪ ﭼﺸﻢﻫﺎ و ﻋﻼﻣﺖ ﻣﺴﺎوي ﻧﺸﺎندﻫﻨﺪة دﻧﺪانﻫﺎي ﮔﺮاز درﯾﺎﯾﯽ اﺳﺖ!
Ahwaz_Hackerz
)(users = list
آرﮔﻮﻣﺎنﻫﺎي ﻣﮑﺎﻧﯽ
ﯾﮑﯽ دﯾﮕﺮ از وﯾﮋﮔﯽﻫﺎي ﺟﺪﯾﺪ در ﭘﺎﯾﺘﻮن 3.8ﻣﻌﺮﻓﯽ ﻗﺎﺑﻠﯿﺘﯽ ﺑﺮاي ﺷﻨﺎﺳﺎﻧﺪن آرﮔﻮﻣﺎنﻫﺎ ﺑﻪﺻﻮرت ﻓﻘﻂ
ﻣﮑﺎﻧﯽ 1ﻣﯽﺑﺎﺷﺪ .ﺑﺎ ﮔﺬاﺷﺘﻦ ﻋﻼﻣﺖ /در آرﮔﻮﻣﺎنﻫﺎي ﺗﺎﺑﻊ ،ﭘﺎﯾﺘﻮن ﻣﺘﻀﻤﻦ ﻣﯽﺷﻮد ﮐﻪ ﻓﺮاﺧﻮاﻧﯽ اﯾﻦ
ﺗﺎﺑﻊ ﺑﺎ آرﮔﻮﻣﺎنﻫﺎي ﺳﻤﺖ ﭼﭗ اﯾﻦ ﻋﻼﻣﺖ ﺑﻪ ﺻﻮرت ﻓﻘﻂ -ﻣﮑﺎﻧﯽ ﻗﺎﺑﻞ اﻧﺠﺎم اﺳﺖ .ﺑﺮاي درك ﺑﻬﺘﺮ ﺑﻪ
ﻣﺜﺎل زﯾﺮ ﺗﻮﺟﻪ ﮐﻨﯿﺪ .در اﯾﻦ ﻣﺜﺎل ﯾﮏ ﺗﺎﺑﻊ ﺳﺎده ﺑﺎ ﻧﺎم minusﺗﻌﺮﯾﻒ ﮐﺮدهاﯾﻢ ﮐﻪ ﻋﺪد دوم را از ﻋﺪد
اول ﮐﻢ ﻣﯽﮐﻨﺪ و ﺟﻮاب را ﺑﺮﻣﯽﮔﺮداﻧﺪ.
))print(minus(10, 6
>> 4
ﺣﺎل ﻣﯽﺗﻮان ﺑﻪﺟﺎي ﮔﺬاﺷﺘﻦ ﭘﺎراﻣﺘﺮﻫﺎ ﺑﻪﺗﺮﺗﯿﺐ ،از ﮐﻠﯿﺪواژه 2آﻧﻬﺎ اﺳﺘﻔﺎده ﮐﺮد.
))print(minus(b=6, a=10
>> 4
اﯾﻦ روش ﻧﻮﺷﺘﻦ درﺣﺎﻟﯽﮐﻪ درﺳﺖ اﺳﺖ وﻟﯽ ﺧﻮاﻧﺎﯾﯽ و ﻓﻬﻢ ﮐﺪ را ﭘﺎﯾﯿﻦ ﻣﯽآورد .ﺑﺎ ﮔﺬاﺷﺘﻦ ﻋﻼﻣﺖ
slashﺑﻌﺪ از آرﮔﻮﻣﺎن bﺑﻪ ﭘﺎﯾﺘﻮن ﻣﯽﮔﻮﯾﯿﻢ ﮐﻪ آرﮔﻮﻣﺎنﻫﺎي ﺳﻤﺖ ﭼﭗ /را ﺑﻪﺻﻮرت ﻓﻘﻂ -ﻣﮑﺎﻧﯽ
درﯾﺎﻓﺖ ﮐﻨﺪ .ﺑﺎ اﯾﻦ ﮐﺎر ﻣﯽﺗﻮان APIﻫﺎي ﺳﺎﺧﺘﺎرﻣﻨﺪﺗﺮي ﺳﺎﺧﺖ.
))print(minus(10, 6
1
Positional-only
2
Keyword
Ahwaz_Hackerz
>> 4
))print(minus(b=6, a=10
ﻫﻤﺎنﻃﻮرﮐﻪ در ﻣﺜﺎل ﺑﺎﻻ ﭘﯿﺪاﺳﺖ در ﺗﺎﺑﻊ minusﮐﻪ ﺳﺎزوﮐﺎر آن ﺑﻪ ﺻﻮرت ﻓﻘﻂ ﻣﮑﺎﻧﯽ ﺑﻬﺘﺮ و
ﻗﺎﺑﻞدركﺗﺮ اﺳﺖ ،اﺳﺘﻔﺎده از ﮐﻠﯿﺪواژهﻫﺎ را ﻣﻨﻊ ﮐﺮدﯾﻢ .ﺑﻨﺎﺑﺮاﯾﻦ ﮐﺪﻫﺎﯾﯽ ﮐﻪ ﻗﺮار اﺳﺖ از اﯾﻦ ﺗﺎﺑﻊ
اﺳﺘﻔﺎده ﮐﻨﻨﺪ ﺑﺴﯿﺎر ﺧﻮاﻧﺎﺗﺮ ﺧﻮاﻫﻨﺪ ﺷﺪ.
در ﻣﻮاردي ﮐﻪ ﮔﺬاﺷﺘﻦ آرﮔﻮﻣﺎنﻫﺎ ﺑﻪﺗﺮﺗﯿﺐ ،ﻣﻨﻄﻖ ﺧﺎﺻﯽ دارد و اﻧﺘﺨﺎب ﮐﻠﯿﺪواژه ﺑﺮاي آرﮔﻮﻣﺎنﻫﺎ ﮐﺎر
دﺷﻮاري اﺳﺖ ،اﺳﺘﻔﺎده از آرﮔﻮﻣﺎن ﻓﻘﻂ ﻣﮑﺎﻧﯽ ﺑﺴﯿﺎر ﮐﺎرﺑﺮدي اﺳﺖ .ﻫﻤﭽﻨﯿﻦ ﺑﺎ ﺗﻌﺮﯾﻒ آرﮔﻮﻣﺎنﻫﺎ ﺑﻪ
ﺻﻮرت ﻓﻘﻂ ﻣﮑﺎﻧﯽ ﻣﯽﺗﻮان ﺗﻐﯿﯿﺮﭘﺬﯾﺮي را اﻓﺰاﯾﺶ داد .ﯾﻌﻨﯽ در آﯾﻨﺪه ﺑﺪون آﻧﮑﻪ ﻧﮕﺮان ﺑﺎﺷﯿﻢ ﻧﺎم
ﮐﻠﯿﺪواژهﻫﺎ ﭼﯿﺴﺖ ،ﻣﯽﺗﻮان آنﻫﺎ را ﺑﻪﺳﺎدﮔﯽ ﺗﻐﯿﯿﺮ داد ،زﯾﺮا ﻣﻄﻤﺌﻦ ﻫﺴﺘﯿﻢ ﮐﺴﺎﻧﯽ ﮐﻪ از اﯾﻦ ﺗﻮاﺑﻊ
اﺳﺘﻔﺎده ﮐﺮدهاﻧﺪ ﻓﻘﻂ ﺑﻪﺻﻮرت ﻣﮑﺎﻧﯽ ﭘﺎراﻣﺘﺮﻫﺎ را ﺑﻪ ﺗﺎﺑﻊ ﭘﺎس دادهاﻧﺪ و از ﮐﻠﯿﺪواژه اﺳﺘﻔﺎده ﻧﮑﺮدهاﻧﺪ.
ﭘﺲ ﺗﻐﯿﯿﺮ ﻧﺎم آﻧﻬﺎ ﮐﺎري اﻣﻦ اﺳﺖ.
در اﯾﻦ ﻣﺜﺎل ﯾﮏ ﺗﺎﺑﻊ ﻓﺮﺿﯽ دارﯾﻢ ﮐﻪ ﭘﯿﺎﻣﯽ را ﺑﻪ ﮐﺎرﺑﺮي ارﺳﺎل ﻣﯽﮐﻨﺪ .در اﯾﻦ ﻣﺜﺎل آرﮔﻮﻣﺎن toﺑﻪ
ﺻﻮرت ﻓﻘﻂ -ﻣﮑﺎﻧﯽ اﺳﺖ ،آرﮔﻮﻣﺎن titleﻫﻢ ﻣﯽﺗﻮاﻧﺪ ﺑﻪﺻﻮرت ﻣﮑﺎﻧﯽ و ﻫﻢ ﺑﻪﺻﻮرت ﮐﻠﯿﺪواژه
اﺳﺘﻔﺎده ﺷﻮد و ﺑﺮاي آرﮔﻮﻣﺎن ،bodyﻓﻘﻂ ﻓﺮاﺧﻮاﻧﯽ ﺑﻪﻫﻤﺮاه ﮐﻠﯿﺪواژة آن ﺻﻮرتﭘﺬﯾﺮ اﺳﺖ.
send_message("Siavash", "Welcome",
)"body="Welcome to our site
1
Keyword-only
Ahwaz_Hackerz
>> to = Siavash
title = Welcome
body = Welcome to our site
ﻻزم ﺑﻪ ذﮐﺮ اﺳﺖ ﺗﺎﺑﻊ send_messageرا ﻣﯽﺗﻮاﻧﺴﺘﯿﻢ ﺑﻪﺻﻮرت زﯾﺮ ﻫﻢ ﻓﺮاﺧﻮاﻧﯽ ﮐﻨﯿﻢ.
send_message("Siavash", title="Welcome",
)"body="Welcome to our site
send_message("Siavash", "Welcome",
)"body="welcome to our site
'>> to='Siavash
'title='Welcome
'body='welcome to our site
https://docs.python.org/3/whatsnew/3.8.html
Ahwaz_Hackerz
ﻓﺼﻞ4
ﭘﺎﯾﺘﻮن 3.9
Ahwaz_Hackerz
c = a | b
)print(c
ﺳﺆاﻟﯽ ﮐﻪ ﻣﻤﮑﻦ اﺳﺖ ﭘﯿﺶ ﺑﯿﺎﯾﺪ اﯾﻦ اﺳﺖ ﮐﻪ اﮔﺮ ﮐﻠﯿﺪي در ﻫﺮ دو دﯾﮑﺸﻨﺮي وﺟﻮد داﺷﺖ ﭼﻪ
ﻣﯽﺷﻮد؟ در اﯾﻦ ﺻﻮرت ﮐﻠﯿﺪ و ﻣﻘﺪاري ﮐﻪ در دﯾﮑﺸﻨﺮي دوم وﺟﻮد دارد ،در ﻧﺘﯿﺠﻪ ﻇﺎﻫﺮ ﻣﯽﺷﻮد.
c = a | b
)print(c
ﺑﺎ اﺳﺘﻔﺎده از =| ﮐﻪ ﺑﻪ آن ﻋﻤﻠﮕﺮ ﺑﺮوزرﺳﺎن 2ﻧﯿﺰ ﮔﻮﯾﻨﺪ ،ﻣﯽﺗﻮان دﯾﮑﺸﻨﺮي را ﺑﻪراﺣﺘﯽ ﺑﺮوزﺳﺎﻧﯽ ﮐﺮد.
)print(a
ﻫﻤﭽﻨﯿﻦ ﺑﺎ ﻋﻤﻠﮕﺮ ﺑﺮوزرﺳﺎن ﻣﯽﺗﻮان از ژﻧﺮاﺗﻮرﻫﺎ ﮐﻪ در ﺑﺨﺶﻫﺎي ﺑﻌﺪ ﺑﺎ آن آﺷﻨﺎ ﻣﯽﺷﻮﯾﺪ ﻫﻢ اﺳﺘﻔﺎده
ﮐﺮد و دﯾﮑﺸﻨﺮي ﺟﺪﯾﺪ ﺳﺎﺧﺖ.
a |= b
1
Merge operator
2
Update operator
Ahwaz_Hackerz
)print(a
"text = "price:5000Toman
))"print(text.removeprefix("price:
>> 5000Toman
))"print(text.removesuffix("Toman
>> price:5000
))"print(text.removesuffix("Rial
>> price:5000Toman
Type Hints
از اﯾﻦ ورژن ﺑﻪ ﺑﻌﺪ ﻣﯽﺗﻮان از ﺗﺎﯾﭗﻫﺎي ﺧﻮدﺳﺎﺧﺘﻪ ﺑﻪﺟﺎي ﺗﺎﯾﭗﻫﺎي ﺟﻨﺮﯾﮏ اﺳﺘﻔﺎده ﮐﺮد .دﯾﮕﺮ ﻧﯿﺎزي
ﺑﻪ importﺗﺎﯾﭗﻫﺎﯾﯽ ﻫﻤﭽﻮن ﻟﯿﺴﺖ و دﯾﮑﺸﻨﺮي ﻧﯿﺴﺖ .ﺑﻪﻋﻨﻮان ﻣﺜﺎل ﺑﻪﺟﺎي آﻧﮑﻪ ﺑﻨﻮﯾﺴﯿﻢ:
import math
)print(g
>> 4
)print(l
>> 120
https://docs.python.org/3.9/whatsnew/3.9.html
Ahwaz_Hackerz
ﺑﺨﺶ2
ﻓﺼﻞ1
ﺳﺒﮏﻫﺎي وﯾﮋة زﺑﺎن
Ahwaz_Hackerz
ﺳﺒﮏﻫﺎي وﯾﮋة ﺑﺮﻧﺎﻣﻪﻧﻮﯾﺴﯽ 1ﺑﯿﺎنﮐﻨﻨﺪة وﯾﮋﮔﯽﻫﺎي ﻣﺨﺼﻮﺻﯽ اﺳﺖ ﮐﻪ در ﯾﮏ زﺑﺎن ﺗﮑﺮار ﻣﯽﺷﻮد و
ﻣﯽﺗﻮان ﺑﻪ آﻧﻬﺎ ﺑﻪﻋﻨﻮان ﻣﻔﻬﻮم ﺑﻨﯿﺎدﯾﻦ در ﻣﺒﺤﺚ اﻟﮕﻮﻫﺎ ﻧﮕﺎه ﮐﺮد Code Smell .ﺑﻪ ﻫﺮ ﺧﺼﯿﺼﻪاي در
ﮐﺪ ﺑﺮﻧﺎﻣﻪ ﻣﯽﮔﻮﯾﻨﺪ ﮐﻪ اﺣﺘﻤﺎﻻً در آﯾﻨﺪه ﺑﺎﻋﺚ ﻣﺸﮑﻼت ﻋﻤﯿﻖﺗﺮي ﻣﯽﺷﻮد .2ﺑﺴﯿﺎري از ﺑﺮﻧﺎﻣﻪﻧﻮﯾﺲﻫﺎ
ﺑﺪون داﺷﺘﻦ اﻃﻼﻋﯽ از اﯾﻦ ﻣﻮارد ﮐﺪﻫﺎﯾﯽ ﺗﻮﻟﯿﺪ ﻣﯽﮐﻨﻨﺪ ﮐﻪ در ﻇﺎﻫﺮ ﺧﻮب و ﮐﺎرآﻣﺪ اﺳﺖ وﻟﯽ ﻣﻤﮑﻦ
اﺳﺖ در آﯾﻨﺪه ﻣﺸﮑﻞﺳﺎز ﺑﺎﺷﺪ .ﻣﻨﻈﻮر از ﻣﺸﮑﻼت ،ﻟﺰوﻣﺎً وﺟﻮد ﺑﺎگﻫﺎي ﻧﺮماﻓﺰاري ﻧﯿﺴﺖ ﺑﻠﮑﻪ ﻣﻮاردي
ﻫﻤﭽﻮن ﺿﻌﻒ در ﺗﻐﯿﯿﺮﭘﺬﯾﺮي ،3ﻣﻘﯿﺎسﭘﺬﯾﺮي ،4ﮐﺎراﯾﯽ 5و دﯾﮕﺮ ﺻﻔﺎت ﮐﯿﻔﯽ ﻣﯽﺷﻮد .ﺑﺎزآراﯾﯽ 6روﺷﯽ
ﺑﺮاي ﺣﻞ اﯾﻦ ﻣﺸﮑﻼت اﺳﺖ .ﺑﺎزآراﯾﯽ ﺑﺮاﺳﺎس ﺗﻌﺮﯾﻒ ،Fowlerﺗﮑﻨﯿﮏ ﻣﻨﺴﺠﻤﯽ ﺑﺮاي ﺑﺎزﺳﺎزي ﮐﺪ ،ﺑﺎ
ﺗﻐﯿﯿﺮ ﺳﺎﺧﺘﺎر دروﻧﯽ آن اﺳﺖ ﮐﻪ رﻓﺘﺎر ﺧﺎرﺟﯽ آن را ﺗﻐﯿﯿﺮ ﻧﻤﯽدﻫﺪ.
در اﯾﻦ ﻓﺼﻞ ﺳﻌﯽ دارﯾﻢ در ﻗﺎﻟﺐ ﻣﺜﺎلﻫﺎﯾﯽ ،ﮐﺪﻫﺎي ﺑﺪ را ﺗﻮﺿﯿﺢ داده و روش ﺑﻬﺒﻮد آﻧﻬﺎ را ﺷﺮح دﻫﯿﻢ.
در اﯾﻦ ﻣﺜﺎلﻫﺎ ،اﺑﺘﺪا ﮐﺪﻫﺎي ﺑﺮﻧﺎﻣﻪﻧﻮﯾﺲ ﻣﺒﺘﺪي و ﺳﭙﺲ ﮐﺪﻫﺎي ﺑﺮﻧﺎﻣﻪﻧﻮﯾﺲ ﺣﺮﻓﻪاي را ﺑﺎ ﻫﻢ ﻣﻘﺎﯾﺴﻪ
ﻣﯽﮐﻨﯿﻢ.
ﺻﻮرتﻣﺴﺌﻠﻪ :ﮐﺪي ﺑﻨﻮﯾﺴﯿﺪ ﮐﻪ ﻟﯿﺴﺘﯽ از ﻧﺎم ﮐﺎرﺑﺮان درﯾﺎﻓﺖ ﮐﻨﺪ و ﻫﺮ ﮐﺎرﺑﺮ را ﺑﻪ ﻫﻤﺮاه
ﺟﺎﯾﮕﺎﻫﺶ در ﻟﯿﺴﺖ ﭼﺎپ ﮐﻨﺪ.
ﺑﺮﻧﺎﻣﻪﻧﻮﯾﺲ ﻣﺒﺘﺪي
]'users = ['Siavash', 'Soroush', 'Shayan', 'Sara
index = 1
1
Programming Idiom
2اﯾﻦ ﺗﻌﺮﯾﻒ اﺑﺘﺪا ﺗﻮﺳﻂ Beckدر اواﺧﺮ ﺳﺎل 1990ﺑﻪوﺟﻮد آﻣﺪ و ﺑﻪﻃﻮر وﯾﮋهاي در ﺳﺎل 1999در ﮐﺘﺎب ﺑﺎزآراﯾﯽ
Fowlerاﺳﺘﻔﺎده ﺷﺪ.
3
Modifiability
4
Scalability
5
Performance
6
Refactoring
Ahwaz_Hackerz
ﺑﺮﻧﺎﻣﻪﻧﻮﯾﺲ ﺣﺮﻓﻪاي
]'users = ['Siavash', 'Soroush', 'Shayan', 'Sara
))enum_obj = list(enumerate(users
)print(enum_obj
ﻫﻤﺎنﻃﻮرﮐﻪ ﻣﺸﺎﻫﺪه ﻣﯽﮐﻨﯿﺪ enumerateﺑﻪ ﻟﯿﺴﺘﯽ از ﺗﺎﭘﻞﻫﺎ ﺗﺒﺪﯾﻞ ﺷﺪ و درك آن راﺣﺖﺗﺮ ﺷﺪ .ﺑﺎ
اﺳﺘﻔﺎده از forدو ﻣﺘﻐﯿﺮه ﻣﯽﺗﻮان از ﺷﻤﺎرﻧﺪه و ﺷﺊ داﺧﻞ آن اﺳﺘﻔﺎده ﮐﺮد .آرﮔﻮﻣﺎن startﺑﺮاي آن
اﺳﺖ ﮐﻪ ﺑﮕﻮﯾﯿﻢ ﺷﻤﺎرﻧﺪه از ﭼﻪ ﺷﻤﺎرهاي ﺷﺮوع ﺑﻪ ﺷﻤﺎرش ﮐﻨﺪ ﮐﻪ ﻣﻘﺪار ﭘﯿﺶﻓﺮض آن ﺻﻔﺮ اﺳﺖ.
ﺻﻮرتﻣﺴﺌﻠﻪ :اﮔﺮ دﻣﺎي ﻫﻮا ﺑﯿﻦ 20ﺗﺎ 25درﺟﻪ ﺑﻮد ،ﻣﺘﻨﯽ را ﭼﺎپ ﮐﻨﺪ.
ﺑﺮﻧﺎﻣﻪﻧﻮﯾﺲ ﻣﺒﺘﺪي
temperature = 21
ﺑﺮﻧﺎﻣﻪﻧﻮﯾﺲ ﺣﺮﻓﻪاي
temperature = 21
ﺗﻮﺿﯿﺤﺎت :از زﻧﺠﯿﺮﮐﺮدن ﻋﻤﻠﮕﺮﻫﺎي ﻣﻘﺎﯾﺴﻪاي ﻧﺘﺮﺳﯿﺪ .ﭘﺎﯾﺘﻮن ﻗﺎدر اﺳﺖ درﺳﺘﯽ آن را ﻣﺤﺎﺳﺒﻪ
ﮐﻨﺪ .ﻣﺜﻼً ﻣﯽﺗﻮان ﻧﻮﺷﺖ 20 < 30 < 45 == 45!= 50ﮐﻪ ﻣﻘﺪار Trueﺑﺮاي آن درﻧﻈﺮ
ﮔﺮﻓﺘﻪ ﻣﯽﺷﻮد .ﭘﺎﯾﺘﻮن ﺑﺮاي ﻣﺤﺎﺳﺒﮥ درﺳﺘﯽ ،از ﺳﻤﺖ ﭼﭗ دوﺑﻪدو ﻣﻘﺎﯾﺴﻪﻫﺎ را اﻧﺠﺎم ﻣﯽدﻫﺪ.
ﻟﯿﺴﺖ ﺧﺎﻟﯽ
ﺻﻮرتﻣﺴﺌﻠﻪ :اﮔﺮ ﻟﯿﺴﺘﯽ ﺧﺎﻟﯽ ﺑﻮد empty ،را ﭼﺎپ ﮐﻨﺪ.
ﺑﺮﻧﺎﻣﻪﻧﻮﯾﺲ ﻣﺒﺘﺪي
][ = cars
>> empty
ﺑﺮﻧﺎﻣﻪﻧﻮﯾﺲ ﺣﺮﻓﻪاي
][ = cars
>> empty
ﺗﻮﺿﯿﺤﺎت :ﯾﮑﯽ از ﻣﺒﺎﺣﺚ ﻣﻬﻢ در ﭘﺎﯾﺘﻮن ﺑﺤﺚ درﺳﺘﯽ اﺳﺖ .در ﭘﺎﯾﺘﻮن ﺗﻨﻬﺎ Trueﯾﺎ False
درﺳﺘﯽ را ﺑﯿﺎن ﻧﻤﯽﮐﻨﻨﺪ ،ﺑﻠﮑﻪ ﺷﺊﻫﺎي دﯾﮕﺮي ﻧﯿﺰ درﺳﺘﯽ را ﺑﯿﺎن ﻣﯽﮐﻨﻨﺪ .در ﺟﺪول 4ﻧﻤﻮﻧﻪاي از اﯾﻦ
ﻣﻮارد را ﺑﺮاﺳﺎس ﻧﻮع ﻣﺘﻐﯿﺮ ﻣﯽﺗﻮاﻧﯿﺪ ﻣﺸﺎﻫﺪه ﮐﻨﯿﺪ .در ﺳﺘﻮن "ﻣﻘﺪار "Falseﺣﺎﻟﺖﻫﺎﯾﯽ ﮐﻪ ﻣﺘﻐﯿﺮ
ﻣﺮﺑﻮﻃﻪ Falseدرﻧﻈﺮ ﮔﺮﻓﺘﻪ ﻣﯽﺷﻮد را ﻣﯽﺗﻮاﻧﯿﺪ ﻣﺸﺎﻫﺪه ﮐﻨﯿﺪ.
Ahwaz_Hackerz
در رﺷﺘﻪﻫﺎي ﺣﺮﻓﯽ ،رﺷﺘﻪاي ﮐﻪ ﺣﺘﯽ ﯾﮏ spaceﻫﻢ داﺷﺘﻪ ﺑﺎﺷﺪ ﺑﺎز ﯾﮏ رﺷﺘﻪ ﺑﺎ ﻃﻮل ﯾﮏ ﺣﺴﺎب
ﻣﯽﺷﻮد و درﻧﺘﯿﺠﻪ درﺳﺖ اﺳﺖ .ﺑﺮاي ﺑﺮرﺳﯽ درﺳﺘﯽ ﻣﯽﺗﻮاﻧﯿﺪ ﺷﯽء ﻣﻮرد ﻧﻈﺮ ﺧﻮد را ﺑﻪ Boolean
ﺗﺒﺪﯾﻞ ﮐﻨﯿﺪ.
))""(print(bool
>> False
))" "(print(bool
>> True
Noneﻫﻤﯿﺸﻪ Falseدرﻧﻈﺮ ﮔﺮﻓﺘﻪ ﻣﯽﺷﻮد .ﺑﺮاي درك درﺳﺖ ﺗﻔﺎوت ﺑﯿﻦ ،Noneرﺷﺘﮥ ﺧﺎﻟﯽ و
رﺷﺘﮥ ﺣﺮﻓﯽ ﻏﯿﺮﺧﺎﻟﯽ ﻣﺜﺎل ﻓﻨﺠﺎن ﻗﻬﻮه زﯾﺮ را ﺑﻪ ﺧﺎﻃﺮ ﺑﺴﭙﺎرﯾﺪ.
ﺻﻮرتﻣﺴﺌﻠﻪ :ﻟﯿﺴﺘﯽ از اﺟﺰاي ﺗﮑﺮاري دارﯾﻢ ،ﺑﺮﻧﺎﻣﻪاي ﺑﻨﻮﯾﺴﯿﺪ ﮐﻪ اﯾﻦ اﺟﺰا را ﺑﺎ ﺗﻌﺪاد ﺗﮑﺮارﺷﺎن
در ﯾﮏ دﯾﮑﺸﻨﺮي ﺟﻤﻊآوري ﮐﻨﺪ.
ﺑﺮﻧﺎﻣﻪﻧﻮﯾﺲ ﻣﺒﺘﺪي
]'chars = ['a','a','a','b','b','c','c','c','c','c','d','d','d
}{ = freq
for char in chars:
if char in freq:
freq[char] += 1
else:
freq[char] = 1
)print(freq
ﺑﺮﻧﺎﻣﻪﻧﻮﯾﺲ ﺣﺮﻓﻪاي
import collections
]'chars = ['a','a','a','b','b','c','c','c','c','c','d','d','d
)freq = collections.Counter(chars
)print(freq
ﺗﻮﺿﯿﺤﺎت :در ﮐﺪ ﻧﻮﺷﺘﻪﺷﺪه ﺗﻮﺳﻂ ﺑﺮﻧﺎﻣﻪﻧﻮﯾﺲ ﻣﺒﺘﺪي ،در اﺑﺘﺪا ﯾﮏ دﯾﮑﺸﻨﺮي ﺧﺎﻟﯽ ﺗﻌﺮﯾﻒﺷﺪه
و ﺑﺮاي ﻫﺮ ﻋﻀﻮ ﻟﯿﺴﺖ ،اﮔﺮ ﻗﺒﻼً در دﯾﮑﺸﻨﺮي وﺟﻮد داﺷﺖ ،ﻣﻘﺪارش ﯾﮑﯽ زﯾﺎد ﻣﯽﺷﻮد و اﮔﺮ وﺟﻮد
ﻧﺪاﺷﺖ ﺑﺎ ﻣﻘﺪار 1در دﯾﮑﺸﻨﺮي اﺿﺎﻓﻪ ﻣﯽﺷﻮد .ﭘﺎﯾﺘﻮن داراي ﻣﺎژول collectionsاﺳﺖ ﮐﻪ ﺣﺎوي
ﮐﻼسﻫﺎ و ﺗﻮاﺑﻊ ﺑﻪﺷﺪت ﮐﺎرﺑﺮدي اﺳﺖ .ﯾﮑﯽ از اﯾﻦ ﮐﻼسﻫﺎ Counterﻣﯽﺑﺎﺷﺪ ﮐﻪ ﺑﺎ درﯾﺎﻓﺖ ﯾﮏ
iterableﺗﻌﺪاد ﺗﮑﺮار اﺟﺰاي آن را ﺑﺮﻣﯽﮔﺮداﻧﺪ .اﯾﻦ روش ﺳﺮﯾﻊ ،ﺧﻼﺻﻪ و ﺧﻮاﻧﺎﺗﺮ از روش اول اﺳﺖ.
Ahwaz_Hackerz
ﺻﻮرتﻣﺴﺌﻠﻪ :اﮔﺮ ﻧﺎم ﮐﺎرﺑﺮي ﯾﮑﯽ از ﻣﻘﺎدﯾﺮ " "Siavashﯾﺎ " "Shayanﯾﺎ ""Samanﺑﻮد
ﭘﯿﻐﺎم okﭼﺎپ ﺷﻮد.
ﺑﺮﻧﺎﻣﻪﻧﻮﯾﺲ ﻣﺒﺘﺪي
"user = "Shayan
>> ok
ﺑﺮﻧﺎﻣﻪﻧﻮﯾﺲ ﺣﺮﻓﻪاي
"user = "Shayan
>> ok
ﺗﻮﺿﯿﺤﺎت :از ﻣﺘﻐﯿﺮ ﺑﺮاي ﭼﮏﮐﺮدن ﻣﻘﺪار آن ﭼﻨﺪﯾﻦ ﺑﺎر اﺳﺘﻔﺎده ﻧﮑﻨﯿﺪ .ﺑﻪراﺣﺘﯽ ﻣﯽﺗﻮاﻧﯿﺪ ﺑﺎ
ﻋﻤﻠﮕﺮ inﮐﺪ ﮐﻤﺘﺮ و ﺧﻮاﻧﺎﺗﺮي ﺑﻨﻮﯾﺴﯿﺪ.
ﺻﻮرتﻣﺴﺌﻠﻪ :ﻣﻘﺪار ﻣﺘﻐﯿﺮ checkرا ﺑﺮﻋﮑﺲ ﮐﻨﯿﺪ .ﯾﻌﻨﯽ اﮔﺮ Trueﺑﻮد False ،ﺷﻮد و ﯾﺎ
ﺑﺮﻋﮑﺲ.
ﺑﺮﻧﺎﻣﻪﻧﻮﯾﺲ ﻣﺒﺘﺪي
if check:
check = False
else:
check = True
ﺑﺮﻧﺎﻣﻪﻧﻮﯾﺲ ﺣﺮﻓﻪاي
check = False if check else True
Ahwaz_Hackerz
ﺗﻮﺿﯿﺤﺎت :درﺳﺖ اﺳﺖ ﮐﻪ ﭘﺎﯾﺘﻮن ﻋﻤﻠﮕﺮ Ternaryﻧﺪارد ،وﻟﯽ ﻣﯽﺗﻮان از ifو elseﯾﮏ ﺧﻄﯽ
اﺳﺘﻔﺎده ﮐﺮد .ﺑﺮاي آﺷﻨﺎﯾﯽ ﺑﺎ ﻧﺤﻮة ﮐﺎر اﯾﻦ دﺳﺘﻮر ﺑﻪ ﺗﺼﻮﯾﺮ 3دﻗﺖ ﮐﻨﯿﺪ.
ﺑﻪﺟﺎي compﻣﯽﺗﻮاﻧﺪ ﻣﺘﻐﯿﺮ ،ﻋﺒﺎرت ﺷﺮﻃﯽ و ﯾﺎ ﯾﮏ ﻣﻘﺪار booleanﻗﺮار ﺑﮕﯿﺮد .اﮔﺮ compدرﺳﺖ
ﺑﺎﺷﺪ اوﻟﯿﻦ ﻣﻘﺪار و اﮔﺮ ﻏﻠﻂ ﺑﺎﺷﺪ دوﻣﯿﻦ ﻣﻘﺪار ﮐﻪ ﺑﻌﺪ از elseاﺳﺖ در ﻣﺘﻐﯿﺮ varﻗﺮار ﻣﯽﮔﯿﺮد.
ﺑﺎﯾﺪ ﺗﻮﺟﻪ داﺷﺖ اﯾﻦ روش ،ﮐﺪ را ﮐﻮﺗﺎهﺗﺮ ﻣﯽﮐﻨﺪ وﻟﯽ ﺷﺎﯾﺪ ﺧﻮاﻧﺎﯾﯽ را ﺑﺎﻻﺗﺮ ﻧﺒﺮد و ﺑﺎ ﻣﻼﺣﻈﻪ ﺑﺎﯾﺪ از
آن اﺳﺘﻔﺎده ﮐﺮد.
ﺑﺮﻧﺎﻣﻪﻧﻮﯾﺲ ﻣﺒﺘﺪي
]numbers = [2,4,6,8,10,22
flag = True
for number in numbers:
if number% 2!= 0:
)"print("mixed
flag = False
break
if flag:
)"print("even
ﺑﺮﻧﺎﻣﻪﻧﻮﯾﺲ ﺣﺮﻓﻪاي
]numbers = [2,4,6,8,10,22
else:
)"print("even
ﺻﻮرتﻣﺴﺌﻠﻪ :ﺗﺎﺑﻌﯽ ﺑﻨﻮﯾﺴﯿﺪ ﮐﻪ رﺷﺘﻪاي را درﯾﺎﻓﺖ ﮐﻨﺪ و ﺳﭙﺲ آن را ﺑﻪ ﺣﺮوف ﺑﺰرگ ﺗﺒﺪﯾﻞ
ﮐﻨﺪ .ﺗﻤﺎم ﺧﻂﻫﺎي ﻓﺎﺻﻠﻪ را ﺑﻪ دو ﻧﻘﻄﻪ و ﻫﻤﭽﻨﯿﻦ ﺗﻤﺎم اﻋﺪاد ﺻﻔﺮ را ﭘﺎك ﮐﻨﺪ.
ﺑﺮﻧﺎﻣﻪﻧﻮﯾﺲ ﻣﺒﺘﺪي
def change_text(text: str) -> str:
)(text = text.upper
)"text = text.replace("-", ":
)"" text = text.replace("0",
return text
ﺑﺮﻧﺎﻣﻪﻧﻮﯾﺲ ﺣﺮﻓﻪاي
def change_text(text: str) -> str:
)"" return text.upper().replace("-", ":").replace("0",
ﺗﻮﺿﯿﺤﺎت :ﺑﻪﺟﺎي آﻧﮑﻪ در ﻫﺮ ﺧﻂ ﺗﻮاﺑﻊ را ﺑﺮ روي رﺷﺘﻪ اﻋﻤﺎل ﮐﻨﯿﻢ و ﺑﺮﮔﺮداﻧﯿﻢ ﻣﯽﺗﻮاﻧﯿﻢ ﺑﻪ-
ﺗﺮﺗﯿﺐ و زﻧﺠﯿﺮوار آﻧﻬﺎ را ﭘﺸﺖ ﺳﺮ ﻫﻢ ﺻﺪا ﺑﺰﻧﯿﻢ .ﺑﺎ اﯾﻦ روش ﺧﻮاﻧﺎﯾﯽ ﮐﺪ ﺑﺴﯿﺎر ﺑﺎﻻﺗﺮ ﻣﯽرود.
اﺳﺘﻔﺎده از indexﻣﻨﻔﯽ
ﺑﺮﻧﺎﻣﻪﻧﻮﯾﺲ ﻣﺒﺘﺪي
"file_name = "document.pdf
)]print(file_name[len(file_name)-3:
Ahwaz_Hackerz
ﺑﺮﻧﺎﻣﻪﻧﻮﯾﺲ ﺣﺮﻓﻪاي
"file_name = "document.pdf
)]print(file_name[-3:
ﺗﻮﺿﯿﺤﺎت :اﺳﺘﻔﺎده از indexﻣﻨﻔﯽ دﻗﯿﻘﺎً ﻫﻤﺎن ﻧﺘﯿﺠﻪ len() – indexرا دارد و از اﻧﺘﻬﺎي
ﻟﯿﺴﺖ و ﯾﺎ رﺷﺘﻪ ،ﺷﺮوع ﺑﻪ ﺷﻤﺎرش ﻣﯽﮐﻨﺪ .در ﺗﺼﻮﯾﺮ 4ﻧﻤﻮﻧﻪاي از اﯾﻦ indexﮔﺬاري را ﻣﯽﺑﯿﻨﯿﻢ.
ﺻﻮرتﻣﺴﺌﻠﻪ :ﺑﺮﻧﺎﻣﻪاي ﺑﻨﻮﯾﺴﯿﺪ ﮐﻪ ﻟﯿﺴﺘﯽ از اﻋﺪاد را درﯾﺎﻓﺖ ﮐﻨﺪ ﺳﭙﺲ ﺟﻤﻊ ﺗﻤﺎم ﻋﻨﺎﺻﺮ،
ﺑﯿﺸﺘﺮﯾﻦ و ﮐﻤﺘﺮﯾﻦ ﻣﻘﺪار آن ﻟﯿﺴﺖ را ﭼﺎپ ﮐﻨﯿﺪ.
ﺑﺮﻧﺎﻣﻪﻧﻮﯾﺲ ﻣﺒﺘﺪي
]numbers = [1,2,3,4,5
sum = 0
for number in numbers:
sum += number
)print(sum
>> 15
]min = numbers[0
for number in numbers:
if number<min:
min = number
)print(min
>> 1
Ahwaz_Hackerz
]max = numbers[0
for number in numbers:
if number>max:
max = number
)print(max
>> 5
ﺑﺮﻧﺎﻣﻪﻧﻮﯾﺲ ﺣﺮﻓﻪاي
]numbers = [1,2,3,4,5
))print(sum(numbers
>> 15
))print(min(numbers
>> 1
))print(max(numbers
>> 5
ﺗﻮﺿﯿﺤﺎت :از ﺗﻮاﺑﻊ min ،sumو maxﮐﻪ ﺑﻪﺻﻮرت ﭘﯿﺶﻓﺮض در ﭘﺎﯾﺘﻮن وﺟﻮد دارد ،اﺳﺘﻔﺎده
ﮐﻨﯿﺪ .اﯾﻦ ﺗﻮاﺑﻊ ﻗﺪرﺗﻤﻨﺪﺗﺮ از آﻧﭽﻪ ﮐﻪ در ﻣﺜﺎل ﺑﺎﻻ ذﮐﺮ ﺷﺪ ،ﻫﺴﺘﻨﺪ .ﺑﺮاي ﻧﻤﻮﻧﻪ ﻣﺴﺌﻠﻪ دﯾﮕﺮي را درﻧﻈﺮ
ﺑﮕﯿﺮﯾﺪ ﮐﻪ ﻧﺎم اﻓﺮاد ﺑﻪ ﺻﻮرت ﻣﻘﺎدﯾﺮ در دﯾﮑﺸﻨﺮي وﺟﻮد دارد و ﻣﯽﺧﻮاﻫﯿﻢ ﻧﺎﻣﯽ را ﮐﻪ ﺑﯿﺸﺘﺮﯾﻦ ﻃﻮل را
دارد ﭘﯿﺪا ﮐﻨﯿﻢ .از اﯾﻦرو ﺑﺎﯾﺪ از ﭘﺎراﻣﺘﺮ keyاﺳﺘﻔﺎده ﮐﻨﯿﻢ .ﭘﺎراﻣﺘﺮ keyﺑﻪ اﺻﻄﻼح ﮐﻠﯿﺪ اﺳﺖ ﮐﻪ ﺑﯿﺎن
ﻣﯽﮐﻨﺪ اﯾﻦ ﺗﻮاﺑﻊ ﭼﻪ ﭼﯿﺰي را ﻣﺪﻧﻈﺮ ﻗﺮار ﺑﺪﻫﻨﺪ .ﭘﺎراﻣﺘﺮ keyﯾﮏ ﺗﺎﺑﻊ درﯾﺎﻓﺖ ﻣﯽﮐﻨﺪ و ﺑﺮاﺳﺎس
ﻣﻘﺪار ﺣﺴﺎبﺷﺪه ﺗﻮﺳﻂ ﺗﺎﺑﻊ درﯾﺎﻓﺖ ﺷﺪه ،ﻣﻘﺪار ﻣﻮرد ﻧﻈﺮ را ﺑﺮﻣﯽﮔﺮداﻧﺪ .ﺑﺮاي روﺷﻦﺷﺪن اﯾﻦ ﻣﻔﻬﻮم
ﻣﺜﺎل زﯾﺮ را ﺑﺮرﺳﯽ ﮐﻨﯿﺪ.
users { =
1: "Siavash",
2: "Shayan",
3: "Peyman",
4: ""Arash
}
)print(max_length
>> Siavash
Ahwaz_Hackerz
در ﻣﺜﺎل ﺑﺎﻻ در ﺑﯿﻦ ﻣﻘﺎدﯾﺮ دﯾﮑﺸﻨﺮي دﻧﺒﺎل ﻋﻨﺼﺮي ﻣﯽﮔﺮدﯾﻢ ﮐﻪ lenﯾﺎ ﻃﻮل آن ﺑﯿﺸﯿﻨﻪ ﺑﺎﺷﺪ .در
اﯾﻨﺠﺎ ﺑﻪﺟﺎي اﺳﺘﻔﺎده از ﺗﺎﺑﻊ ،از ﻋﺒﺎرت lambdaاﺳﺘﻔﺎده ﮐﺮدﯾﻢ .اﮔﺮ ﺑﺎ آن آﺷﻨﺎﯾﯽ ﻧﺪارﯾﺪ در ﻓﺼﻞ
ﺑﺮﻧﺎﻣﻪﻧﻮﯾﺴﯽ ﺗﺎﺑﻌﯽ آن را ﻣﻔﺼﻞ ﺑﺮرﺳﯽ ﻣﯽﮐﻨﯿﻢ.
ﺻﻮرتﻣﺴﺌﻠﻪ :ﻟﯿﺴﺘﯽ را درﻧﻈﺮ ﺑﮕﯿﺮﯾﺪ ،ﻣﯽﺧﻮاﻫﯿﻢ ﻋﻨﺼﺮ اول را در ﻣﺘﻐﯿﺮ ،headﻋﻨﺼﺮ آﺧﺮ را در
ﻣﺘﻐﯿﺮ tailو ﺗﻤﺎم ﻋﻨﺎﺻﺮ ﻣﯿﺎﻧﯽ را در ﻣﺘﻐﯿﺮ middleذﺧﯿﺮه ﮐﻨﯿﻢ.
ﺑﺮﻧﺎﻣﻪﻧﻮﯾﺲ ﻣﺒﺘﺪي
]numbers = [1, 2, 3, 4, 5, 6, 7, 8
]head = numbers[0
]middle = numbers[1:len(numbers)-1
]tail = numbers[len(numbers)-1
ﺑﺮﻧﺎﻣﻪﻧﻮﯾﺲ ﺣﺮﻓﻪاي
]numbers = [1, 2, 3, 4, 5, 6, 7, 8
ﺗﻮﺿﯿﺤﺎت :اﺳﺘﻔﺎده از * اﯾﻦ اﻣﺮ را ﺳﺮﻋﺖ ﻣﯽﺑﺨﺸﺪ .ﺑﺮاي اﻧﺘﺴﺎب ﺑﻘﯿﮥ اﺟﺰاي ﻟﯿﺴﺖ ،از * ﻣﯽﺗﻮان
اﺳﺘﻔﺎده ﮐﺮد .ﻣﺜﻼً ﺑﺮاي ﮔﺮﻓﺘﻦ ﺳﻪ ﻣﻘﺪار اول ﻟﯿﺴﺖ و دو ﻣﻘﺪار اﻧﺘﻬﺎﯾﯽ ﻟﯿﺴﺖ ﺑﺪﯾﻦﮔﻮﻧﻪ ﻋﻤﻞ ﻣﯽﮐﻨﯿﻢ:
)print(head2
>> 2
)print(tail1
>> 7
)print(middle
ﻋﻨﺼﺮ، ﻋﻨﺼﺮ اول ﻧﺎم. ﻓﺮض ﮐﻨﯿﺪ اﻃﻼﻋﺎت ﯾﮏ ﮐﺎرﺑﺮ در ﻟﯿﺴﺘﯽ ذﺧﯿﺮه ﺷﺪه ﺑﺎﺷﺪ: ﺻﻮرتﻣﺴﺌﻠﻪ
ﺗﺎﺑﻌﯽ ﺑﻨﻮﯾﺴﯿﺪ ﮐﻪ اﯾﻦ. ﻋﻨﺼﺮ ﺳﻮم ﺳﻦ و درﻧﻬﺎﯾﺖ ﻋﻨﺼﺮ ﭼﻬﺎرم اﯾﻤﯿﻞ ﮐﺎرﺑﺮ ﺑﺎﺷﺪ،دوم ﻧﺎم ﺧﺎﻧﻮادﮔﯽ
.ﻟﯿﺴﺖ را ﺑﮕﯿﺮد و ﻣﺸﺨﺼﺎت ﮐﺎرﺑﺮ را ﭼﺎپ ﮐﻨﺪ
ﺑﺮﻧﺎﻣﻪﻧﻮﯾﺲ ﻣﺒﺘﺪي
def print_user_detail(user_list):
print(f"first_name={user_list[0]}")
print(f"last_name={user_list[1]}")
print(f"age={user_list[2]}")
print(f"email={user_list[3]}")
>> first_name=shayan
last_name=soroushpour
age=35
email=shayan@gmail.com
ﺑﺮﻧﺎﻣﻪﻧﻮﯾﺲ ﺣﺮﻓﻪاي
from collections import namedtuple
User = namedtuple('User',
['first_name', 'last_name', 'age', 'email'])
def print_user_detail(user_list):
user = User._make(user_list)
print(f"{user.first_name=}")
print(f"{user.last_name=}")
print(f"{user.age=}")
print(f"{user.email=}")
print_user_detail(user_sample)
Ahwaz_Hackerz
'>> user.first_name='shayan
'user.last_name='soroushpour
user.age=35
'user.email='shayan@gmail.com
ﺗﻮﺿﯿﺤﺎت :ﻗﺒﻼً ﺑﺎ ﮐﻼس Counterآﺷﻨﺎ ﺷﺪه ﺑﻮدﯾﻢ .ﯾﮑﯽ دﯾﮕﺮ از ﮐﻼسﻫﺎي ﺑﺴﯿﺎر ﮐﺎرﺑﺮدي در
آن ﭘﮑﯿﺞ namedtuple ،اﺳﺖ .ﺑﺎ اﺳﺘﻔﺎده از اﯾﻦ ﮐﻼس ﻣﯽﺗﻮان دادهﻫﺎ را ﺑﻪدرﺳﺘﯽ دﺳﺘﻪﺑﻨﺪي ﮐﺮد.
در ﺑﺴﯿﺎري از ﻣﺎژولﻫﺎي ارﺗﺒﺎط ﺑﺎ ﭘﺎﯾﮕﺎه داده ،اﻃﻼﻋﺎت در ﻗﺎﻟﺐ ﺳﻄﺮي و ﺑﻪﺻﻮرت ﻟﯿﺴﺖ ﺑﺮﮔﺮداﻧﺪه
ﻣﯽﺷﻮد .ﻣﻤﮑﻦ اﺳﺖ داﻧﺴﺘﻦ اﯾﻨﮑﻪ ﮐﺪام دادة ﻣﺎ ﭼﻨﺪﻣﯿﻦ دادة ﻟﯿﺴﺖ ﺑﺎﺷﺪ ﺑﻌﺪ از ﻣﺪﺗﯽ ﺳﺨﺖ ﺷﻮد،
ﺑﻨﺎﺑﺮاﯾﻦ ﻧﻮﺷﺘﻦ user.last_nameﺧﯿﻠﯽ ﺑﻬﺘﺮ از ﻧﻮﺷﺘﻦ ] user_list[0اﺳﺖ.
)user = User._make(user_list
))print(type(user
ﺳﭙﺲ ﯾﮏ iterableﺣﺎوي ﻓﯿﻠﺪﻫﺎي ﮐﻼس ﺑﻪ آن ﭘﺎس داده ﻣﯽﺷﻮد .درﻧﻬﺎﯾﺖ ﺑﺎ ﺗﺎﺑﻊ _makeﻟﯿﺴﺖ را
ﺑﻪ namedtupleﺗﺒﺪﯾﻞ ﻣﯽﮐﻨﯿﻢ و ﺑﺪﯾﻦﺗﺮﺗﯿﺐ دﺳﺘﺮﺳﯽ ﺑﻪ ﻓﯿﻠﺪﻫﺎي آن راﺣﺖﺗﺮ ﻣﯽﺷﻮد .اﻟﺒﺘﻪ
ﻣﯽﺗﻮاﻧﺴﺘﯿﻢ ﺑﺎ اﺳﺘﻔﺎده از ﮐﻼس داده ﻫﻢ اﯾﻦ ﮐﺎر را اﻧﺠﺎم دﻫﯿﻢ.
Ahwaz_Hackerz
ﻓﺼﻞ2
ﻧﮑﺎت ﮐﻠﯽ
Ahwaz_Hackerz
در اﯾﻦ ﻓﺼﻞ ﺑﻪ ﻧﮑﺎت ﮐﻠﯽ درﻣﻮرد ﻣﻮاردي ﮐﻪ ﺑﻬﺘﺮ اﺳﺖ در ﮐﺪزدن رﻋﺎﯾﺖ ﮐﻨﯿﻢ ،ﻣﯽﭘﺮدازﯾﻢ.
https://www.python.org/dev/peps/pep-0008
1
.2اﺳﺘﻔﺎده از ﮔﯿﺖ و ﺳﺎﻣﺎﻧﮥ ﻣﺪﯾﺮﯾﺖ ﻧﺴﺨﻪ
.3ﻧﻮﺷﺘﻦ ﮐﺪﻫﺎي ﺷﯽﮔﺮا
.4ﻧﻮﺷﺘﻦ دﻗﯿﻖ و ﮐﺎﻓﯽ ﻣﺴﺘﻨﺪات
.5اﺳﺘﻔﺎده از Virtual Environment
.6ﻧﻮﺷﺘﻦ ﮐﺪﻫﺎي ﺧﻮاﻧﺎ
.7ﮐﺪﻫﺎي ﺑﺎگدار را ﺑﻪﺳﺮﻋﺖ رﻓﻊ ﮐﻨﯿﺪ
.8از PyPIاﺳﺘﻔﺎده ﮐﻨﯿﺪ.
2
.9ﺧﻮدت را ﺗﮑﺮار ﻧﮑﻦ!
ﺳﻌﯽ ﮐﻨﯿﺪ ﮐﺪﻫﺎﯾﯽ ﮐﻪ ﯾﮑﺒﺎر ﻧﻮﺷﺘﻪاﯾﺪ را دوﺑﺎره ﺗﮑﺮار ﻧﮑﻨﯿﺪ و از ﺗﻮاﺑﻊ ﯾﺎ ﺣﻠﻘﻪﻫﺎ اﺳﺘﻔﺎده ﮐﻨﯿﺪ.
.10داﻧﺴﺘﻦ ﺳﺎزوﮐﺎر import
در ﭘﺎﯾﺘﻮن اﺳﺘﻔﺎده از ﻣﺎژول و ﭘﮑﯿﺞﻫﺎي دﯾﮕﺮ ﺑﺴﯿﺎر راﯾﺞ اﺳﺖ ،وﻟﯽ اﺿﺎﻓﻪﮐﺮدن آن ﺑﻪ ﭘﺮوژة اﺻﻠﯽ
ﺧﻮدﻣﺎن ﻧﮑﺎﺗﯽ را ﺑﻪ ﻫﻤﺮاه دارد .ﻧﺨﺴﺖ ﺑﻪ ﺳﺎزوﮐﺎر importﻣﯽﭘﺮدازﯾﻢ .در ﭘﺎﯾﺘﻮن ،ﻣﺎژول ﺑﻪ ﯾﮏ
ﻓﺎﯾﻞ ﺣﺎوي ﮐﺪﻫﺎي ﭘﺎﯾﺘﻮن ﮔﻔﺘﻪ ﻣﯽﺷﻮد ﮐﻪ ﺣﺎوي ﭘﺴﻮﻧﺪ .pyاﺳﺖ .ﭘﮑﯿﺞ ﺑﻪ داﯾﺮﮐﺘﻮري ﺣﺎوي
ﭼﻨﺪﯾﻦ ﻣﺎژول ﮔﻔﺘﻪ ﻣﯽﺷﻮد .درواﻗﻊ ،وﻗﺘﯽ ﭘﮑﯿﺠﯽ را importﻣﯽﮐﻨﯿﻢ ،ﻓﺎﯾﻞ __init__.py
داﺧﻞ آن importﻣﯽﺷﻮد.
اﮔﺮ ﺑﻨﻮﯾﺴﯿﻢ:
import dibagaran
1
)Version Control System (VCS
2
)Don't Repeat Yourself (DRY
3ﺟﺎﯾﯽ ﮐﻪ ﺗﻤﺎم ﻣﺎژولﻫﺎﯾﯽ ﮐﻪ ﻗﺒﻼً وارد ﺑﺮﻧﺎﻣﻪ ﺷﺪهاﻧﺪ ،ﮐﺶ ﺷﺪه اﺳﺖ،
Ahwaz_Hackerz
اﮔﺮ ﻣﻮﻓﻖ ﺑﻪ ﭘﯿﺪاﮐﺮدن آن ﻧﺸﺪ ،ﺑﻪ دﻧﺒﺎل اﯾﻦ ﻧﺎم در ﻣﺎژولﻫﺎي ﺧﻮدﺳﺎﺧﺘﻪ 1ﭘﺎﯾﺘﻮن ﻣﯽﮔﺮدد .درﻧﻬﺎﯾﺖ
در آدرسﻫﺎي sys.pathﺑﻪ دﻧﺒﺎل اﯾﻦ ﻧﺎم ﻣﯽﮔﺮدد .اوﻟﯿﻦ ﻣﻘﺪار ﻟﯿﺴﺖ sys.pathﺑﻪ داﯾﺮﮐﺘﻮري
2
ﺟﺎري اﺷﺎره دارد .اﮔﺮ در ﻫﯿﭻﮐﺪام از اﯾﻦ ﻣﺴﯿﺮﻫﺎ ﻣﺎژول ﻣﻮرد ﻧﻈﺮ ﭘﯿﺪا ﻧﺸﺪ ،اﺳﺘﺜﻨﺎ
ModuleNotFoundErrorرخ ﻣﯽدﻫﺪ.
ﺑﺮاي اﺳﺘﻔﺎده از ﭼﻨﺪﯾﻦ iterableﻫﻢاﻧﺪازه ﺑﻪﺻﻮرت ﻫﻢزﻣﺎن ﻣﯽﺗﻮان از zipاﺳﺘﻔﺎده ﮐﺮد .ﻓﺮض ﮐﻨﯿﺪ
دو ﻟﯿﺴﺖ ﻣﺠﺰا ﯾﮑﯽ ﺣﺎوي اﺳﻢ و دﯾﮕﺮي ﺣﺎوي ﺳﻦ اﻓﺮاد دارﯾﻢ و ﻣﯽﺧﻮاﻫﯿﻢ روي ﺟﻔﺖ اﯾﻦ ﻟﯿﺴﺖﻫﺎ
ﺑﻪ ﺻﻮرت ﻫﻢزﻣﺎن ﮔﺮدش ﮐﻨﯿﻢ .اﺳﺘﻔﺎده از zipﻫﻤﺎﻧﻨﺪ زﯾﭗ ﻟﺒﺎس اﯾﻦ دو ﻟﯿﺴﺖ را ﺑﻪ ﻫﻢ ﮔﺮه ﻣﯽزﻧﺪ.
ﻣﺜﺎل ﺳﺎدة زﯾﺮ روش اﺳﺘﻔﺎده از اﯾﻦ ﺗﺎﺑﻊ را ﻧﺸﺎن ﻣﯽدﻫﺪ.
1
Built-in
2
Exception
Ahwaz_Hackerz
ﺑﺨﺶ3
ﺑﺮﻧﺎﻣﻪﻧﻮﯾﺴﯽ ﺗﺎﺑﻌﯽ
Ahwaz_Hackerz
ﻓﺼﻞ1
ﻣﻘﺪﻣﻪ
Ahwaz_Hackerz
ﺑﺮﻧﺎﻣﻪﻧﻮﯾﺴﯽ ﺗﺎﺑﻌﯽ ،1ﯾﮏ ﭘﺎراداﯾﻢ ﺑﺮﻧﺎﻣﻪﻧﻮﯾﺴﯽ اﺳﺖ ﮐﻪ در آن ﺑﺮﻧﺎﻣﻪ ﺗﻮﺳﻂ ﺗﻮاﺑﻌﯽ ﺗﺸﮑﯿﻞ ﺷﺪه ﮐﻪ ﺑﺎ
اﺟﺮا و ﺗﺮﮐﯿﺐ ﯾﮑﺪﯾﮕﺮ ﻫﺪف ﺑﺮﻧﺎﻣﻪ را ﻣﺤﻘﻖ ﻣﯽﺳﺎزﻧﺪ .در ﺑﺮﻧﺎﻣﻪﻧﻮﯾﺴﯽ ﺗﺎﺑﻌﯽ ،ﺗﻮاﺑﻊ ﺑﻪﻋﻨﻮان ﺟﺰ اﺻﻠﯽ
ﺑﺮﻧﺎﻣﻪ ﺑﻪﺣﺴﺎب ﻣﯽآﯾﻨﺪ و ﻣﯽﺗﻮان آﻧﻬﺎ را در ﻣﺘﻐﯿﺮﻫﺎ ذﺧﯿﺮه ﮐﺮد ،ﺑﻪﻋﻨﻮان آرﮔﻮﻣﺎن ﺑﻪ ﺗﻮاﺑﻊ دﯾﮕﺮ ﭘﺎس
داد و ﺣﺘﯽ از ﯾﮏ ﺗﺎﺑﻊ ﺑﺮﮔﺸﺖ داد .ﭘﺎﯾﺘﻮن از ﺑﺮﻧﺎﻣﻪﻧﻮﯾﺴﯽ ﺗﺎﺑﻌﯽ ﭘﺸﺘﯿﺒﺎﻧﯽ ﻣﯽﮐﻨﺪ ،اﻣﺎ ﺑﺴﯿﺎري از
ﺑﺮﻧﺎﻣﻪﻧﻮﯾﺴﺎن ﭘﺎﯾﺘﻮن از اﯾﻦ ﻗﺎﺑﻠﯿﺖﻫﺎ اﺳﺘﻔﺎده ﻧﻤﯽﮐﻨﻨﺪ.
ﻣﺎژوﻻر ﺷﺪن :ﺑﺮﻧﺎﻣﻪﻧﻮﯾﺴﯽ ﺑﻪﺻﻮرت ﺗﺎﺑﻌﯽ ﺑﺎﻋﺚ ﻣﺎژوﻻرﺷﺪن ﻣﯽﺷﻮد .ﻫﺮ ﺗﺎﺑﻊ ﮐﺎر ﺑﻪﺧﺼﻮﺻﯽ را
اﻧﺠﺎم ﻣﯽدﻫﺪ و اﮔﺮ ﺧﻮاﺳﺘﯿﻢ ﻗﺴﻤﺘﯽ را ﺗﻐﯿﯿﺮ دﻫﯿﻢ ﮐﺎﻓﯿﺴﺖ ﺗﻨﻬﺎ ﺗﺎﺑﻊ ﻣﺮﺑﻮﻃﻪ را ﺗﻐﯿﯿﺮ دﻫﯿﻢ.
اﺧﺘﺼﺎر :ﺑﺮﻧﺎﻣﻪﻧﻮﯾﺴﯽ ﺗﺎﺑﻌﯽ ﻣﻌﻤﻮﻻً از ﭘﺎراداﯾﻢﻫﺎي دﯾﮕﺮ ﻧﯿﺎز ﺑﻪ ﻧﻮﺷﺘﻦ ﮐﺪﻫﺎي ﮐﻤﺘﺮي دارد.
ﻫﻤﺮوﻧﺪي :ﺑﺮﻧﺎﻣﻪﻧﻮﯾﺴﯽ ﺗﺎﺑﻌﯽ از آﻧﺠﺎﮐﻪ ﺗﻮاﺑﻊ از ﻫﻢ ﺟﺪا ﻫﺴﺘﻨﺪ ،ﻣﯽﺗﻮاﻧﺪ ﺑﻪﺻﻮرت ﻫﻤﺮوﻧﺪ اﻧﺠﺎم
ﺷﻮﻧﺪ ﮐﻪ ﺑﻪ ﮐﺎراﯾﯽ و ﻣﻘﯿﺎسﭘﺬﯾﺮي ﮐﻤﮏ ﻣﯽﮐﻨﻨﺪ.
ﻗﺎﺑﻠﯿﺖ ﺗﺴﺖ :از آﻧﺠﺎﯾﯽﮐﻪ ﺑﺮﻧﺎﻣﻪ ﻣﺎ را ﯾﮏ ﺳﺮي ﺗﺎﺑﻊ ﺗﺸﮑﯿﻞ داده ﮐﻪ ورودي و ﺧﺮوﺟﯽ آﻧﻬﺎ
ﻣﺸﺨﺺ اﺳﺖ؛ ﺑﻨﺎﺑﺮاﯾﻦ ﻧﻮﺷﺘﻦ ﺗﺴﺖ ﺑﺮاي آﻧﻬﺎ ﺑﺴﯿﺎر ﺳﺎده اﺳﺖ.
def tim(name):
)"}print(f"Welcome, {name
timﯾﮑﯽ از دوﺳﺘﺎن ﻣﺎ اﺳﺖ ﮐﻪ ﻣﯽﺗﻮاﻧﯿﻢ ﺧﻮدﻣﺎن او را ﺻﺪا ﺑﺰﻧﯿﻢ ﮐﻪ ﮐﺎرش را اﻧﺠﺎم دﻫﺪ و ﯾﺎ ﺑﻪ
دوﺳﺖ دﯾﮕﺮي ﺑﺪﻫﯿﻢ و او ﻫﺮﮔﻮﻧﻪ ﮐﻪ دﻟﺶ ﺧﻮاﺳﺖ ﻣﯽﺗﻮاﻧﺪ ﺑﺎ ﺗﯿﻢ رﻓﺘﺎر ﮐﻨﺪ .درواﻗﻊ ،ﻫﺮﮔﺎه ﺑﻌﺪ از
timﭘﺮاﻧﺘﺰ ﺑﺎز ﺷﻮد ﯾﻌﻨﯽ ﺻﺪازدن و ﯾﺎ ﻓﺮاﺧﻮاﻧﯽ ﺗﺎﺑﻊ اﻧﺠﺎم ﺷﺪه اﺳﺖ و timﺑﻼﻓﺎﺻﻠﻪ ﺷﺮوع ﺑﻪ اﻧﺠﺎم
ﮐﺎرش ﻣﯽﮐﻨﺪ .ﺗﺼﻮﯾﺮ 5ﻧﺸﺎندﻫﻨﺪة آن اﺳﺖ ﮐﻪ زﻣﺎﻧﯽ ﺗﯿﻢ را ﺑﺎ اﺳﻤﯽ ﺻﺪا ﻣﯽزﻧﯿﻢ ﺑﻪ او ﻣﯽﮔﻮﯾﯿﻢ:
»ﺗﯿﻢ ﻓﻮراً ﺑﻪ اﯾﻦ اﺳﻢ ﺧﻮشآﻣﺪﮔﻮﯾﯽ ﮐﻦ« وﻟﯽ وﻗﺘﯽ ﻓﻘﻂ timرا ﺑﻨﻮﯾﺴﯿﻢ ﯾﻌﻨﯽ ﺑﺎ ﺧﻮد Timﮐﺎر
دارﯾﻢ و ﺑﻪ او ﻧﮕﻔﺘﯿﻢ ﮐﺎري اﻧﺠﺎم ﺑﺪﻫﺪ!
1
Functional Programming
Ahwaz_Hackerz
ﺑﯿﺎﯾﯿﺪ ﺑﻪ ﺗﺎﯾﭗ timوﻗﺘﯽﮐﻪ آن را ﺻﺪا ﻣﯽزﻧﯿﻢ و وﻗﺘﯽﮐﻪ ﺻﺪا ﻧﻤﯽزﻧﯿﻢ ،ﻧﮕﺎﻫﯽ ﺑﯿﺎﻧﺪازﯾﻢ.
))print(type(tim
)))"print(type(tim("Siavash
ﺑﺴﯿﺎر واﺿﺢ اﺳﺖ ﮐﻪ وﻗﺘﯽ timرا ﺻﺪا ﻣﯽزﻧﯿﻢ ﻣﻘﺪاري ﺑﺮﻧﻤﯽﮔﺮداﻧﺪ؛ ﺑﻨﺎﺑﺮاﯾﻦ از ﻧﻮع Noneاﺳﺖ وﻟﯽ
در ﻣﻮرد ﺧﺮوﺟﯽ اول ،وﻗﺘﯽﮐﻪ timرا ﺑﻪﺗﻨﻬﺎﯾﯽ اﺳﺘﻔﺎده ﮐﺮدﯾﻢ ،ﻧﻮع آن ﻣﺘﻔﺎوت اﺳﺖ.
timﺑﻪﻋﻨﻮان ﯾﮏ ﺗﺎﺑﻊ ﯾﺎ functionﺗﻠﻘﯽ ﻣﯽﺷﻮد.
ﺣﺎل ﻓﺮض ﮐﻨﯿﺪ دوﺳﺖ دﯾﮕﺮي ﺑﺎ ﻧﺎم Mikeدارﯾﻢ ﮐﻪ ﮐﺎر او ﺧﺪاﺣﺎﻓﻈﯽ اﺳﺖ.
def mike():
)"!print("Bye
ﺣﺎل ﯾﮏ ﻣﺪﯾﺮ ﺑﺮﻧﺎﻣﻪ ﺑﺎ ﻧﺎم managerاﺳﺘﺨﺪام ﻣﯽﮐﻨﯿﻢ .ﮐﺎر او آن اﺳﺖ ﮐﻪ دو آدم را ﻣﯽﮔﯿﺮد و از
آﻧﻬﺎ ﺑﺮاي ﯾﮏ ﺟﻠﺴﻪ ﺧﻮشآﻣﺪﮔﻮﯾﯽ و ﺧﺪاﺣﺎﻓﻈﯽ اﺳﺘﻔﺎده ﻣﯽﮐﻨﺪ .ﺧﻮﺷﺒﺨﺘﺎﻧﻪ ﻣﺎ اﯾﻦ دو آدم را ﺑﻪﻋﻨﻮان
دوﺳﺘﺎﻧﻤﺎن دارﯾﻢ و ﻣﯽﺗﻮاﻧﯿﻢ mikeو timرا ﺑﻪ ﻣﺪﯾﺮ ﺑﺮﻧﺎﻣﻪ ﺑﺪﻫﯿﻢ .ﻣﺪﯾﺮ ﺑﺮﻧﺎﻣﻪ ﺑﺮاﯾﺶ ﻣﻬﻢ ﻧﯿﺴﺖ ﻧﺎم
اﯾﻦ اﻓﺮاد ﭼﯿﺴﺖ و ﻓﻘﻂ ﻣﯽﺧﻮاﻫﺪ آﻧﻬﺎ را ﺻﺪا ﺑﺰﻧﺪ ﺗﺎ ﮐﺎرﺷﺎن را اﻧﺠﺎم دﻫﻨﺪ .ﭘﺲ ﺑﻪ آﻧﻬﺎ person1و
person2ﻣﯽﮔﻮﯾﺪ.
def tim(name):
)"}print(f"Welcome, {name
def mike():
)"!print("Bye
Ahwaz_Hackerz
در ﻣﺜﺎل ﺑﺎﻻ دﯾﺪﯾﻢ ﮐﻪ ﻣﺎ ﻧﺎﻣﯽ را ﺑﻪ ﻣﺪﯾﺮ ﺑﺮﻧﺎﻣﻪ دادﯾﻢ ﺗﺎ ﺑﺮﻧﺎﻣﻪ ﺧﻮشآﻣﺪﮔﻮﯾﯽ و ﺧﺪاﺣﺎﻓﻈﯽ را اﻧﺠﺎم
دﻫﺪ و ﻫﻤﭽﻨﯿﻦ اﺧﺘﯿﺎر timو mikeرا ﻫﻢ ﺑﻪ آن دادﯾﻢ ﺗﺎ ﻫﺮﮔﺎه ﺧﻮاﺳﺖ آﻧﻬﺎ را ﺻﺪا ﺑﺰﻧﺪ .ﺑﺪﯾﻦﮔﻮﻧﻪ
ﻣﯽﺗﻮاﻧﯿﻢ ﺗﻮاﺑﻊ را ﺑﻪ ﯾﮑﺪﯾﮕﺮ ﭘﺎس دﻫﯿﻢ و از ﺗﺎﺑﻌﯽ در ﺗﺎﺑﻊ دﯾﮕﺮ اﺳﺘﻔﺎده ﮐﻨﯿﻢ .ﻓﻬﻢ درﺳﺖ اﯾﻦ ﻗﻀﯿﻪ در
ﺑﺮﻧﺎﻣﻪﻧﻮﯾﺴﯽ ﺗﺎﺑﻌﯽ ﺑﺴﯿﺎر ﮐﻤﮏ ﻣﯽﮐﻨﺪ .
ﺗﻮاﺑﻊ Lambda
ﻫﻤﭽﻮن زﺑﺎنﻫﺎي ﺑﺮﻧﺎﻣﻪﻧﻮﯾﺴﯽ ﺳﯽ ،ﺟﺎوا و ...ﭘﺎﯾﺘﻮن ﻧﯿﺰ از ﺗﻮاﺑﻊ Lambdaﭘﺸﺘﯿﺒﺎﻧﯽ ﻣﯽﮐﻨﺪ .اﯾﻦ ﺗﻮاﺑﻊ
ﺑﯽﻧﺎم ﻫﺴﺘﻨﺪ و از ﺗﻮاﺑﻌﯽ ﮐﻪ ﻗﺒﻼً ﻣﯽﻧﻮﺷﺘﯿﻢ ﻣﺤﺪودﺗﺮ و ﻣﺨﺘﺼﺮﺗﺮ ﻫﺴﺘﻨﺪ .ﺟﺒﺮ Lambdaﭘﺎﯾﮥ
ﺑﺮﻧﺎﻣﻪﻧﻮﯾﺴﯽ ﺗﺎﺑﻌﯽ اﺳﺖ ﮐﻪ ﺗﻮﺳﻂ ﭼﺮچ در دﻫﮥ 1930ﻣﻌﺮﻓﯽ ﺷﺪ .ﭘﺎﯾﺘﻮن زﺑﺎﻧﯽ ﺑﻪﻃﻮر ذاﺗﯽ ﺗﺎﺑﻌﯽ
ﻧﯿﺴﺖ وﻟﯽ ﺗﻮاﺑﻌﯽ ﻫﻤﭽﻮن filter ،mapو reduceدارد ﮐﻪ از ﻫﻤﺎن ﻣﻔﺎﻫﯿﻢ ﺑﺮﻧﺎﻣﻪﻧﻮﯾﺴﯽ ﺗﺎﺑﻌﯽ
ﭘﯿﺮوي ﻣﯽﮐﻨﺪ.
روش ﮐﻠﯽ ﻧﻮﺷﺘﻦ ﺗﺎﺑﻊ Lambdaدر ﺗﺼﻮﯾﺮ 6آﻣﺪه اﺳﺖ .ﭘﺲ از ﻧﻮﺷﺘﻦ ،lambdaوروديﻫﺎي ﺗﺎﺑﻊ را
ﻣﯽﻧﻮﯾﺴﯿﻢ و ﺑﺎ ﻋﻼﻣﺖ دوﻧﻘﻄﻪ ﺧﺮوﺟﯽ را از ورودي ﺟﺪا ﻣﯽﮐﻨﯿﻢ.
ﺗﺎﺑﻌﯽ دارﯾﻢ ﮐﻪ ﯾﮏ ورودي ﻣﯽﮔﯿﺮد و آن را ﺑﻼﻓﺎﺻﻠﻪ ﺑﺮﻣﯽﮔﺮداﻧﺪ .ﻫﺮ دو ﻣﺪل ﻧﻮﺷﺘﻦ اﯾﻦ ﺗﺎﺑﻊ در ﻣﺜﺎل
زﯾﺮ آﻣﺪه اﺳﺖ ﮐﻪ ﻣﻌﺎدل ﯾﮑﺪﯾﮕﺮ ﻫﺴﺘﻨﺪ و ﺗﻨﻬﺎ ﺗﻔﺎوت ﻣﯿﺎن آﻧﻬﺎ اﯾﻦ اﺳﺖ ﮐﻪ ﺗﺎﺑﻊ Lambdaﻧﺎم ﻧﺪارد.
اﯾﻦ ﺣﺎﻟﺖ ﺳﺎدهﺗﺮﯾﻦ ﺣﺎﻟﺖ ﻧﻮﺷﺘﻦ ﯾﮏ ﺗﺎﺑﻊ Lambdaاﺳﺖ.
Ahwaz_Hackerz
def simple(x):
return x
lambda x: x
ﺣﺎل ﻣﯽﺧﻮاﻫﯿﻢ ﺗﺎﺑﻌﯽ ﺑﻨﻮﯾﺴﯿﻢ ﮐﻪ ﻋﺪدي را درﯾﺎﻓﺖ ﮐﺮده و آن را ﺑﺎ 5ﺟﻤﻊ ﮐﻨﺪ و ﺑﺮﮔﺮداﻧﺪ.
lambda x: x + 5
ﺑﺮاي اﺳﺘﻔﺎده از آن ،ﭘﺲ از ﺻﺪازدن ﺗﺎﺑﻊ ﺑﺎ ﻋﺪد ،7ﺟﻮاب را در ﻣﺘﻐﯿﺮي ﻣﯽرﯾﺰﯾﻢ.
)print(result
>> 12
)print(avg
>> 16.0
در ﻣﺜﺎل زﯾﺮ ﺗﺎﺑﻊ ﺳﺎﺧﺘﻪﺷﺪه را در ﯾﮏ ﻣﺘﻐﯿﺮ ﻣﯽﮔﺬارﯾﻢ ،ﺑﺎ اﯾﻦ ﮐﺎر ﻧﺎﻣﯽ ﺑﻪ ﺗﺎﺑﻊ ﻣﯽدﻫﯿﻢ و ﺑﻌﺪاً از آن
اﺳﺘﻔﺎده ﻣﯽﮐﻨﯿﻢ .ﻫﻤﺎنﻃﻮرﮐﻪ ﻣﺸﺎﻫﺪه ﻣﯽﮐﻨﯿﺪ در آرﮔﻮﻣﺎنﻫﺎي ورودي ﺗﺎﺑﻊ ،ﻣﺠﺎز ﺑﻪ اﺳﺘﻔﺎده از ﺗﻤﺎم
ﺣﺎﻟﺖﻫﺎﯾﯽ ﮐﻪ ﻗﺒﻼً اﺳﺘﻔﺎده ﻣﯽﮐﺮدﯾﻢ ،ﻫﺴﺘﯿﻢ.
>> 8
درﺳﺖ اﺳﺖ ﮐﻪ ﻧﻮﺷﺘﻦ ﺗﻮاﺑﻊ Lambdaدر ﯾﮏ ﺧﻂ ﺑﺴﯿﺎر ﺟﺎﻟﺐ اﺳﺖ وﻟﯽ ﺑﺎﯾﺪ از آﻧﻬﺎ ﮐﻢ و ﺑﺎ اﺣﺘﯿﺎط
اﺳﺘﻔﺎده ﺷﻮد.
Ahwaz_Hackerz
ﺗﺎﺑﻊ Map
ﺗﺎﺑﻊ ) map(func, *iterablesﺗﺎﺑﻌﯽ ﮐﺎرﺑﺮدي اﺳﺖ ﮐﻪ ﺑﺎ درﯾﺎﻓﺖ ﯾﮏ ﺗﺎﺑﻊ و ﯾﮏ ﯾﺎ ﭼﻨﺪ
iterableﻣﺎﻧﻨﺪ ﻟﯿﺴﺖ ،ﺗﺎﺑﻊ را ﺑﺮاي ﺗﻤﺎم ﻋﻨﺎﺻﺮ داﺧﻞ iterableﻫﺎ اﺟﺮا ﻣﯽﮐﻨﺪ .اﯾﻦ ﺗﺎﺑﻊ دﻗﯿﻘﺎً ﮐﺎري
را ﮐﻪ در رﯾﺎﺿﯿﺎت ﺑﺎ ﻣﻔﻬﻮم ﻧﮕﺎﺷﺖ از آن ﯾﺎد ﻣﯽﮐﻨﯿﻢ را اﻧﺠﺎم ﻣﯽدﻫﺪ .ﺧﺮوﺟﯽ اﯾﻦ ﺗﺎﺑﻊ ﯾﮏ ژﻧﺮاﺗﻮر
اﺳﺖ و ﺑﺮاي اﯾﻨﮑﻪ ﺑﺘﻮاﻧﯿﻢ اﺟﺰاي آن را ﺑﺮرﺳﯽ ﮐﻨﯿﻢ ﺑﺎ )( listآن را ﺑﻪ ﻟﯿﺴﺖ ﺗﺒﺪﯾﻞ ﻣﯽﮐﻨﯿﻢ.
ﻓﺮض ﮐﻨﯿﺪ ﻟﯿﺴﺘﯽ از اﻋﺪاد دارﯾﻢ و ﻣﯽﺧﻮاﻫﯿﻢ ﺗﻤﺎم اﻋﺪاد اﯾﻦ ﻟﯿﺴﺖ را ﺑﻪ ﺗﻮان 2ﺑﺮﺳﺎﻧﯿﻢ .اﮔﺮ از map
اﺳﺘﻔﺎده ﻧﮑﻨﯿﻢ اﺣﺘﻤﺎﻻً ﮐﺪ ﻣﺎ ﺑﻪ اﯾﻦ ﺷﮑﻞ ﺧﻮاﻫﺪ ﺑﻮد:
][ = power_two
)print(power_two
def pow2(x):
return x ** 2
ﻧﮑﺘﮥ ﻗﺎﺑﻞ ﺗﻮﺟﻪ اﯾﻦ اﺳﺖ ﮐﻪ mapﻫﻢ ﻣﺜﻞ ﺗﺎﺑﻊ ﻣﺪﯾﺮ ﺑﺮﻧﺎﻣﻪ )(managerﮐﮫ ﻗﺒﻼً ﺗﻮﺿﯿﺢ دادﯾﻢ ،ﺗﺎﺑﻊ
را ﻣﯽﮔﯿﺮد و ﺧﻮدش آن را ﺻﺪا ﻣﯽزﻧﺪ .ﻫﻤﺎنﻃﻮرﮐﻪ ﻣﯽﺑﯿﻨﯿﻢ mapاﻋﺪاد را ﺑﻪﺗﺮﺗﯿﺐ ﮔﺮﻓﺘﻪ و ﺗﺎﺑﻊ ﺗﻮان
دو را ﺑﺮاي ﻫﺮﮐﺪام ﺻﺪا ﻣﯽزﻧﺪ و ﻧﺘﯿﺠﻪ را ﺑﺮﻣﯽﮔﺮداﻧﺪ .ﺣﺎل ﺑﺎ ﺗﻮﺟﻪ ﺑﻪ آﻣﻮزش ﻗﺒﻠﯽ ﻣﯽﺗﻮاﻧﯿﻢ ﺗﺎﺑﻊ
ﺳﺎده و اﺑﺘﺪاﯾﯽ ﺗﻮان رﺳﺎﻧﺪن را ﺑﻪﺻﻮرت Lambdaﺑﻨﻮﯾﺴﯿﻢ ﺗﺎ ﮐﺪ ﮐﻮﺗﺎهﺗﺮ ﺷﺪه و ﺧﻮاﻧﺎﯾﯽ آن ﺑﺎﻻﺗﺮ
ﺑﺮود .ﻫﻤﺎنﻃﻮرﮐﻪ ﻗﺒﻼً اﺷﺎره ﮐﺮدﯾﻢ ﺑﺎﯾﺪ ﺑﻪ اﺳﺘﻔﺎده از ﺗﻮاﺑﻊ ﯾﮏ ﺧﻄﯽ ﺗﻮﺟﻪ داﺷﺖ .اﯾﻦ ﮐﺪ ،ﻣﺜﺎل
ﺧﻮﺑﯽ اﺳﺖ ﮐﻪ ﺑﺒﯿﻨﯿﻢ ﻧﻮﺷﺘﻦ آﻧﻬﺎ در اﯾﻨﺠﺎ ﺧﻮب اﺳﺖ ،زﯾﺮا ﺗﺎﺑﻊ ﺳﺎده اﺳﺖ و ﻣﯽﺧﻮاﻫﯿﻢ ﺳﺮﯾﻌﺎً ﻧﻮﺷﺘﻪ
و از آن اﺳﺘﻔﺎده ﮐﻨﯿﻢ.
در ﻣﺜﺎل ﺑﻌﺪي ﻓﺮض ﮐﻨﯿﺪ ﻣﺎ ﺳﻪ ﻟﯿﺴﺖ از اﻋﺪاد ﺑﻪ ﻃﻮل ﯾﮑﺴﺎن دارﯾﻢ و ﻣﯽﺧﻮاﻫﯿﻢ ﻟﯿﺴﺖ ﺟﺪﯾﺪي
درﺳﺖ ﮐﻨﯿﻢ ﮐﻪ در ﻫﺮ ﺧﺎﻧﻪ ﺑﺰرﮔﺘﺮﯾﻦ ﻋﺪد ﻫﻤﺎن ﺧﺎﻧﻪ در ﯾﮑﯽ از ﺳﻪ ﻟﯿﺴﺖ ﺑﺎﺷﺪ .ﺗﺼﻮﯾﺮ 7
ﺻﻮرتﻣﺴﺌﻠﻪ را ﺷﻔﺎفﺗﺮ ﺗﻮﺿﯿﺢ ﻣﯽدﻫﺪ.
در ﮐﺪ زﯾﺮ ﺗﺎﺑﻊ maxﺑﺮاي ﺳﻪ ﻋﻨﺼﺮ اول ﻟﯿﺴﺖ اﺟﺮاﺷﺪه و ﻧﺘﯿﺠﻪ را در ﺧﺎﻧﮥ اول ﺟﻮاب ﻣﯽﮔﺬارد.
ﺗﺎﺑﻊ Filter
ﺗﺎﺑﻊ ) filter(func, iterableﺑﺮاي ﻓﯿﻠﺘﺮﮐﺮدن ﯾﮏ iterableاﺳﺘﻔﺎده ﻣﯽﺷﻮد .ﺑﺮﻋﮑﺲ
ﺗﺎﺑﻊ ،mapﻓﻘﻂ ﯾﮏ iterableدرﯾﺎﻓﺖ ﻣﯽﮐﻨﺪ و ﺗﮏﺗﮏ ﻋﻨﺎﺻﺮ iterableرا ﺑﻪ ﺗﺎﺑﻊ ورودي ﺧﻮد
ﻣﯽدﻫﺪ ،اﮔﺮ ﻣﻘﺪار Trueدرﯾﺎﻓﺖ ﮐﻨﺪ آن را در ﺧﺮوﺟﯽ ﻣﯽﮔﺬارد و اﮔﺮ Falseﺑﻮد از آن ﻣﯽﮔﺬرد .در
ﻣﺜﺎل زﯾﺮ ﺗﻤﺎم اﻋﺪاد زوج ﯾﮏ ﻟﯿﺴﺖ را ﻓﯿﻠﺘﺮ ﻣﯽﮐﻨﯿﻢ .ﻻزم ﺑﻪ ذﮐﺮ اﺳﺖ ﮐﻪ ﻫﻤﭽﻮن ﺗﺎﺑﻊ ،mapاﯾﻦ ﺗﺎﺑﻊ
ﻧﯿﺰ ژﻧﺮاﺗﻮر ﺑﺮﻣﯽﮔﺮداﻧﺪ ،ﺑﻨﺎﺑﺮاﯾﻦ ﺑﺮاي ﻣﺸﺎﻫﺪه اﺟﺰاي آن ﺑﺎﯾﺪ آن را ﺑﻪ ﻟﯿﺴﺖ ﺗﺒﺪﯾﻞ ﮐﺮد .در ﻓﺼﻞﻫﺎي
آﯾﻨﺪه در ﻣﻮرد ژﻧﺮاﺗﻮر ﺻﺤﺒﺖ ﺧﻮاﻫﯿﻢ ﮐﺮد و ﺑﺮاي اﺳﺘﻔﺎده از آن ﻫﻤﯿﺸﻪ ﻧﯿﺎزي ﺑﻪ ﺗﺒﺪﯾﻞ ﺑﻪ ﻟﯿﺴﺖ
ﻧﺪارﯾﻢ.
ﻓﺮض ﮐﻨﯿﺪ ﻟﯿﺴﺘﯽ از رﺷﺘﻪﻫﺎﯾﯽ دارﯾﻢ ﮐﻪ در آن اﻓﺮاد ﺑﺎ ﻧﺎم و ﺳﻦ ﺑﺎ ﮐﺎراﮐﺘﺮ – ﺟﺪا ﺷﺪهاﻧﺪ .ﺣﺎل
ﻣﯽﺧﻮاﻫﯿﻢ ﻓﻘﻂ ﮐﺎرﺑﺮاﻧﯽ را اﻧﺘﺨﺎب ﮐﻨﯿﻢ ﮐﻪ ﻧﺎﻣﺸﺎن ﺑﺎ sﺷﺮوع ﺷﺪه و ﺳﻦ آﻧﻬﺎ ﺑﯿﺸﺘﺮ از 30ﺑﺎﺷﺪ.
)print(qualified_user
در ﻣﺜﺎل ﺑﺎﻻ ﻣﺸﺎﻫﺪه ﻣﯽﮐﻨﯿﻢ ﮐﻪ ﺗﺎﺑﻊ ﻓﯿﻠﺘﺮﮐﻨﻨﺪه ﭘﯿﭽﯿﺪه ﺑﻮده و ﻧﻮﺷﺘﻦ آن ﺑﺎ Lambdaاﺻﻼً ﺗﻮﺻﯿﻪ
ﻧﻤﯽﺷﻮد.
Ahwaz_Hackerz
ﺗﺎﺑﻊ Reduce
ﺗﺎﺑﻊ )] reduce(func, iterable[, initialﯾﮏ ﺗﺎﺑﻊ ﺑﺎ دو آرﮔﻮﻣﺎن را درﯾﺎﻓﺖ
ﻣﯽﮐﻨﺪ و ﻋﻨﺎﺻﺮ ﯾﮏ iterableرا ﺑﻪ ﺻﻮرت ﺗﺠﻤﻌﯽ ﺑﺎ ﻫﻤﺎن ﺗﺎﺑﻊ ﺣﺴﺎب ﻣﯽﮐﻨﺪ .ﺑﺮاي ﻣﺜﺎل ﻓﺮض ﮐﻨﯿﺪ
ﺗﺎﺑﻌﯽ ﺑﻪ ﻧﺎم addدارﯾﻢ ﮐﻪ دو ﻋﺪد ﻣﯽﮔﯿﺮد و آنﻫﺎ را ﺑﺎ ﻫﻢ ﺟﻤﻊ ﻣﯽﮐﻨﺪ .ﺗﺎﺑﻊ reduceﺑﺎ درﯾﺎﻓﺖ
اﯾﻦ ﺗﺎﺑﻊ و ﻟﯿﺴﺘﯽ از اﻋﺪاد ،ﺑﻪﺻﻮرت ﺗﺠﻤﻌﯽ اﯾﻦ اﻋﺪاد را ﺟﻤﻊ ﻣﯽﮐﻨﺪ .ﻣﻨﻈﻮر از ﺗﺠﻤﻌﯽ آن اﺳﺖ ﮐﻪ
ﻧﺨﺴﺖ ﻋﺪد اول و دوم ﺑﺎ ﻫﻢ ﺟﻤﻊ ﻣﯽﺷﻮد ،ﺳﭙﺲ ﺟﻮاب آﻧﻬﺎ ﺑﺎ ﻋﺪد ﺳﻮم ﺟﻤﻊ ﻣﯽﺷﻮد .ﺑﻪ ﻫﻤﯿﻦ
ﺗﺮﺗﯿﺐ اﯾﻦ ﮐﺎر را ﺗﺎ ﺟﺎﯾﯽ ﮐﻪ ﺗﻤﺎم ﻟﯿﺴﺖ ﻃﯽ ﺷﻮد ،ﺗﮑﺮار ﻣﯽﮐﻨﺪ .ﺗﺼﻮﯾﺮ 8ﻧﻤﺎﯾﯽ از اﯾﻦ ﻋﻤﻠﯿﺎت را ﺑﻪ
ﺗﺮﺗﯿﺐ از ﭘﺎﯾﯿﻦ ﺑﻪ ﺑﺎﻻ ﻧﻤﺎﯾﺶ ﻣﯽدﻫﺪ.
)print(result
>> 10
Ahwaz_Hackerz
ﺗﺎﺑﻊ reduceﯾﮏ ﻣﻘﺪار اوﻟﯿﻪ ﺑﻪﺻﻮرت اﺧﺘﯿﺎري ﻫﻢ درﯾﺎﻓﺖ ﻣﯽﮐﻨﺪ .ﻣﺜﺎل ﺑﻌﺪي را ﺑﺎ ﭘﺎراﻣﺘﺮ ﻣﻘﺪار
اوﻟﯿﻪ 8اﺟﺮا ﮐﺮدﯾﻢ.
>> 18
ﻣﻘﺪار اوﻟﯿﻪ اﯾﻦﮔﻮﻧﻪ ﻋﻤﻞ ﻣﯽﮐﻨﺪ ﮐﻪ در اﺑﺘﺪا ﺑﻪﺟﺎي آﻧﮑﻪ ﺗﺎﺑﻊ reduceﻋﻨﺼﺮ اول و دوم را ﺑﻪ ﺗﺎﺑﻊ
ﺑﺪﻫﺪ ،ﻧﺨﺴﺖ ﻣﻘﺪار اوﻟﯿﻪ و ﻋﻨﺼﺮ اول را ﺑﻪ ﺗﺎﺑﻊ ﻣﯽدﻫﺪ و ﻣﺮاﺣﻞ را ﻣﺎﻧﻨﺪ ﻗﺒﻞ اداﻣﻪ ﻣﯽدﻫﺪ.
List Comprehension
در اﯾﻦ ﻓﺼﻞ دﯾﺪﯾﻢ ﮐﻪ ﺑﺮاي ﺳﺎﺧﺖ ﻟﯿﺴﺖ ﻣﯽﺗﻮان از ﺣﻠﻘﻪ forو ﻫﻤﭽﻨﯿﻦ از ﺗﺎﺑﻊ mapاﺳﺘﻔﺎده ﮐﺮد.
وﻟﯽ ﺗﻮﺻﯿﻪ ﻣﯽﺷﻮد ﻟﯿﺴﺖﻫﺎ را ﺑﺎ ﺗﮑﻨﯿﮏ List Comprehensionﺑﺴﺎزﯾﺪ .روش ﮐﺎر ﺑﺴﯿﺎر ﺳﺎده اﺳﺖ،
ﺑﻪ ﻧﻤﻮﻧﻪ ﻧﻮﺷﺘﺎر آن دﻗﺖ ﮐﻨﯿﺪ:
:expression ﻋﺒﺎرﺗﯽ اﺳﺖ ﮐﻪ ﻣﯽﺧﻮاﻫﯿﻢ وارد ﻟﯿﺴﺖ ﺷﻮد .ﻣﯽﺗﻮاﻧﺪ ﯾﮏ ﻣﻘﺪار ﺛﺎﺑﺖ ،ﺗﺎﺑﻊ ،ﺷﺊ و ﯾﺎ
ﯾﮏ ﻋﺒﺎرت رﯾﺎﺿﯽ ﺑﺎﺷﺪ.
:member ﻋﻀﻮي از iterableاﺳﺖ ﮐﻪ در ﻫﺮ ﻣﺮﺣﻠﻪ ﺑﻪ اﯾﻦ ﻣﺘﻐﯿﺮ ﻣﻨﺴﻮب ﻣﯽﺷﻮد.
:iterable ﯾﮏ ﻟﯿﺴﺖ ،ﻣﺠﻤﻮﻋﻪ ،ژﻧﺮاﺗﻮر ﯾﺎ ﻫﺮ ﮐﻼﺳﯽ ﮐﻪ ﺑﺘﻮان ﻋﻨﺎﺻﺮ آن را در ﯾﮏ زﻣﺎن ﻣﺸﺨﺺ
درﯾﺎﻓﺖ ﮐﺮد.
:conditional ﯾﮏ ﻋﺒﺎرت ﺷﺮﻃﯽ ﻣﯽﺗﻮاﻧﺪ ﺑﺎﺷﺪ و اﮔﺮ اﯾﻦ ﻋﺒﺎرت درﺳﺖ ﺑﺎﺷﺪ expressionدر
ﻟﯿﺴﺖ ﻗﺮار ﻣﯽﮔﯿﺮد.
اﯾﻦ ﻋﺒﺎرت را ﺑﻪﺳﺎدﮔﯽ ﻣﯽﺗﻮان ﺑﺎ ﺣﻠﻘﻪ forﻧﻮﺷﺖ .ﺑﻪ ﺗﺼﻮﯾﺮ 10دﻗﺖ ﮐﻨﯿﺪ و اﯾﻦ دو روش را ﺑﺎ ﻫﻢ
ﻣﻘﺎﯾﺴﻪ ﮐﻨﯿﺪ.
Ahwaz_Hackerz
اﯾﻦ روش ﺧﻮاﻧﺎﯾﯽ ﮐﺪ را اﻓﺰاﯾﺶ داده و ﻧﯿﺎز ﺑﻪ ﮐﺪ ﮐﻤﺘﺮي دارد .در اداﻣﻪ ﺑﺎ ﻣﺜﺎلﻫﺎي ﻣﺘﻨﻮع ﺳﻌﯽ ﺷﺪه
ﺗﺎ ﻃﺮﯾﻘﻪ اﺳﺘﻔﺎده از اﯾﻦ روش ﺑﻪﺧﻮﺑﯽ آﻣﻮزش داده ﺷﻮد.
در اوﻟﯿﻦ ﻣﺜﺎل ﺳﻌﯽ دارﯾﻢ ﻟﯿﺴﺘﯽ درﺳﺖ ﮐﻨﯿﻢ ﮐﻪ در آن 10ﻋﺪد ﺻﻔﺮ ﺑﺎﺷﺪ.
)print(my_list
)print(my_list
)print(my_list
]>> [2.0, 2.4, 2.8, 3.2, 3.6, 4.0, 4.4, 4.8, 5.2, 5.6
Ahwaz_Hackerz
)print(my_list
]'>> ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9
ﻣﯽﺧﻮاﻫﯿﻢ ﻟﯿﺴﺘﯽ ﺑﺴﺎزﯾﻢ ﮐﻪ ﺗﻨﻬﺎ ﮐﻠﻤﺎﺗﯽ ﮐﻪ در ﻟﯿﺴﺖ دﯾﮕﺮي ﺣﺎوي ﺣﺮف sﻫﺴﺘﻨﺪ را در ﺧﻮد
ﺟﺎي دﻫﺪ.
)print(my_list
ﻟﯿﺴﺘﯽ از ﺗﺎﭘﻞﻫﺎي ﻧﺎم و ﺳﻦ وﺟﻮد دارد ،ﻣﯽﺧﻮاﻫﯿﻢ آﻧﻬﺎ را ﺑﺎ ﮐﺎراﮐﺘﺮ – ﺑﻪ ﻫﻢ ﺑﭽﺴﺒﺎﻧﯿﻢ و در
ﻟﯿﺴﺘﯽ ﺟﺪﯾﺪ ﺑﮕﺬارﯾﻢ.
)print(my_list
ﻓﺮض ﮐﻨﯿﺪ ﺗﺎﺑﻌﯽ ﺑﺎ ﻧﺎم diceدارﯾﻢ ﮐﻪ ﺑﻪ ﺻﻮرت ﻣﺠﺎزي ﺗﺎس ﻣﯽاﻧﺪازد .ﻣﯽﺧﻮاﻫﯿﻢ ﻟﯿﺴﺘﯽ درﺳﺖ
ﮐﻨﯿﻢ ﮐﻪ 10ﺑﺎر ﺗﺎس ﺑﯿﺎﻧﺪازد و اﮔﺮ ﻣﻘﺪار ﺗﺎس ﺑﺰرﮔﺘﺮ از 3ﺑﻮد در ﻟﯿﺴﺖ ﻗﺮار دﻫﺪ.
def dice():
)return randint(1, 6
)print(my_list
ﻋﻤﻠﮕﺮ Walrusرا ﺑﻪ ﺧﺎﻃﺮ ﺑﯿﺎورﯾﺪ .در ﺗﻮﺻﯿﻒ ﻟﯿﺴﺖ ﻣﯽﺗﻮان از اﯾﻦ ﻋﺒﺎرت اﺳﺘﻔﺎده ﮐﺮد .در ﻗﺴﻤﺖ
ﺷﺮﻃﯽ ،ﺗﺎﺑﻊ ﺗﺎس را ﻓﺮاﺧﻮاﻧﯽ ﮐﺮدﯾﻢ و ﻣﻘﺪار آن را در ﻣﺘﻐﯿﺮ tempذﺧﯿﺮه ﮐﺮدﯾﻢ و ﺷﺮط آﻧﮑﻪ ﺑﯿﺸﺘﺮ
از 3ﺑﺎﺷﺪ را ﺑﺮرﺳﯽ ﮐﺮدﯾﻢ ،ﺳﭙﺲ ﻣﺘﻐﯿﺮ tempرا در ﻟﯿﺴﺖ ﻗﺮار دادﯾﻢ.
Set Comprehension
ﺳﺎﺧﺖ ﻣﺠﻤﻮﻋﻪﻫﺎ ﺑﻪﺻﻮرت ﺗﮏﺧﻄﯽ دﻗﯿﻘﺎً ﻣﺸﺎﺑﻪ ﺳﺎﺧﺖ ﻟﯿﺴﺖ اﺳﺖ ﺑﺎ اﯾﻦ ﺗﻔﺎوت ﮐﻪ ﺑﻪﺟﺎي دو
ﮐﺎراﮐﺘﺮ [ و ] در اﺑﺘﺪاي آن از { و } اﺳﺘﻔﺎده ﻣﯽﺷﻮد.
در ﻣﺜﺎل زﯾﺮ ﻣﯽﺧﻮاﻫﯿﻢ ﺣﺮوف ﯾﮏ ﺟﻤﻠﻪ را در ﻣﺠﻤﻮﻋﻪاي ﺑﺮﯾﺰﯾﻢ .ﺣﺘﻤﺎً ﺑﻪ ﺧﺎﻃﺮ دارﯾﺪ ﮐﻪ در ﻣﺠﻤﻮﻋﻪ
ﻋﻀﻮ ﺗﮑﺮاري وﺟﻮد ﻧﺪارد.
)print(my_set
>> {'T', 'i', 'n', 'x', 'c', 'h', 'e', 's', 'l', 't', 'p',
}''m', ' ', 'a
))print(len(my_set
>> 14
Ahwaz_Hackerz
Dictionary Comprehension
ﺳﺎﺧﺖ دﯾﮑﺸﻨﺮي ﻫﻤﺎﻧﻨﺪ ﺳﺎﺧﺖ ﻣﺠﻤﻮﻋﻪ اﺳﺖ ﺑﺎ اﯾﻦ ﺗﻔﺎوت ﮐﻪ ﺳﺎﺧﺖ دﯾﮑﺸﻨﺮي ﻧﯿﺎز ﺑﻪ ﮐﻠﯿﺪ و ﻣﻘﺪار
دارد و ﺑﺎﯾﺪ ﺑﻪﺻﻮرت key:valueدر دﯾﮑﺸﻨﺮي ﻗﺮار ﺑﮕﯿﺮد .اﮔﺮ از اﯾﻦ روش ﻧﻮﺷﺘﻦ ،ﯾﮏ ﻋﻨﺼﺮ
ﺑﮕﺬارﯾﻢ ،ﻣﺠﻤﻮﻋﻪ ﺳﺎﺧﺘﻪ ﻣﯽﺷﻮد و اﮔﺮ از ﺗﺮﮐﯿﺐ ﮐﻠﯿﺪ-ﻣﻘﺪار اﺳﺘﻔﺎده ﮐﻨﯿﻢ ،دﯾﮑﺸﻨﺮي ﺗﺤﻮﯾﻞ
ﻣﯽﮔﯿﺮﯾﻢ.
ﻣﯽﺧﻮاﻫﯿﻢ دﯾﮑﺸﻨﺮي ﺑﺴﺎزﯾﻢ ﮐﻪ اﻋﺪاد 1ﺗﺎ 10را ﺑﻪ ﺗﻮان ﺳﻮم آﻧﻬﺎ ﻧﮕﺎﺷﺖ ﮐﻨﺪ.
)print(my_dict
ﻓﺼﻞ2
ژﻧﺮاﺗﻮر
Ahwaz_Hackerz
ژﻧﺮاﺗﻮر ﭼﯿﺴﺖ؟
ﻫﻤﺎنﻃﻮرﮐﻪ در PEP255آﻣﺪه اﺳﺖ ،ژﻧﺮاﺗﻮرﻫﺎ 1ﺑﺮاي ﺳﺎﺧﺖ iteratorاﺳﺘﻔﺎده ﻣﯽﺷﻮﻧﺪ .اﺳﺘﻔﺎده از
ژﻧﺮاﺗﻮرﻫﺎ ﺑﺴﯿﺎر ﺑﻬﯿﻨﻪ اﺳﺖ و ﻣﺼﺮف ﺣﺎﻓﻈﮥ ﮐﻤﯽ دارﻧﺪ .ﺑﺎ ﺳﺎﺧﺖ ژﻧﺮاﺗﻮرﻫﺎ ﻣﯽﺗﻮان ﻣﺠﻤﻮﻋﻪاي ﺳﺎﺧﺖ
ﮐﻪ در ﻫﺮ زﻣﺎن ﺗﻨﻬﺎ ﯾﮏ ﻋﻨﺼﺮ ﺑﺮﻣﯽﮔﺮداﻧﻨﺪ .ژﻧﺮاﺗﻮر ﺗﺎﺑﻌﯽ اﺳﺖ ﮐﻪ ﯾﮏ lazy iteratorﺑﺮﻣﯽﮔﺮداﻧﺪ.
ﻣﯽﺗﻮان روي آن ﺑﺎ forﻫﻤﺎﻧﻨﺪ ﻟﯿﺴﺖ ﭘﯿﻤﺎﯾﺶ ﮐﺮد ،اﻣﺎ ﺑﺮﺧﻼف ﻟﯿﺴﺖ ﻣﺤﺘﻮاي ﺧﻮد را در ﺣﺎﻓﻈﻪ
ﻧﮕﻪ ﻧﻤﯽدارﻧﺪ .ﺣﺎل ﮐﻪ ﯾﮏ دﯾﺪ ﮐﻠﯽ ﻧﺴﺒﺖ ﺑﻪ ژﻧﺮاﺗﻮرﻫﺎ ﭘﯿﺪا ﮐﺮدﯾﺪ ،ﺑﻪ روش اﺳﺘﻔﺎده از آن ﻣﯽﭘﺮدازﯾﻢ.
ﺳﺎﺧﺖ ژﻧﺮاﺗﻮر
ﺳﺎﺧﺖ ژﻧﺮاﺗﻮر ﺑﺴﯿﺎر ﺳﺎده اﺳﺖ ،ﻫﻤﺎﻧﻨﺪ ﺗﺎﺑﻊ ﻣﻌﻤﻮﻟﯽ ﻣﯽﺗﻮاﻧﯿﻢ ژﻧﺮاﺗﻮرﻫﺎي ﺧﻮد را ﺑﻨﻮﯾﺴﯿﻢ وﻟﯽ ﺗﻨﻬﺎ
ﺗﻔﺎوﺗﯽ ﮐﻪ در ﺗﺎﺑﻊ و ژﻧﺮاﺗﻮر وﺟﻮد دارد اﯾﻦ اﺳﺖ ﮐﻪ ﺑﻪﺟﺎي آﻧﮑﻪ ﻣﻘﺪاري را returnﮐﻨﯿﻢ ﺑﺎﯾﺪ آن را
yieldﮐﻨﯿﻢ.
ﺑﻪ ﺑﯿﺎن ﺳﺎدهﺗﺮ ﻣﯽﺗﻮان ژﻧﺮاﺗﻮر را ﺗﺎﺑﻌﯽ ﺗﺼﻮر ﮐﺮد ﮐﻪ ﻫﺮ زﻣﺎن ﺗﺎﺑﻊ nextرا ﺑﺮ روي آن ﺻﺪا ﺑﺰﻧﯿﻢ ﯾﮏ
ﻣﻘﺪار را ﺑﻪ ﻣﺎ ﺑﺮﻣﯽﮔﺮداﻧﺪ و ﺗﺎﺑﻊ ﺗﺎ yieldﺑﻌﺪي ﻣﺘﻮﻗﻒ ﻣﯽﺷﻮد .ﻣﺜﺎﻟﯽ را ﺑﺎ ﻫﻢ ﺑﺒﯿﻨﯿﻢ ﮐﻪ ژﻧﺮاﺗﻮري
را ﻣﯽﺳﺎزﯾﻢ ﮐﻪ ﺳﻪ ﺣﺮف اول اﻟﻔﺒﺎي اﻧﮕﻠﯿﺴﯽ را ﺑﻪ ﺗﺮﺗﯿﺐ ﺑﺮﻣﯽﮔﺮداﻧﺪ.
def three_letter():
"yield "a
"yield "b
"yield "c
)(generator_object = three_letter
))print(next(generator_object
>> a
))print(next(generator_object
>> b
))print(next(generator_object
>> c
))print(next(generator_object
!> StopIteration
1
Generator
Ahwaz_Hackerz
دﻗﺖ ﮐﻨﯿﺪ ﮐﻪ ﺑﺎ ﺻﺪازدن اﯾﻦ ﺗﺎﺑﻊ ﯾﮏ ﺷﯽ ژﻧﺮاﺗﻮر ﺗﻮﻟﯿﺪ ﻣﯽﺷﻮد ﮐﻪ ﺑﺎﯾﺪ از آن اﺳﺘﻔﺎده ﮐﺮد .اﯾﻦ ﯾﮏ
روش ﻫﻮﺷﻤﻨﺪ ﺑﺮاي اﺳﺘﻔﺎده از ژﻧﺮاﺗﻮرﻫﺎ اﺳﺖ زﯾﺮا اﮔﺮ ﺑﻪ اﯾﻦ ﺷﮑﻞ ﻧﺒﻮد ﺑﺮاي ﻫﺮ ژﻧﺮاﺗﻮر ﺑﺎﯾﺪ ﯾﮏ ﺗﺎﺑﻊ
ﺟﺪﯾﺪ ﻣﯽﺳﺎﺧﺘﯿﻢ و ژﻧﺮاﺗﻮرﻫﺎ درواﻗﻊ ﯾﮑﺒﺎر ﻣﺼﺮف ﻣﯽﺑﻮدﻧﺪ .ﺑﺎ ﻫﺮ ﺑﺎر ﺻﺪازدن ﺗﺎﺑﻊ ،nextژﻧﺮاﺗﻮر ﺑﻪ-
دﻧﺒﺎل ﻣﻘﺪار ﺑﻌﺪي ﻣﯽرود و آن را ﺑﺮﻣﯽﮔﺮداﻧﺪ .ﻫﻤﺎنﻃﻮرﮐﻪ ﻗﺒﻼً اﺷﺎره ﺷﺪ ﻣﯽﺗﻮان از ژﻧﺮاﺗﻮرﻫﺎ در
ﺣﻠﻘﻪﻫﺎي forﻧﯿﺰ اﺳﺘﻔﺎده ﮐﺮد.
)(generator_object = three_letter
for i in generator_object:
)print(i
>> a
b
c
ﺑﺮاي درك ﺑﻬﺘﺮ از ﺳﺎزوﮐﺎر ژﻧﺮاﺗﻮر ،ﭼﻨﺪ ﻋﺪد دﺳﺘﻮر printﺑﻪ ﺗﺎﺑﻊ اﺿﺎﻓﻪ ﻣﯽﮐﻨﯿﻢ.
def three_letter():
)"print("before a
"yield "a
)"print("before b
"yield "b
)"print("before c
"yield "c
)(generator_object = three_letter
))print(next(generator_object
))print(next(generator_object
ﻫﻤﺎنﻃﻮرﮐﻪ ﻣﺸﺎﻫﺪه ﻣﯽﮐﻨﯿﺪ ژﻧﺮاﺗﻮر وﻗﺘﯽ ﺻﺪا زده ﻣﯽﺷﻮد ﮐﺎرﻫﺎﯾﯽ را اﻧﺠﺎم ﻣﯽدﻫﺪ ﺗﺎ ﺑﻪ اوﻟﯿﻦ
yieldﺑﺮﺳﺪ ،ﺳﭙﺲ ﻗﻔﻞ ﻣﯽﺷﻮد و ﺑﺎر دﯾﮕﺮ ﮐﻪ ﺻﺪا ﺑﺸﻮد روﻧﺪ ﮐﻠﯽ را ﺗﺎ رﺳﯿﺪن ﺑﻪ yieldﺑﻌﺪي
ﻃﯽ ﻣﯽﮐﻨﺪ.
ژﻧﺮاﺗﻮرﻫﺎ ﺑﺮاي دادهﻫﺎي ﺣﺠﯿﻢ و زﯾﺎدي ﮐﻪ در ﺣﺎﻓﻈﻪ ﺟﺎ ﻧﻤﯽﺷﻮﻧﺪ ﻣﻨﺎﺳﺐ اﺳﺖ .ﺑﯿﺎﯾﯿﺪ ﺑﺎ
sys.getsizeofﻣﻘﺪار ﺣﺎﻓﻈﮥ ﻣﺼﺮﻓﯽ ﯾﮏ ﻟﯿﺴﺖ از اﻋﺪاد 0ﺗﺎ 50ﻣﯿﻠﯿﻮن را اﻧﺪازهﮔﯿﺮي ﮐﻨﯿﻢ.
Ahwaz_Hackerz
import sys
))numbers = list(range(50000000
))print(sys.getsizeof(numbers
>> 200000028
def numbers_gen():
for i in range(50000000):
yield i
)(numbers = numbers_gen
))print(sys.getsizeof(numbers
>> 56
ﻣﻘﺪار ﺣﺎﻓﻈﮥ ﻣﺼﺮﻓﯽ ژﻧﺮاﺗﻮر و ﻟﯿﺴﺖ ﺑﺴﯿﺎر ﺑﺎ ﻫﻢ ﻣﺘﻔﺎوت اﺳﺖ .در ژﻧﺮاﺗﻮر ﻟﯿﺴﺖ ﺑﻪﻃﻮر ﮐﺎﻣﻞ ﺳﺎﺧﺘﻪ
ﻧﻤﯽﺷﻮد ﺑﻠﮑﻪ ﻫﺮﮔﺎه ﻧﯿﺎزي ﺑﻪ ﻋﺪدي ﺑﺎﺷﺪ از ژﻧﺮاﺗﻮر ﺗﻨﻬﺎ ﯾﮏ ﻋﺪد ﮔﺮﻓﺘﻪ ﻣﯽﺷﻮد و ﻧﯿﺎزي ﺑﻪ ﻧﮕﻬﺪاري
ﮐﻞ اﻋﺪاد در ﺣﺎﻓﻈﻪ ﻧﯿﺴﺖ.
ﻣﺜﺎل دﯾﮕﺮي از اﺳﺘﻔﺎده و ﺑﻬﯿﻨﮕﯽ ژﻧﺮاﺗﻮر را ﺑﺮرﺳﯽ ﻣﯽﮐﻨﯿﻢ .ﻓﺮض ﮐﻨﯿﺪ ﻓﺎﯾﻞ sample.txtﺣﺠﻤﯽ
ﺣﺪود 4ﮔﯿﮕﺎﺑﺎﯾﺖ دارد و ﻣﯽﺧﻮاﻫﯿﻢ ﺗﻌﺪاد ﺧﻂﻫﺎي اﯾﻦ ﻓﺎﯾﻞ را ﺑﺸﻤﺎرﯾﻢ .اﮔﺮ از روش ﺧﻮاﻧﺪن و
ﺷﻤﺎرش ﺑﻪ ﺻﻮرت ﻟﯿﺴﺖ اﺿﺎﻓﻪ ﮐﻨﯿﻢ:
def file_reader(file_name):
)(return open(file_name, "r").read().splitlines
)'line_list = file_reader('sample.txt
)line_count = len(line_list
)print(line_count
!> MemoryError
در اﯾﻦ ﺣﺎﻟﺖ ﭼﻮن ﺣﺎﻓﻈﮥ ﮐﺎﻓﯽ ﺑﺮاي ﺟﺎيدادن اﯾﻦ ﻟﯿﺴﺖ وﺟﻮد ﻧﺪارد ،ﺧﻄﺎي MemoryError
درﯾﺎﻓﺖ ﻣﯽﮐﻨﯿﻢ .روش ﺑﻬﺘﺮ اﺳﺘﻔﺎده از ژﻧﺮاﺗﻮر اﺳﺖ ﮐﻪ ﻫﺮ ﺧﻂ را ﺑﻪ ﻣﺎ ﺗﺤﻮﯾﻞ دﻫﺪ و ﻣﺎ ﭘﺲ از
ﺷﻤﺎرش ﺧﻂ ﺑﻌﺪي را درﯾﺎﻓﺖ ﮐﻨﯿﻢ و ﺧﻄﻮط را در ﺣﺎﻓﻈﻪ ﻧﮕﻬﺪاري ﻧﮑﻨﯿﻢ زﯾﺮا ﺑﻪ آن اﺣﺘﯿﺎﺟﯽ ﻧﺪارﯾﻢ و
ﻓﻘﻂ ﯾﮏ ﻣﻘﺪار intدر ﺣﺎﻓﻈﻪ ﻧﮕﻬﺪاري ﻣﯽﮐﻨﯿﻢ.
Ahwaz_Hackerz
def file_reader(file_name):
for line in open(file_name, "r"):
yield line
)'gen_obj = file_reader('sample.txt
line_count = 0
for i in gen_obj:
line_count += 1
)print(line_count
>> 7462966
Generator Expression
ﻫﻤﭽﻮن روش List Comprehensionﺑﺮاي ﺳﺎﺧﺖ ﻟﯿﺴﺖ ،ﻣﯽﺗﻮان ژﻧﺮاﺗﻮرﻫﺎي ﺗﮏﺧﻄﯽ ﻧﯿﺰ ﺳﺎﺧﺖ.
ﻧﻮﺷﺘﻦ آﻧﻬﺎ دﻗﯿﻘﺎً ﻣﺎﻧﻨﺪ ﻟﯿﺴﺖ اﺳﺖ وﻟﯽ ﺑﺎ اﯾﻦ ﺗﻔﺎوت ﮐﻪ ﺑﻪﺟﺎي اﺳﺘﻔﺎده از ﮐﺎراﮐﺘﺮ [ و ] در اﺑﺘﺪا و
اﻧﺘﻬﺎي آن ﺑﺎﯾﺪ از ﭘﺮاﻧﺘﺰ اﺳﺘﻔﺎده ﻧﻤﻮد.
))print(next(numbers
>> 0
))print(next(numbers
>> 2
))print(next(numbers
>> 4
ﺗﺎﺑﻊ send
ژﻧﺮاﺗﻮرﻫﺎ در ﻃﻮل ﺣﯿﺎت ﺧﻮد ﻣﯽﺗﻮاﻧﻨﺪ ﻣﻘﺎدﯾﺮي را از ﺑﯿﺮون درﯾﺎﻓﺖ ﮐﻨﻨﺪ ،اﯾﻦ ﻋﻤﻞ ﺗﻮﺳﻂ ﺗﺎﺑﻊ send
اﻧﺠﺎم ﻣﯽﺷﻮد .ﺑﻪ ﻣﺜﺎل زﯾﺮ دﻗﺖ ﮐﻨﯿﺪ ،ﯾﮏ ژﻧﺮاﺗﻮر دارﯾﻢ ﮐﻪ ﯾﮏ ﺷﻤﺎرﺷﮕﺮ ﻣﻌﮑﻮس اﺳﺖ .ﮐﺎري
ﻣﯽﮐﻨﯿﻢ ﮐﻪ اﯾﻦ ﺷﻤﺎرﺷﮕﺮ ﺑﺎ درﯾﺎﻓﺖ ﻋﺪد ﺟﺪﯾﺪ ﻣﻘﺪار ﺧﻮد را ﺑﻪروزرﺳﺎﻧﯽ ﮐﻨﺪ و ﺷﻤﺎرش را از آن ﻋﺪد
درﯾﺎﻓﺘﯽ اداﻣﻪ دﻫﺪ.
Ahwaz_Hackerz
def counter():
num = 10
while True:
i = yield num
if i is not None:
num = i
continue
num -= 1
)(counter_obj = counter
))print(next(counter_obj
>> 10
))print(next(counter_obj
>> 9
)x = counter_obj.send(20
)print(x
>> 20
))print(next(counter_obj
>> 19
))print(next(counter_obj
>> 18
زﻣﺎﻧﯽﮐﻪ ﺗﺎﺑﻊ sendرا ﻓﺮاﺧﻮاﻧﯽ ﻣﯽﮐﻨﯿﻢ ﻣﻘﺪار ارﺳﺎﻟﯽ ﺑﻪ اوﻟﯿﻦ yieldﻣﯽرﺳﺪ و ﺑﺮﻧﺎﻣﻪ ﺗﺎ رﺳﯿﺪن ﺑﻪ
yieldﺑﻌﺪي اداﻣﻪ ﻣﯽﯾﺎﺑﺪ .در اﯾﻦ ﻣﺜﺎل اﺑﺘﺪا ﻣﻘﺪار 20ارﺳﺎل ﺷﺪه و در ﻣﺘﻐﯿﺮ iﻗﺮار ﮔﺮﻓﺘﻪ اﺳﺖ
)ﺣﺘﻤﺎً ﺑﺎﯾﺪ ﻣﻘﺪار آن ﭼﮏ ﺷﻮد ،زﯾﺮا در ﺣﺎﻟﺖ ﻣﻌﻤﻮﻟﯽ ﮐﻪ nextرا ﺻﺪا ﻣﯽزﻧﯿﻢ ﻣﻘﺪار آن None
اﺳﺖ( ﺳﭙﺲ ﺗﺎﺑﻊ ﺗﺎ ﻣﻘﺪار yieldﺑﻌﺪي ﻣﯽرود و آن را ﺑﺮﻣﯽﮔﺮداﻧﺪ .درواﻗﻊ ،وﻗﺘﯽ ﺗﺎﺑﻊ sendرا ﺻﺪا
ﻣﯽزﻧﯿﻢ اﻧﮕﺎر ﻣﻘﺪاري را ﺑﻪ yieldﻣﯽدﻫﯿﻢ و ﺳﭙﺲ nextرا ﺻﺪا ﻣﯽزﻧﯿﻢ.
ﺗﺎﺑﻊ throw
ﺑﺎ ﺑﻬﺮهﮔﯿﺮي از اﯾﻦ ﺗﺎﺑﻊ ﻣﯽﺗﻮاﻧﯿﻢ در ﻓﺮاﺧﻮاﻧﯽ ﺑﻌﺪي از ﯾﮏ ژﻧﺮاﺗﻮر ،ﯾﮏ Exceptionﺑﺮﮔﺮداﻧﯿﻢ .ﺑﺮاي
ﻣﺜﺎل ﻓﺮض ﮐﻨﯿﺪ ژﻧﺮاﺗﻮري دارﯾﻢ ﮐﻪ اﻧﺪاﺧﺘﻦ ﺗﺎس را ﺷﺒﯿﻪﺳﺎزي ﻣﯽﮐﻨﺪ .ﻣﯽﺧﻮاﻫﯿﻢ اﯾﻦ ژﻧﺮاﺗﻮر ﺗﺎ
زﻣﺎﻧﯽ ﮐﻪ 6ﻧﯿﺎورده اﺳﺖ ،ﻋﺪد ﭼﺎپ ﮐﻨﺪ.
for i in dice_gen:
)print(i
if i == 6:
))"!dice_gen.throw(ValueError("You Got a 6
>> 5
4
3
2
6
!!> ValueError: You Got a 6
ژﻧﺮاﺗﻮر ﻣﺎ ﮐﻪ ﯾﮏ ﺷﺒﯿﻪﺳﺎز ﺗﺎس اﺳﺖ ،در ﺻﻮرت ﻓﺮاﺧﻮاﻧﯽ ،ﯾﮏ ﻋﺪد ﺑﯿﻦ 1ﺗﺎ 6ﺑﺮﻣﯽﮔﺮداﻧﺪ .در اﯾﻦ
ﻣﺜﺎل ﺑﺎ اﺳﺘﻔﺎده از ﯾﮏ ﺣﻠﻘﻪ ،forاز ژﻧﺮاﺗﻮر diceآﻧﻘﺪر اﺳﺘﻔﺎده ﻣﯽﮐﻨﯿﻢ ﺗﺎ ﺑﻪ اوﻟﯿﻦ ﻋﺪد 6ﺑﺮﺳﯿﻢ،
ﺳﭙﺲ ValueErrorرا ﺑﻪﻋﻨﻮان Exceptionﺑﻪ ژﻧﺮاﺗﻮر ﻣﯽدﻫﯿﻢ ،ﺳﭙﺲ در ﻓﺮاﺧﻮاﻧﯽ ﺑﻌﺪي ﮐﻪ در
ﺣﻠﻘﻪ forاﻧﺠﺎم ﻣﯽﺷﻮد ،اﯾﻦ Exceptionرخ ﻣﯽدﻫﺪ.
ﺗﺎﺑﻊ close
ﺗﺎﺑﻊ closeﺑﺮاي اﺗﻤﺎم ﮐﺎر ﯾﮏ ژﻧﺮاﺗﻮر اﺳﺘﻔﺎده ﻣﯽﺷﻮد .ﻓﺮض ﮐﻨﯿﺪ ژﻧﺮاﺗﻮري دارﯾﻢ ﮐﻪ ﺗﻤﺎم اﻋﺪاد را از
ﺻﻔﺮ ﺗﺎ ﺑﯽﻧﻬﺎﯾﺖ ﺑﻪﺗﺮﺗﯿﺐ ﺗﻮﻟﯿﺪ ﻣﯽﮐﻨﺪ .در ﺑﺮﻧﺎﻣﻪاي ﮐﻪ ﻣﺎ ﻧﻮﺷﺘﯿﻢ ﺗﺎ ﻋﺪد 5ﺑﯿﺸﺘﺮ ﻧﻤﯽﺧﻮاﻫﯿﻢ از آن
اﺳﺘﻔﺎده ﮐﻨﯿﻢ ،ﺑﻨﺎﺑﺮاﯾﻦ ﺑﻌﺪ از آﻧﮑﻪ ﻋﺪد 5را درﯾﺎﻓﺖ ﮐﺮدﯾﻢ ،آن را ﻣﯽﺑﻨﺪﯾﻢ.
def numbers():
num = 0
while True:
yield num
num += 1
)(numbers_gen = numbers
for i in numbers_gen:
)print(i
if i == 5:
)(numbers_gen.close
>> 0
1
2
3
4
5
Ahwaz_Hackerz
اﮔﺮ ﺑﺎر دﯾﮕﺮ ﺑﺮاي اﯾﻦ ژﻧﺮاﺗﻮر ﺗﺎﺑﻊ nextرا ﺻﺪا ﺑﺰﻧﯿﻢ StopIterationدرﯾﺎﻓﺖ ﻣﯽﮐﻨﯿﻢ.
)next(numbers_gen
!> StopIteration
در اﺻﻞ ﺳﺎزوﮐﺎر ﺣﻠﻘﻪ forﺑﻪ اﯾﻦ ﺻﻮرت اﺳﺖ ﮐﻪ آﻧﻘﺪر ﺗﺎﺑﻊ nextرا ﺻﺪا ﻣﯽزﻧﺪ ﺗﺎ ﺑﻪ
StopIterationﺑﺮﺳﺪ .ﺗﻮاﺑﻊ forرا ﻣﯽﺗﻮان ﺑﺎ whileﺑﻪ ﺻﻮرت زﯾﺮ ﺷﺒﯿﻪﺳﺎزي ﮐﺮد .اﯾﻦ ﻣﺜﺎل
دﯾﺪ وﺳﯿﻌﯽ ﺑﻪ ﺷﻤﺎ از ﻧﺤﻮة اﺳﺘﻔﺎده از ژﻧﺮاﺗﻮرﻫﺎ next ،و StopIterationﻣﯽدﻫﺪ.
while True:
try:
)i = next(gen
)print(i
except StopIteration:
break
>> 1
4
9
ﮐﺪ ﺳﺎده :ﺑﺎ ﻣﺸﺎﻫﺪه ﻣﺜﺎلﻫﺎي ﺑﺎﻻ ﺣﺘﻤﺎً درﯾﺎﻓﺘﻪاﯾﺪ ﮐﻪ ﻧﻮﺷﺘﻦ و ﺳﺎﺧﺖ ﯾﮏ ژﻧﺮاﺗﻮر ﺑﺴﯿﺎر ﺳﺎده
اﺳﺖ و در ﺑﺴﯿﺎري از ﻣﻮاﻗﻊ ﻣﯽﺗﻮان از آن اﺳﺘﻔﺎده ﮐﺮد.
ﺑﻬﯿﻨﮕﯽ :ژﻧﺮاﺗﻮرﻫﺎ ﺑﻪﺻﻮرت ﺗﻨﺒﻞ ﮐﺎر ﻣﯽﮐﻨﻨﺪ ،ﯾﻌﻨﯽ ﻫﺮﮔﺎه از آﻧﻬﺎ درﺧﻮاﺳﺘﯽ ﺷﻮد ،ﺑﻪدﻧﺒﺎل ﻧﺘﯿﺠﮥ
آن ﻣﯽروﻧﺪ .اﯾﻦ ﻋﻤﻞ ﺑﺎﻋﺚ ﻣﯽﺷﻮد ﺗﺎ در ﻣﺼﺮف ﺣﺎﻓﻈﻪ ﺑﻪﺷﺪت ﺻﺮﻓﻪﺟﻮﯾﯽ ﺷﻮد.
Ahwaz_Hackerz
ﻓﺼﻞ3
Decorator
Ahwaz_Hackerz
ﺗﺎﺑﻊ داﺧﻠﯽ
ﭘﯿﺶ از آﻧﮑﻪ در ﻣﻮرد Decoratorﺻﺤﺒﺖ ﮐﻨﯿﻢ ،ﻧﺨﺴﺖ ﻣﯽﺑﺎﯾﺴﺖ درك درﺳﺘﯽ از ﺗﺎﺑﻊ داﺧﻠﯽ داﺷﺘﻪ
ﺑﺎﺷﯿﻢ .ﺗﺎﺑﻊ داﺧﻠﯽ 1ﺗﺎﺑﻌﯽ اﺳﺖ ﮐﻪ در ﯾﮏ ﺗﺎﺑﻊ دﯾﮕﺮ ﺗﻌﺮﯾﻒ ﺷﺪه و ﻣﯽﺗﻮاﻧﺪ ﻫﻤﺎنﺟﺎ ﺻﺪا زده ﺷﻮد و ﯾﺎ
ﺑﺮﮔﺮداﻧﺪه ﺷﻮد.
def root():
)"print("/
def boot():
)"print("/boot
def proc():
)"print("/proc
)(boot
)(proc
)(root
>> /
/boot
/proc
در ﻣﺜﺎل ﺑﺎﻻ دو ﺗﺎﺑﻊ bootو procدر ﺗﺎﺑﻊ rootﺗﻌﺮﯾﻒ ﺷﺪهاﻧﺪ و در ﻫﻤﺎنﺟﺎ از آﻧﻬﺎ اﺳﺘﻔﺎده ﺷﺪ.
اﮔﺮ ﺑﺨﻮاﻫﯿﻢ ﺗﺎﺑﻊ bootرا در ﺑﯿﺮون از ﺗﺎﺑﻊ rootاﺳﺘﻔﺎده ﮐﻨﯿﻢ ﺑﺎ ﺧﻄﺎ ﻣﻮاﺟﻪ ﻣﯽﺷﻮﯾﻢ.
)(boot
ﺗﺎﺑﻊ اﺻﻠﯽ ﻣﯽﺗﻮاﻧﺪ ﺗﺎﺑﻊﻫﺎي داﺧﻞ ﺧﻮد را ﻧﯿﺰ ﺑﺮﮔﺮداﻧﺪ .ﻓﺮض ﮐﻨﯿﺪ ﺗﺎﺑﻌﯽ ﺑﺎ ﻋﻨﻮان ﺗﺎﺑﻊ ﻋﻤﻠﮕﺮ دارﯾﻢ ﮐﻪ
ﻋﻤﻠﮕﺮ ﻣﺜﺒﺖ ﯾﺎ ﻣﻨﻔﯽ را ﺑﺮﻣﯽﮔﺮداﻧﺪ و ﻣﯽﺗﻮاﻧﯿﻢ از ﺧﻮد ﺗﺎﺑﻊﻫﺎي ﺑﺮﮔﺮداﻧﺪهﺷﺪه اﺳﺘﻔﺎده ﮐﻨﯿﻢ.
def get_operator(name):
1
Inner-function
Ahwaz_Hackerz
else:
)"Exception("Operator Not Found.
)result = get_operator("add")(9,5
)print(result
>> 14
)result = get_operator("sub")(9,5
)print(result
>> 4
در ﻣﺜﺎل ﺑﺎﻻ دﯾﺪﯾﻢ ﮐﻪ ﭼﮕﻮﻧﻪ از ﺗﺎﺑﻊ ﺑﺮﮔﺮداﻧﺪه ﺷﺪه از ﺗﺎﺑﻊ اﺻﻠﯽ اﺳﺘﻔﺎده ﮐﺮدﯾﻢ.
Decoratorﭼﯿﺴﺖ؟
Decoratorﻣﺴﺌﻮل زﯾﻨﺖﺑﺨﺸﯿﺪن ﺑﻪ ﺗﻮاﺑﻊ ﯾﺎ ﮐﻼسﻫﺎ اﺳﺖ Decorator .ﺑﺎ اﺳﺘﻔﺎده از ﺗﺎﺑﻊ
ﺑﺴﺘﻪﺑﻨﺪي 1ﺗﻮاﺑﻊ ﻣﺎ را ﮐﺎدوﭘﯿﭻ ﻣﯽﮐﻨﺪ و زﯾﻨﺖ ﻣﯽﺑﺨﺸﺪ .در اﺑﺘﺪا ﮐﻤﯽ اﯾﻦ ﺗﻌﺮﯾﻒ ﮔﻨﮓ و ﻣﺒﻬﻢ اﺳﺖ
وﻟﯽ در اداﻣﻪ ﺑﺎ ﻣﺜﺎلﻫﺎﯾﯽ در ﻋﻤﻞ اﯾﻦ ﺗﻌﺮﯾﻒ ﺑﺮاﯾﻤﺎن ﺟﺎ ﻣﯽاﻓﺘﺪ.
Decoratorﻫﺎ ﺗﻮاﺑﻊ و ﯾﺎ ﮐﻼسﻫﺎي ﻣﺎ را درﯾﺎﻓﺖ ﮐﺮده و ﻗﺒﻞ و ﺑﻌﺪ آنﻫﺎ دﺳﺘﻮراﺗﯽ اﺿﺎﻓﻪ ﻣﯽﮐﻨﻨﺪ.
ﻣﯽﺗﻮاﻧﻨﺪ ﺧﺮوﺟﯽ و ورودي ﺗﻮاﺑﻊ را دﺳﺖﮐﺎري ﮐﻨﻨﺪ .ﺑﻪﻧﻮﻋﯽ آﻧﻬﺎ ﺗﻮاﺑﻊ ﻣﺎ را ﺑﺎ دﺳﺘﻮرات و
دﺳﺖﮐﺎريﻫﺎي ﺧﻮد ،ﮐﺎدوﭘﯿﭻ ﻣﯽﮐﻨﻨﺪ .ﺗﺼﻮﯾﺮ 11ﻧﻤﺎﯾﯽ ﻣﻔﻬﻮﻣﯽ از Decoratorرا ﻧﺸﺎن ﻣﯽدﻫﺪ.
1
Wrapper
Ahwaz_Hackerz
در اﺑﺘﺪا ﺳﺎدهﺗﺮﯾﻦ Decoratorﻣﻤﮑﻦ را ﻣﯽﺳﺎزﯾﻢ ﮐﻪ ﻗﺒﻞ از اﺟﺮاي ﺗﺎﺑﻊ ﻣﺎ ،ﮐﻠﻤﻪ Helloرا ﭼﺎپ
ﻣﯽﮐﻨﺪ .در اﯾﻦ ﻣﺜﺎل ﻣﺎ ﭼﯿﺰ ﺟﺪﯾﺪي ﯾﺎد ﻧﻤﯽﮔﯿﺮﯾﻢ ﺑﻠﮑﻪ از ﻫﻤﺎن ﻣﻔﺎﻫﯿﻤﯽ ﮐﻪ در اﯾﻦ ﺑﺨﺶ آﻣﻮﺧﺘﯿﻢ،
اﺳﺘﻔﺎده ﺧﻮاﻫﯿﻢ ﮐﺮد.
def hello(func):
def wrapper():
)"print("Hello
)(func
return wrapper
def sample():
)"print("sample
)sample = hello(sample
)(sample
>> Hello
sample
Ahwaz_Hackerz
ﺗﺎﺑﻊ helloﯾﮏ ﺗﺎﺑﻊ ﺑﻪﻋﻨﻮان ورودي درﯾﺎﻓﺖ ﻣﯽﮐﻨﺪ و ﯾﮏ ﺗﺎﺑﻊ داﺧﻠﯽ ﺑﺎ ﻧﺎم wrapperﻧﯿﺰ دارد ﮐﻪ
ﮐﺎر ﺑﺴﺘﻪﺑﻨﺪي ﺗﺎﺑﻊ ورودي را ﺑﺮﻋﻬﺪه دارد .ﺗﺎﺑﻊ wrapperﻗﺒﻞ از اﺟﺮاي ﺗﺎﺑﻊ ورودي ) ،(funcﮐﻠﻤﻪ
Helloرا ﭼﺎپ ﻣﯽﮐﻨﺪ .ﺗﺎﺑﻊ اﺻﻠﯽ ﯾﻌﻨﯽ helloﺗﺎﺑﻊ داﺧﻠﯽ wrapperرا ﺑﺮﻣﯽﮔﺮداﻧﺪ .ﺣﺎل ﺗﺎﺑﻊ
sampleرا ﺑﺎ ) hello(sampleﺟﺎﯾﮕﺰﯾﻦ ﻣﯽﮐﻨﯿﻢ .در اﺻﻞ ﮐﺎري ﮐﻪ در اﯾﻨﺠﺎ اﻧﺠﺎم ﺷﺪ اﯾﻦ ﺑﻮد
ﮐﻪ ﻣﺎ ﺗﺎﺑﻊ اﺻﻠﯽ را ﺑﻪ ﺗﺎﺑﻊ helloدادﯾﻢ و ﺑﺴﺘﻪﺑﻨﺪيﺷﺪه آن را ﺗﺤﻮﯾﻞ ﮔﺮﻓﺘﯿﻢ.
ﺑﻪ زﺑﺎن ﺳﺎده Decorator :ﯾﮏ ﺗﺎﺑﻊ را درﯾﺎﻓﺖ ﻣﯽﮐﻨﺪ ،در آن ﺗﻐﯿﯿﺮاﺗﯽ اﻧﺠﺎم ﻣﯽدﻫﺪ و آن را
ﺑﺮﻣﯽﮔﺮداﻧﺪ.
def hello(func):
def wrapper():
)"print("Hello
)(func
return wrapper
@hello
def sample():
)"print("sample
)(sample
>> Hello
sample
)sample = hello(sample
از Decoratorﻣﯽﺗﻮاﻧﯿﻢ ﭼﻨﺪﯾﻦ ﺑﺎر اﺳﺘﻔﺎده ﮐﻨﯿﻢ و اﯾﻦ ﮐﺎر ﺑﺎﻋﺚ ﺟﻠﻮﮔﯿﺮي از ﻧﻮﺷﺘﻦ ﮐﺪﻫﺎي ﺗﮑﺮاري
ﻣﯽﺷﻮد .ﻣﺜﺎﻟﯽ را ﺑﺎ ﻫﻢ ﺑﺮرﺳﯽ ﻣﯽﮐﻨﯿﻢ و ﺳﭙﺲ ﻣﺸﮑﻼت آن را رﻓﻊ ﻣﯽﮐﻨﯿﻢ .ﻣﯽﺧﻮاﻫﯿﻢ ﯾﮏ
Decoratorﺟﺪﯾﺪ ﺑﺴﺎزﯾﻢ ﮐﻪ ﻫﺮ ﺗﺎﺑﻌﯽ را 3ﺑﺎر اﺟﺮا ﻣﯽﮐﻨﺪ.
Ahwaz_Hackerz
def three_times(func):
def wrapper():
)(func
)(func
)(func
return wrapper
@three_times
def sample():
)"print("sample
)(sample
>> sample
sample
sample
ﺗﺎﺑﻊ wrapperدر اﯾﻦ Decoratorاﯾﻦ وﻇﯿﻔﻪ را دارد ﮐﻪ ﺗﺎﺑﻊ ورودي را ﺳﻪ ﻣﺮﺗﺒﻪ ﻓﺮاﺧﻮاﻧﯽ ﮐﻨﺪ.
ﻫﻤﺎنﻃﻮريﮐﻪ ﻗﺒﻼً ﮔﻔﺘﯿﻢ Decoratorرا ﻣﯽﺗﻮان در ﺗﺎﺑﻊﻫﺎي ﻣﺨﺘﻠﻒ اﺳﺘﻔﺎده ﮐﺮد وﻟﯽ ﺑﺮاي ﺗﺎﺑﻌﯽ ﺑﻪ
ﺷﮑﻞ زﯾﺮ ﭼﻪﮐﺎر ﮐﻨﯿﻢ؟
def say_name(name):
)'}print(f'you are {name
ﻣﺸﮑﻞ از آﻧﺠﺎ ﻧﺎﺷﯽ ﻣﯽﺷﻮد ﮐﻪ ﺗﺎﺑﻊ wrapperﻣﺎ ﻫﯿﭻ آرﮔﻮﻣﺎﻧﯽ درﯾﺎﻓﺖ ﻧﻤﯽﮐﻨﺪ وﻟﯽ ﺗﺎﺑﻊ
say_nameﯾﮏ آرﮔﻮﻣﺎن درﯾﺎﻓﺖ ﻣﯽﮐﻨﺪ ،ﺑﺮاي ﺣﻞ اﯾﻦ ﻣﺸﮑﻞ از *args,**kwargsاﺳﺘﻔﺎده
ﻣﯽﮐﻨﯿﻢ ﺗﺎ ﺑﺘﻮاﻧﯿﻢ ﺗﻤﺎم آرﮔﻮﻣﺎنﻫﺎي ورودي ﻫﺮ ﺗﺎﺑﻌﯽ را ﻣﺪﯾﺮﯾﺖ ﮐﻨﯿﻢ.
ﺗﻮﺟﻪ ﺑﻪ اﯾﻦ ﻧﮑﺘﻪ ﺑﺴﯿﺎر ﻣﻬﻢ اﺳﺖ ﮐﻪ ﭘﺲ از آﻧﮑﻪ از Decoratorاﺳﺘﻔﺎده ﮐﺮدﯾﻢ ﺑﺎﯾﺪ ﺗﻮﺟﻪ ﮐﻨﯿﻢ ﮐﻪ
ﺗﺎﺑﻊ ﻣﺎ ﻫﻤﺎن ﺗﺎﺑﻊ wrapperﻣﯽﺷﻮد و وروديﻫﺎي ﺗﺎﺑﻊ ﻣﺪﻧﻈﺮ ﻣﺎ ﺑﺎﯾﺪ ﺑﺎ وروديﻫﺎي ﺗﺎﺑﻊ wrapper
ﯾﮑﺴﺎن ﺑﺎﺷﺪ.
def three_times(func):
def wrapper(*args, **kwargs):
)func(*args, **kwargs
)func(*args, **kwargs
)func(*args, **kwargs
return wrapper
Ahwaz_Hackerz
@three_times
def say_name(name):
)'}print(f'you are {name
)"say_name("siavash
def border(func):
def wrapper(*args, **kwargs):
)result = func(*args, **kwargs
'}return f'{result:*^20
return wrapper
@border
def add(a, b):
return a + b
))print(add(2,4
**********>> *********6
)print(add
)__print(add.__name
>> wrapper
Ahwaz_Hackerz
ﻫﻤﺎنﻃﻮريﮐﻪ ﻣﯽﺑﯿﻨﯿﺪ اﯾﻦ ﺗﻮاﺑﻊ ﺑﻪﮐﻠﯽ ﺗﻐﯿﯿﺮ ﭘﯿﺪا ﮐﺮدﻧﺪ و ﻫﻮﯾﺖ آﻧﻬﺎ دﻗﯿﻘﺎً ﻫﻤﺎن ﺗﺎﺑﻊ wrapperدر
Decoratorﺷﺪه اﺳﺖ .ﺑﺮاي اﯾﻨﮑﻪ ﻧﺎم و ﻣﺸﺨﺼﺎﺗﺸﺎن ﺗﻐﯿﯿﺮ ﻧﮑﻨـﺪ از ﯾـﮏ Decoratorدﯾﮕـﺮ در اﯾـﻦ
Decoratorاﺳــﺘﻔﺎده ﻣــﯽﮐﻨــﯿﻢ! زﻣــﺎﻧﯽﮐــﻪ @wrapsاز ﻣــﺎژول functoolsرا در ﺑــﺎﻻي ﺗــﺎﺑﻊ
wrapperﻣــﯽﮔــﺬارﯾﻢ ،اﯾــﻦ اﻃﻤﯿﻨــﺎن را ﻣــﯽدﻫــﯿﻢ ﮐــﻪ ﺗــﺎﺑﻊ اﺻــﻠﯽ ﻣﺸﺨﺼــﺎت ﺧــﻮد از ﻗﺒﯿــﻞ
__ __doc__ ،__moduleو __ __nameرا ﺣﻔﻆ ﮐﻨﺪ.
def border(func):
)@wraps(func
def wrapper(*args, **kwargs):
)result = func(*args, **kwargs
'}return f'{result:*^20
return wrapper
@border
def add(a, b):
return a + b
)print(add
)__print(add.__name
>> add
ﺷﺎﯾﺪ ﺑﺮاﯾﺘﺎن ﺳﺆال ﭘﯿﺶ ﺑﯿﺎﯾﺪ ﮐﻪ wrapsﭼﮕﻮﻧﻪ ورودي درﯾﺎﻓﺖ ﻣﯽﮐﻨﺪ .در ﻗﺴﻤﺖ ﺑﻌﺪ درﯾﺎﻓﺖ ورودي
ﺗﻮﺳﻂ Decoratorرا ﻣﻮرد ﺑﺤﺚ ﻗﺮار ﻣﯽدﻫﯿﻢ.
Decoratorﺑﺎ ورودي
Decoratorﺑﺎ ورودي درواﻗﻊ ﺗﺎﺑﻌﯽ اﺳﺖ ﮐﻪ ورودي درﯾﺎﻓﺖ ﮐﺮده و ﯾﮏ Decoratorﺑﺮﻣﯽﮔﺮداﻧﺪ.
ﻣﺜﺎﻟﯽ ﮐﻪ ﺗﺎﺑﻌﯽ را ﺳﻪ ﻣﺮﺗﺒﻪ ﺻﺪا ﻣﯽزد را ﺑﻪ ﺧﺎﻃﺮ ﺑﯿﺎورﯾﺪ .ﻣﯽﺧﻮاﻫﯿﻢ آن را ﺗﻐﯿﯿﺮﭘﺬﯾﺮ ﮐﻨﯿﻢ و ﺑﮕﻮﯾﯿﻢ
ﭼﻨﺪﺑﺎر ﺗﮑﺮار را اﻧﺠﺎم دﻫﺪ .ﭘﺲ ﺑﺎﯾﺪ ﺗﺎﺑﻌﯽ داﺷﺘﻪ ﺑﺎﺷﯿﻢ ﮐﻪ وﻗﺘﯽ آن را ﺻﺪا زدﯾﻢ ﯾﮏ Decorator
ﻣﺜﻞ ﻗﺒﻞ ﺑﻪ ﻣﺎ ﺑﺮﮔﺮداﻧﺪ.
Ahwaz_Hackerz
def retry(n):
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
for _ in range(n):
func(*args, **kwargs)
return wrapper
return decorator
@retry(5)
def test():
print("test")
test()
>> test
test
test
test
test
ﭼﯿﺰي ﺟﺰ ﯾﮏ ﺗﺎﺑﻊ ﻣﻌﻤﻮﻟﯽ ﻧﯿﺴﺖ ﮐﻪ ﺑﺎ درﯾﺎﻓﺖ ﯾﮏ ﭘﺎراﻣﺘﺮretry ﺑﺎ ﮐﻤﯽ دﻗﺖ درﻣﯽﯾﺎﺑﯿﻢ ﮐﻪ ﺗﺎﺑﻊ
. ﺑﺮﻣﯽﮔﺮداﻧﺪDecorator ﯾﮏ
def last_time(func):
@wraps(func)
def wrapper(*args, **kwargs):
wrapper.last_time = datetime.now()
return func(*args, **kwargs)
wrapper.last_time = None
return wrapper
Ahwaz_Hackerz
@last_time
def test():
)"print("test
)(test
>> test
)print(test.last_time
ﺑﺎ ﺗﻌﺮﯾﻒ ﻣﻘﺪار last_timeدر ﺗﺎﺑﻊ wrapperدرواﻗﻊ ﺑﻪ ﺗﺎﺑﻊ testاﯾﻦ ﻣﻘﺪار را اﺿﺎﻓﻪ ﮐﺮدﯾﻢ،
ﮐﻪ ﻫﺮﮔﺎه اﯾﻦ ﺗﺎﺑﻊ ﺻﺪا زده ﺷﺪ ،ﻣﻘﺪار آن ﺑﺎ ﺗﺎرﯾﺦ ﮐﻨﻮﻧﯽ ﺑﻪروزرﺳﺎﻧﯽ ﺷﻮد ﺗﺎ از آن ﺑﻌﺪاً اﺳﺘﻔﺎده ﮐﻨﯿﻢ.
Decoratorدر ﮐﻼس
ﺳﺎﺧﺖ Decoratorﺑﺮاي ﮐﻼسﻫﺎ دﻗﯿﻘﺎً ﻣﺸﺎﺑﻪ آن ﭼﯿﺰي اﺳﺖ ﮐﻪ ﺑﺮاي ﺗﺎﺑﻊ ﻣﯽﺳﺎﺧﺘﯿﻢ ﺑﺎ اﯾﻦ ﺗﻔﺎوت
ﮐﻪ ﺑﻪﺟﺎي ورودي funcاز clsاﺳﺘﻔﺎده ﻣﯽﮐﻨﯿﻢ .اﯾﻦ ﺗﻐﯿﯿﺮ ﻧﺎم ﻓﻘﻂ ﺑﻪ ﺧﺎﻃﺮ آن اﺳﺖ ﮐﻪ ﻣﺘﻮﺟﻪ
ﺷﻮﯾﻢ اﯾﻦ ﯾﮏ Decoratorﺑﺮاي ﮐﻼس اﺳﺖ .در اﯾﻦ ﺣﺎﻟﺖ ﺳﺎﺧﺖ ﯾﮏ ﻧﻤﻮﻧﻪ از ﯾﮏ ﮐﻼس در ﺗﺎﺑﻊ
wrapperاﻧﺠﺎم ﻣﯽﺷﻮد.
def modify_repr(cls):
)@wraps(cls
def wrapper(*args, **kwargs):
class child(cls):
def __repr__(self):
'return f'{cls.__name__.upper()} object
return wrapper
Ahwaz_Hackerz
@modify_repr
class Siavash:
pass
@modify_repr
class Mehran:
pass
print(Siavash())
print(Mehran())
ﻣﻄﺎﻟﻌﮥ ﺑﯿﺸﺘﺮ
. را ﻣﻄﺎﻟﻌﻪ ﮐﻨﯿﺪPEP318 ﻣﯽﺗﻮاﻧﯿﺪDecorator ﺑﺮاي ﻣﻄﺎﻟﻌﻪ درﻣﻮرد ﭘﯿﺶزﻣﯿﻨﻪ
https://github.com/micheles/decorator
: ﻫﺎي ﮐﺎرﺑﺮديDecorator ﻟﯿﺴﺘﯽ از
https://github.com/lord63/awesome-python-decorator
Ahwaz_Hackerz
4ﻓﺼﻞ
Context Manager
Ahwaz_Hackerz
ﻣﻘﺪﻣﻪ
اﯾﻦ ﻓﺼﻞ رﺑﻂ ﺧﺎﺻﯽ ﺑﻪ ﺑﺮﻧﺎﻣﻪﻧﻮﯾﺴﯽ ﺗﺎﺑﻌﯽ ﻧﺪارد وﻟﯽ ﺑﻪ ﻣﺒﺤﺚ ژﻧﺮاﺗﻮرﻫﺎ ﻣﺮﺑﻮط اﺳﺖ و ﻣﯽﺗﻮان
Context Managerرا ﺑﺎ ژﻧﺮاﺗﻮر ﭘﯿﺎدهﺳﺎزي ﮐﺮد .ﻓﺮض ﮐﻨﯿﺪ در ﻣﯿﺎن ﯾﮏ اﻟﮕﻮرﯾﺘﻢ ﭘﯿﭽﯿﺪه ﻧﯿﺎز
دارﯾﺪ ﺗﺎ ﻣﻨﺎﺑﻌﯽ را از ﺟﻤﻠﻪ ﻓﺎﯾﻞ و ﯾﺎ ﭘﺎﯾﮕﺎه داده اﺳﺘﻔﺎده ﮐﻨﯿﺪ و در اﻧﺘﻬﺎ ﺣﻮاﺳﺘﺎن ﺑﺎﺷﺪ ﮐﻪ اﯾﻦ ﻣﻨﺎﺑﻊ را
آزاد ﮐﻨﯿﺪ Context Manager .ﺑﻪ ﻣﺎ ﮐﻤﮏ ﻣﯽﮐﻨﺪ ﺗﺎ از ﺗﮑﺮار ﮐﺪﻫﺎﯾﯽ ﻗﺒﻞ و ﺑﻌﺪ از ﮐﺪ اﺻﻠﯽ
ﺟﻠﻮﮔﯿﺮي ﮐﻨﯿﻢ .ﺑﺮاي ﻣﺜﺎل ﻓﺮض ﮐﻨﯿﺪ ﻣﯽﺧﻮاﻫﯿﻢ ﻓﺎﯾﻠﯽ را ﺑﺎز ﮐﻨﯿﻢ و ﻣﺘﻨﯽ در آن ﻧﻮﺷﺘﻪ و آن را
ﺑﺒﻨﺪﯾﻢ.
اﯾﻦ ﮐﺎر را ﺑﻪراﺣﺘﯽ ﺑﺎ Context Managerﻣﯽﺗﻮاﻧﯿﻢ اﻧﺠﺎم دﻫﯿﻢ .ﺑﺎ دﺳﺘﻮر withﯾﮏ
Context Managerرا ﺑﻪﻋﻨﻮان ﯾﮏ ﻣﺘﻐﯿﺮ درﻧﻈﺮ ﻣﯽﮔﯿﺮﯾﻢ .در اﯾﻦ ﺻﻮرت اﯾﻦ ﻣﺘﻐﯿﺮ ﺗﺎ اﻧﺘﻬﺎي
ﻣﺤﺪوده ﻗﺎﺑﻞ اﺳﺘﻔﺎده اﺳﺖ و وﻗﺘﯽ از ﺑﻼك withﺧﺎرج ﺷﻮﯾﻢ ﺑﻪ ﺻﻮرت ﺧﻮدﮐﺎر ﻓﺎﯾﻞ ﺑﺴﺘﻪ ﻣﯽﺷﻮد.
وﻗﺘﯽ اﯾﻦ ﻣﺜﺎل را ﺑﺎ ﻣﺜﺎل اول ﻣﻘﺎﯾﺴﻪ ﻣﯽﮐﻨﯿﺪ ﻣﺘﻮﺟﻪ ﻣﯽﺷﻮﯾﺪ ﮐﻪ ﻧﯿﺎزي ﺑﻪ ﻧﻮﺷﺘﻦ ﮐﺪﻫﺎي اﺿﺎﻓﻪ
ﻧﯿﺴﺖ .ﺑﺰرگﺗﺮﯾﻦ ﻣﺰﯾﺖ ﻧﻮﺷﺘﻦ ﮐﺪﻫﺎ ﺑﺎ withاﻃﻤﯿﻨﺎن ﺧﺎﻃﺮ از ﺑﺴﺘﻪﺷﺪن ﻓﺎﯾﻞ اﺳﺖ و دﯾﮕﺮ ﻧﮕﺮاﻧﯽ در
ﻣﻮرد ﻓﺮاﻣﻮﺷﯽ از ﺑﺴﺘﻦ ﻣﻨﺎﺑﻊ ﭘﯿﺶ ﻧﻤﯽآﯾﺪ.
class Resource(object):
def __init__(self):
self.number = 1
Ahwaz_Hackerz
def __enter__(self):
)"print("Allocating a recourse ...
return self
در اﺑﺘﺪاي دﺳﺘﻮر withﺗﺎﺑﻊ __ __enterﺻﺪا زده ﻣﯽﺷﻮد و دﺳﺘﻮرات داﺧﻞ آن اﺟﺮا ﻣﯽﺷﻮد و
اﮔﺮ ﻧﯿﺎز ﺑﺎﺷﺪ ﺷﯽ ﯾﺎ ﻣﻘﺪاري ﺑﺮﮔﺮداﻧﺪه ﻣﯽﺷﻮد و ﺑﺎ ﺷﻨﺎﺳﻪاي ﮐﻪ ﺑﻌﺪ از asﻣﯽآﯾﺪ ﻗﺎﺑﻞ اﺳﺘﻔﺎده اﺳﺖ.
زﻣﺎﻧﯽ ﮐﻪ از ﺑﻼك withﺑﻪﻃﻮر ﻋﺎدي و ﯾﺎ ﺑﺎ ﺧﻄﺎ ﺧﺎرج ﺷﻮﯾﻢ ﺗﺎﺑﻊ __ __exitﻓﺮاﺧﻮاﻧﯽ ﻣﯽﺷﻮد و
در اﯾﻦ ﺗﺎﺑﻊ ﺑﺎﯾﺪ دﺳﺘﻮرات آزادﺳﺎزي ﻣﻨﺎﺑﻊ را اﻧﺠﺎم دﻫﯿﻢ .ﺗﺎﺑﻊ __ __exitﺑﻪﺟﺰ selfﺳﻪ آرﮔﻮﻣﺎن
دﯾﮕﺮ ﻧﯿﺰ ﻣﯽﮔﯿﺮد ﮐﻪ درﺻﻮرﺗﯽﮐﻪ ﮐﺪ داﺧﻞ ﺑﻼك withﺑﺎ ﺧﻄﺎ ﻣﻮاﺟﻪ ﺷﻮد اﺳﺘﺜﻨﺎ ﺗﻮﻟﯿﺪﺷﺪه ﺑﻪ اﯾﻦ
ﺗﺎﺑﻊ ﭘﺎس داده ﻣﯽﺷﻮد.
ژﻧﺮاﺗﻮر ﮐﺎر ﺧﻮد را از دﺳﺘﻮر، ﭘﺲ از اﺗﻤﺎم دﺳﺘﻮرات و ﯾﺎ ﺑﺮوز ﺧﻄﺎ. اﻧﺠﺎم ﻣﯽﺷﻮدwith داﺧﻞ ﺑﻼك
. ﺑﻪ ﺑﻌﺪ اﻧﺠﺎم ﻣﯽدﻫﺪyield
@contextmanager
def file_handler(file_name):
f = open(file_name, 'w')
try:
yield f
except:
print("Something went wrong!")
finally:
f.close()
with file_handler("test.txt") as f:
f.write("Test!")
Ahwaz_Hackerz
4ﺑﺨﺶ
ﻻگ
Ahwaz_Hackerz
ﻓﺼﻞ1
ﻣﻘﺪﻣﻪ
Ahwaz_Hackerz
ﻣﻘﺪﻣﻪ
ﻣﺎﻧﻨﺪ ﺑﺴﯿﺎري از زﺑﺎنﻫﺎي ﺑﺮﻧﺎﻣﻪﻧﻮﯾﺴﯽ ،زﺑﺎن ﭘﺎﯾﺘﻮن ﻫﻢ ﻗﺎﺑﻠﯿﺖ ﻻگ 1را دارد و ﻣﺎژول loggingﺟﺰو
ﻣﺎژولﻫﺎي ﺧﻮدﺳﺎﺧﺘﻪ ﭘﺎﯾﺘﻮن ﺑﻮده و ﻣﻨﺎﺳﺐ ذﺧﯿﺮة ﻻگ اﺳﺖ.
در اداﻣﻪ ﻣﯽﺧﻮاﻫﯿﻢ ﺑﻪ ﻣﻔﺎﻫﯿﻢ ﻣﺮﺑﻮط ﺑﻪ ﻻگ ،اﯾﻨﮑﻪ ﭼﺮا ﻻگ ﻣﯽﮐﻨﯿﻢ ،ﭼﻪ ﭼﯿﺰﻫﺎﯾﯽ را ﻻگ ﮐﻨﯿﻢ و
ﻣﻌﺮﻓﯽ ﻣﺎژول loggingﺑﭙﺮدازﯾﻢ .اﮔﺮ ﺷﻤﺎ ﻫﻢ ﺑﺮاي ﻻگاﻧﺪاﺧﺘﻦ از دﺳﺘﻮر printاﺳﺘﻔﺎده ﻣﯽﮐﻨﯿﺪ،
ﭘﺲ ﺣﺘﻤﺎً اﯾﻦ ﺑﺨﺶ را ﻣﻄﺎﻟﻌﻪ ﮐﻨﯿﺪ.
ﻻگ ﭼﯿﺴﺖ؟
در ﺑﺮﻧﺎﻣﻪﻧﻮﯾﺴﯽ ﻻگ ﺑﻪ ﻣﻌﻨﯽ ﺿﺒﻂ دادهﻫﺎي ورودي ،ﻓﺮاﯾﻨﺪﻫﺎي اﺟﺮاﺷﺪه و دادهﻫﺎي ﺧﺮوﺟﯽ اﺳﺖ.
ﺑﺮاي ﻣﺜﺎل ،ﮐﺎرﺑﺮي ﺑﺮﻧﺎﻣﻪ ﻣﺎﺷﯿﻦ ﺣﺴﺎب ﺷﻤﺎ را ﺑﺎز ﮐﺮده و ﻣﯽﺧﻮاﻫﺪ ﻋﺪد 4و 8را ﺟﻤﻊ ﮐﻨﺪ.
دادهﻫﺎﯾﯽ ﮐﻪ ﻻگ ﻣﯽﺷﻮد ﻣﯽﺗﻮاﻧﺪ ﺑﻪ اﯾﻦ ﺻﻮرت ﺑﺎﺷﺪ:
ﮐﺎرﺑﺮ از ﮐﺎﻣﭙﯿﻮﺗﺮ ﺷﻤﺎرة 2در ﺗﺎرﯾﺦ ﻣﺸﺨﺼﯽ وارد ﺳﯿﺴﺘﻢ ﺷﺪه اﺳﺖ.
ﮐﺎرﺑﺮ ﺑﺮﻧﺎﻣﻪ ﻣﺎﺷﯿﻦﺣﺴﺎب را ﺑﺎز ﮐﺮد و ﺑﺮﻧﺎﻣﻪ ﭘﺲ از 200ﻣﯿﻠﯽﺛﺎﻧﯿﻪ ﻧﻤﺎﯾﺶ داده ﺷﺪ.
ﮐﺎرﺑﺮ ﺑﺮ روي ﻋﺪد 4ﮐﻠﯿﮏ ﮐﺮد.
ﮐﺎرﺑﺮ ﺑﺮ روي ﺟﻤﻊ ﮐﻠﯿﮏ ﮐﺮد.
ﮐﺎرﺑﺮ ﺑﺮ روي ﻋﺪد 8ﮐﻠﯿﮏ ﮐﺮد.
ﮐﺎرﺑﺮ ﮐﻠﯿﺪ enterرا زد.
ﺟﻮاب 12ﺑﺮاي ﮐﺎرﺑﺮ ﺑﻪ ﻧﻤﺎﯾﺶ در آﻣﺪ.
ﮐﺎرﺑﺮ ﺑﺮﻧﺎﻣﻪ را ﺑﺴﺖ .ﮐﺪ ﺧﺮوج 0اﺳﺖ.
اﯾﻦ ﺑﺎور اﺷﺘﺒﺎﻫﯽ اﺳﺖ ﮐﻪ ﻓﻘﻂ ﺧﻄﺎﻫﺎي ﺑﺮﻧﺎﻣﻪ ﺟﺰو ﻻگ ﻣﺤﺴﻮب ﻣﯽﺷﻮﻧﺪ ،ﺑﻠﮑﻪ ﺗﻤﺎم دادهﻫﺎي
ورودي ،ﻓﺮاﯾﻨﺪﻫﺎي ﻃﯽﺷﺪه ﺗﻮﺳﻂ ﮐﺎرﺑﺮ ،ﺗﻌﺎﻣﻼت ﮐﺎرﺑﺮ ،ﻧﺘﺎﯾﺞ ﺧﺮوﺟﯽ ،ﺧﻄﺎﻫﺎي درﯾﺎﻓﺖ ﺷﺪه و
دادهﻫﺎي ﺧﺮوﺟﯽ ﻣﯽﺗﻮاﻧﺪ ﺑﺎﺷﺪ .ﭘﺲ ﻻگ را اﯾﻦﮔﻮﻧﻪ ﺗﻌﺮﯾﻒ ﻣﯽﮐﻨﯿﻢ:
»ﻻگ ﺑﻪ ﻓﺮاﯾﻨﺪ ﺿﺒﻂ اﻋﻤﺎل و ﺣﺎﻟﺖﻫﺎي ﺑﺮﻧﺎﻣﻪ در ﯾﮏ راﺑﻂ 2ﺛﺎﻧﻮﯾﻪ ﮔﻔﺘﻪ ﻣﯽﺷﻮد«.
1
Log
2
Interface
Ahwaz_Hackerz
ﭼﺮا ﻻگ ﮐﻨﯿﻢ؟
ﺷﺎﯾﺪ اﯾﻦ ﺳﺆال ﺑﺮاي ﺷﻤﺎ ﻫﻢ ﭘﯿﺶ ﺑﯿﺎﯾﺪ ﮐﻪ اﺻﻼً ﭼﺮا ﺑﺎﯾﺪ از ﻻگ اﺳﺘﻔﺎده ﮐﻨﯿﻢ .ﻋﻤﻠﯿﺎت ﻻگ ﻋﻤﻠﯽ
اﺳﺖ ﮐﻪ ﺑﺎﯾﺪ از روز اول ﺳﺎﺧﺖ ﯾﮏ ﭘﺮوژه ﺑﻪ ﻓﮑﺮ آن ﺑﺎﺷﯿﺪ و آن را وﻇﯿﻔﮥ ﻣﻬﻤﯽ ﺗﻠﻘﯽ ﮐﻨﯿﺪ و ﻧﻪ
وﻇﯿﻔﻪاي وﻗﺖﮔﯿﺮ و ﻧﺎﮐﺎرآﻣﺪ ،اﻣﺎ ﭼﺮا؟
ﻻگﻫﺎ ﺑﻪ ﺗﻮﺳﻌﻪدﻫﻨﺪﮔﺎن ﭼﺸﻢ ﺳﻮﻣﯽ ﻣﯽدﻫﺪ ﺗﺎ ﻣﻮاردي را ﮐﻪ در ﺗﻮﺳﻌﻪ ﺑﻪ آن ﻓﮑﺮ ﻧﮑﺮدهاﻧﺪ را ﻣﺘﻮﺟﻪ
ﺷﻮﻧﺪ .ﻻگﻫﺎ ﻧﻪﺗﻨﻬﺎ ﻣﺸﮑﻼت ﺑﺮﻧﺎﻣﻪ را ﮔﺰارش ﻣﯽﮐﻨﻨﺪ ﺑﻠﮑﻪ اﻃﻼﻋﺎت ﺑﺴﯿﺎر ارزﺷﻤﻨﺪي را ﺑﺮاي ﻣﻄﺎﻟﻌﻪ
و ارزﯾﺎﺑﯽ از ﯾﮏ ﺳﯿﺴﺘﻢ در اﺧﺘﯿﺎر ﻣﺎ ﻗﺮار ﻣﯽدﻫﻨﺪ .ﻻگﻫﺎ اﻃﻼﻋﺎﺗﯽ ﻫﻤﭽﻮن آﻧﺎﻟﯿﺰ ﮐﺴﺐوﮐﺎر،
ﻣﻘﯿﺎسﭘﺬﯾﺮي ﺳﯿﺴﺘﻢ و ﮐﺎراﯾﯽ ﺑﺮﻧﺎﻣﻪ را در اﺧﺘﯿﺎر ﻣﺎ ﻗﺮار ﻣﯽدﻫﻨﺪ .در اداﻣﻪ ﭼﻨﺪﯾﻦ ﻓﺎﯾﺪة ﻻگ را ﺑﺎ ﻫﻢ
ﺑﺮرﺳﯽ ﻣﯽﮐﻨﯿﻢ.
ﺗﺸﺨﯿﺺ رﻓﺘﺎرﻫﺎي ﻏﯿﺮﻣﻨﺘﻈﺮه :ﺑﺴﯿﺎري از رﻓﺘﺎرﻫﺎي ﺑﺮﻧﺎﻣﻪ ﻣﻤﮑﻦ اﺳﺖ از ﭼﺸﻢ ﺗﻮﺳﻌﻪدﻫﻨﺪﮔﺎن
در ﻃﻮل ﺗﻮﺳﻌﻪ دور ﺑﻤﺎﻧﺪ .ﻻگﻫﺎ ﮐﻤﮏ ﻣﯽﮐﻨﻨﺪ ﺗﺎ اﮔﺮ رﻓﺘﺎرﻫﺎي ﻏﯿﺮﻣﻨﺘﻈﺮهاي رخ داد ،ﮔﺰارشﻫﺎي
ﻣﺮﺑﻮط ﺑﻪ آن ،در اﺧﺘﯿﺎر ﺗﻮﺳﻌﻪدﻫﻨﺪﮔﺎن ﺑﺮاي ﺗﻮﻟﯿﺪ ﻣﺠﺪد آن رﻓﺘﺎر ﻗﺮار ﺑﮕﯿﺮد و ﻣﺸﮑﻞزداﯾﯽ از ﺑﺮﻧﺎﻣﻪ
را ﺳﺮﻋﺖ ﺑﺒﺨﺸﺪ.
ﻋﯿﺐﯾﺎﺑﯽ ﺧﻄﺎﻫﺎ :ﻫﻤﺎﻧﻨﺪ ﺗﺼﻮر ﺑﯿﺸﺘﺮ اﻓﺮاد ﻻگﻫﺎ ﺑﺮاي ﺧﻄﺎﯾﺎﺑﯽ و ﻋﯿﺐﯾﺎﺑﯽ ﺳﯿﺴﺘﻢ ﺑﺴﯿﺎر ﮐﺎرآﻣﺪ
و ﻣﻔﯿﺪ ﻫﺴﺘﻨﺪ و ﻣﯽﺗﻮاﻧﻨﺪ ﻣﺸﮑﻞ رخداده ﺷﺪه را ﺑﺎ ﺟﺰﺋﯿﺎت و ﻣﮑﺎن ﮐﺪ و ﺗﺎرﯾﺦ اﯾﺠﺎد را ﮔﺰارش ﮐﻨﻨﺪ.
ﺷﻨﺎﺳﺎﯾﯽ رﻓﺘﺎرﻫﺎي ﻣﺨﺮب :ﻻگﻫﺎ ﻫﻤﭽﻨﯿﻦ ﮐﻤﮏ ﻣﯽﮐﻨﻨﺪ ﺗﺎ رﻓﺘﺎرﻫﺎي ﻣﺨﺮب را ﺷﻨﺎﺳﺎﯾﯽ و
ﻣﺴﺪود ﮐﻨﯿﺪ .ﺑﺮاي ﻣﺜﺎل در ﯾﮏ وب ﺳﺮوﯾﺲ ،ﺷﻤﺎ ﻣﺘﻮﺟﻪ ﻣﯽﺷﻮﯾﺪ ﮐﻪ ﯾﮏ IPﺧﺎص ﻣﺸﻐﻮل ﺧﺮاﺑﮑﺎري
در ﺳﯿﺴﺘﻢ ﺷﻤﺎ اﺳﺖ ،ﭘﺲ آن IPرا در Firewallﺧﻮد ﻣﺴﺪود ﻣﯽﮐﻨﯿﺪ.
ﻣﺒﺎﺣﺚ ﻗﺎﻧﻮﻧﯽ و ﺣﻘﻮﻗﯽ :در ﺑﺮﺧﯽ ﻣﻮارد ﻧﯿﺎز اﺳﺖ ﮐﻪ دادهﻫﺎ ﺗﺎ ﭼﻨﺪﯾﻦ ﺳﺎل و ﯾﺎ ﺗﺎ ﻫﻤﯿﺸﻪ ﺿﺒﻂ
ﺷﻮﻧﺪ ﺗﺎ در ﻣﻮارد ﻧﯿﺎز از ﺳﻤﺖ ﻗﺎﻧﻮن ﻣﻮرد ﺑﺮرﺳﯽ ﻗﺮار ﺑﮕﯿﺮﻧﺪ.
ﺗﺼﻤﯿﻢ ﺑﺮاي ﮐﺎراﯾﯽ و ﻣﻘﯿﺎسﭘﺬﯾﺮي :در ﺑﺮﺧﯽ ﻣﻮارد ﺑﺎ ﻻگﮐﺮدن ﻣﺸﺨﺼﻪﻫﺎي
Non-Functionalﻣﺎﻧﻨﺪ ﮐﺎراﯾﯽ ﻣﯽﺗﻮاﻧﯿﻢ ﺗﺼﻤﯿﻤﯽ ﺑﺮاي ﺑﯿﺸﺘﺮﮐﺮدن ﻣﻨﺎﺑﻊ ﺧﻮد داﺷﺘﻪ ﺑﺎﺷﯿﻢ .ﺑﺮاي
ﻣﺜﺎل در ﻻگﻫﺎي ﺧﻮد ﻣﺘﻮﺟﻪ ﻣﯽﺷﻮﯾﺪ در ﺑﺎزة 9ﺗﺎ 12ﺷﺐ ﺗﺮاﻓﯿﮏ ﻣﺼﺮﻓﯽ ﮐﺎرﺑﺮان زﯾﺎد و ﺳﺮﻋﺖ
ﭘﺎﺳﺦﮔﻮﯾﯽ ﺑﺮﻧﺎﻣﻪ ﮐﻢ ﺷﺪه اﺳﺖ ،ﺑﻨﺎﺑﺮاﯾﻦ در اﯾﻦ ﺳﺎﻋﺎت ﻣﻨﺎﺑﻊ ﺧﻮد را اﻓﺰاﯾﺶ ﻣﯽدﻫﯿﻢ.
Ahwaz_Hackerz
ﻫﺪف از ﻻگ
در ﺣﺎﻟﺖ ﮐﻠﯽ ﻻگﮐﺮدن دو ﻫﺪف ﻣﻬﻢ را ﺑﻪدﻧﺒﺎل دارد.
-1ﻋﯿﺐﯾﺎﺑﯽ :وﻗﺘﯽ روﻧﺪ ﮐﻠﯽ ﺑﺮﻧﺎﻣﻪ را ﻻگ ﻣﯽﮐﻨﯿﻢ و ﺑﺮﻧﺎﻣﻪ دﭼﺎر ﻣﺸﮑﻞ ﻣﯽﺷﻮد ،ﻣﯽﺗﻮاﻧﯿﻢ آن ﺧﻄﺎ
را دوﺑﺎره ﺗﻮﻟﯿﺪ ،ﺑﺮرﺳﯽ و در ﻧﻬﺎﯾﺖ رﻓﻊ ﮐﻨﯿﻢ .ﺑﺪون ﻻگﮐﺮدن اﯾﻦ اﻃﻼﻋﺎت ،ﭼﻨﯿﻦ ﭼﯿﺰي ﻣﻤﮑﻦ
ﻧﯿﺴﺖ .درﺳﺖ اﺳﺖ ﮐﻪ زﻣﺎﻧﯽﮐﻪ ﻣﺎ ﮐﺪي را ﻣﯽﻧﻮﯾﺴﯿﻢ و ﺑﺎ ﺧﻄﺎﯾﯽ ﻣﻮاﺟﻪ ﻣﯽﺷﻮﯾﻢ IDEﻫﺎي اﻣﺮوزه از
ﺟﻤﻠﻪ PyCharmﮐﻪ ﺑﺴﯿﺎر ﻫﻮﺷﻤﻨﺪ ﺷﺪهاﻧﺪ ،ﻻگﻫﺎﯾﯽ را ﺑﺮاي ﻣﺎ ﺑﻪ ﻧﻤﺎﯾﺶ ﻣﯽﮔﺬارﻧﺪ ،اﻣﺎ در
ﻣﺤﯿﻂﻫﺎي اﺳﺘﻘﺮار ﺑﺪﯾﻦ ﺷﮑﻞ ﻧﯿﺴﺖ و ﺑﺎﯾﺪ ﺗﻤﺎم اﯾﻦ ﻣﻮارد ﺑﻪﺧﻮﺑﯽ ﺛﺒﺖ ﺷﻮﻧﺪ .ﻓﺮض ﮐﻨﯿﺪ ﺑﺮﻧﺎﻣﻪاي
دارﯾﺪ ﮐﻪ دﭼﺎر ﻣﺸﮑﻞ ﺷﺪه اﺳﺖ و ﺗﻌﺪادي از ﮐﺎرﺑﺮان در اﺟﺮاي آن ﺑﻪ ﻣﺸﮑﻞ ﺑﺮﻣﯽﺧﻮرﻧﺪ .ﺣﺎل اﮔﺮ
ﻻﮔﯽ ﺛﺒﺖ ﻧﮑﺮده ﺑﺎﺷﯿﺪ ،ﭼﮕﻮﻧﻪ ﻣﯽﺧﻮاﻫﯿﺪ ﺑﻔﻬﻤﯿﺪ ﻣﺸﮑﻞ ﺑﺮﻧﺎﻣﻪ از ﮐﺪام ﻗﺴﻤﺖ ﺑﻮده و ﭼﻪ رﻓﺘﺎر و
ﺣﺎﻻﺗﯽ ﺑﺎﻋﺚ ﺑﻪوﺟﻮدآﻣﺪن آن ﺷﺪه اﺳﺖ؟
-2ﺣﺴﺎﺑﺮﺳﯽ :اﯾﻦ ﻻگﻫﺎ ﺑﺮاي ﻣﻘﺎﺻﺪ ﺗﺠﺎري و ﻗﺎﻧﻮﻧﯽ ﺛﺒﺖ ﻣﯽﺷﻮد .ﺑﺮاي ﻣﺜﺎل در ﯾﮏ ﺳﯿﺴﺘﻢ ﺑﺎﻧﮑﯽ
ﺗﻤﺎم اﻃﻼﻋﺎﺗﯽ ﮐﻪ ﭘﻮﻟﯽ از ﮐﺠﺎ ﺑﻪ ﭼﻪ ﮐﺴﯽ و ﺗﻮﺳﻂ ﮐﺪام ﮐﺎرﻣﻨﺪ ﻣﻨﺘﻘﻞ ﺷﺪه ﺿﺒﻂ و ذﺧﯿﺮه ﻣﯽﺷﻮد.
ﻣﻌﻤﻮﻻً در اﯾﻦ ﻧﻮع ﻻگ ،اﻃﻼﻋﺎﺗﯽ از ﻗﺒﯿﻞ اﯾﻨﮑﻪ ﭼﻪ ﮐﺴﯽ ﭼﻪ ﮐﺎري را در ﭼﻪ زﻣﺎﻧﯽ اﻧﺠﺎم داده ﺛﺒﺖ
ﻣﯽﺷﻮد.
داﻧﺴﺘﻦ ﺗﻔﺎوت اﯾﻦ دو ﺑﻪ اﯾﻦ ﻣﻌﻨﺎ ﻧﯿﺴﺖ ﮐﻪ ﯾﮑﯽ اﺧﺘﯿﺎري و دﯾﮕﺮي ﺿﺮوري اﺳﺖ ،ﺑﻠﮑﻪ ﺑﺎ ﺗﻮﺟﻪ ﺑﻪ
ﻧﯿﺎزﻣﻨﺪي ﻧﺮماﻓﺰار ﺗﺼﻤﯿﻢﮔﯿﺮي ﺻﻮرت ﻣﯽﮔﯿﺮد ﮐﻪ ﺑﺎ ﭼﻪ ﻫﺪﻓﯽ ﻻگ ﻧﻮﺷﺘﻪ ﺷﻮد .ﻻزم ﺑﻪ ذﮐﺮ اﺳﺖ ﮐﻪ
ﻣﻤﮑﻦ اﺳﺖ در ﻧﺮماﻓﺰاري ﻫﺮ دو ﻫﺪف ﻧﯿﺎز ﺑﻪ ﺗﺤﻘﻖ داﺷﺘﻪ ﺑﺎﺷﺪ و ﻣﺎژولﻫﺎي ﻻگ ﺑﻪﺧﻮﺑﯽ از ﭘﺲ
ﺑﺮآوردهﮐﺮدن اﯾﻦ دو ﻫﺪف ﺑﺮﻣﯽآﯾﻨﺪ.
در ﺑﺮﻧﺎﻣﻪ ﺑﻬﺘﺮ اﺳﺖ ﺗﻤﺎم ﺧﻄﺎﻫﺎ را ﺿﺒﻂ ﮐﻨﯿﻢ ،زﯾﺮا ﺧﻄﺎﯾﯽ ﮐﻪ رخ داده ﻣﻤﮑﻦ اﺳﺖ ﺑﻌﺪاً ﻣﺸﮑﻞﺳﺎز
ﺷﻮد و ﺑﻬﺘﺮ اﺳﺖ ﺳﺮﯾﻌﺎً رﻓﻊ ﺷﻮد .داﻧﺴﺘﻦ ﻣﻨﺒﻊ ﺧﻄﺎ ،زﻣﺎن اﺗﻔﺎق ،ﺣﺎﻟﺖﻫﺎي ﺑﺮﻧﺎﻣﻪ ،ﺗﺎﺑﻊ رﺧﺪاد ،ﺧﻂ
Ahwaz_Hackerz
رﺧﺪاد و ﭘﯿﺎم ﺧﻄﺎي آن ﻣﯽﺗﻮاﻧﺪ ﺑﺴﯿﺎر ﻣﻔﯿﺪ ﺑﺎﺷﺪ ،ﭘﺲ ﺑﺴﺘﻪ ﺑﻪ ﻧﯿﺎز ﺧﻮد ﻣﯽﺗﻮاﻧﯿﻢ اﯾﻦ اﻣﮑﺎﻧﺎت را در
ﻻگﻫﺎي ﺧﻮد ﻟﺤﺎظ ﮐﻨﯿﻢ.
ﯾﮑﯽ دﯾﮕﺮ از ﻣﻮاردي ﮐﻪ ﺧﻮب اﺳﺖ آنﻫﺎ را ﻻگ ﮐﻨﯿﻢ ،ارﺗﺒﺎط ﺑﺎ اﺑﺰارﻫﺎي ﺛﺎﻟﺚ اﺳﺖ .ﺑﺮاي ﻣﺜﺎل ﺷﻤﺎ از
ﯾﮏ APIﺗﺸﺨﯿﺺ دﺳﺖﺧﻂ در ﺑﺮﻧﺎﻣﻪ ﺧﻮد اﺳﺘﻔﺎده ﻣﯽﮐﻨﯿﺪ و ﺑﺮﻧﺎﻣﻪ ﺷﻤﺎ ﻣﺪام ﺑﺎ اﯾﻦ APIارﺗﺒﺎط دارد،
ﺑﻨﺎﺑﺮاﯾﻦ ﺿﺒﻂ ﻻگﻫﺎي ارﺗﺒﺎط و ﻧﺘﺎﯾﺞ ارﺗﺒﺎﻃﺎت ﺑﺎ اﯾﻦ APIﻣﯽﺗﻮاﻧﺪ ﺑﺴﯿﺎر ﻣﻔﯿﺪ ﺑﺎﺷﺪ.
از دﯾﺪ ﺑﺎﻻﺗﺮ اﯾﻦ 5ﻣﻮرد را ﻣﯽﺗﻮان در ﻻگﮐﺮدن ﻣﻮرد ﺗﻮﺟﻪ ﻗﺮار داد:
-3دﺳﺘﺮسﭘﺬﯾﺮي :ﺗﻤﺎم اﻃﻼﻋﺎت ﺳﺮور را ﺿﺒﻂ ﮐﻨﯿﻢ .اﯾﻨﮑﻪ در ﭼﻪ زﻣﺎﻧﯽ ﺧﺎﻣﻮش ﺑﻮده ،ﭼﻪ زﻣﺎﻧﯽ
دوﺑﺎره ﮐﺎر ﺧﻮد را ﺷﺮوع ﮐﺮده اﺳﺖ و ﺳﺮوﯾﺲ ﻣﺎ در ﭼﻪ زﻣﺎنﻫﺎﯾﯽ ﭘﺎﺳﺨﮕﻮ ﺑﻮده اﺳﺖ.
-4ﺗﻬﺪﯾﺪﻫﺎ :ﺗﻤﺎم ﺗﻬﺪﯾﺪات از ﺟﻤﻠﻪ ﺗﻬﺪﯾﺪات اﻣﻨﯿﺘﯽ و دادهﻫﺎي ورودي اﺷﺘﺒﺎه را ﻻگ ﮐﻨﯿﻢ.
ﺗﻤﺎم ﻣﻮارد ﮔﻔﺘﻪﺷﺪه ﻧﺒﺎﯾﺪ ﻻگ ﺷﻮد وﻟﯽ اﯾﻦ ﺑﺪان ﻣﻌﻨﺎ ﻧﯿﺴﺖ ﮐﻪ اﮔﺮ ﮐﺎرﺑﺮي وارد ﺳﯿﺴﺘﻢ ﺷﺪ ورود
اﯾﺸﺎن را ﺿﺒﻂ ﻧﮑﻨﯿﻢ ﺑﻠﮑﻪ ﺑﺎﯾﺪ ﺑﺎ ﻣﻮاردي و ﯾﺎ ﺑﺎ ﯾﮏ آﯾﺪي ﺧﺎص آن را ﺿﺒﻂ ﮐﻨﯿﻢ ﮐﻪ اﮔﺮ ﻓﺮد ﻏﯿﺮي
اﯾﻦ اﻃﻼﻋﺎت را ﻣﻄﺎﻟﻌﻪ ﮐﺮد ﻧﺘﻮاﻧﺪ ﮐﺎرﺑﺮ اﺻﻠﯽ را ﭘﯿﺪا ﮐﻨﺪ.
1
Personally Identification Information
Ahwaz_Hackerz
ﻧﮑﺘﮥ دﯾﮕﺮ اﯾﻦ اﺳﺖ ﮐﻪ ﻻگﻫﺎ درواﻗﻊ اﺷﮑﺎلزدا ﻧﯿﺴﺘﻨﺪ و ﺗﻤﺎم اﺗﻔﺎقﻫﺎي رخ داده ﺷﺪه در اﯾﺠﺎد آن
ﺧﻄﺎ را ﺑﻬﺘﺮ اﺳﺖ ﮐﻪ ذﺧﯿﺮه ﻧﮑﻨﻨﺪ و ﻓﺎﯾﻞ ﻻگ را ﺑﯿﺨﻮد ﺷﻠﻮغ ﻧﮑﻨﻨﺪ .ﻫﻤﯿﻦﮐﻪ در ﭼﻪ زﻣﺎﻧﯽ و ﺑﻪ ﭼﻪ
ﺧﺎﻃﺮ اﯾﻦ ﺧﻄﺎ رخ داده اﺳﺖ ،ﮐﺎﻓﯽ اﺳﺖ.
ﻣﻮرد دﯾﮕﺮي ﮐﻪ ﺑﺎﯾﺪ ﺑﻪ آن ﺗﻮﺟﻪ ﮐﺮد اﯾﻦ اﺳﺖ ﮐﻪ ﻧﺒﺎﯾﺪ ﻫﻤﻪﭼﯿﺰ را ﻻگ ﮐﺮد .اﮔﺮ ﺑﺨﻮاﻫﯿﺪ ﻫﻤﻪﭼﯿﺰ را
ﻻگ ﮐﻨﯿﺪ ﺑﻪ اﻣﯿﺪ آﻧﮑﻪ ﺷﺎﯾﺪ روزي ﺑﻪ ﮐﺎرﺗﺎن ﺧﻮاﻫﺪ آﻣﺪ ،ﮐﺎر اﺷﺘﺒﺎﻫﯽ اﺳﺖ زﯾﺮا ﻓﺎﯾﻞ ﻻگ ﻣﻘﺪاري از
ﻻگﻫﺎ را ﻧﮕﻪ ﻣﯽدارد .ﺑﯿﺎﯾﯿﺪ ﻣﺜﺎﻟﯽ را ﺑﺮرﺳﯽ ﮐﻨﯿﻢ ﮐﻪ ﺷﻤﺎ ﺳﺎﯾﺘﯽ دارﯾﺪ ﮐﻪ ﮐﺎرﺑﺮ ﻫﺮ ﺗﻌﺎﻣﻠﯽ در آن
ﻣﯽﮐﻨﺪ را در ﻓﺎﯾﻞ ﻻﮔﯽ ذﺧﯿﺮه ﻣﯽﮐﻨﯿﺪ .ﻣﺜﻼً ﮐﺴﯽ ﮐﻪ ﯾﮏ ﺻﻔﺤﻪ را رﻓﺮش ﻣﯽﮐﻨﺪ ﺷﻤﺎ در ﻓﺎﯾﻞ ﺧﻮد
ﻣﯽﻧﻮﯾﺴﯿﺪ:
ﺷﻤﺎ ﺗﻨﻈﯿﻤﺎت را ﺟﻮري اﻧﺠﺎم دادهاﯾﺪ ﮐﻪ اﯾﻦ ﻓﺎﯾﻞ ﺗﺎ 100ﻣﮕﺎﺑﺎﯾﺖ داده را ﻧﮕﻬﺪاري ﮐﻨﺪ ﺣﺎل ﻓﺮض
ﮐﻨﯿﺪ ﺷﺨﺼﯽ ﻣﺪاوم اﯾﻦ ﺻﻔﺤﻪ را رﻓﺮش ﮐﻨﺪ و ﺷﻤﺎ در ﻓﺎﯾﻞ ﺧﻮد ﻓﻘﻂ ﻻگﻫﺎي اﯾﻦ ﻓﺮد را دارﯾﺪ و
ﻧﻤﯽﺗﻮاﻧﯿﺪ دﯾﮕﺮ ﺑﻪ ﻻگﻫﺎي ﺧﻄﺎﻫﺎي ﺧﻮد دﺳﺘﺮﺳﯽ داﺷﺘﻪ ﺑﺎﺷﯿﺪ .اﯾﻦ ﺳﻨﺎرﯾﻮ ﯾﮑﯽ از ﺣﻤﻼﺗﯽ اﺳﺖ ﮐﻪ
ﻣﻤﮑﻦ اﺳﺖ ﺑﻪ ﺑﺮﻧﺎﻣﻪ ﺷﻤﺎ وارد ﺷﻮد.
ﻧﮑﺘﮥ ﻣﻬﻢ دﯾﮕﺮ آن اﺳﺖ ﮐﻪ ﺗﻤﺎم اﻃﻼﻋﺎت را در ﯾﮏ ﻓﺎﯾﻞ ﻻگ ﻧﮑﻨﯿﺪ ،اﻃﻼﻋﺎت را ﺑﺎ ﺗﻔﮑﯿﮏ ﻧﻮع در
ﻓﺎﯾﻞﻫﺎي ﺟﺪا ذﺧﯿﺮه ﮐﻨﯿﺪ .ﺑﺮاي ﻣﺜﺎل اﻃﻼﻋﺎت ﻋﻤﻠﮑﺮدي ﮐﺎرﺑﺮ ﻣﺜﻞ ﮐﻠﯿﮏﻫﺎ را ﺑﺎ ﻓﺎﯾﻞ ﺧﻄﺎﻫﺎي
ﺳﯿﺴﺘﻢ ﺟﺪا ﮐﻨﯿﺪ .ﺑﻪ اﯾﻦ ﺻﻮرت ﻣﺸﮑﻞ ﻗﺒﻠﯽ را ﺗﺎ ﻗﺴﻤﺘﯽ ﺣﻞ ﻣﯽﮐﻨﯿﺪ و ﺧﻮاﻧﺪن ﻻگﻫﺎ ﺑﺮاي ﺷﻤﺎ
راﺣﺖﺗﺮ ﻣﯽﺷﻮد.
Ahwaz_Hackerz
ﻓﺼﻞ2
ﻣﺎژول Logging
Ahwaz_Hackerz
ﻣﻌﺮﻓﯽ
ﻣﺎژول loggingﯾﮏ اﺑﺰار ﻗﺪرﺗﻤﻨﺪ ﺑﺮاي ﺿﺒﻂ ﻻگﻫﺎ در ﭘﺎﯾﺘﻮن اﺳﺖ ﮐﻪ ﻧﯿﺎزﻫﺎي ﯾﮏ ﺑﺮﻧﺎﻣﻪﻧﻮﯾﺲ
ﻣﺒﺘﺪي ﺗﺎ ﯾﮏ ﺑﺮﻧﺎﻣﻪ ﺑﺰرگ ﺷﺮﮐﺘﯽ را ﺑﺮآورده ﻣﯽﮐﻨﺪ .اﺿﺎﻓﻪﮐﺮدن اﯾﻦ ﻣﺎژول ﺑﺴﯿﺎر ﺳﺎده اﺳﺖ.
import logging
ﺑﻪﻃﻮر ﭘﯿﺶﻓﺮض 5ﺳﻄﺢ ﻣﺘﻔﺎوت ﺑﺎ درﺟﻪ اﻫﻤﯿﺖ ﻣﺨﺘﻠﻒ در ﭘﺎﯾﺘﻮن وﺟﻮد دارد ﮐﻪ ﻫﺮﮐﺪام ﺷﻤﺎرهاي از
ﺟﻬﺖ اﻫﻤﯿﺖ دارﻧﺪ .در ﺟﺪول 5ﺳﻄﺢ اﻫﻤﯿﺖ ﻻگﻫﺎ ﺑﻪ ﺗﺮﺗﯿﺐ از ﺑﺎﻻ ﺑﻪ ﭘﺎﯾﯿﻦ ﻧﻮﺷﺘﻪ ﺷﺪه اﺳﺖ.
ﻻگﻫﺎ ﺑﺮاﺳﺎس ﺳﻄﺤﯽ ﮐﻪ ﺗﻨﻈﯿﻢ ﺷﺪه ﺑﺎﺷﺪ ﻧﻤﺎﯾﺶ داده ﻣﯽﺷﻮﻧﺪ ،ﺑﺮاي ﻣﺜﺎل اﮔﺮ ﺳﻄﺢ INFOرا
اﻧﺘﺨﺎب ﮐﻨﯿﻢ ،از ﻫﻤﺎن ﺳﻄﺢ و ﺳﻄﺢﻫﺎي ﺑﺎﻻﺗﺮ ﯾﻌﻨﯽ ERROR ،WARNINGو CRITICALﻻگ
ﻣﯽﺷﻮد.
اﯾﻦ ﻣﺎژول ﯾﮏ ﻻﮔﺮ 1در اﺧﺘﯿﺎر ﻣﺎ ﻗﺮار ﻣﯽدﻫﺪ ﮐﻪ ﻣﯽﺗﻮاﻧﯿﻢ ﺑﺎ )( getLoggerآن را درﯾﺎﻓﺖ ﮐﻨﯿﻢ.
اﯾﻦ ﺗﺎﺑﻊ ﻓﻘﻂ ﯾﮏ ﻧﻤﻮﻧﻪ از ﻻﮔﺮ را ﺑﺎز ﻣﯽﮔﺮداﻧﺪ؛ ﺑﻨﺎﺑﺮاﯾﻦ ﻧﯿﺎزي ﻧﯿﺴﺖ آن را ﺑﯿﻦ ﺗﻮاﺑﻊ و ﻣﺎژولﻫﺎ ﭘﺎس
دﻫﯿﻢ و ﮐﺎﻓﯿﺴﺖ ﻫﺮﮐﺠﺎ ﺑﻪ آن ﻧﯿﺎز داﺷﺘﯿﻢ ﺗﻨﻬﺎ ﺗﻮﺳﻂ )( getLoggerآن را درﯾﺎﻓﺖ ﮐﻨﯿﻢ.
ﺑﺎ ﻻﮔﺮ ﺗﻤﺎم ﺳﻄﻮح ﺑﺎﻻ را ﻣﯽﺗﻮاﻧﯿﻢ ﻻگ ﮐﻨﯿﻢ .ﺗﻤﺎم ﺗﻮاﺑﻊ ﺑﺮاي ﻻگ ﺑﺎ ﻫﻤﺎن ﻧﺎﻣﯽ ﮐﻪ در ﺟﺪول 5
ﻧﻮﺷﺘﻪ ﺷﺪه ،ﻗﺎﺑﻞ اﺳﺘﻔﺎده اﺳﺖ )ﺑﻪﺟﺰ .(NOTSETﻧﺎم اﯾﻦ ﺗﺎﺑﻊ PEP8را ﻧﻘﺾ ﻣﯽﮐﻨﺪ وﻟﯽ اﯾﻦ ﻧﺎم از
ﻣﺎژول log4jدر زﺑﺎن ﺟﺎوا ﮔﺮﻓﺘﻪ ﺷﺪه و ﻣﯽداﻧﯿﻢ ﻗﻮاﻧﯿﻦ ﻧﺎمﮔﺬاري در ﺟﺎوا ﺑﻪﺻﻮرت CamelCase
اﺳﺖ.
1
logger
Ahwaz_Hackerz
import logging
)(logger = logging.getLogger
)'logger.debug('This is debug
)' logger.info('This is info
)'logger.warning('This is warning
)'logger.error('This is error
)'logger.critical('This is critical
از ﭘﯿﺎمﻫﺎي ﺑﺎﻻ ﺗﻨﻬﺎ 3ﻻگ ﭼﺎپ ﺷﺪ ،زﯾﺮا ﺑﻪﺻﻮرت ﭘﯿﺶﻓﺮض ﺳﻄﺢ ﻻﮔﺮ WARNINGﺗﻨﻈﯿﻢ ﺷﺪه
اﺳﺖ .ﻓﺮﻣﺖ ﭘﯿﺶﻓﺮض ﻧﯿﺰ ﺑﻪ اﯾﻦ ﺻﻮرت اﺳﺖ ﮐﻪ اﺑﺘﺪا ﺳﻄﺢ ﻻگ ﺳﭙﺲ ) rootﻧﺎﻣﯿﺴﺖ ﮐﻪ ﺑﻪ
ﺻﻮرت ﭘﯿﺶﻓﺮض ﺑﻪ ﻻﮔﺮ ﻣﺎ داده ﻣﯽﺷﻮد( و درﻧﻬﺎﯾﺖ ﭘﯿﺎم ﻻگ ﭼﺎپ ﻣﯽﺷﻮد.
:datefmtﻓﺮﻣﺖ ﺗﺎرﯾﺦ را ﻣﺸﺨﺺ ﻣﯽﮐﻨﺪ) .درﻣﻮرد ﻓﺮﻣﺖ ﺗﺎرﯾﺦ ﺑﻪ ﺟﺪول 2ﻣﺮاﺟﻌﻪ ﮐﻨﯿﺪ(.
import logging
logging.basicConfig(filename='test.log', filemode='w',
)level=logging.INFO
)(logger = logging.getLogger
در اﯾﻦ ﻣﺜﺎل ﻓﺎﯾﻠﯽ ﺑﺎ ﻧﺎم test.logﺳﺎﺧﺘﻪ ﻣﯽﺷﻮد ﮐﻪ ﺳﻄﺢ ﻻگ آن INFOاﺳﺖ ،ﯾﻌﻨﯽ ﺧﻮد آن و
ﺳﻄﻮح ﺑﺎﻻﺗﺮ آن ﻻگ ﻣﯽﺷﻮﻧﺪ .ﻓﺎﯾﻞ را ﺑﺎ ﻣﻮد wﺑﺎزﮐﺮدهاﯾﻢ ﯾﻌﻨﯽ ﻫﺮﮔﺎه ﺑﺮﻧﺎﻣﻪ از اول ﺷﺮوع ﺷﻮد ﻓﺎﯾﻞ
ﻻگ ﻗﺒﻠﯽ ﭘﺎك ﻣﯽﺷﻮد.
ﺑﺪﯾﻦﺻﻮرت ﺑﺎﯾﺪ. ﺷﻤﺎره ﺧﻂ و ﭘﯿﺎم ﻻگ را در ﻓﺎﯾﻠﯽ ﭼﺎپ ﮐﻨﯿﻢ، ﻧﺎم ﻓﺎﯾﻞ، ﺳﻄﺢ ﻻگ،ﻣﯽﺧﻮاﻫﯿﻢ ﺗﺎرﯾﺦ
.ﺗﻨﻈﯿﻤﺎت را اﻧﺠﺎم دﻫﯿﻢ
import logging
logging.basicConfig(filename='test.log', format=log_format)
logger = logging.getLogger()
ﺑﺎ اﯾﻦ روش ﻓﺮﻣﺖ ﻻگﻫﺎي ﺧﺮوﺟﯽ را ﺗﻐﯿﯿﺮ ﻣﯽدﻫﯿﻢ اﻣﺎ ﺑﺮاي ﺗﻐﯿﯿﺮ ﻓﺮﻣﺖ ﺗﺎرﯾﺦ ﺑﺎﯾﺪ از ﭘﺎراﻣﺘﺮ
. اﺳﺘﻔﺎده ﮐﻨﯿﻢdatefmt
import logging
date_format = "day:%d%H:%M:%S"
logging.basicConfig(filename='test.log', format=log_format,
datefmt=date_format)
logger = logging.getLogger()
import logging
logging.basicConfig(filename='test.log')
logger = logging.getLogger()
numbers = [1, 2, 3, 4]
try:
numbers[5] += 1
except Exception as e:
logger.error("Exception Occurred", exc_info=True)
اﺳﺘﻔﺎده ﮐﺮد ﮐﻪ ﻋﻤﻠﮑﺮد آن دﻗﯿﻘﺎً ﺑﺎ ﻣﺜﺎل ﻗﺒﻠﯽ ﺑﺮاﺑﺮ اﺳﺖ و ﻧﯿﺎزي ﺑﻪexception ﻣﯽﺗﻮان از ﻣﺘﺪ
. ﻧﯿﺴﺖexc_info=True ﻧﻮﺷﺘﻦ
import logging
logging.basicConfig(filename='test.log')
logger = logging.getLogger()
numbers = [1, 2, 3, 4]
try:
numbers[5] += 1
except Exception as e:
logger.exception("Exception Occurred")
Ahwaz_Hackerz
ﻧﺎم ﻻﮔﺮ
در ﻗﺴﻤﺖ ﻗﺒﻞ دﯾﺪﯾﻢ ﮐﻪ ﻻﮔﺮ ﮐﻼﺳﯽ اﺳﺖ ﮐﻪ ﻣﺎ از ﺷﯽﻫﺎي آن اﺳﺘﻔﺎده ﻣﯽﮐﻨﯿﻢ و ﻣﺘﺪﻫﺎي ﻧﻤﺎﯾﺶ
ﻻگ را ﻓﺮاﺧﻮاﻧﯽ ﻣﯽﮐﻨﯿﻢ .ﻫﻤﺎنﻃﻮريﮐﻪ ﻗﺒﻼً ﻫﻢ ﮔﻔﺘﻪ ﺷﺪ ﺗﺎﺑﻊ )( getLoggerﯾﮏ ﺷﯽ از اﯾﻦ
ﮐﻼس را ﺑﺮﻣﯽﮔﺮداﻧﺪ و ﺧﻮﺑﯽ آن اﯾﻦ اﺳﺖ ﮐﻪ در ﻣﺎژولﻫﺎي ﻣﺨﺘﻠﻒ ﻣﯽﺗﻮان ﺳﺮﯾﻊ از آن اﺳﺘﻔﺎده ﮐﺮد.
اﻣﺎ در ﺑﻌﻀﯽ ﻣﻮاﻗﻊ ﻧﯿﺎز ﺑﻪ ﭼﻨﺪﯾﻦ ﻻﮔﺮ دارﯾﻢ .اﯾﻦ ﺗﺎﺑﻊ ﻣﯽﺗﻮاﻧﺪ ﺑﺎ درﯾﺎﻓﺖ ﻧﺎم ،ﻻﮔﺮ ﻣﺮﺑﻮط ﺑﻪ آن ﻧﺎم را
ﺑﺮﮔﺮداﻧﺪ.
import logging
)'logger = logging.getLogger('our_logger
در اﯾﻦ ﻣﺜﺎل ﯾﮏ ﻻﮔﺮ ﺑﺎ ﻧﺎم our_loggerﺳﺎﺧﺘﯿﻢ و اﮔﺮ در ﻓﺎﯾﻞﻫﺎ و ﻣﺎژولﻫﺎي دﯾﮕﺮ ﺑﻪ اﯾﻦ ﻻﮔﺮ
ﻧﯿﺎز داﺷﺘﯿﻢ ﮐﺎﻓﯿﺴﺖ ﺑﺎ ﻫﻤﯿﻦ ﻧﺎم آن را درﯾﺎﻓﺖ ﮐﻨﯿﻢ .اﮔﺮ ﺑﺮاي ﻻﮔﺮ ﻧﺎم اﻧﺘﺨﺎب ﻧﺸﻮد ،ﻻﮔﺮي ﺑﺎ ﻧﺎم
ﭘﯿﺶﻓﺮض ' 'rootﺳﺎﺧﺘﻪ ﻣﯽﺷﻮد.
ﭘﯿﺸﻨﻬﺎد ﻣﯽﺷﻮد ﮐﻪ در ﺳﺎﺧﺖ ﻻﮔﺮﻫﺎ از ﻧﺎم ﻫﻤﺎن ﻣﺎژوﻟﯽ ﮐﻪ ﻻﮔﺮ در آن ﻗﺮار دارد اﺳﺘﻔﺎده ﺷﻮد،
ﺑﻨﺎﺑﺮاﯾﻦ ﻣﯽﺗﻮان از ﻣﺘﻐﯿﺮ __name__1اﺳﺘﻔﺎده ﮐﺮد؛ زﯾﺮا در اﯾﻦ ﺻﻮرت ﻣﯽﺗﻮاﻧﯿﻢ درﯾﺎﺑﯿﻢ ﮐﻪ ﻻگﻫﺎ
از ﮐﺪام ﻣﺎژول ﮔﺰارش ﻣﯽﺷﻮﻧﺪ.
اﺳﺘﻔﺎده از Handler
زﻣﺎﻧﯽﮐﻪ ﻧﯿﺎز ﺑﻪ اﺳﺘﻔﺎده از ﭼﻨﺪﯾﻦ ﻻﮔﺮ دارﯾﻢ و ﯾﺎ ﻣﯽﺧﻮاﻫﯿﻢ ﻻﮔﺮ ﺧﻮدﻣﺎن را ﺗﻨﻈﯿﻢ ﮐﻨﯿﻢ ،ﭘﺎي
Handlerﯾﺎ ﮐﻨﺘﺮلﮐﻨﻨﺪه ﺑﻪ ﻣﯿﺎن ﺑﺎز ﻣﯽﺷﻮد .ﯾﮏ ﻻﮔﺮ ﻣﯽﺗﻮاﻧﺪ ﭼﻨﺪﯾﻦ ﮐﻨﺘﺮلﮐﻨﻨﺪه داﺷﺘﻪ ﺑﺎﺷﺪ ،در
اداﻣﻪ ﺑﺎ ﭼﻨﺪﯾﻦ ﮐﻨﺘﺮلﮐﻨﻨﺪه آﺷﻨﺎ ﻣﯽﺷﻮﯾﻢ:
logging.StreamHandler
logging.FileHandler
logging.handlers.RotatingFileHandler
__ 1 __nameﯾﮏ ﻣﺘﻐﯿﺮ ﺧﻮدﺳﺎﺧﺘﻪ وﯾﮋه اﺳﺖ ﮐﻪ ﻧﺎم ﻣﺎژول ﺣﺎل ﺣﺎﺿﺮ را در ﺧﻮد ﺣﻔﻆ ﻣﯽﮐﻨﺪ.
Ahwaz_Hackerz
ﻻگﻫﺎ را در ﻓﺎﯾﻞ ذﺧﯿﺮه ﻣﯽﮐﻨﺪ ،ﺑﺎ اﯾﻦ ﺗﻔﺎوت ﮐﻪ اﮔﺮ ﺣﺠﻢ ﻓﺎﯾﻞ از ﯾﮏ ﻣﻘﺪاري ﻓﺮاﺗﺮ رود ،ﻻگﻫﺎي
ﺟﺪﯾﺪ را ﺑﺮ روي ﻻگﻫﺎي ﻗﺪﯾﻢ ﻣﯽﻧﻮﯾﺴﺪ.
logging.handlers.TimeRotatingFileHandler
ﻻگﻫﺎ را در ﻓﺎﯾﻞ ذﺧﯿﺮه ﻣﯽﮐﻨﺪ ،ﺑﺎ اﯾﻦ ﺗﻔﺎوت ﮐﻪ در زﻣﺎن ﻣﻘﺮر ﻓﺎﯾﻞ ﻻگ را ﺗﻌﻮﯾﺾ ﻣﯽﮐﻨﺪ.
logging.handlers.SocketHandler
logging.handlers.SMTPHandler
logging.handlers.HTTPHandler
logging.handlers.SysLogHandler
logging.handlers.NTEventLogHandler
logging.NullHandler
در ﻋﻤﻞ ﻫﯿﭻﮐﺎري ﻧﻤﯽﮐﻨﺪ و ﺑﯿﺸﺘﺮ ﺗﻮﺳﻂ ﺗﻮﺳﻌﻪدﻫﻨﺪﮔﺎن دﯾﮕﺮ در ﮐﺘﺎﺑﺨﺎﻧﻪﻫﺎﯾﺸﺎن اﺳﺘﻔﺎده ﻣﯽﺷﻮد ﺗﺎ
از ﻻﮔﺮ اﺳﺘﻔﺎده ﮐﻨﻨﺪ و ﮐﺴﺎﻧﯽ ﮐﻪ ﻣﯽﺧﻮاﻫﻨﺪ ﻻگ ﻧﻤﺎﯾﺶ دﻫﻨﺪ از ﻻﮔﺮ ﺧﻮد ﺑﻪﺟﺎي آن اﺳﺘﻔﺎده ﮐﻨﻨﺪ.
import logging.handlers
)__logger = logging.getLogger(__name
f_handler = logging.handlers.RotatingFileHandler('test.log',
)maxBytes=5 * 1024, backupCount=4
)(s_handler = logging.StreamHandler
)f_handler.setLevel(logging.WARNING
)s_handler.setLevel(logging.ERROR
Ahwaz_Hackerz
)f_handler.setFormatter(f_format
)s_handler.setFormatter(s_format
)logger.addHandler(f_handler
)logger.addHandler(s_handler
while True:
)"logger.warning("Warning Log
ﻧﺘﯿﺠﻪ اﯾﻦ ﮐﺪ ﭘﺲ از ﻣﺪﺗﯽ 5ﻓﺎﯾﻞ اﺳﺖ و در ﺧﺮوﺟﯽ ﭼﯿﺰي ﭼﺎپ ﻧﻤﯽﺷﻮد ،زﯾﺮا ﮐﻨﺘﺮلﮐﻨﻨﺪه
Streamﺳﻄﺢ ﻻگ logging.ERRORرا دارد ،ﺑﻨﺎﺑﺮاﯾﻦ ﻻگ ﺑﺎ ﺳﻄﺢ Warningرا ﭼﺎپ
ﻧﻤﯽﮐﻨﺪ.
Ahwaz_Hackerz
import logging.handlers
logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG)
s_handler = logging.StreamHandler()
s_format = logging.Formatter('%(levelname)s%(name)s '
'%(message)s')
s_handler.setFormatter(s_format)
logger.addHandler(s_handler)
class SampleFilter(logging.Filter):
def filter(self, record):
if "Second" in record.msg:
return False
return True
logger.addFilter(SampleFilter())
ﺗﺎﺑﻊ ﻓﯿﻠﺘﺮي ﮐﻪ ﭘﯿﺎدهﺳﺎزي ﻣﯽﺷﻮد ﺑﺎﯾﺪ. اﺿﺎﻓﻪ ﮐﺮدﯾﻢ.addFilter در اﯾﻦ ﻣﺜﺎل ﯾﮏ ﻓﯿﻠﺘﺮ ﺑﺎ ﻣﺘﺪ
ﺑﺮﮔﺮداﻧﯿﺪTrue ﺷﺮاﯾﻂ ﻣﻮرد ﻧﻈﺮ را داﺷﺖ ﻣﻘﺪارrecord اﮔﺮ. را ﺑﺮﮔﺮداﻧﺪFalse ﯾﺎTrue ﻣﻘﺪار
record ﻻزم ﺑﻪ ذﮐﺮ اﺳﺖ ﮐﻪ.ﺗﺎ ﻻگ ﻧﻤﺎﯾﺶ داده ﺷﻮد در ﻏﯿﺮاﯾﻦﺻﻮرت ﻻگ ﻧﺎدﯾﺪه ﮔﺮﻓﺘﻪ ﻣﯽﺷﻮد
ﺗﻤﺎم اﻃﻼﻋﺎت وﻗﻮع ﯾﮏ ﻻگ را ﻫﻤﭽﻮنLogRecord . اﺳﺖLogRecord از ﻧﻮعfilter در ﺗﺎﺑﻊ
"Second" در اﯾﻦ ﻣﺜﺎل ﻫﻤﺎنﻃﻮريﮐﻪ دﯾﺪﯾﻢ اﮔﺮ ﮐﻠﻤﻪ. را دارد... وname ،asctime ،levelname
.در ﭘﯿﺎم ﻻگ ﺑﺎﺷﺪ ﻧﺎدﯾﺪه ﮔﺮﻓﺘﻪ ﻣﯽﺷﻮد
Ahwaz_Hackerz
version: 1
formatters:
s_format:
'format: '%(name)s:%(levelname)s %(message)s
f_format:
'format: '%(asctime)s %(message)s
handlers:
s_handler:
class: logging.StreamHandler
level: DEBUG
formatter: s_format
f_handler:
class: logging.FileHandler
level: WARNING
formatter: f_format
filename: test.log
mode: w
loggers:
s_logger:
level: DEBUG
]handlers: [s_handler
f_logger:
level: WARNING
]handlers: [f_handler
اﯾﻦ ﺗﻨﻈﯿﻤﺎت را در ﻓﺎﯾﻠﯽ ﺑﺎ ﻧﺎم config.ymlذﺧﯿﺮه ﻣﯽﮐﻨﯿﻢ .در اﯾﻦ ﻓﺎﯾﻞ دو ﻓﺮﻣﺖ ﻣﺨﺘﻠﻒ ﺑﺎ ﻧﺎمﻫﺎي
s_formatو f_formatﺗﻌﺮﯾﻒ ﮐﺮدهاﯾﻢ و ﺳﭙﺲ ﮐﻨﺘﺮلﮐﻨﻨﺪهﻫﺎ را ﺑﺎ ﻓﺮﻣﺖ ﻧﻈﯿﺮﺷﺎن ﺗﻨﻈﯿﻢ
ﮐﺮدﯾﻢ و درﻧﻬﺎﯾﺖ ﮐﻨﺘﺮلﮐﻨﻨﺪهﻫﺎ را ﺑﻪ ﻻﮔﺮ ﻣﺨﺼﻮص ﺧﻮدﺷﺎن اﻧﺘﺴﺎب ﮐﺮدﯾﻢ .ﺑﺮاي اﺟﺮاي اﯾﻦ ﻓﺎﯾﻞ
ﭘﯿﮑﺮﺑﻨﺪي ،ﻣﯽﺗﻮاﻧﯿﻢ در ﮐﺪ ﭘﺎﯾﺘﻮن ﺧﻮد ﺑﺪﯾﻦﺷﮑﻞ ﻋﻤﻞ ﮐﻨﯿﻢ:
import logging.config
import yaml
logging.config.dictConfig(config)
f_logger = logging.getLogger('f_logger')
s_logger = logging.getLogger('s_logger')
f_logger.warning('Warning Log')
s_logger.warning('Warning Log')
:ﺑﺮاي اﻃﻼﻋﺎت ﺑﯿﺸﺘﺮ و ﻣﺸﺎﻫﺪه ﻣﺴﺘﻨﺪات ﺑﯿﺸﺘﺮ ﻣﯽﺗﻮاﻧﯿﺪ ﺑﻪ آدرس زﯾﺮ ﻣﺮاﺟﻌﻪ ﮐﻨﯿﺪ
https://docs.python.org/3/library/logging.config.html
Ahwaz_Hackerz
ﺑﺨﺶ5
ﺗﺴﺖ
Ahwaz_Hackerz
ﻓﺼﻞ1
ﻣﻘﺪﻣﻪ
Ahwaz_Hackerz
ﻣﻘﺪﻣﻪ
ﺑﻪ ﻓﺮاﯾﻨﺪ آﻧﺎﻟﯿﺰ اﺟﺰاي ﻧﺮماﻓﺰار ﮐﻪ ﻗﺎدر اﺳﺖ ﺗﻔﺎوت ﻣﯿﺎن ﺷﺮاﯾﻂ ﮐﻨﻮﻧﯽ و ﺷﺮاﯾﻂ ﻣﻮرد اﻧﺘﻈﺎر را ﻣﺸﺨﺺ
ﮐﻨﺪ ،ﺗﺴﺖ ﻧﺮماﻓﺰار ﻣﯽﮔﻮﯾﻨﺪ .1ﺑﺮاي ﻣﺜﺎل ﻓﺮض ﮐﻨﯿﺪ ﻣﯽﺧﻮاﻫﯿﻢ ﺑﺮﻧﺎﻣﻪاي ﺳﺎده ﻫﻤﭽﻮن ﻣﺎﺷﯿﻦﺣﺴﺎب
ﺑﻨﻮﯾﺴﯿﻢ .اﻧﺘﻈﺎر ﻣﺎ اﯾﻦ اﺳﺖ ﮐﻪ اﯾﻦ ﻣﺎﺷﯿﻦﺣﺴﺎب ﺟﻤﻊ و ﺗﻔﺮﯾﻖ را ﺑﻪدرﺳﺘﯽ اﻧﺠﺎم دﻫﺪ ،ﺣﺎل ﺑﺎ ﺗﺴﺖ
اﯾﻦ ﻧﺮماﻓﺰار درﻣﯽﯾﺎﺑﯿﻢ ﮐﻪ آﯾﺎ ﻧﺮماﻓﺰار ﻣﻮﺟﻮد ﻗﺎﺑﻠﯿﺖﻫﺎي ﻣﻮرد اﻧﺘﻈﺎر ﻣﺎ ﮐﻪ ﺟﻤﻊ و ﺗﻔﺮﯾﻖ ﺑﻮد را
ﺑﻪدرﺳﺘﯽ اﻧﺠﺎم ﻣﯽدﻫﺪ و ﯾﺎ ﺧﯿﺮ.
اﻫﻤﯿﺖ ﺗﺴﺖ
ﺑﺎ ﺗﺴﺖ ﻧﺮماﻓﺰار ﻣﯽﺗﻮاﻧﯿﻢ ﺑﺎگﻫﺎ و ﻣﺸﮑﻼت ﻧﺮماﻓﺰاري را ﭘﯿﺶ از ﺗﺤﻮﯾﻞ ﻣﺤﺼﻮل ﺷﻨﺎﺳﺎﯾﯽ ،ﮐﺸﻒ و
درﻧﻬﺎﯾﺖ رﻓﻊ ﮐﻨﯿﻢ .ﯾﮏ ﻧﺮماﻓﺰار ﺗﺴﺖﺷﺪه ﻗﺎﺑﻠﯿﺖ اﺗﮑﺎ ،اﻣﻨﯿﺖ و ﮐﺎراﯾﯽ ﺑﺎﻻ را ﺗﻀﻤﯿﻦ ﻣﯽﮐﻨﺪ ﮐﻪ
درﻧﺘﯿﺠﻪ رﺿﺎﯾﺖ ﻣﺸﺘﺮي و ﺻﺮﻓﻪﺟﻮﯾﯽ در زﻣﺎن و ﻫﺰﯾﻨﻪ را ﺑﻪ ﻫﻤﺮاه ﺧﻮاﻫﺪ داﺷﺖ .ﺗﺴﺖ از اﻫﻤﯿﺖ
ﺑﺎﻻﯾﯽ ﺑﺮﺧﻮردار اﺳﺖ زﯾﺮا ﻣﯽﺗﻮاﻧﺪ از ﺧﻄﺮات ﻣﺎﻟﯽ و ﺟﺎﻧﯽ ﺟﻠﻮﮔﯿﺮي ﮐﻨﺪ .ﺑﺮاي ﻣﺜﺎل ﺷﺮﮐﺖ
ﻣﺎﺷﯿﻦﺳﺎزي ﻧﯿﺴﺎن ﻣﺠﺒﻮر ﺑﻪ ﺟﻤﻊآوري ﺑﯿﺶ از 1ﻣﯿﻠﯿﻮن ﺧﻮدروي ﺧﻮد از ﺑﺎزار ﺷﺪ ﮐﻪ دﻟﯿﻞ آن ﻧﻘﺺ
در ﺳﻨﺴﻮر ﮐﯿﺴﮥ ﻫﻮا و ﺷﮑﺴﺖ در ﻧﺮماﻓﺰار ﺑﻮد ﮐﻪ ﭼﻨﺪﯾﻦ ﺗﺼﺎدف را ﻧﯿﺰ ﺑﻪ ﻫﻤﺮاه داﺷﺖ.
ﭘﺎﯾﺪاري ﺳﯿﺴﺘﻢ
ﮐﺎراﯾﯽ ﺳﯿﺴﺘﻢ
ﮐﺎرﮐﺮد ﺳﯿﺴﺘﻢ
ﻗﺎﺑﻠﯿﺖ ﺗﺮﻣﯿﻢ ﺳﯿﺴﺘﻢ ﭘﺲ از ﺷﮑﺴﺖ
ﯾﮑﭙﺎرﭼﮕﯽ ﺳﯿﺴﺘﻢ
اﻣﻨﯿﺖ ﺳﯿﺴﺘﻢ
ﭘﺸﺘﯿﺒﺎنﮔﯿﺮي ﺳﯿﺴﺘﻢ
ﻣﻮارد ﺑﺎﻻ از ﺟﻤﻠﻪ ﻣﻮاردي اﺳﺖ ﮐﻪ ﺑﺎﯾﺪ در ﺗﺴﺖ آﻧﻬﺎ را درﻧﻈﺮ ﺑﮕﯿﺮﯾﻢ.
1
ANSI/IEEE 1059
Ahwaz_Hackerz
ﺟﻌﺒﮥ ﺳﻔﯿﺪ
ﺗﺴﺖ ﺟﻌﺒﮥ ﺳﻔﯿﺪ 1ﮐﻪ ﺑﻪ آن ﺗﺴﺖ ﺟﻌﺒﻪ ﺷﯿﺸﻪاي ،2ﺗﺴﺖ ﺟﻌﺒﻪ ﺷﻔﺎف 3و ﯾﺎ ﺗﺴﺖ ﺳﺎﺧﺘﺎري 4ﻧﯿﺰ
ﻣﯽﮔﻮﯾﻨﺪ ﺑﻪ ﺗﺴﺖ در ﺧﺼﻮص ﺳﺎﺧﺘﺎر ﮐﺪﻫﺎي داﺧﻠﯽ ﻧﺮماﻓﺰار ﻣﯽﭘﺮدازد .در ﺗﺴﺖ ﺟﻌﺒﻪ ﺳﻔﯿﺪ ﺑﻪ داﺧﻞ
ﺳﯿﺴﺘﻢ ﻧﮕﺎه ﻣﯽﮐﻨﯿﻢ و ﺳﻌﯽ دارﯾﻢ آن را ﺗﺴﺖ ﮐﻨﯿﻢ.
ﺟﻌﺒﮥ ﺳﯿﺎه
ﺗﺴﺖ ﺟﻌﺒﮥ ﺳﯿﺎه 5ﮐﻪ ﺑﻪ آن ﺗﺴﺖ رﻓﺘﺎري 6و ﺗﺴﺖ ورودي-ﺧﺮوﺟﯽ ﻧﯿﺰ ﻣﯽﮔﻮﯾﻨﺪ ﮐﻪ ﺣﺎﻟﺘﯽ اﺳﺖ ﮐﻪ ﻣﺎ
ﻧﺮماﻓﺰار را از ﺣﯿﺚ ﮐﺎرﮐﺮد ﻣﻮرد ﺗﺴﺖ ﻗﺮار ﻣﯽدﻫﯿﻢ و از ﺳﺎﺧﺘﺎر ﮐﺪ داﺧﻠﯽ اﻃﻼﻋﯽ ﻧﺪارﯾﻢ.
ﺗﺴﺖ واﺣﺪ
واﺣﺪ درواﻗﻊ ﻣﯽﺗﻮاﻧﺪ ﯾﮏ ﺗﺎﺑﻊ ﮐﻮﭼﮏ ﺗﺎ ﯾﮏ زﯾﺮﺳﯿﺴﺘﻢ ﺑﺎﺷﺪ ،اﻣﺎ ﻣﻌﻤﻮﻻً ﻣﻨﻈﻮر ﻣﺎ ﯾﮏ ﺟﺰ از ﺳﯿﺴﺘﻢ
ﻣﯽﺑﺎﺷﺪ ﮐﻪ ﻣﻌﻤﻮﻻً ﯾﮏ ﺷﯽ و ﯾﺎ ﯾﮏ ﮐﻼس اﺳﺖ .ﺑﺎ ﻧﮕﺎه ﺑﻪ اﯾﻦ ﺟﺰ ﻣﯽﺗﻮاﻧﯿﻢ ﻣﻮارد آزﻣﻮﻧﯽ ﻣﺸﺨﺺ
ﮐﻨﯿﻢ ﮐﻪ ﻋﻤﻠﮑﺮد اﯾﻦ واﺣﺪ را ﺗﺴﺖ ﮐﻨﺪ .ﻣﻮارد آزﻣﻮن ﺣﺎوي ﺗﻌﺪادي ورودي ﻣﻌﺘﺒﺮ و ﻏﯿﺮﻣﻌﺘﺒﺮ اﺳﺖ ﮐﻪ
ﻣﺸﺨﺺ ﻣﯽﮐﻨﺪ ﯾﮏ واﺣﺪ ﺑﻪدرﺳﺘﯽ و ﻫﻤﺎنﻃﻮريﮐﻪ اﻧﺘﻈﺎر ﻣﯽرود ،ﻋﻤﻞ ﮐﻨﺪ .روﯾﮑﺮد ﻣﺎ در ﺗﺴﺖ
واﺣﺪ 7ﺗﺴﺖ ﺟﻌﺒﮥ ﺳﻔﯿﺪ اﺳﺖ .ﺗﺴﺖ واﺣﺪ ﻣﻌﻤﻮﻻً ﻗﺒﻞ از ارﺳﺎل ﮐﺪﻫﺎ ﺑﻪ ﻣﺨﺰن ﮐﺪ اﺟﺮا ﻣﯽﺷﻮد .ﯾﮑﯽ
از اﻫﺪاف ﺗﺴﺖ واﺣﺪ ،ﮔﺮﻓﺘﻦ ﭘﻮﺷﺶ 100درﺻﺪي ﮐﺪ اﺳﺖ ﯾﻌﻨﯽ ﭘﺲ از اﺟﺮاي ﻣﻮارد آزﻣﻮن ﺗﻤﺎم
ﻗﺴﻤﺖﻫﺎي ﮐﺪ اﺟﺮا ﺷﻮد .ﺑﺮاي روﺷﻦﺗﺮﺷﺪن ﻣﺒﺤﺚ ﺑﻪ ﮐﺪ ﺳﺎده زﯾﺮ ﺗﻮﺟﻪ ﮐﻨﯿﺪ.
1
White box
2
Glass box
3
Clear box
4
Structural
5
Black box
6
Behavioral
7
Unit test
Ahwaz_Hackerz
در ﮐﺪ ﺑﺎﻻ ﺑﺮاي اﯾﻨﮑﻪ ﭘﻮﺷﺶ ﺻﺪدرﺻﺪي ﻓﺮاﻫﻢ ﺷﻮد ﺑﺎﯾﺪ ﯾﮏ ﺑﺎر ﻣﺘﻐﯿﺮ xرا ﮐﻤﺘﺮ از 10و ﯾﮏ ﺑﺎر
ﺑﯿﺸﺘﺮ از 10اﺧﺘﯿﺎر ﮐﻨﯿﻢ ﺗﺎ ﺗﻤﺎم ﻗﺴﻤﺖﻫﺎي ﮐﺪ ﺣﺪاﻗﻞ ﯾﮏﺑﺎر اﺟﺮا ﺷﻮﻧﺪ.
ﺗﺴﺖ ﯾﮑﭙﺎرﭼﻪﺳﺎزي
ﺗﺴﺖ ﯾﮑﭙﺎرﭼﻪﺳﺎزي 1ﺑﻌﺪ از ﺗﺴﺖ واﺣﺪ اﻧﺠﺎم ﻣﯽﺷﻮد و زﻣﺎﻧﯽ اﺳﺖ ﮐﻪ ﻣﯽﺧﻮاﻫﯿﻢ ﭼﻨﺪﯾﻦ واﺣﺪ و ﯾﺎ
ﻣﺎژول را در ﮐﻨﺎر ﻫﻢ ﺗﺴﺖ ﮐﻨﯿﻢ ﺗﺎ از ﻫﻤﺎﻫﻨﮕﯽ ﺑﯿﻦ آﻧﻬﺎ ﻣﻄﻤﺌﻦ ﺷﻮﯾﻢ .درواﻗﻊ ،ﺗﺴﺖ ﯾﮑﭙﺎرﭼﻪﺳﺎزي
ﺑﺮاي آن اﺳﺖ ﮐﻪ ﺗﻌﺎﻣﻞ ﺑﯿﻦ واﺣﺪﻫﺎي ﻣﺨﺘﻠﻒ را در ﯾﮏ ﺳﯿﺴﺘﻢ ﺗﺴﺖ ﮐﺮده و از ﺑﺮآوردهﺷﺪن
ﻧﯿﺎزﻣﻨﺪيﻫﺎي ﺳﯿﺴﺘﻢ اﻃﻤﯿﻨﺎن ﺣﺎﺻﻞ ﮐﻨﯿﻢ.
ﺗﺴﺖ ﺳﯿﺴﺘﻢ
ﻫﺪف ﺗﺴﺖ ﺳﯿﺴﺘﻢ 2اﻋﺘﺒﺎر ﺳﻨﺠﯽ ﺑﯿﻦ ﺗﻤﺎم ﻣﺎژولﻫﺎ ،واﺣﺪﻫﺎ ،دادهﻫﺎ ،ﭘﯿﮑﺮﺑﻨﺪيﻫﺎ ﻣﯽﺑﺎﺷﺪ ﮐﻪ
ﻧﯿﺎزﻣﻨﺪيﻫﺎي ﮐﻞ ﺳﯿﺴﺘﻢ را ﺑﺮآورده ﮐﻨﺪ .اﯾﻦ ﺗﺴﺖ درواﻗﻊ ﯾﮏ ﺗﺴﺖ ﺟﻌﺒﮥ ﺳﯿﺎه اﺳﺖ و اﯾﻦ اﻃﻤﯿﻨﺎن
را ﻣﯽدﻫﺪ ﮐﻪ ﻧﺮماﻓﺰار ﺑﺮ روي ﺳﯿﺴﺘﻢﻫﺎي ﻣﻘﺼﺪ ﺑﻪدرﺳﺘﯽ و ﻫﻤﺎنﮔﻮﻧﻪ ﮐﻪ اﻧﺘﻈﺎر ﻣﯽرود ﮐﺎر ﮐﻨﺪ.
ﺗﺴﺖ ﭘﺬﯾﺮش
ﺗﺴﺖ ﭘﺬﯾﺮش 3ﺑﻪ ﻧﯿﺎزﻣﻨﺪيﻫﺎ و ﻓﺮاﯾﻨﺪﻫﺎي ﮐﺴﺐوﮐﺎري ﻣﺮﺑﻮط اﺳﺖ و ﻣﺸﺨﺺ ﻣﯽﮐﻨﺪ ﮐﻪ آﯾﺎ ﺳﯿﺴﺘﻢ
ﻣﯽﺗﻮاﻧﺪ ﻣﻌﯿﺎرﻫﺎي ﭘﺬﯾﺮش ﺗﻮﺳﻂ ﮐﺎرﺑﺮ ﻧﻬﺎﯾﯽ ،ﻣﺸﺘﺮﯾﺎن و ذﯾﻨﻔﻌﺎن را ﺑﺮآورده ﮐﻨﺪ ﯾﺎ ﺧﯿﺮ .ﺗﺴﺖﻫﺎﯾﯽ از
ﻗﺒﯿﻞ ﺗﺴﺖ آﻟﻔﺎ ،ﺗﺴﺖ ﺑﺘﺎ و ﺗﺴﺖ A/Bاز ﺟﻤﻠﻪ ﺗﺴﺖﻫﺎي ﭘﺬﯾﺮش ﻫﺴﺘﻨﺪ.
1
Integration test
2
System test
3
Acceptance test
Ahwaz_Hackerz
2ﻓﺼﻞ
PyTest ﻣﺎژول
Ahwaz_Hackerz
ﻣﻌﺮﻓﯽ
PyTestﯾﮑﯽ از ﮐﺘﺎﺑﺨﺎﻧﻪﻫﺎي ﻣﻌﺮوف و ﻣﺤﺒﻮب ﭘﺎﯾﺘﻮن اﺳﺖ ﮐﻪ ﻣﯽﺗﻮان ﺳﻄﻮح ﻣﺨﺘﻠﻒ ﺗﺴﺖ را ﺑﺎ آن
اﻧﺠﺎم داد .از اﯾﻦ ﮐﺘﺎﺑﺨﺎﻧﻪ ﺷﺮﮐﺖﻫﺎي ﺑﺰرﮔﯽ ﻫﻤﭽﻮن Dropbox ،Spotifyو Mozillaﺑﻪ ﺟﻬﺖ ﺗﺴﺖ
اﺳﺘﻔﺎده ﻣﯽﮐﻨﻨﺪ .اﯾﻦ ﮐﺘﺎﺑﺨﺎﻧﻪ ﺑﻪراﺣﺘﯽ ﺑﺎ IDEﻫﺎي ﻣﺤﺒﻮب ﻫﻤﭽﻮن PyCharmﺑﻪﺧﻮﺑﯽ ﻫﻤﺎﻫﻨﮓ
ﻣﯽﺷﻮد و ﺑﺎ اﻧﻌﻄﺎفﭘﺬﯾﺮي ﺑﺎﻻ وﯾﮋﮔﯽﻫﺎي ﺑﺴﯿﺎري را ﺑﺮاي ﺗﻮﺳﻌﻪدﻫﻨﺪﮔﺎن ﺑﻪ ارﻣﻐﺎن ﻣﯽآورد .ﭘﺎﯾﺘﻮن
ﻫﻤﺮاه ﺑﺎ ﯾﮏ ﻣﺎژول ﺗﺴﺖ ﺑﺎ ﻧﺎم unittestاراﺋﻪ ﻣﯽﺷﻮد ﮐﻪ ﺗﻨﻬﺎ ﺑﺮاي ﺗﺴﺖ واﺣﺪ ﻣﯽﺗﻮان از آن اﺳﺘﻔﺎده
ﮐﺮد.
ﺑﺮاي اﺳﺘﻔﺎده از PyTestاﺑﺘﺪا ﺑﺎﯾﺪ آن را ﻧﺼﺐ ﮐﺮد .ﺑﺎ اﺳﺘﻔﺎده از ﻣﺪﯾﺮﯾﺖ ﮐﻨﻨﺪة pipﻫﻤﺎﻧﻨﺪ دﺳﺘﻮر زﯾﺮ
ﻣﯽﺗﻮاﻧﯿﻢ آن را ﻧﺼﺐ ﮐﻨﯿﻢ.
اﺳﺘﻔﺎده از PyTest
ﺑﺮاي ﺷﺮوع ﯾﺎدﮔﯿﺮي ﻧﺤﻮة اﺳﺘﻔﺎده از اﯾﻦ ﻣﺎژول اﺑﺘﺪا ﮐﻼس ﺳﺒﺪ ﺧﺮﯾﺪ را در ﻓﺎﯾﻠﯽ ﺑﺎ ﻧﺎم cart.py
ﻣﯽﻧﻮﯾﺴﯿﻢ و ﺳﭙﺲ آن را ﺗﺴﺖ ﻣﯽﮐﻨﯿﻢ.
class Cart:
def __len__(self):
)return len(self.items
ﺑﺮاي ﻧﻮﺷﺘﻦ ﻣﻮارد آزﻣﻮن ،ﯾﮏ ﻓﺎﯾﻞ ﺟﺪﯾﺪ ﺑﺎ ﻧﺎم cart_test.pyﻣﯽﺳﺎزﯾﻢ و ﮐﻼس Cartرا در
آن importﻣﯽﮐﻨﯿﻢ .ﯾﮑﯽ از روشﻫﺎﯾﯽ ﮐﻪ PyTestﻓﺎﯾﻞﻫﺎي ﺗﺴﺖ را ﭘﯿﺪا ﻣﯽﮐﻨﺪ ﻫﻤﯿﻦ ﻧﺎمﮔﺬاري
آﻧﻬﺎ اﺳﺖ .اﯾﻦ ﻣﺎژول ﻓﺎﯾﻞﻫﺎﯾﯽ ﮐﻪ ﺑﺎ _ testﺷﺮوع و ﯾﺎ ﺑﺎ _test.pyﺧﺎﺗﻤﻪ ﻣﯽﯾﺎﺑﻨﺪ را ﻣﻮرد
ﺑﺮرﺳﯽ ﻗﺮار ﻣﯽدﻫﺪ.
Ahwaz_Hackerz
ﭘﯿﺶ از ﻧﻮﺷﺘﻦ ﺗﺴﺖﻫﺎ ﻧﯿﺎز اﺳﺖ ﺗﺎ ﺑﺎ دﺳﺘﻮر assertآﺷﻨﺎ ﺷﻮﯾﻢ ،اﯾﻦ دﺳﺘﻮر ﯾﮏ ﻋﺒﺎرت ﺑﻮﻟﯿﻦ را
ﻣﯽﮔﯿﺮد و اﮔﺮ آن ﻋﺒﺎرت ﻣﻘﺪار Falseداﺷﺘﻪ ﺑﺎﺷﺪ ،اﺳﺘﺜﻨﺎي AssertionErrorرا ﺑﺮﻣﯽﮔﺮداﻧﺪ.
ﺑﺮاي ﻣﺜﺎل ﻣﯽﺧﻮاﻫﯿﻢ ﺑﺮرﺳﯽ ﮐﻨﯿﻢ ﮐﻪ ﺟﻤﻊ اﻋﺪاد ﯾﮏ ﻟﯿﺴﺖ ﮐﻤﺘﺮ از 10اﺳﺖ و آن را ﺑﺎ assert
ﻣﯽﻧﻮﯾﺴﯿﻢ ﺗﺎ رﻓﺘﺎر اﯾﻦ دﺳﺘﻮر را ﻣﺸﺎﻫﺪه ﮐﻨﯿﻢ .در اﺑﺘﺪا ﻟﯿﺴﺘﯽ را ﻣﯽدﻫﯿﻢ ﮐﻪ اﯾﻦ ﺷﺮاﯾﻂ را داﺷﺘﻪ
ﺑﺎﺷﯿﺪ.
ﭘﺲ از اﺟﺮاي ﮐﺪ ﺑﺎﻻ ﻫﯿﭻ اﺗﻔﺎﻗﯽ ﻧﻤﯽاﻓﺘﺪ و ﺑﺮﻧﺎﻣﻪ ﭘﺲ از اﺟﺮا ﺑﺎ ﻣﻮﻓﻘﯿﺖ ﺑﻪ ﮐﺎر ﺧﻮد ﭘﺎﯾﺎن ﻣﯽدﻫﺪ و
ﺧﺎرج ﻣﯽﺷﻮد ﺣﺎل ﺑﺎ ﻟﯿﺴﺘﯽ ﮐﻪ ﺟﻤﻊ اﻋﺪاد آن ﺑﯿﺸﺘﺮ از 10اﺳﺖ اﯾﻦ ﻣﺜﺎل را ﻣﺠﺪد ﺑﺮرﺳﯽ ﻣﯽﮐﻨﯿﻢ.
AssertionError
ﺑﺮاي ﻧﻮﺷﺘﻦ ﻣﻮرد آزﻣﻮن ﯾﺎ Test Caseﯾﮏ ﺗﺎﺑﻊ ﺗﻌﺮﯾﻒ ﻣﯽﮐﻨﯿﻢ ﮐﻪ ﻋﻤﻠﯿﺎت اﺿﺎﻓﻪﮐﺮدن دو ﻣﺤﺼﻮل ﺑﻪ
ﻟﯿﺴﺖ ﺧﺮﯾﺪ را ﺗﺴﺖ ﻣﯽﮐﻨﺪ .ﺑﺎﯾﺪ ﺗﻮﺟﻪ داﺷﺘﻪ ﺑﺎﺷﯿﻢ ﮐﻪ ﻧﺎم اﯾﻦ ﺗﻮاﺑﻊ ﺑﺎﯾﺪ ﺑﺎ _ testﺷﺮوع ﺷﻮد.
def test_add_item():
)(cart = Cart
)"cart.add_item("Pizza
)"cart.add_item("Burger
"assert cart.items[0] == "Pizza
"assert cart.items[1] == "Burger
ﭘﺲ از ﻧﻮﺷﺘﻦ ﮐﺪ ﺑﺎﻻ ،دﺳﺘﻮر pytestرا در ﺗﺮﻣﯿﻨﺎل وارد ﻣﯽﮐﻨﯿﻢ و در اداﻣﻪ اﯾﻦ ﻣﺎژول ﺑﻪ ﺑﺮرﺳﯽ
ﺗﻮاﺑﻊ ﺗﺴﺖ در ﻓﺎﯾﻞﻫﺎي ﻣﺮﺑﻮط ﻣﯽﭘﺮدازد و ﻫﺮﮐﺪام را اﺟﺮا ﻣﯽﮐﻨﺪ و ﻧﺘﯿﺠﻪ را ﺑﺮﻣﯽﮔﺮداﻧﺪ.
Ahwaz_Hackerz
$> pytest
]test_cart.py. [100%
در روﺑﺮوي ﻓﺎﯾﻞ test_cart.pyﯾﮏ ﻋﺪد ﮐﺎراﮐﺘﺮ ﻧﻘﻄﻪ آﻣﺪه اﺳﺖ ﮐﻪ ﺑﺪﯾﻦ ﻣﻌﻨﺎ اﺳﺖ ﮐﻪ
pytestدر اﯾﻦ ﻓﺎﯾﻞ ﯾﮏ ﺗﺎﺑﻊ را ﺗﺴﺖ ﮐﺮده و ﺑﺪون ﻫﯿﭻ ﻣﺸﮑﻠﯽ ﺗﺴﺖ اﻧﺠﺎم ﺷﺪه اﺳﺖ .ﺑﻪﻃﻮرﮐﻠﯽ
در ﻣﻘﺎﺑﻞ اﯾﻦ ﻓﺎﯾﻞ ﯾﮏ ﯾﺎ ﭼﻨﺪ ﮐﺎراﮐﺘﺮ ﻣﯽآﯾﺪ ﮐﻪ ﺗﻌﺪاد آﻧﻬﺎ ﻧﺸﺎندﻫﻨﺪة ﺗﻌﺪاد ﺗﻮاﺑﻊ ﺗﺴﺖ در داﺧﻞ آن
ﻓﺎﯾﻞ اﺳﺖ و ﮐﺎراﮐﺘﺮ آن ﻣﯽﺗﻮاﻧﺪ ﯾﮑﯽ از ﺣﺎﻟﺖﻫﺎي زﯾﺮ ﺑﺎﺷﺪ:
ﻻزم ﺑﻪ ذﮐﺮ اﺳﺖ ﮐﻪ در اﯾﻨﺠﺎ ﻓﺎﯾﻞ ﺗﺴﺖ و ﮐﻼس اﺻﻠﯽ در ﯾﮏ داﯾﺮﮐﺘﻮري ﻗﺮار دارد اﻣﺎ در ﻋﻤﻞ ﺑﻬﺘﺮ
اﺳﺖ ﻓﺎﯾﻞﻫﺎي ﺗﺴﺖ را در داﯾﺮﮐﺘﻮري ﺟﺪا ﻧﮕﻪ داﺷﺖ ﺗﺎ در زﻣﺎن اﺳﺘﻘﺮار ﺑﺮﻧﺎﻣﻪ ﺑﻪ اﺷﺘﺒﺎه ،اﯾﻦ ﻓﺎﯾﻞﻫﺎ در
ﻣﺤﯿﻂ اﺳﺘﻘﺮار ﻗﺮار ﻧﮕﯿﺮﻧﺪ.
ﻣﺪﯾﺮﯾﺖ اﺳﺘﺜﻨﺎ
1
ﺑﺮﺧﯽ ﻣﻮاﻗﻊ ﻧﯿﺎز دارﯾﻢ ﺗﺎ ﻣﻮاردي را ﺗﺴﺖ ﮐﻨﯿﻢ ﮐﻪ ﻣﻄﻤﺌﻦ ﺷﻮﯾﻢ ﭘﺲ از اﯾﻦ ﻋﻤﻠﯿﺎت ﯾﮏ اﺳﺘﺜﻨﺎ
درﯾﺎﻓﺖ ﻣﯽﮐﻨﯿﻢ .ﺑﺮاي ﻣﺜﺎل ﻣﯽﺧﻮاﻫﯿﻢ ﭘﺲ از اﺿﺎﻓﻪﮐﺮدن ﯾﮏ ﺟﻨﺲ اﻃﻤﯿﻨﺎن ﭘﯿﺪا ﮐﻨﯿﻢ ﮐﻪ ﺟﻨﺲ
دﯾﮕﺮي در ﺳﺒﺪ ﺧﺮﯾﺪ ﻧﯿﺴﺖ ﭘﺲ وﻗﺘﯽ ﮐﻪ دوﻣﯿﻦ ﺟﻨﺲ را در ﺳﺒﺪ ﺧﺮﯾﺪ درﯾﺎﻓﺖ ﮐﺮدﯾﻢ ﯾﮏ اﺳﺘﺜﻨﺎ
درﯾﺎﻓﺖ ﺧﻮاﻫﯿﻢ ﮐﺮد ﮐﻪ ﻧﺸﺎن از درﺳﺘﯽ ﺳﯿﺴﺘﻢ دارد .ﺑﺮاي ﺑﺮرﺳﯽ اﯾﻦ ﻧﻮع از ﺗﺴﺖﻫﺎ ﺑﺎﯾﺪ از
pytest.raisesاﺳﺘﻔﺎده ﮐﺮد .ﭘﺲ ﺑﻪ ﻓﺎﯾﻞ ﻗﺒﻠﯽ دو ﺗﺴﺖ دﯾﮕﺮ اﺿﺎﻓﻪ ﻣﯽﮐﻨﯿﻢ ،در ﯾﮑﯽ اوﻟﯿﻦ
ﻣﺤﺼﻮل ﺳﺒﺪ ﺧﺮﯾﺪ ﮐﻪ اﺿﺎﻓﻪ ﺷﺪه را درﯾﺎﻓﺖ و در ﺗﺴﺖ ﺑﻌﺪي ﻣﺤﺼﻮل دوم را از ﺳﺒﺪ ﺧﺮﯾﺪ ﺑﯿﺮون
ﻣﯽﮐﺸﯿﻢ .در ﺗﺴﺖ اول ﮐﺪ ﻣﺎ ﺑﺎﯾﺪ ﺑﺪون ﻣﺸﮑﻞ اﺟﺮا ﺷﻮد وﻟﯽ ﭼﻮن از pytest.raisesاﺳﺘﻔﺎده
ﮐﺮدﯾﻢ ،ﺑﺮاي ﻗﺒﻮﻟﯽ از ﺗﺴﺖ ﺑﺎﯾﺪ ﺣﺘﻤﺎً ﯾﮏ اﺳﺘﺜﻨﺎ ﯾﺎ Exceptionرخ دﻫﺪ و ﭼﻮن اﯾﻦ اﺗﻔﺎق ﻧﻤﯽاﻓﺘﺪ
ﭘﺲ ﺗﺴﺖ ﻣﺎ ﺑﺎ ﺷﮑﺴﺖ ﻣﻮاﺟﻪ ﻣﯽﺷﻮد ،اﻣﺎ ﺗﺴﺖ ﺑﻌﺪي ﺑﺎ ﻣﻮﻓﻘﯿﺖ ﭘﺬﯾﺮﻓﺘﻪ ﻣﯽﺷﻮد.
1
Exception
Ahwaz_Hackerz
def test_add_item():
cart = Cart()
cart.add_item("Pizza")
cart.add_item("Burger")
assert cart.items[0] == "Pizza"
assert cart.items[1] == "Burger"
def test_index_error_fail():
cart = Cart()
cart.add_item("Pizza")
with pytest.raises(IndexError):
cart.items[0]
def test_index_error():
cart = Cart()
cart.add_item("Pizza")
with pytest.raises(IndexError):
cart.items[1]
$> pytest
test_cart.py.F. [100%]
def test_index_error_fail():
cart = Cart()
cart.add_item("Pizza")
with pytest.raises(IndexError):
> cart.items[0]
E Failed: DID NOT RAISE <type 'exceptions.IndexError'>
test_cart.py:17: Failed
========== 1 failed, 2 passed in 0.12 seconds ==========
ﻣﯽﺷﻮد ،ﺗﺴﺖ ﻣﺎ ﺑﺎ ﺷﮑﺴﺖ ﻣﻮاﺟﻪ ﻣﯽﺷﻮد .ﺑﻪ زﺑﺎن دﯾﮕﺮ ،ﺑﺮاي ﺗﺴﺖ ﺑﺮﺧﯽ از ﻋﻤﻠﮑﺮدﻫﺎي ﺳﯿﺴﺘﻢ ﻧﯿﺎز
اﺳﺖ ﺗﺎ ﻣﻄﻤﺌﻦ ﺷﻮﯾﻢ ﮐﻪ رﻓﺘﺎر ﻏﯿﺮﻃﺒﯿﻌﯽ ﻧﺪاﺷﺘﻪ ﺑﺎﺷﯿﻢ .ﺑﺮاي ﻣﺜﺎل ﺑﺎ اﯾﻦ روش ﭼﮏ ﻣﯽﮐﻨﯿﻢ ﮐﻪ در
ﺻﻮرت اﺿﺎﻓﻪﮐﺮدن ﯾﮏ ﻣﺤﺼﻮل ،ﻣﺤﺼﻮل دﯾﮕﺮي در ﺳﺒﺪ ﺧﺮﯾﺪ اﺿﺎﻓﻪ ﻧﺸﻮد.
اﺳﺘﻔﺎده از Fixture
در ﺑﺮﺧﯽ ﻣﻮارد ﻧﯿﺎز اﺳﺖ ﻗﺒﻞ ﯾﺎ ﺑﻌﺪ از ﺗﺴﺖ ﯾﮏ ﺳﺮي ﻋﻤﻠﯿﺎت اﻧﺠﺎم ﺷﻮد .اﯾﻦ ﻋﻤﻠﯿﺎت ﻣﯽﺗﻮاﻧﺪ ﯾﮑﺒﺎر
ﺑﺮاي ﺗﻤﺎم ﺗﺴﺖﻫﺎي ﯾﮏ ﻓﺎﯾﻞ ﯾﺎ ﺑﺮاي ﺗﮏﺗﮏ ﺗﺴﺖﻫﺎ اﻧﺠﺎم ﺷﻮد .ﺑﺮاي ﻣﺜﺎل در ﺷﺮاﯾﻂ زﯾﺮ Fixture
ﻣﯽﺗﻮاﻧﺪ ﻣﻔﯿﺪ ﺑﺎﺷﺪ.
ﺑﺮاي ﺗﺴﺖ ﯾﮏ APIﺑﻬﺘﺮ اﺳﺖ اﺑﺘﺪاي ﺗﺴﺖ آن را ﻣﻘﺪاردﻫﯽ ﮐﻨﯿﻢ.
ﺑﺮاي ﺗﺴﺖ دﯾﺘﺎﺑﯿﺲ ﺑﺎﯾﺪ ارﺗﺒﺎط را در اﺑﺘﺪاي ﺗﺴﺖ ﺑﺮﻗﺮار و در آﺧﺮ ارﺗﺒﺎط را ﻗﻄﻊ ﮐﻨﯿﻢ.
ﺑﺮاي ﻋﺪم ﺗﮑﺮار در ﺗﺴﺖﻫﺎ و ﻣﻘﺪار دﻫﯽﻫﺎي اوﻟﯿﻪ
ﺑﻪ ﺳﻪ ﺗﺎﺑﻊ ﺗﺴﺖ زﯾﺮ دﻗﺖ ﮐﻨﯿﺪ.
from cart import Cart
import pytest
def test_add_item():
)(cart = Cart
)"cart.add_item("Pizza
)"cart.add_item("Burger
"assert cart.items[0] == "Pizza
"assert cart.items[1] == "Burger
def test_index_error():
)(cart = Cart
)"cart.add_item("Pizza
with pytest.raises(IndexError):
]cart.items[1
def test_len():
)(cart = Cart
)"cart.add_item("Pizza
)"cart.add_item("Burger
assert len(cart) == 2
ﻫﻤﺎنﻃﻮريﮐﻪ ﻣﺸﺎﻫﺪه ﻣﯽﮐﻨﯿﺪ در اﺑﺘﺪاي ﻫﻤﮕﯽ اﯾﻦ ﺗﺴﺖﻫﺎ از ﮐﻼس ﺳﺒﺪ ﺧﺮﯾﺪ ﯾﮏ ﺷﯽ ﺳﺎﺧﺘﻪ
ﻣﯽﺷﻮد .ﻣﯽﺗﻮاﻧﯿﻢ ﺑﺮاي ﺳﻬﻮﻟﺖ و ﺟﻠﻮﮔﯿﺮي از ﻣﺸﮑﻼت در ﻧﮕﻬﺪاري ﻧﺮماﻓﺰار اﯾﻦ ﺷﯽ را در ﯾﮏ ﺗﺎﺑﻊ
دﯾﮕﺮ ﺑﺴﺎزﯾﻢ .ﺑﻨﺎﺑﺮاﯾﻦ ﺑﺎﻗﯽ ﺗﺴﺖﻫﺎ ﺑﺎﯾﺪ ﺑﻪﻋﻨﻮان آرﮔﻮﻣﺎن ﺷﯽ ﺳﺒﺪ ﺧﺮﯾﺪ ﮐﻪ در Fixtureﺳﺎﺧﺘﻪ ﺷﺪه و
ﺑﺮﮔﺮداﻧﺪه ﻣﯽﺷﻮد را درﯾﺎﻓﺖ ﮐﻨﻨﺪ .ﺑﻪﻣﻨﻈﻮر ﺷﻨﺎﺳﺎﻧﺪن ﯾﮏ ﺗﺎﺑﻊ ﺑﻪﻋﻨﻮان Fixtureاز Decoratorﺑﺎ
ﺷﻨﺎﺳﻪ pytest.fixtureاﺳﺘﻔﺎده ﻣﯽﮐﻨﯿﻢ.
Ahwaz_Hackerz
@pytest.fixture
def cart():
""" Creating an object from Cart class
)(return Cart
def test_add_item(cart):
)"cart.add_item("Pizza
)"cart.add_item("Burger
"assert cart.items[0] == "Pizza
"assert cart.items[1] == "Burger
def test_index_error(cart):
)"cart.add_item("Pizza
with pytest.raises(IndexError):
]cart.items[1
def test_len(cart):
)"cart.add_item("Pizza
)"cart.add_item("Burger
assert len(cart) == 2
دﻗﺖ ﮐﻨﯿﺪ ﮐﻪ ﻧﺎم ﺗﺎﺑﻊ Fixtureﺑﺎ ﻧﺎم ورودي آرﮔﻮﻣﺎن ﺗﺴﺖﻫﺎ ﺑﺎﯾﺪ ﯾﮑﺴﺎن ﺑﺎﺷﺪ .ﻧﮑﺘﮥ دﯾﮕﺮي ﮐﻪ ﺑﺎﯾﺪ
ﺑﻪ آن ﺗﻮﺟﻪ ﮐﺮد ،آن اﺳﺖ ﮐﻪ اﯾﻦ Fixtureﻗﺒﻞ از ﻫﺮ ﺗﺴﺖ اﻧﺠﺎم ﻣﯽﺷﻮد ﺑﻨﺎﺑﺮاﯾﻦ ﺗﺴﺖ دوم و ﺳﻮم ﺑﺎ
ﻣﻮﻓﻘﯿﺖ ﭘﺬﯾﺮﻓﺘﻪ ﻣﯽﺷﻮد.
ﺳﻌﯽ ﮐﻨﯿﺪ ﺑﺮاي ﺗﻤﺎم Fixtureﻫﺎﯾﯽ ﮐﻪ ﻣﯽﻧﻮﯾﺴﯿﺪ ﺣﺘﻤﺎً docstringﺑﻨﻮﯾﺴﯿﺪ ﺗﺎ دﯾﮕﺮ ﺗﻮﺳﻌﻪدﻫﻨﺪﮔﺎن
ﻣﺴﺘﻨﺪات ﺷﻤﺎ را ﺑﺒﯿﻨﺪ و درﺻﻮرت ﻟﺰوم از آﻧﻬﺎ اﺳﺘﻔﺎده ﮐﻨﻨﺪ .ﺑﺎ دﺳﺘﻮر زﯾﺮ ﻣﯽﺗﻮاﻧﯿﺪ Fixtureﻫﺎي
ﻣﻮﺟﻮد را ﻣﺸﺎﻫﺪه ﮐﻨﯿﺪ.
ﻣﯽﺗﻮاﻧﯿﻢ ﺑﺮاي Fixtureﻫﺎ ﺑﻪوﺳﯿﻠﮥ ﭘﺎراﻣﺘﺮ scopeﻣﺤﺪوده ﺗﻌﺮﯾﻒ ﮐﻨﯿﻢ .ﻣﺤﺪوده ﻣﺸﺨﺺ ﻣﯽﮐﻨﺪ ﮐﻪ
ﻫﺮ fixtureﭼﻪ زﻣﺎﻧﯽ اﺟﺮا ﺷﻮد .ﻣﻘﺪارﻫﺎي ﻗﺎﺑﻞ اﺳﺘﻔﺎده ﺑﺮاي ﻣﺤﺪوده ،ﻋﺒﺎرﺗﻨﺪ از:
Fixture :"package" ﺑﻌﺪ از اﺟﺮاي ﺗﻤﺎم ﺗﻮاﺑﻊ ﺗﺴﺖ در ﭘﮑﯿﺞ ﺟﺎري ﻧﺎﺑﻮد ﻣﯽﺷﻮد.
Fixture :"session" ﺑﻌﺪ از اﺟﺮاي ﺗﻤﺎم ﺗﻮاﺑﻊ ﺗﺴﺖ در ﻧﺸﺴﺖ ﺟﺎري ﻧﺎﺑﻮد ﻣﯽﺷﻮد.
ﺑﻪﻣﻨﻈﻮر آﻧﮑﻪ ﺗﺴﺘﯽ اﻧﺠﺎم دﻫﯿﻢ ﻓﺎﯾﻠﯽ ﺑﺎ ﻧﺎم conftest.pyﻣﯽﺳﺎزﯾﻢ و Fixtureﺧﻮد را ﮐﻪ
ﻣﺤﺪوده آن از ﻧﻮع ﮐﻼس اﺳﺖ در آن ﺗﻌﺮﯾﻒ ﻣﯽﮐﻨﯿﻢ.
import pytest
)"@pytest.fixture(scope="class
def cart():
""" """ Creating an Cart Object
)(return Cart
اﮔﺮ ﺗﺴﺖ ﻗﺒﻠﯽ را اﺟﺮا ﮐﻨﯿﻢ ﺑﺎ ﻣﺸﮑﻞ ﻣﻮاﺟﻪ ﻣﯽﺷﻮﯾﻢ؛ زﯾﺮا در اﯾﻦ ﺻﻮرت ﺗﻨﻬﺎ ﯾﮏ ﺷﯽ از ﮐﻼس ﺳﺒﺪ
ﺧﺮﯾﺪ ﺳﺎﺧﺘﻪ ﻣﯽﺷﻮد و Fixtureﺗﺎ ﭘﺎﯾﺎن ﺗﺴﺖﻫﺎي ﯾﮏ ﮐﻼس ﺑﺎﻗﯽ ﻣﯽﻣﺎﻧﺪ در اﯾﻦﺻﻮرت در ﺗﺴﺖ دوم
ﭼﻮن ﻣﺤﺼﻮل دوم در ﺳﺒﺪ ﺧﺮﯾﺪ ﻫﻤﭽﻨﺎن ﻣﻮﺟﻮد اﺳﺖ اﺳﺘﺜﻨﺎ درﯾﺎﻓﺖ ﻧﻤﯽﮐﻨﯿﻢ و اﯾﻦ ﯾﻌﻨﯽ ﺷﮑﺴﺖ در
ﺗﺴﺖ و در ﺗﺴﺖ آﺧﺮ دﯾﮕﺮ ﮐﺎﻻﻫﺎي داﺧﻞ ﺳﺒﺪ ﺧﺮﯾﺪ 2ﻋﺪد ﻧﯿﺴﺖ و ﺑﺎز ﻫﻢ ﺷﮑﺴﺖ ﺻﻮرت ﻣﯽﭘﺬﯾﺮد.
ﺑﺮاي اﯾﻦ روش ﺗﺴﺖ ﺑﺎﯾﺪ ﺗﻮﺟﻪ ﮐﻨﯿﻢ ﮐﻪ ﭼﻮن ﻣﺤﺪودة Fixtureدر ﮐﻼس ﺗﻌﺮﯾﻒ ﺷﺪه و ﻣﺎ در ﻫﺮ ﺗﺎﺑﻊ
در ﮐﻼس ،ﻫﻤﺎن ﺷﯽ ﻗﺒﻠﯽ را درﯾﺎﻓﺖ ﻣﯽﮐﻨﯿﻢ ،ﭘﺲ ﺗﻮاﺑﻊ ﺗﺴﺖ را ﺑﻪﺻﻮرت زﯾﺮ ﺑﺎزﻧﻮﯾﺴﯽ ﻣﯽﮐﻨﯿﻢ.
import pytest
def test_add_item(cart):
)"cart.add_item("Pizza
)"cart.add_item("Burger
"assert cart.items[0] == "Pizza
"assert cart.items[1] == "Burger
def test_index_error(cart):
)"cart.add_item("Pizza
with pytest.raises(IndexError):
]cart.items[3
def test_len(cart):
)"cart.add_item("Pizza
)"cart.add_item("Burger
assert len(cart) == 5
Ahwaz_Hackerz
$> pytest
ﭘﺎراﻣﺘﺮيﮐﺮدن ﺗﺴﺖﻫﺎ
ﺑﺮاي ﻣﺜﺎل ﻓﺮض ﮐﻨﯿﺪ ﯾﮏ ﺗﺎﺑﻊ.در ﺑﺴﯿﺎري ﻣﻮاﻗﻊ ﺗﺴﺖﮐﺮدن ﺑﺎﯾﺪ ﺑﺎ ﭘﺎراﻣﺘﺮﻫﺎي ﻣﺨﺘﻠﻒ اﻧﺠﺎم ﺷﻮد
.دارﯾﻢ ﮐﻪ واﺣﺪ ﻓﻮت را ﺑﻪ ﻣﺘﺮ ﺗﺎ ﺳﻪ رﻗﻢ اﻋﺸﺎر ﺗﺒﺪﯾﻞ ﻣﯽﮐﻨﺪ
def foot_to_metre(length):
return round(length / 3.281, 3)
. ﺑﺎ داﻧﺶ ﻓﻌﻠﯽ ﻣﯽﺗﻮاﻧﯿﻢ ﺑﺪﯾﻦﮔﻮﻧﻪ ﻋﻤﻞ ﮐﻨﯿﻢ.ﻣﯽﺧﻮاﻫﯿﻢ اﯾﻦ ﺗﺎﺑﻊ را ﺑﺎ اﻋﺪاد ﻣﺘﻔﺎوت ﺗﺴﺖ ﮐﻨﯿﻢ
def test_feet_to_metre1():
assert foot_to_metre(1) == 0.305
def test_feet_to_metre2():
assert foot_to_metre(2) == 0.610
def test_feet_to_metre3():
assert foot_to_metre(3) == 0.914
def test_feet_to_metre4():
assert foot_to_metre(5) == 1.524
ﺑﺮاي ﺑﻬﺒﻮد ﮐﺪ ﺑﺎﻻ ﻣﯽﺗـﻮاﻧﯿﻢ.ﻣﯽﺑﯿﻨﯿﻢ ﮐﻪ در اﯾﻦ ﻣﺜﺎل ﺗﺴﺖﻫﺎ ﯾﮑﺴﺎن وﻟﯽ ﺗﻨﻬﺎ ﭘﺎراﻣﺘﺮﻫﺎ ﻣﺘﻔﺎوت اﺳﺖ
ﺑــــﺎ ﺷﻨﺎﺳــــﻪDecorator ﯾــــﮏ ﺗﺴــــﺖ ﭘــــﺎراﻣﺘﺮيﺷــــﺪه ﺑﻨﻮﯾﺴــــﯿﻢ و اﯾــــﻦ ﮐــــﺎر را ﺑــــﺎ
. اﻧﺠﺎم ﻣﯽدﻫﯿﻢpytest.mark.parametrize
@pytest.mark.parametrize('length,expected_value',
[(1, 0.305),
(2, 0.610),
(3, 0.914),
(5, 1.524)])
Ahwaz_Hackerz
def test_feet_to_metre(length,expected_value):
assert foot_to_metre(length) == expected_value
ﺗﻤﺎم ﺗﺴﺖﻫﺎي ﻗﺒﻠﯽ را در ﯾﮏ ﺗﺴﺖ ﭘﺎراﻣﺘﺮيﺷﺪه ﮔﺮدآوري ﮐﺮدﯾﻢ .اﯾﻦ Decoratorﺑﻪ اﯾﻦﺻﻮرت
ﻋﻤﻞ ﻣﯽﮐﻨﺪ ﮐﻪ دو ورودي ﻣﯽﮔﯿﺮد ،ورودي اول ﯾﮏ رﺷﺘﻪ اﺳﺖ ﮐﻪ ﺑﺎ ﮐﺎﻣﺎ ،ﻧﺎم ﭘﺎراﻣﺘﺮﻫﺎ از ﻫﻢ ﺟﺪا
ﺷﺪه اﺳﺖ و ﭘﺎراﻣﺘﺮ دوم ﻟﯿﺴﺘﯽ از ﺗﺎﭘﻞﻫﺎ اﺳﺖ ﮐﻪ ﺑﻪﺗﺮﺗﯿﺐ ﻣﺘﻨﺎﻇﺮ ﺑﺎ ﭘﺎراﻣﺘﺮﻫﺎي ورودي اول اﺳﺖ.
در ﺗﺎﺑﻊ ﺗﺴﺖ ﻧﯿﺰ ﺑﺎﯾﺪ ﻧﺎم آرﮔﻮﻣﺎنﻫﺎ را ﺑﺎ ﻧﺎم ﭘﺎراﻣﺘﺮﻫﺎ ﯾﮑﺴﺎن درﻧﻈﺮ ﺑﮕﯿﺮﯾﻢ و در ﺗﺎﺑﻊ از آﻧﻬﺎ اﺳﺘﻔﺎده
ﮐﻨﯿﻢ PyTest .ﺑﺮاي ﺗﺴﺖ ﺑﺮاي ﻫﺮﮐﺪام از ﺗﺎﭘﻞﻫﺎي داﺧﻞ ﻟﯿﺴﺖ ﯾﮑﺒﺎر ﺗﺎﺑﻊ ﺗﺴﺖ را اﺟﺮا ﻣﯽﮐﻨﺪ .ﭘﺲ
در ﻣﺜﺎل ﻣﻮﺟﻮد 4ﺗﺴﺖ اﻧﺠﺎم ﻣﯽﺷﻮد.
$> pytest
ﻧﺎدﯾﺪهﮔﺮﻓﺘﻦ ﺗﺴﺖﻫﺎ
در ﺑﺴﯿﺎري ﻣﻮارد ﺗﺎﺑﻊ ﻣﻮرد ﻧﻈﺮ را ﮐﺎﻣﻞ ﭘﯿﺎدهﺳﺎزي ﻧﮑﺮدﯾﻢ ﯾﺎ ﭘﺎراﻣﺘﺮﻫﺎي ﺗﺴﺖ را ﻧﺪارﯾﻢ و ﯾﺎ ﺗﺎﺑﻊ ﺗﺴﺖ
ﻣﺎ ﻧﯿﺎز ﺑﻪ ﺑﺎزﻧﮕﺮي دارد ،اﻣﺎ ﺑﺮاي ﺟﻠﻮﮔﯿﺮي از ﻓﺮاﻣﻮﺷﯽ ﺑﺮاي ﺗﺴﺖ ،ﻣﯽﺧﻮاﻫﯿﻢ ﺗﺎﺑﻊ ﺗﺴﺖ وﺟﻮد داﺷﺘﻪ
ﺑﺎﺷﺪ .ﺑﻪ اﯾﻦ ﻣﻨﻈﻮر ﻣﯽﺗﻮاﻧﯿﻢ از Decoratorﺑﺎ ﺷﻨﺎﺳﻪ pytest.mark.skipاﺳﺘﻔﺎده ﮐﻨﯿﻢ.
ﭘﺎراﻣﺘﺮ msgﻧﯿﺰ ﺑﺮاي ﺑﯿﺎن دﻟﯿﻞ ﻧﺎدﯾﺪهﮔﺮﻓﺘﻦ ﺗﺴﺖ اﺳﺘﻔﺎده ﻣﯽﺷﻮد.
import pytest
ﭘﺲ از اﺟﺮاي دﺳﺘﻮر PyTestدر ﺗﺮﻣﯿﻨﺎل ﻧﺘﯿﺠﻪ ﺑﻪ ﺷﮑﻞ زﯾﺮ اﺳﺖ ﮐﻪ ﮐﺎراﮐﺘﺮ sﻧﺸﺎندﻫﻨﺪة ﻧﺎدﯾﺪه-
ﮔﺮﻓﺘﻦ ﯾﮏ ﺗﺴﺖ در اﯾﻦ ﻓﺎﯾﻞ اﺳﺖ.
$> pytest
اﻓﺰوﻧﻪﻫﺎي PyTest
ﯾﮑﯽ از ﻗﺎﺑﻠﯿﺖﻫﺎي ﺗﺤﺴﯿﻦﺑﺮاﻧﮕﯿﺰ ﮐﺘﺎﺑﺨﺎﻧﻪ PyTestاﻧﻌﻄﺎفﭘﺬﯾﺮي ﺑﺎﻻي آن اﺳﺖ و ﺑﻪ ﻫﻤﯿﻦ دﻟﯿﻞ ﺑﺮاي
آن اﻓﺰوﻧﻪﻫﺎي ﺑﺴﯿﺎري ﺗﻮﺳﻌﻪ داده ﺷﺪه اﺳﺖ .در اداﻣﻪ ﺑﻪ ﺑﺮرﺳﯽ ﺑﺮﺧﯽ از اﯾﻦ اﻓﺰوﻧﻪﻫﺎي ﮐﺎرﺑﺮدي
ﻣﯽﭘﺮدازﯾﻢ.
def check_number(number):
if number > 0:
"return "Positive
elif number < 0:
"return "Negative
else:
return Zero
def test_positive():
"assert check_number(10) == "Positive
ﺗﺴﺖ ﻣﺎ ﺑﺎ ﻣﻮﻓﻘﯿﺖ اﻧﺠﺎم ﻣﯽﺷﻮد اﻣﺎ ﺗﻤﺎم ﻗﺴﻤﺖﻫﺎي ﮐﺪ ﺣﺪاﻗﻞ ﯾﮑﺒﺎر اﺟﺮا ﻧﻤﯽﺷﻮد .ﺑﺮاي ﺑﺮرﺳﯽ
درﺻﺪ ﭘﻮﺷﺶ از اﻓﺰوﻧﻪ pytest-covاﺳﺘﻔﺎده ﻣﯽﮐﻨﯿﻢ .ﺑﺮاي ﺷﺮوع ﺑﻪوﺳﯿﻠﮥ دﺳﺘﻮر زﯾﺮ اﯾﻦ اﻓﺰوﻧﻪ را
ﻧﺼﺐ ﻣﯽﮐﻨﯿﻢ.
ﭘﺲ از ﻧﺼﺐ ﺑﺎ دﺳﺘﻮر pytest --covﺗﺴﺖﻫﺎ اﺟﺮا ﻣﯽﺷﻮد و درﺻﺪ ﭘﻮﺷﺶ ﻧﯿﺰ ﮔﺰارش ﻣﯽﺷﻮد.
]test_number.py. [100%
Ahwaz_Hackerz
درﺻﺪ اﺳﺖ؛ زﯾﺮا50 ﺑﺮاﺑﺮcheck_number.py ﻫﻤﺎنﻃﻮرﮐﻪ ﻣﺸﺎﻫﺪه ﻣﯽﮐﻨﯿﻢ درﺻﺪ ﭘﻮﺷﺶ در ﻓﺎﯾﻞ
ﺑﻪ ﻣﻨﻈﻮر.در ﺗﺎﺑﻊ ﻣﺎ دو ﺷﺎﺧﻪ ﺻﻔﺮ و ﻋﺪد ﻣﻨﻔﯽ ﭼﮏ ﻧﺸﺪه اﺳﺖ و ﺗﻤﺎم ﺧﻄﻮط ﮐﺪ اﺟﺮا ﻧﺸﺪه اﺳﺖ
اﯾﻨﮑﻪ ﺗﺴﺖ ﻣﺎ ﺗﻤﺎم ﺣﺎﻻت را ﺑﺮرﺳﯽ ﮐﻨﺪ و ﺗﻤﺎم ﻗﺴﻤﺖﻫﺎي ﮐﺪ را ﭘﻮﺷﺶ دﻫﺪ دو ﺗﺴﺖ دﯾﮕﺮ ﺑﻪﺻﻮرت
.ﭘﺎراﻣﺘﺮيﺷﺪه ﺑﻪ ﮐﺪ اﺿﺎﻓﻪ ﻣﯽﮐﻨﯿﻢ ﺗﺎ ﺗﻤﺎم ﺷﺎﺧﻪﻫﺎي دﺳﺘﻮر ﺷﺮﻃﯽ را اﺟﺮا ﮐﻨﺪ
import pytest
@pytest.mark.parametrize('number,expected_value', [
(10, "Positive"),
(-10, "Negative"),
(0, "Zero")
])
def test_number(number, expected_value):
assert check_number(number) == expected_value
pytest --cov
test_number.py..F [100%]
@pytest.mark.parametrize('number,expected_value', [
(10, "Positive"),
(-10, "Negative"),
(0, "Zero")
Ahwaz_Hackerz
)]
def test_number(number, expected_value):
> assert check_number(number) == expected_value
test_number.py:12:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
number = 0
def check_number(number):
if number > 0:
"return "Positive
elif number < 0:
"return "Negative
else:
> return Zero
E NameError: global name 'Zero' is not defined
check_number.py:7: NameError
در اﺑﺘﺪا ﻣﯽﺑﯿﻨﯿﻢ ﮐﻪ ﯾﮑﯽ از ﺗﺴﺖﻫﺎي ﻣﺎ ﺑﺎ ﺷﮑﺴﺖ ﻣﻮاﺟﻪ ﺷﺪه و دﻟﯿﻞ آن ،اﺷﺘﺒﺎه ﺑﺮﮔﺮداﻧﺪن ﻣﻘﺪار
Zeroاﺳﺖ ﮐﻪ ﺑﻪ ﺟﺎي آﻧﮑﻪ ﺑﻪ ﺻﻮرت رﺷﺘﻪاي ﺑﺮﮔﺮداﻧﺪه ﺷﻮد ،ﺑﻪ ﺻﻮرت ﯾﮏ ﻣﺘﻐﯿﺮ ﺑﺮﮔﺮداﻧﺪه ﻣﯽﺷﻮد
و ﭼﻮن اﯾﻦ ﻣﺘﻐﯿﺮ ﺑﺎ اﯾﻦ ﻧﺎم ﺗﻌﺮﯾﻒ ﻧﺸﺪه اﺳﺖ اﺳﺘﺜﻨﺎ NameErrorرخ ﻣﯽدﻫﺪ .در ﻋﯿﻦ ﺣﺎل ﻣﻘﺪار
ﭘﻮﺷﺶ دﯾﮕﺮ 100درﺻﺪ ﺷﺪه و ﺗﻤﺎم ﻗﺴﻤﺖﻫﺎي ﮐﺪ ﺣﺪاﻗﻞ ﯾﮑﺒﺎر اﺟﺮا ﺷﺪه اﺳﺖ.
ﭘﺲ دﯾﺪم ﮐﻪ اﮔﺮ ﭘﻮﺷﺶ ﮐﺎﻣﻞ را رﻋﺎﯾﺖ ﻧﻤﯽﮐﺮدﯾﻢ ﻣﻤﮑﻦ ﺑﻮد ﻫﯿﭻﮔﺎه ﺑﻪ اﯾﻦ ﺧﻄﺎ ﭘﯽ ﻧﺒﺮﯾﻢ و ﺑﻌﺪاً
ﺑﺮاﯾﻤﺎن ﻣﺸﮑﻞﺳﺎز ﺷﻮد.
ﮐﻨﯿﺪ .ﻫﻤﭽﻨﯿﻦ اﯾﻦ اﻓﺰوﻧﻪ داراي progressbarاﺳﺖ ﮐﻪ ﺑﺴﯿﺎر روﻧﺪ ﺗﺴﺖ را ﮐﺎرﺑﺮﭘﺴﻨﺪ ﻣﯽﮐﻨﺪ .ﺑﺮاي
اﺳﺘﻔﺎده از اﯾﻦ اﻓﺰوﻧﻪ ﮐﺎر ﺧﺎﺻﯽ ﻧﯿﺎز ﻧﯿﺴﺖ و ﻓﻘﻂ ﺑﺎﯾﺪ آن را ﺑﺎ اﺳﺘﻔﺎده از دﺳﺘﻮر زﯾﺮ ﻧﺼﺐ ﮐﻨﯿﺪ.
$> pytest
plugins: sugar-0.9.4
collecting ...
✓ test_number.py ███ 33%
✓✓ test_number.py ██████ 67%
✓✓✓ test_number.py ██████████ 100%
ﺳﭙﺲ ﺑﺎ دﺳﺘﻮر زﯾﺮ ﺧﺮوﺟﯽ در ﻓﺎﯾﻠﯽ ﺑﺎ ﻧﺎم report.htmlذﺧﯿﺮه ﻣﯽﺷﻮد و ﻗﺎﺑﻞ ﻣﺸﺎﻫﺪه اﺳﺖ .در اﯾﻦ
ﮔﺰارش ﺷﻤﺎ روﻧﺪ ﮐﻠﯽ ﻫﺮ ﺗﺴﺖ را ﻣﺸﺎﻫﺪه ﮐﺮده و ﻣﯽﺗﻮاﻧﯿﺪ ﺑﺮاﺳﺎس ﻧﺘﺎﯾﺞ ،ﺗﺴﺖﻫﺎ را ﻓﯿﻠﺘﺮ ﮐﻨﯿﺪ.
ﺧﺮوﺟﯽ ﺗﻮﻟﯿﺪﺷﺪه ﻫﻤﺎﻧﻨﺪ ﺗﺼﻮﯾﺮ 12اﺳﺖ.
pytest --html=report.html
و ﺳﭙﺲ ﺑﺮاي ﻓﻌﺎلﺳﺎزي اﯾﻦ اﻓﺰوﻧﻪ از دﺳﺘﻮر pytestﺑﻪ ﻫﻤﺮاه آرﮔﻮﻣﺎن -vvاﺳﺘﻔﺎده ﮐﻨﯿﺪ.
Ahwaz_Hackerz
ﺟﻤﻊﺑﻨﺪي
در اﯾﻦ ﻓﺼﻞ ﺑﻪ ﺑﺮرﺳﯽ ﻣﺎژول pytestﭘﺮداﺧﺘﯿﻢ و ﮐﻠﯿﺎﺗﯽ در ﻣﻮرد آن ﻣﻄﺮح ﮐﺮدﯾﻢ .اﯾﻦ ﻣﺎژول ﺑﺴﯿﺎر
ﻣﺎژول ﮔﺴﺘﺮده و ﭘﺮﮐﺎرﺑﺮدي ﺑﺮاي اﻧﻮاع ﺳﻄﻮح ﺗﺴﺖ اﺳﺖ و ﺑﻪدﻟﯿﻞ اﻧﻌﻄﺎفﭘﺬﯾﺮي ﺑﺎﻻ ،ﺑﺮاي آن
اﻓﺰوﻧﻪﻫﺎي ﺑﺴﯿﺎري ﺗﻮﺳﻌﻪ داده ﺷﺪه اﺳﺖ .ﺳﻌﯽ ﮐﻨﯿﺪ در ﺷﺮوع ﯾﮏ ﭘﺮوژه ،ﻫﻤﺎﻧﻨﺪ ﻻگ ،ﺗﺴﺖﻫﺎي ﺧﻮد
را ﺑﺮاي ﻗﺴﻤﺖﻫﺎي ﻣﺨﺘﻠﻒ ﮐﺪ از ﺟﻤﻠﻪ ﺗﻮاﺑﻊ ،ﮐﻼسﻫﺎ ،ﻣﺎژولﻫﺎ و APIﻫﺎ ﺑﻨﻮﯾﺴﯿﺪ.
ﭘﻮﺷﺶ 100درﺻﺪي را ﺗﺎ ﺣﺪ ﻣﻤﮑﻦ اﺟﺮا ﮐﻨﯿﺪ و ﻫﻤﯿﺸﻪ ﻗﺒﻞ از ﻗﺮاردادن ﮐﺪﻫﺎ در ﻣﺤﯿﻂ اﺳﺘﻘﺮار ،آﻧﻬﺎ
را ﺗﺴﺖ ﮐﻨﯿﺪ .ﺑﺮاي ﻣﻄﺎﻟﻌﮥ ﮔﺴﺘﺮدهﺗﺮ ﻣﯽﺗﻮاﻧﯿﺪ ﻣﺴﺘﻨﺪات pytestرا در آدرس زﯾﺮ ﻣﻄﺎﻟﻌﻪ ﻓﺮﻣﺎﯾﯿﺪ.
https://docs.pytest.org/en/stable
Ahwaz_Hackerz
ﺑﺨﺶ6
ﭘﮑﯿﺞ
Ahwaz_Hackerz
ﻓﺼﻞ1
ﺳﺎﺧﺖ ﯾﮏ ﭘﮑﯿﺞ ﺳﺎده
Ahwaz_Hackerz
ﭘﮑﯿﺞ ﭼﯿﺴﺖ؟
ﻣﯽداﻧﯿﻢ ﮐﻪ ﻣﺎژول ﭼﯿﺰي ﺑﻪ ﺟﺰ ﯾﮏ ﻓﺎﯾﻞ ﭘﺎﯾﺘﻮن ﻧﯿﺴﺖ .ﭘﮑﯿﺞ داﯾﺮﮐﺘﻮري اﺳﺖ ﮐﻪ ﺣﺎوي ﭼﻨﺪﯾﻦ
ﻣﺎژول اﺳﺖ .درواﻗﻊ ﯾﮏ ﭘﮑﯿﺞ ﻣﯽﺗﻮاﻧﺪ داﺧﻞ ﭘﮑﯿﺞ دﯾﮕﺮي ﻧﯿﺰ ﺑﺎﺷﺪ ،ﭘﺲ ﺑﻬﺘﺮ اﺳﺖ ﺑﮕﻮﯾﯿﻢ ﭘﮑﯿﺞ ﺷﺎﻣﻞ
ﭼﻨﺪﯾﻦ ﻣﺎژول و ﭘﮑﯿﺞﻫﺎي دﯾﮕﺮ ﻣﯽﺗﻮاﻧﺪ ﺑﺎﺷﺪ.
ﭘﺎﯾﺘﻮن ﺑﺎ ﺑﺴﯿﺎري از ﭘﮑﯿﺞﻫﺎي ﮐﺎرﺑﺮدي و ﻣﻔﯿﺪ اراﺋﻪ ﻣﯽﺷﻮد .ﻋﻼوهﺑﺮ ﭘﮑﯿﺞﻫﺎي ﺧﻮدﺳﺎﺧﺘﻪ ،ﺑﺴﯿﺎري از
ﺗﻮﺳﻌﻪدﻫﻨﺪﮔﺎن ﭘﮑﯿﺞﻫﺎي ﮐﺎرﺑﺮدي را در PyPIﻣﻨﺘﺸﺮ ﻣﯽﮐﻨﻨﺪ و ﻫﻤﮥ اﯾﻨﻬﺎ ﺑﺎﻋﺚ ﺷﺪه ﺗﺎ ﭘﺎﯾﺘﻮن ﺑﻪ
زﺑﺎﻧﯽ ﻗﺪرﺗﻤﻨﺪ و آﺳﺎن ﺗﺒﺪﯾﻞ ﮔﺮدد .در اﯾﻦ ﻓﺼﻞ ﻣﯽﺧﻮاﻫﯿﻢ ﺑﻪ ﺑﺮرﺳﯽ ﭼﮕﻮﻧﮕﯽ ﺳﺎﺧﺖ ﯾﮏ ﭘﮑﯿﺞ و
اﻧﺘﺸﺎر آن در PyPIﺑﭙﺮدازﯾﻢ.
printvarcolor
│
├─── printvarcolor
│ │ helper.py
│ │ __init__.py
│
│
├─── test
│ │ test_cprint.py
│ │
│
└─── setup.py
ﻓﺎﯾﻞ helper.py
اﯾﻦ ﻓﺎﯾﻞ درﺑﺮدارﻧﺪة ﻣﻨﻄﻖ ﺑﺮﻧﺎﻣﻪ ﻣﺎﺳﺖ ،اﮔﺮﭼﻪ ﮐﻪ ﻣﯽﺗﻮاﻧﯿﻢ از ﭼﻨﺪﯾﻦ ﻓﺎﯾﻞ ﺑﺎ ﻧﺎمﻫﺎي دﯾﮕﺮ اﺳﺘﻔﺎده
ﮐﻨﯿﻢ وﻟﯽ ﭼﻮن ﻣﺜﺎل ﻣﺎ ﻣﺜﺎل ﺳﺎدهاي اﺳﺖ ،ﺳﺎﺧﺖ ﯾﮏ ﻓﺎﯾﻞ ﮐﻔﺎﯾﺖ ﻣﯽﮐﻨﺪ.
Ahwaz_Hackerz
در اﯾﻦ ﺑﺮﻧﺎﻣﻪ ﻣﺎ از ﮐﺘﺎﺑﺨﺎﻧﮥ دﯾﮕﺮي ﺑﺎ ﻧﺎم termcolorاﺳﺘﻔﺎده ﮐﺮدﯾﻢ .اﯾﻦ ﮐﺘﺎﺑﺨﺎﻧﻪ اﯾﻦ اﻣﮑﺎن را ﺑﻪ
ﻣﺎ ﻣﯽدﻫﺪ ﺗﺎ ﺧﺮوﺟﯽﻫﺎي ﭼﺎپﺷﺪه در ﺗﺮﻣﯿﻨﺎل را رﻧﮕﯽ ﮐﻨﯿﻢ .ﺗﺎﺑﻊ make_colorful_textﯾﮏ
ﻣﺘﻦ و ﯾﮏ رﻧﮓ درﯾﺎﻓﺖ ﮐﺮده و ﻣﺘﻦ رﻧﮕﯽ را ﺑﺮﻣﯽﮔﺮداﻧﺪ .درواﻗﻊ ،ﺑﺮاي رﻧﮕﯽﮐﺮدن ﻣﺘﻦ ،اﯾﻦ ﮐﺘﺎﺑﺨﺎﻧﻪ
ﺗﻌﺪادي ﮐﺎراﮐﺘﺮ ﺧﺎص را ﺑﻪ اول و آﺧﺮ ﻣﺘﻦ اﺿﺎﻓﻪ ﻣﯽﮐﻨﺪ و آﻧﻬﺎ را رﻧﮕﯽ ﻣﯽﮐﻨﺪ .ﺗﺎﺑﻊ cprint
ﺗﻌﺪادي آرﮔﻮﻣﺎن ﺑﺎ ﮐﻠﯿﺪواژه درﯾﺎﻓﺖ ﻣﯽﮐﻨﺪ و آنﻫﺎ را ﺑﺎ ﻧﺎﻣﺸﺎن ﭼﺎپ ﻣﯽﮐﻨﺪ ﮐﻪ ﻧﺎم آرﮔﻮﻣﺎن ﺑﻪ رﻧﮓ
ﻗﺮﻣﺰ و ﻣﻘﺪار آن ﺑﻪ رﻧﮓ ﺳﺒﺰ در ﻣﯽآﯾﺪ.
ﻓﺎﯾﻞ __init__.py
اﯾﻦ ﻓﺎﯾﻞ ﻫﻤﺎنﻃﻮريﮐﻪ از ﻧﺎﻣﺶ ﭘﯿﺪاﺳﺖ ﻣﻌﻨﯽ ﺧﺎﺻﯽ ﺑﺮاي ﭘﺎﯾﺘﻮن دارد و ﻧﺸﺎندﻫﻨﺪة
root directoryﭘﺮوژه ﻣﺎ اﺳﺖ و ﻣﺤﻞ ﺧﻮﺑﯽ ﺑﺮاي ﻗﺮاردادن ورژن ﭘﮑﯿﺞ و ﻣﺴﺘﻨﺪات و ﺛﺎﺑﺖﻫﺎ اﺳﺖ.
ﺑﺮاي ﻣﺜﺎل در اﯾﻦ ﻓﺎﯾﻞ ورژن را ﻟﺤﺎظ ﻣﯽﮐﻨﯿﻢ.
"__version__ = "1.0.0
)__print(pvc.__version
>> 1.0.0
cprint(var="value")
ﮐﻼس و، ﺑﻨﺎﺑﺮاﯾﻦ ﺗﻨﻬﺎ ﺗﻮاﺑﻊ. ﮐﺮدimport آن راhelper.py ﺑﺮاي اﺳﺘﻔﺎده از اﯾﻦ ﺗﺎﺑﻊ ﺑﺎﯾﺪ از
__ وارد ﺷﺪه ﺑﺎﺷﻨﺪ ﺑﻪﺻﻮرت ﻣﺴﺘﻘﯿﻢ از ﻃﺮﯾﻖ ﭘﮑﯿﺞ ﻗﺎﺑﻞ اﺳﺘﻔﺎده ﻫﺴﺘﻨﺪinit__.py ﻣﺘﻐﯿﺮﻫﺎﯾﯽ ﮐﻪ در
. ﺷﻮﻧﺪimport و ﺑﺎﻗﯽ ﻣﻮارد ﺑﺎﯾﺪ ﺑﻪﺻﻮرت ﮐﺎﻣﻞ
print(blue_text)
>> Something
test_cprint.py ﻓﺎﯾﻞ
ﻫﻤﺎنﻃﻮريﮐﻪ در ﻓﺼﻞ ﻗﺒﻞ ﺑﻪ آن اﺷﺎره ﮐﺮدﯾﻢ ﺑﻬﺘﺮ اﺳﺖ ﻓﺎﯾﻞﻫﺎي ﺗﺴﺖ را از ﺑﺮﻧﺎﻣﮥ اﺻﻠﯽ ﺟﺪا ﮐﻨﯿﻢ؛
. ﻗﺮار دادﯾﻢtest ﺑﻨﺎﺑﺮاﯾﻦ اﯾﻦ ﻓﺎﯾﻞ را در داﯾﺮﮐﺘﻮري
def test_cprint_no_var(capsys):
cprint()
captured = capsys.readouterr()
assert captured.out == ''
def test_cprint_one_var(capsys):
cprint(ping="pong")
captured = capsys.readouterr()
assert captured.out == ('\x1b[31mping\x1b[0m: '
'\x1b[32mpong\x1b[0m\n')
Ahwaz_Hackerz
اﯾﻦ ﺗﺎﺑﻊ ﺑﺎ ﺗﺎﺑﻊﻫﺎي ﻗﺒﻠﯽ ﺗﻔﺎوت دارد و ﻣﻘﺪاري را ﺑﺮﻧﻤﯽﮔﺮداﻧﺪ و ﻣﺘﻨﯽ را در ﺧﺮوﺟﯽ ﭼﺎپ ﻣﯽﮐﻨﺪ.
ﺑﺮاي درﯾﺎﻓﺖ رﺷﺘﻪﻫﺎي ﭼﺎپﺷﺪه در ﺧﺮوﺟﯽ ) (stdoutﻣﯽﺗﻮاﻧﯿﻢ از Fixtureﺧﺎﺻﯽ ﺑﺎ ﻧﺎم capsys
اﺳﺘﻔﺎده ﮐﻨﯿﻢ .ﺑﺎ ﻓﺮاﺧﻮاﻧﯽ ﻣﺘﺪ )( readouterrﻣﯽﺗﻮاﻧﯿﻢ ﺧﺮوﺟﯽ و ﺧﻄﺎﻫﺎﯾﯽ ﮐﻪ ﻗﺒﻞ از آن ﭼﺎپ-
ﺷﺪه را درﯾﺎﻓﺖ ﮐﻨﯿﻢ و ﺳﭙﺲ ﺑﻪ ﺑﺮرﺳﯽ درﺳﺘﯽ آن ﺑﭙﺮدازﯾﻢ .ﺑﺮاي درﯾﺎﻓﺖ ﺧﺮوﺟﯽ و ﺧﻄﺎ ﻣﯽﺗﻮاﻧﯿﻢ
ﻫﻤﺎﻧﻨﺪ ﻣﺜﺎل زﯾﺮ ﻋﻤﻞ ﮐﻨﯿﻢ.
)(captured = capsys.readouterr
"" == assert captured.out
"" == assert captured.err
$> pytest
]tests\test_cprint.py.. [100%
ﻓﺎﯾﻞ setup.py
ﻓﺎﯾﻞ ﻧﺼﺐ ﺑﺎﯾﺪ در ﺑﺎﻻﺗﺮﯾﻦ ﺳﻄﺢ داﯾﺮﮐﺘﻮري ﺷﻤﺎ ﺑﺎﺷﺪ .اﯾﻦ ﻓﺎﯾﻞ اﻃﻼﻋﺎﺗﯽ ﺑﺮاي ﻧﺼﺐ و ﻫﻤﭽﻨﯿﻦ ﺑﺮاي
اﻧﺘﺸﺎر در PyPIرا در ﺧﻮد دارد .در اداﻣﻪ ﻣﺤﺘﻮﯾﺎت اﯾﻦ ﻓﺎﯾﻞ را ﻣﺸﺎﻫﺪه ﻣﯽﮐﻨﯿﺪ.
(setup
name="printvarcolor",
version="1.0.0",
author="Siyanew",
author_email="printvarcolor@Siyanew.ir",
description="Print Colorful variables.",
long_description="Print Colorful variables In Terminal.",
url=" https://pypi.org/project/printvarcolor",
packages=find_packages(exclude=("test",)),
]"install_requires=["termcolor
)
در اﯾﻦ ﻓﺎﯾﻞ از ﺗﺎﺑﻊ setupﺑﺮاي اﻃﻼﻋﺎت اﺳﺘﻔﺎده ﺷﺪه ﮐﻪ ﺑﻪ ﺗﻮﺿﯿﺢ ﭘﺎراﻣﺘﺮﻫﺎي آن ﻣﯽﭘﺮدازﯾﻢ.
Ahwaz_Hackerz
name
ﻧﺎم ﭘﮑﯿﺞ را ﺑﻪﺻﻮرت رﺷﺘﻪاي درﯾﺎﻓﺖ ﻣﯽﮐﻨﺪ.
version
ورژن ﭘﮑﯿﺞ را ﺑﻪﺻﻮرت رﺷﺘﻪاي درﯾﺎﻓﺖ ﻣﯽﮐﻨﺪ.
author
ﻧﺎم ﺗﻮﺳﻌﻪدﻫﻨﺪة ﭘﮑﯿﺞ را ﺑﻪﺻﻮرت رﺷﺘﻪاي درﯾﺎﻓﺖ ﻣﯽﮐﻨﺪ.
author_email
ﭘﺴﺖ اﻟﮑﺘﺮوﻧﯿﮏ ﺗﻮﺳﻌﻪدﻫﻨﺪة ﭘﮑﯿﺞ را ﺑﻪﺻﻮرت رﺷﺘﻪاي درﯾﺎﻓﺖ ﻣﯽﮐﻨﺪ.
description
ﺗﻮﺿﯿﺤﺎت ﻣﺨﺘﺼﺮ ﭘﮑﯿﺞ را ﺑﻪﺻﻮرت رﺷﺘﻪاي درﯾﺎﻓﺖ ﻣﯽﮐﻨﺪ.
long_description
ﺗﻮﺿﯿﺤﺎت ﮐﺎﻣﻞ ﭘﮑﯿﺞ را ﺑﻪﺻﻮرت رﺷﺘﻪاي درﯾﺎﻓﺖ ﻣﯽﮐﻨﺪ.
url
آدرس اﯾﻨﺘﺮﻧﺘﯽ ﭘﮑﯿﺞ را ﺑﻪﺻﻮرت رﺷﺘﻪاي درﯾﺎﻓﺖ ﻣﯽﮐﻨﺪ.
packages
ﻟﯿﺴﺘﯽ از رﺷﺘﻪﻫﺎ را درﯾﺎﻓﺖ ﻣﯽﮐﻨﺪ ﮐﻪ ﺣﺎوي ﻧﺎم ﭘﮑﯿﺞﻫﺎﺳﺖ .در اﯾﻨﺠﺎ ﺑﺎ اﺳﺘﻔﺎده از ﺗﺎﺑﻊ
find_packagesﭘﮑﯿﺞﻫﺎ ﺑﻪ ﺻﻮرت ﺧﻮدﮐﺎر ﭘﯿﺪا ﺷﺪه و ﭘﮑﯿﺞ ﺗﺴﺖ ﻧﺎدﯾﺪه ﮔﺮﻓﺘﻪ ﻣﯽﺷﻮد.
install_requires
ﻟﯿﺴﺘﯽ از ﻧﺎم ﭘﮑﯿﺞﻫﺎﯾﯽ ﮐﻪ ﭘﮑﯿﺞ ﻣﺎ ﺑﻪ آنﻫﺎ واﺑﺴﺘﻪ اﺳﺖ را درﯾﺎﻓﺖ ﻣﯽﮐﻨﺪ.
در اﯾﻨﺠﺎ ﺑﻪ ﺑﺮرﺳﯽ ﺗﻌﺪادي از ﻣﻬﻤﺘﺮﯾﻦ ﭘﺎراﻣﺘﺮﻫﺎي ﺗﺎﺑﻊ setupﭘﺮداﺧﺘﯿﻢ اﻣﺎ ﺗﻌﺪاد اﯾﻦ ﭘﺎراﻣﺘﺮﻫﺎ ﺑﺴﺘﻪ
ﺑﻪ ﭘﮑﯿﺞﻫﺎﯾﯽ ﮐﻪ ﺗﻮﺳﻌﻪ ﻣﯽدﻫﯿﺪ ﻣﯽﺗﻮاﻧﺪ ﺑﯿﺸﺘﺮ ﺑﺎﺷﺪ .ﺑﺮاي ﻣﻄﺎﻟﻌﮥ ﺑﯿﺸﺘﺮ ﻣﯽﺗﻮاﻧﯿﺪ ﺑﻪ ﻣﺴﺘﻨﺪات
رﺳﻤﯽ setuptoolsﺑﻪ آدرس زﯾﺮ ﻣﺮاﺟﻌﻪ ﮐﻨﯿﺪ.
https://setuptools.readthedocs.io/en/latest/setuptools.html
Ahwaz_Hackerz
ﻓﺼﻞ2
اﻧﺘﺸﺎر ﭘﮑﯿﺞ در PyPI
Ahwaz_Hackerz
https://pypi.org/account/register
ﺳﺎﺧﺖ ﭘﮑﯿﺞ
در PyPIﮐﺪﻫﺎ ﺑﻪ ﺻﻮرت ﻣﺘﻦ ﺑﺎز و ﻓﺎﯾﻞ ﺑﻪ ﻓﺎﯾﻞ ﻗﺮار ﻧﺪارد و ﺑﻪﺻﻮرت ﯾﮏ ﻓﺎﯾﻞ ﺑﺴﺘﻪﺑﻨﺪيﺷﺪه ﺗﻮزﯾﻊ
ﻣﯽﺷﻮد .ﻓﺮﻣﺖ اﯾﻦ ﻓﺎﯾﻞ .whlاﺳﺖ و ﺑﺎ داﺷﺘﻦ اﯾﻦ ﻓﺎﯾﻞ ﻣﯽﺗﻮاﻧﯿﻢ ﭘﮑﯿﺞ را ﺑﺎ دﺳﺘﻮر زﯾﺮ ﻧﺼﺐ ﮐﻨﯿﻢ.
ﺑﺮاي ﻧﺼﺐ اﺑﺰار wheelﺑﺮاي ﺑﺴﺘﻪﺑﻨﺪي ﻓﺎﯾﻞﻫﺎي ﭘﮑﯿﺞ ،اﺑﺘﺪا ﺑﺎ دﺳﺘﻮر زﯾﺮ آن را ﻧﺼﺐ ﻣﯽﮐﻨﯿﻢ.
ﺑﺮاي ﺳﺎﺧﺖ ﻓﺎﯾﻞ ﺑﺴﺘﻪﺑﻨﺪيﺷﺪه ﭘﮑﯿﺞ ﻓﻌﻠﯽ ،از دﺳﺘﻮر زﯾﺮ اﺳﺘﻔﺎده ﻣﯽﮐﻨﯿﻢ و آن را داﺧﻞ ﺗﺮﻣﯿﻨﺎل وارد
ﻣﯽﮐﻨﯿﻢ.
build
ﺣﺎوي اﻃﻼﻋﺎت ﭘﮑﯿﺞ ﺳﺎﺧﺘﻪ ﺷﺪه اﺳﺖ.
dist
ﺣﺎوي ﻓﺎﯾﻞ .whlاﺳﺖ ﮐﻪ ﺑﺮاي اﻧﺘﺸﺎر ﭘﮑﯿﺞ ﺑﻪﮐﺎر ﻣﯽرود.
printvarcolor.egg-info
ﺣﺎوي اﻃﻼﻋﺎﺗﯽ ﻫﻤﭽﻮن ﺑﺎﯾﺖﮐﺪﻫﺎ ،اﻃﻼﻋﺎت ﭘﮑﯿﺞ و واﺑﺴﺘﮕﯽﻫﺎي ﭘﮑﯿﺞ اﺳﺖ.
ﻧﺼﺐ Twine
ﺑﺮاي ارﺳﺎل ﭘﮑﯿﺞﻫﺎ ﺑﻪ PyPIاز اﺑﺰاري ﺑﻪ ﻧﺎم twineاﺳﺘﻔﺎده ﻣﯽﮐﻨﯿﻢ ،اﯾﻦ اﺑﺰار را ﺑﺎ دﺳﺘﻮر زﯾﺮ و
ﺑﻪوﺳﯿﻠﮥ pipﻣﯽﺗﻮاﻧﯿﺪ ﺑﺮ روي ﺳﯿﺴﺘﻢ ﺧﻮد ﻧﺼﺐ ﮐﻨﯿﺪ.
ﭘﺲ از ﻧﺼﺐ twineﻓﺎﯾﻞ wheelﺳﺎﺧﺘﻪ ﺷﺪه را ﭼﮏ ﻣﯽﮐﻨﯿﻢ ﺗﺎ اﮔﺮ ﺧﻄﺎﯾﯽ در آن وﺟﻮد دارد ﻗﺒﻞ از
ارﺳﺎل ﺑﺮرﺳﯽ و رﻓﻊ ﮔﺮدد .ﺑﺮاي اﯾﻦ ﮐﺎر از دﺳﺘﻮر زﯾﺮ اﺳﺘﻔﺎده ﻣﯽﮐﻨﯿﻢ.
زﻣﺎﻧﯽﮐﻪ ﺑﺮرﺳﯽ ﻓﺎﯾﻞ wheelﺗﻤﺎم ﺷﺪ و ﻧﺘﯿﺠﻪ PASSEDﺷﺪ ﻧﻮﺑﺖ ﺑﻪ ارﺳﺎل ﻧﻬﺎﯾﯽ ﻣﯽرﺳﺪ .ﺑﺮاي
ارﺳﺎل ﺑﻪ PyPIاز دﺳﺘﻮر uploadاﺳﺘﻔﺎده ﻣﯽﮐﻨﯿﻢ .ﭘﺲ از اﺟﺮاي اﯾﻦ دﺳﺘﻮر از ﺷﻤﺎ ﻧﺎم ﮐﺎرﺑﺮي و رﻣﺰ
ﻋﺒﻮري ﮐﻪ ﺑﺎ آن ﺣﺴﺎب ﮐﺎرﺑﺮي اﯾﺠﺎد ﮐﺮدهاﯾﺪ ،ﭘﺮﺳﯿﺪه ﻣﯽﺷﻮد ﺳﭙﺲ ﻋﻤﻠﯿﺎت ﺑﺎرﮔﺬاري ﺑﺮ روي PyPI
اﻧﺠﺎم ﻣﯽﺷﻮد و درﻧﻬﺎﯾﺖ آدرس ﭘﮑﯿﺞ ﺷﻤﺎ ﺑﺮ روي ﺳﺎﯾﺖ PyPIﻧﺸﺎن داده ﻣﯽﺷﻮد.
View at:
https://pypi.org/project/printvarcolor/1.0.0
. ﻣﯽﺗﻮاﻧﻨﺪ ﭘﮑﯿﺞ ﺷﻤﺎ را درﯾﺎﻓﺖ ﮐﻨﻨﺪpip ﺗﻮﺳﻌﻪدﻫﻨﺪﮔﺎن دﯾﮕﺮ ﺑﺎ اﺳﺘﻔﺎده از دﺳﺘﻮر،ﭘﺲ از اﻧﺘﺸﺎر
ﻓﺼﻞ3
ﻣﺤﯿﻂ ﻣﺠﺎزي
Ahwaz_Hackerz
ﻧﺼﺐ virtualenv
اﮔﺮ از ﭘﺎﯾﺘﻮن 3.3ﯾﺎ ﺟﺪﯾﺪﺗﺮ اﺳﺘﻔﺎده ﻣﯽﮐﻨﯿﺪ ،ﻧﯿﺎزي ﺑﻪ ﻧﺼﺐ virtualenvﻧﯿﺴﺖ و اﯾﻦ اﺑﺰار ﻫﻤﺮاه ﺑﺎ
ﭘﺎﯾﺘﻮن اراﺋﻪ ﻣﯽﺷﻮد .ﭘﺲ اﮔﺮ ﭘﺎﯾﺘﻮن ﺷﻤﺎ اﯾﻦ ﻣﺎژول را دارد ﻣﯽﺗﻮاﻧﯿﺪ اﯾﻦ ﻗﺴﻤﺖ را ﻧﺎدﯾﺪه ﺑﮕﯿﺮﯾﺪ.
ﺑﺮاي ﺳﺎﺧﺖ ﯾﮏ ﻣﺤﯿﻂ ﻣﺠﺎزي ﺑﻪ داﯾﺮﮐﺘﻮري ﭘﺮوژه ﺧﻮد رﻓﺘﻪ و دﺳﺘﻮر زﯾﺮ را ﺑﺮاي ﺳﺎﺧﺖ ﯾﮏ ﻣﺤﯿﻂ
ﻣﺠﺎزي ﺑﺎ ﻧﺎم envاﺟﺮا ﮐﻨﯿﺪ.
1
Virtual Environment
Ahwaz_Hackerz
و ﻫﻤﭽﻨﯿﻦ ﺑﺎ زدن دﺳﺘﻮر زﯾﺮ ﻣﺤﯿﻂ ﻣﺠﺎزي در ﺳﯿﺴﺘﻢﻋﺎﻣﻞ وﯾﻨﺪوز ﻓﻌﺎل ﻣﯽﺷﻮد.
$> .\env\Scripts\activate
ﭘﺲ از ﻓﻌﺎلﺳﺎزي در اﺑﺘﺪاي ﺗﺮﻣﯿﻨﺎل ﻧﺎم ﻣﺤﯿﻂ ﻣﺠﺎزي در ﭘﺮاﻧﺘﺰ ﻧﻮﺷﺘﻪ ﻣﯽﺷﻮد.
deactivate
ﺗﻮﺟﻪ ﮐﻨﯿﺪ ﮐﻪ ﺑﺮاي ﻓﻌﺎلﺳﺎزي دوﺑﺎره ﻧﯿﺎز ﺑﻪ ﺳﺎﺧﺖ ﻣﺠﺪد ﻣﺤﯿﻂ ﻣﺠﺎزي ﻧﯿﺴﺖ و ﺗﻨﻬﺎ اﺳﺘﻔﺎده از
دﺳﺘﻮرات ﻗﺴﻤﺖ ﻓﻌﺎلﺳﺎزي ﮐﻔﺎﯾﺖ ﻣﯽﮐﻨﺪ.
ﻧﺼﺐ ﭘﮑﯿﺞ
ﭘﺲ از ﺳﺎﺧﺖ ﻣﺤﯿﻂ ﻣﺠﺎزي ،ﭘﮑﯿﺞﻫﺎ را ﺑﺎ اﺳﺘﻔﺎده ﻣﺴﺘﻘﯿﻢ دﺳﺘﻮر pipو ﺑﺪون ذﮐﺮ ﻧﺴﺨﻪ ﻣﯽﺗﻮاﻧﯿﻢ
اﺟﺮا ﮐﻨﯿﻢ؛ زﯾﺮا pipﻣﻮﺟﻮد ﻫﻤﺎن ﻧﺴﺨﻪاي اﺳﺖ ﮐﻪ ﺑﺎ آن ﻣﺤﯿﻂ ﻣﺠﺎزي را ﺳﺎﺧﺘﯿﻢ.
ﺑﻪﻋﻨﻮان ﻣﺜﺎل ﭘﮑﯿﺞ requestsﮐﻪ ﺑﺮاي اﻧﺠﺎم درﺧﻮاﺳﺖﻫﺎي HTTPاﺳﺖ را ﻧﺼﺐ ﮐﻨﯿﻢ.
اﯾﻦ ﭘﮑﯿﺞ ﺗﻨﻬﺎ در اﯾﻦ ﻣﺤﯿﻂ ﻣﺠﺎزي ﻧﺼﺐﺷﺪه و در ﭘﺮوژهﻫﺎي دﯾﮕﺮ ﻗﺎﺑﻞ اﺳﺘﻔﺎده ﻧﯿﺴﺖ.
Ahwaz_Hackerz
chardet==3.0.4
idna==2.10
requests==2.24.0
urllib3==1.25.10
ﺑﻪﻣﻨﻈﻮر ﺳﺎﺧﺖ ﯾﮏ ﻣﺤﯿﻂ ﺟﺪﯾﺪ ﺑﺎ ﭘﮑﯿﺞﻫﺎي ﻧﺼﺒﯽ ﻣﺸﺨﺺ ،اﺑﺘﺪا ﯾﮏ ﻣﺤﯿﻂ ﻣﺠﺎزي ﻣﯽﺳﺎزﯾﻢ و
ﺳﭙﺲ آن را ﻓﻌﺎل ﻣﯽﮐﻨﯿﻢ و ﺑﺎ اﺳﺘﻔﺎده از دﺳﺘﻮر زﯾﺮ و ﻓﺎﯾﻞ requirements.txtﭘﮑﯿﺞﻫﺎي ﻗﺒﻠﯽ
را ﻧﺼﺐ ﻣﯽﮐﻨﯿﻢ .درواﻗﻊ pipﺑﺎ ﺧﻮاﻧﺪن ﺧﻂ ﺑﻪ ﺧﻂ اﯾﻦ ﻓﺎﯾﻞ ،ﭘﮑﯿﺞﻫﺎ را ﻧﺼﺐ ﻣﯽﮐﻨﺪ و ﻣﺤﯿﻂ ﻣﺠﺎزي
ﺟﺪﯾﺪ ﻫﻤﭽﻮن ﻣﺤﯿﻂ ﻣﺠﺎزي ﻗﺪﯾﻤﯽ ﺣﺎوي ﭘﮑﯿﺞﻫﺎي ﭘﯿﺸﯿﻦ اﺳﺖ.