Download as pdf or txt
Download as pdf or txt
You are on page 1of 149

Ahwaz_Hackerz

‫‪Ahwaz_Hackerz‬‬

‫ﺑﻪ ﻧﺎم ﺧﺪا‬

‫ﻣﺒﺎﺣﺚﭘﯿﺸﺮﻓﺘﻪدر‬

‫ﭘﺎﯾﺘﻮن‬

‫ﻣﺆﻟﻒ‪:‬‬

‫ﺳﯿﺎوش ﮔﻨﺠﯽ‬
‫‪Ahwaz_Hackerz‬‬

‫‪ ‬ﻋﻨﻮﺍﻥ ﻛﺘﺎﺏ‪ :‬ﻣﺒﺎﺣﺚ ﭘﯿﺸﺮﻓﺘﻪ در ﭘﺎﯾﺘﻮن‬


‫‪ ‬ﻣﻮﻟﻒ ‪ :‬ﺳﯿﺎوش ﮔﻨﺠﯽ‬
‫‪ ‬ﻧﺎﺷﺮ‪ :‬ﻣﻮﺳﺴﻪ ﻓﺮﻫﻨﮕﯽ ﻫﻨﺮي دﯾﺒﺎﮔﺮان ﺗﻬﺮان‬
‫‪‬وﯾﺮاﺳﺘﺎر‪ :‬ﻣﻬﺪﯾﻪ ﻣﺨﺒﺮي‬
‫ﺳﺮﺷﻨﺎﺳﻪ‪:‬ﮔﻨﺠﯽ‪،‬ﺳﯿﺎوش‪-1374 ،‬‬ ‫‪‬ﺻﻔﺤﻪ ﺁﺭﺍﻳﻲ‪ :‬ﻓﺮﻧﻮش ﻋﺒﺪاﻟﻬﯽ‬
‫ﻋﻨﻮان و ﻧﺎم ﭘﺪﯾﺪآور‪:‬ﻣﺒﺎﺣﺚ ﭘﯿﺸﺮﻓﺘﻪ در ﭘﺎﯾﺘﻮن‪/‬ﻣﻮﻟﻒ‪:‬‬ ‫‪‬ﻃﺮﺍﺡ ﺟﻠﺪ‪:‬دارﯾﻮش ﻓﺮﺳﺎﯾﯽ‬
‫ﺳﯿﺎوش ﮔﻨﺠﯽ‪.‬‬
‫‪ ‬ﻧﻮﺑﺖ ﭼﺎﭖ‪ :‬اول‬
‫ﻣﺸﺨﺼﺎت ﻧﺸﺮ‪ :‬ﺗﻬﺮان ‪ :‬دﯾﺒﺎﮔﺮان ﺗﻬﺮان ‪1399:‬‬
‫‪ ‬ﺗﺎﺭﻳﺦ ﻧﺸﺮ‪1399 :‬‬
‫ﻣﺸﺨﺼﺎت ﻇﺎﻫﺮي‪148:‬ص‪:‬ﻣﺼﻮر‪،‬‬
‫‪ ‬ﭼﺎﭖ ﻭ ﺻﺤﺎﻓﻲ‪:‬ﺻﺪف‬
‫ﺷﺎﺑﮏ‪978 -622-218-364-6 :‬‬
‫‪ ‬ﺗﻴﺮﺍﮊ‪ 100:‬ﺟﻠﺪ‬
‫وﺿﻌﯿﺖ ﻓﻬﺮﺳﺖ ﻧﻮﯾﺴﯽ‪ :‬ﻓﯿﭙﺎ‬

‫ﻣﻮﺿﻮع‪:‬ﭘﺎﯾﺘﻮن )زﺑﺎن ﺑﺮﻧﺎﻣﻪ ﻧﻮﯾﺴﯽ ﮐﺎﻣﭙﯿﻮﺗﺮ(‬


‫ﻣﻮﺿﻮع‪Python(computer program language) :‬‬ ‫‪ ‬ﺷﺎﺑﻚ‪978-622-218-364-6 :‬‬
‫ﻣﻮﺿﻮع‪:‬زﺑﺎن ﻫﺎي ﻧﻮﺷﺘﺎري )ﮐﺎﻣﭙﯿﻮﺗﺮ(‬ ‫ﻧﺸﺎﻧﻲ ﻭﺍﺣﺪ ﻓﺮﻭﺵ‪ :‬ﺗﻬﺮﺍﻥ‪ ،‬ﻣﻴﺪﺍﻥ ﺍﻧﻘﻼﺏ‪،‬‬
‫ﻣﻮﺿﻮع‪scripting language(computer science):‬‬ ‫ﺥ ﻛﺎﺭﮔﺮ ﺟﻨﻮﺑﻲ‪ ،‬ﺭﻭﺑﺮﻭﻱ ﭘﺎﺳﺎﮊ ﻣﻬﺴﺘﺎﻥ‪،‬‬
‫رده ﺑﻨﺪي ﮐﻨﮕﺮه‪QA 76/73 :‬‬ ‫ﭘﻼﻙ ‪١٢٥١‬‬
‫رده ﺑﻨﺪي دﯾﻮﯾﯽ‪005/133 :‬‬ ‫ﺗﻠﻔﻦ‪٢٢٠٨٥١١١-٦٦٤١٠٠٤٦ :‬‬
‫ﺷﻤﺎره ﮐﺘﺎﺑﺸﻨﺎﺳﯽ ﻣﻠﯽ‪7345248 :‬‬ ‫ﻓﺮﻭﺷﮕﺎﻫﻬﺎﻱ ﺍﻳﻨﺘﺮﻧﺘﻲ ﺩﻳﺒﺎﮔﺮﺍﻥ ﺗﻬﺮﺍﻥ ‪:‬‬
‫‪WWW.MFTBOOK.IR‬‬
‫‪www.dibagarantehran.com‬‬
‫‪www.dibbook.ir‬‬
‫ﻧﺸﺎﻧﯽ اﯾﻨﺴﺘﺎﮔﺮام دﯾﺒﺎ ‪dibagaran_publishing‬‬ ‫ﻧﺸﺎﻧﯽ ﺗﻠﮕﺮام‪@mftbook:‬‬
‫ﻫﺮﮐﺘﺎب دﯾﺒﺎﮔﺮان ‪،‬ﯾﮏ ﻓﺮﺻﺖ ﺟﺪﯾﺪ ﺷﻐﻠﯽ‪.‬‬
‫ﻫﺮﮔﻮﺷﯽ ﻫﻤﺮاه‪،‬ﯾﮏ ﻓﺮوﺷﮕﺎه ﮐﺘﺎب دﯾﺒﺎﮔﺮان ﺗﻬﺮان‪.‬‬
‫از ﻃﺮﯾﻖ ﺳﺎﯾﺘﻬﺎ و اپ دﯾﺒﺎﮔﺮان‪،‬در ﻫﺮ ﺟﺎي اﯾﺮان ﺑﻪ ﮐﺘﺎﺑﻬﺎي ﻣﺎ دﺳﺘﺮﺳﯽ دارﯾﺪ‪.‬‬
‫‪Ahwaz_Hackerz‬‬

‫‪3‬‬ ‫ﺑﺨﺶ اول ‪ /‬ﻓﺼﻞ ‪ / 1‬ﭘﺎﯾﺘﻮن ‪306‬‬

‫ﻓﻬﺮﺳﺖﻣﻄﺎﻟﺐ‬
‫ﺑﺨﺶ ‪ : 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‬‬

‫ﻣﺒﺎﺣﺚ ﭘﯿﺸﺮﻓﺘﻪ در ﭘﺎﯾﺘﻮن ‪ /‬ﻣﻮﺳﺴﻪ ﻓﺮﻫﻨﮕﯽ ﻫﻨﺮي دﯾﺒﺎﮔﺮان ﺗﻬﺮان‬ ‫‪4‬‬

‫ﻟﯿﺴﺖ ﺧﺎﻟﯽ ‪43 ...................................................................................................‬‬


‫ﺷﻤﺎرش اﺟﺰاي ﻟﯿﺴﺖ ‪45 ........................................................................................‬‬
‫ﻣﻘﺎﯾﺴﻪ ﺑﺎ ﭼﻨﺪﯾﻦ ﻣﻘﺪار ‪46.......................................................................................‬‬
‫اﺳﺘﻔﺎده از ‪ if‬و ‪ else‬ﯾﮏ ﺧﻄﯽ ‪46..............................................................................‬‬
‫اﺳﺘﻔﺎده از ‪ else‬در ﺣﻠﻘﻪ ‪47 ....................................................................................‬‬
‫اﺳﺘﻔﺎده از ﺗﻮاﺑﻊ ﺑﻪ ﺻﻮرت زﻧﺠﯿﺮوار ‪48 .........................................................................‬‬
‫اﺳﺘﻔﺎده از ‪ index‬ﻣﻨﻔﯽ ‪48 .....................................................................................‬‬
‫ﺟﻤﻊ‪ ،‬ﮐﻤﺘﺮﯾﻦ و ﺑﯿﺸﺘﺮﯾﻦ ﻣﻘﺪار ‪49 .............................................................................‬‬
‫اﺑﺘﺪا‪ ،‬اﻧﺘﻬﺎ و ﻣﯿﺎﻧﮥ ﻟﯿﺴﺖ ‪51 .....................................................................................‬‬
‫اﺳﺘﻔﺎده از ‪52 ................................................................................... namedtuple‬‬
‫ﻓﺼﻞ ‪ / 2‬ﻧﮑﺎت ﮐﻠﯽ ‪54 .........................................................................................‬‬
‫ﺑﺨﺶ ‪ : 3‬ﺑﺮﻧﺎﻣﻪﻧﻮﯾﺴﯽ ﺗﺎﺑﻌﯽ ‪57 ..........................................................................‬‬
‫ﻓﺼﻞ‪ / 1‬ﻣﻘﺪﻣﻪ ‪58 ...............................................................................................‬‬
‫ﺗﺎﺑﻊ ﺑﻪﻋﻨﻮان ﭘﺎراﻣﺘﺮ ‪59 ...........................................................................................‬‬
‫ﺗﻮاﺑﻊ ‪61............................................................................................... Lambda‬‬
‫ﺗﺎﺑﻊ ‪63...................................................................................................... Map‬‬
‫ﺗﺎﺑﻊ ‪65.................................................................................................... Filter‬‬
‫ﺗﺎﺑﻊ ‪66................................................................................................. Reduce‬‬
‫‪67................................................................................... List Comprehension‬‬
‫‪70 ................................................................................... Set Comprehension‬‬
‫‪71 ....................................................................... Dictionary Comprehension‬‬
‫ﻓﺼﻞ‪ / 2‬ژﻧﺮاﺗﻮر ‪72 .............................................................................................‬‬
‫ژﻧﺮاﺗﻮر ﭼﯿﺴﺖ؟ ‪73 ...............................................................................................‬‬
‫ﺳﺎﺧﺖ ژﻧﺮاﺗﻮر ‪73 .................................................................................................‬‬
‫‪76................................................................................. Generator Expression‬‬
‫ﺗﺎﺑﻊ ‪76...................................................................................................... send‬‬
‫ﺗﺎﺑﻊ ‪77 ................................................................................................... throw‬‬
‫ﺗﺎﺑﻊ ‪78 .................................................................................................... close‬‬
‫ﻣﺰاﯾﺎي اﺳﺘﻔﺎده از ژﻧﺮاﺗﻮر ‪79 .....................................................................................‬‬
‫ﻓﺼﻞ ‪80.................................................................................. DECORATOR / 3‬‬
‫ﺗﺎﺑﻊ داﺧﻠﯽ ‪81 ....................................................................................................‬‬
‫‪ Decorator‬ﭼﯿﺴﺖ؟ ‪82 .......................................................................................‬‬
‫روش ﻧﻮﺷﺘﻦ ﺑﻬﺘﺮ ‪84 .............................................................................................‬‬
‫‪Ahwaz_Hackerz‬‬

‫‪5‬‬ ‫ﺑﺨﺶ اول ‪ /‬ﻓﺼﻞ ‪ / 1‬ﭘﺎﯾﺘﻮن ‪306‬‬

‫ﺗﻐﯿﯿﺮ ﺧﺮوﺟﯽ ﺗﺎﺑﻊ ﺑﺎ اﺳﺘﻔﺎده از ‪86.............................................................. Decorator‬‬


‫‪ Decorator‬ﺑﺎ ورودي ‪87 .......................................................................................‬‬
‫ﻧﮕﻬﺪاري اﻃﻼﻋﺎت در ‪88 ......................................................................... Decorator‬‬
‫‪ Decorator‬در ﮐﻼس ‪89 ......................................................................................‬‬
‫ﻣﻄﺎﻟﻌﮥ ﺑﯿﺸﺘﺮ ‪90 ..................................................................................................‬‬
‫ﻓﺼﻞ‪91 ...................................................................... CONTEXT MANAGER / 4‬‬
‫ﻣﻘﺪﻣﻪ ‪92 ..........................................................................................................‬‬
‫ﭘﯿﺎدهﺳﺎزي ‪ Context Manager‬ﺑﺎ ﮐﻼس ‪92 ...............................................................‬‬
‫ﭘﯿﺎدهﺳﺎزي ‪ Context Manager‬ﺑﺎ ژﻧﺮاﺗﻮر ‪93 ..............................................................‬‬
‫ﺑﺨﺶ ‪ : 4‬ﻻگ ‪95 ...............................................................................................‬‬
‫ﻓﺼﻞ‪ / 1‬ﻣﻘﺪﻣﻪ ‪96 ...............................................................................................‬‬
‫ﻣﻘﺪﻣﻪ ‪97 ..........................................................................................................‬‬
‫ﻻگ ﭼﯿﺴﺖ؟ ‪97 ..................................................................................................‬‬
‫ﭼﺮا ﻻگ ﮐﻨﯿﻢ؟ ‪98 ...............................................................................................‬‬
‫ﻫﺪف از ﻻگ ‪99 ...................................................................................................‬‬
‫ﭼﻪ ﭼﯿﺰي را ﻻگ ﮐﻨﯿﻢ؟ ‪99 .....................................................................................‬‬
‫ﭼﻪ ﭼﯿﺰي را ﻻگ ﻧﮑﻨﯿﻢ؟ ‪100...................................................................................‬‬
‫ﻓﺼﻞ ‪ / 2‬ﻣﺎژول ‪102.............................................................................. LOGGING‬‬
‫ﻣﻌﺮﻓﯽ ‪103.........................................................................................................‬‬
‫ﻓﺮﻣﺖ ﺧﺮوﺟﯽ ﻻگ ‪105..........................................................................................‬‬
‫ﮔﺰارش ‪ Stack Traces‬از اﺳﺘﺜﻨﺎ ‪107.........................................................................‬‬
‫ﻧﺎم ﻻﮔﺮ ‪108.......................................................................................................‬‬
‫اﺳﺘﻔﺎده از ‪108....................................................................................... Handler‬‬
‫اﺳﺘﻔﺎده از ‪111........................................................................................... Filter‬‬
‫اﺳﺘﻔﺎده از ﻓﺎﯾﻞ ﭘﯿﮑﺮﺑﻨﺪي ‪112...................................................................................‬‬
‫ﺑﺨﺶ ‪ : 5‬ﺗﺴﺖ ‪114 ............................................................................................‬‬
‫ﻓﺼﻞ‪ / 1‬ﻣﻘﺪﻣﻪ ‪115 ..............................................................................................‬‬
‫ﻣﻘﺪﻣﻪ ‪116 .........................................................................................................‬‬
‫اﻫﻤﯿﺖ ﺗﺴﺖ ‪116 .................................................................................................‬‬
‫ﭼﻪ ﭼﯿﺰي را ﺗﺴﺖ ﮐﻨﯿﻢ؟ ‪116 ...................................................................................‬‬
‫روﯾﮑﺮدﻫﺎي ﻣﺨﺘﻠﻒ ﺗﺴﺖ ‪117...................................................................................‬‬
‫ﺳﻄﻮح ﻣﺨﺘﻠﻒ ﺗﺴﺖ ‪117.........................................................................................‬‬
‫‪Ahwaz_Hackerz‬‬

‫ﻣﺒﺎﺣﺚ ﭘﯿﺸﺮﻓﺘﻪ در ﭘﺎﯾﺘﻮن ‪ /‬ﻣﻮﺳﺴﻪ ﻓﺮﻫﻨﮕﯽ ﻫﻨﺮي دﯾﺒﺎﮔﺮان ﺗﻬﺮان‬ ‫‪6‬‬

‫ﻓﺼﻞ ‪ / 2‬ﻣﺎژول ‪119................................................................................. PYTEST‬‬


‫ﻣﻌﺮﻓﯽ ‪120.........................................................................................................‬‬
‫اﺳﺘﻔﺎده از ‪120......................................................................................... PyTest‬‬
‫ﻣﺪﯾﺮﯾﺖ اﺳﺘﺜﻨﺎ ‪122...............................................................................................‬‬
‫اﺳﺘﻔﺎده از ‪124......................................................................................... Fixture‬‬
‫ﭘﺎراﻣﺘﺮيﮐﺮدن ﺗﺴﺖﻫﺎ ‪127......................................................................................‬‬
‫ﻧﺎدﯾﺪهﮔﺮﻓﺘﻦ ﺗﺴﺖﻫﺎ ‪128........................................................................................‬‬
‫اﻓﺰوﻧﻪﻫﺎي ‪129......................................................................................... PyTest‬‬
‫ﺟﻤﻊﺑﻨﺪي ‪133....................................................................................................‬‬
‫ﺑﺨﺶ ‪ : 6‬ﭘﮑﯿﺞ ‪134............................................................................................‬‬
‫ﻓﺼﻞ‪ / 1‬ﺳﺎﺧﺖ ﯾﮏ ﭘﮑﯿﺞ ﺳﺎده ‪135.........................................................................‬‬
‫ﺳﺎﺧﺖ ﯾﮏ ﭘﮑﯿﺞ ﺳﺎده ‪136 ......................................................................................‬‬
‫ﻓﺎﯾﻞ ‪136 ........................................................................................... helper.py‬‬
‫ﻓﺎﯾﻞ ‪137........................................................................................ __init__.py‬‬
‫ﻓﺎﯾﻞ ‪138..................................................................................... test_cprint.py‬‬
‫ﻓﺎﯾﻞ ‪139............................................................................................. setup.py‬‬
‫ﻓﺼﻞ ‪ / 2‬اﻧﺘﺸﺎر ﭘﮑﯿﺞ در ‪141 ........................................................................ PYPI‬‬
‫ﺳﺎﺧﺖ ﺣﺴﺎب ﮐﺎرﺑﺮي در ‪142........................................................................... PyPI‬‬
‫ﺳﺎﺧﺖ ﭘﮑﯿﺞ ‪142..................................................................................................‬‬
‫ﻧﺼﺐ ‪143............................................................................................... Twine‬‬
‫ﻓﺼﻞ ‪ / 3‬ﻣﺤﯿﻂ ﻣﺠﺎزي ‪145 ...................................................................................‬‬
‫ﻣﺤﯿﻂ ﻣﺠﺎزي ﭼﯿﺴﺖ؟ ‪146 .....................................................................................‬‬
‫ﻧﺼﺐ ‪146 .......................................................................................... virtualenv‬‬
‫ﺳﺎﺧﺖ ﯾﮏ ﻣﺤﯿﻂ ﻣﺠﺎزي ‪146 ..................................................................................‬‬
‫ﻓﻌﺎلﺳﺎزي ﯾﮏ ﻣﺤﯿﻂ ﻣﺠﺎزي ‪147..............................................................................‬‬
‫ﻏﯿﺮﻓﻌﺎلﮐﺮدن ﻣﺤﯿﻂ ﻣﺠﺎزي ‪147...............................................................................‬‬
‫ﻧﺼﺐ ﭘﮑﯿﺞ ‪147...................................................................................................‬‬
‫ﻟﯿﺴﺖ ﭘﮑﯿﺞﻫﺎي ﻧﺼﺒﯽ ‪148......................................................................................‬‬
‫‪Ahwaz_Hackerz‬‬

‫ﻣﻘﺪﻣﻪ ﻧﺎﺷﺮ‬

‫ ‪ 5632 6‬ارات‪ 467‬د


‪8‬ان‪4‬انبا‪ 75‬ا

‫‪6‬‬ ‫ ‬ ‫‪76 9‬‬ ‫‬
‫ ا‬
‫‪ # $%‬روز !
‪  4 3‬و  ر را   ا
ن ‪  7‬د‪.‬‬

‫‪9 3‬‬
‫ب د‪8‬ان ‪4‬ان‪،‬ﯾ ‪ 54‬ﯾ  و ‬
‫ﺣﻤﺪ و ﺳﭙﺎس اﻳﺰد ﻣﻨﺎن را ﻛﻪ ﺑﺎ اﻟﻄﺎف ﺑﻴﻜﺮان ﺧﻮد اﻳﻦ ﺗﻮﻓﻴﻖ را ﺑﻪ ﻣﺎ ارزاﻧﻲ داﺷﺖ ﺗﺎ ﺑﺘﻮاﻧﻴﻢ در راه‬

‫ارﺗﻘﺎي داﻧﺶ ﻋﻤﻮﻣﻲ و ﻓﺮﻫﻨﮕﻲ اﻳﻦ ﻣﺮز و ﺑﻮم در زﻣﻴﻨﻪ ﭼﺎپ و ﻧﺸﺮ ﻛﺘﺐ ﻋﻠﻤﻲ داﻧﺸﮕﺎﻫﻲ‪ ،‬ﻋﻠﻮم ﭘﺎﻳﻪ و‬

‫ﺑﻪ وﻳﮋه ﻋﻠﻮم ﻛﺎﻣﭙﻴﻮﺗﺮ و اﻧﻔﻮرﻣﺎﺗﻴﻚ ﮔﺎمﻫﺎﻳﻲ ﻫﺮﭼﻨﺪ ﻛﻮﭼﻚ ﺑﺮداﺷﺘﻪ و در اﻧﺠﺎم رﺳﺎﻟﺘﻲ ﻛﻪ ﺑﺮ ﻋﻬﺪه‬

‫دارﻳﻢ‪ ،‬ﻣﺆﺛﺮ واﻗﻊ ﺷﻮﻳﻢ‪.‬‬

‫ﮔﺴﺘﺮدﮔﻲ ﻋﻠﻮم و ﺗﻮﺳﻌﻪ روزاﻓﺰون آن‪ ،‬ﺷﺮاﻳﻄﻲ را ﺑﻪ وﺟﻮد آورده ﻛﻪ ﻫﺮ روز ﺷﺎﻫﺪ ﺗﺤﻮﻻت اﺳﺎﺳﻲ‬

‫ﭼﺸﻤﮕﻴﺮي در ﺳﻄﺢ ﺟﻬﺎن ﻫﺴﺘﻴﻢ‪ .‬اﻳﻦ ﮔﺴﺘﺮش و ﺗﻮﺳﻌﻪ ﻧﻴﺎز ﺑﻪ ﻣﻨﺎﺑﻊ ﻣﺨﺘﻠﻒ از ﺟﻤﻠﻪ ﻛﺘﺎب را ﺑﻪ‬

‫ﻋﻨﻮان ﻗﺪﻳﻤﻲﺗﺮﻳﻦ و راﺣﺖﺗﺮﻳﻦ راه دﺳﺘﻴﺎﺑﻲ ﺑﻪ اﻃﻼﻋﺎت و اﻃﻼعرﺳﺎﻧﻲ‪ ،‬ﺑﻴﺶ از ﭘﻴﺶ روﺷﻦ ﻣﻲﻧﻤﺎﻳﺪ‪.‬‬

‫در اﻳﻦ راﺳﺘﺎ‪ ،‬واﺣﺪ اﻧﺘﺸﺎرات ﻣﺆﺳﺴﻪ ﻓﺮﻫﻨﮕﻲ ﻫﻨﺮي دﻳﺒﺎﮔﺮان ﺗﻬﺮان ﺑﺎ ﻫﻤﻜﺎري ﺟﻤﻌﻲ از اﺳﺎﺗﻴﺪ‪،‬‬

‫ﻣﺆﻟﻔﺎن‪ ،‬ﻣﺘﺮﺟﻤﺎن‪ ،‬ﻣﺘﺨﺼﺼﺎن‪ ،‬ﭘﮋوﻫﺸﮕﺮان‪ ،‬ﻣﺤﻘﻘﺎن و ﻧﻴﺰ ﭘﺮﺳﻨﻞ ورزﻳﺪه و ﻣﺎﻫﺮ در زﻣﻴﻨﻪ اﻣﻮر ﻧﺸﺮ‬

‫درﺻﺪد ﻫﺴﺘﻨﺪ ﺗﺎ ﺑﺎ ﺗﻼشﻫﺎي ﻣﺴﺘﻤﺮ ﺧﻮد ﺑﺮاي رﻓﻊ ﻛﻤﺒﻮدﻫﺎ و ﻧﻴﺎزﻫﺎي ﻣﻮﺟﻮد‪ ،‬ﻣﻨﺎﺑﻌﻲ ﭘ‪‬ﺮﺑﺎر‪ ،‬ﻣﻌﺘﺒﺮ و‬

‫ﺑﺎ ﻛﻴﻔﻴﺖ ﻣﻨﺎﺳﺐ در اﺧﺘﻴﺎر ﻋﻼﻗﻤﻨﺪان ﻗﺮار دﻫﻨﺪ‪.‬‬

‫و ﺗﻼش ﺟﻤﻌﻲ از ﻫﻤﻜﺎران اﻧﺘﺸﺎرات‬ ‫"‬‫ﻛﺘﺎﺑﻲ ﻛﻪ در دﺳﺖ دارﻳﺪ ﺑﺎ ﻫﻤﺖ "ﺟﻨﺎب آﻗﺎي ﺳﻴﺎوش ﮔﻨﺠﻲ‬
‫ﻣﻴﺴﺮ ﮔﺸﺘﻪ ﻛﻪ ﺷﺎﻳﺴﺘﻪ اﺳﺖ از ﻳﻜﺎﻳﻚ اﻳﻦ ﮔﺮاﻣﻴﺎن ﺗﺸﻜﺮ و ﻗﺪرداﻧﻲ ﻛﻨﻴﻢ‪.‬‬

‫زﻫﺮه ﻗﺰﻟﺒﺎش‬ ‫ﻛﺎرﺷﻨﺎﺳﻲ و ﻧﻈﺎرت ﺑﺮ ﻣﺤﺘﻮا‪:‬‬

‫در ﺧﺎﺗﻤﻪ ﺿﻤﻦ ﺳﭙﺎﺳﮕﺰاري از ﺷﻤﺎ داﻧﺶﭘﮋوه ﮔﺮاﻣﻲ درﺧﻮاﺳﺖ ﻣﻲﻧﻤﺎﻳﺪ ﺑﺎ ﻣﺮاﺟﻌﻪ ﺑﻪ آدرس‬

‫)ارﺗﺒﺎط ﺑﺎ ﻣﺸﺘﺮي( ﻓﺮم ﻧﻈﺮﺳﻨﺠﻲ را ﺑﺮاي ﻛﺘﺎﺑﻲ ﻛﻪ در دﺳﺖ دارﻳﺪ ﺗﻜﻤﻴﻞ و‬ ‫‪dibagaran.mft.info‬‬
‫ارﺳﺎل ﻧﻤﻮده‪ ،‬اﻧﺘﺸﺎرات دﻳﺒﺎﮔﺮان ﺗﻬﺮان را ﻛﻪ ﺟﻠﺐ رﺿﺎﻳﺖ و وﻓﺎداري ﻣﺸﺘﺮﻳﺎن را ﻫﺪف ﺧﻮد ﻣﻲداﻧﺪ‪،‬‬

‫ﻳﺎري ﻓﺮﻣﺎﻳﻴﺪ‪.‬‬

‫اﻣﻴﺪوارﻳﻢ ﻫﻤﻮاره ﺑﻬﺘﺮ از ﮔﺬﺷﺘﻪ ﺧﺪﻣﺎت و ﻣﺤﺼﻮﻻت ﺧﻮد را ﺗﻘﺪﻳﻢ ﺣﻀﻮرﺗﺎن ﻧﻤﺎﻳﻴﻢ‪.‬‬

‫ﻣﺪﻳﺮ اﻧﺘﺸﺎرات‬

‫ﻣﺆﺳﺴﻪ ﻓﺮﻫﻨﮕﻲ ﻫﻨﺮي دﻳﺒﺎﮔﺮان ﺗﻬﺮان‬


‫‪bookmarket@mft.info‬‬
‫‪Ahwaz_Hackerz‬‬

‫ﻣﺒﺎﺣﺚ ﭘﯿﺸﺮﻓﺘﻪ در ﭘﺎﯾﺘﻮن ‪ /‬ﻣﻮﺳﺴﻪ ﻓﺮﻫﻨﮕﯽ ﻫﻨﺮي دﯾﺒﺎﮔﺮان ﺗﻬﺮان‬ ‫‪8‬‬

‫‪ ‬ﻣﻘﺪﻣﻪ ﻣﺆﻟﻒ‬
‫زﺑﺎن ﭘﺎﯾﺘﻮن ﺑﻪدﻟﯿﻞ ﺳﺎدﮔﯽ و اﻣﮑﺎﻧﺎت ﻓﺮاواﻧﯽ ﮐﻪ دارد ﺑﻪ ﯾﮑﯽ از زﺑﺎنﻫﺎي ﻣﺤﺒﻮب و ﭘﺮاﺳﺘﻔﺎده در ﺣﻮزهﻫﺎي‬
‫ﻣﺨﺘﻠﻒ ﺗﺒﺪﯾﻞ ﮔﺸﺘﻪ و اﻓﺮاد ﺑﺴﯿﺎري از رﺷﺘﻪﻫﺎي ﻣﺨﺘﻠﻒ ﺑﻪ ﻓﺮاﮔﯿﺮي اﯾﻦ زﺑﺎن ﻣﯽﭘﺮدازﻧﺪ‪ .‬ﺑﺴﯿﺎري از‬
‫ﺑﺮﻧﺎﻣﻪﻧﻮﯾﺴﺎن ﺑﺎ داﺷﺘﻦ ﺗﺠﺮﺑﮥ ﮐﺎر ﺑﺎ زﺑﺎن ﺑﺮﻧﺎﻣﻪﻧﻮﯾﺴﯽ دﯾﮕﺮ‪ ،‬ﺷﺮوع ﺑﻪ ﯾﺎدﮔﯿﺮي ﭘﺎﯾﺘﻮن ﻣﯽﮐﻨﻨﺪ‪ .‬ﺑﺴﯿﺎري دﯾﮕﺮ ﻫﻢ‬
‫از ﻫﻤﺎن اﺑﺘﺪا ﺑﺎ دﺳﺘﻮرات اﺑﺘﺪاﯾﯽ ﭘﺎﯾﺘﻮن آﺷﻨﺎ ﺷﺪه و در ﺳﻄﺢ ﺑﺮﻧﺎﻣﻪﻧﻮﯾﺲ ﻣﺒﺘﺪي ﺑﺎﻗﯽﻣﺎﻧﺪهاﻧﺪ‪ .‬در اﯾﻦ ﮐﺘﺎب‬
‫ﻗﺼﺪ دارم ﺑﻪ اﯾﻦ اﻓﺮاد ﻣﺒﺎﺣﺚ ﭘﯿﺸﺮﻓﺘﻪ و ﻻزم در زﺑﺎن ﭘﺎﯾﺘﻮن را ﮐﻪ ﺑﺮاي ﻣﻮﻗﻌﯿﺖﻫﺎي ﺷﻐﻠﯽ ﻻزم و ﺣﯿﺎﺗﯽ اﺳﺖ‬
‫را در ﺷﺶ ﺑﺨﺶ ﺑﻪﺻﻮرت ﮐﺎرﺑﺮدي آﻣﻮزش دﻫﻢ‪.‬‬
‫در ﺑﺨﺶ اول ﺑﻪ ﺷﺮح درﻣﻮرد وﯾﮋﮔﯽﻫﺎي ﺟﺪﯾﺪ در ﭼﻬﺎر ﻧﺴﺨﮥ آﺧﺮ ﭘﺎﯾﺘﻮن ﭘﺮداﺧﺘﻪام‪ .‬وﯾﮋﮔﯽﻫﺎﯾﯽ ﮐﻪ در ﻫﺮ‬
‫ﻧﺴﺨﻪ از ﭘﺎﯾﺘﻮن ﺑﻪ آن اﺿﺎﻓﻪ ﻣﯽﺷﻮد ﺑﺴﯿﺎر زﯾﺎد ﻫﺴﺘﻨﺪ و ﻧﮕﺎرش ﻫﻤﮥ آﻧﻬﺎ ﺧﻮد ﮐﺘﺎﺑﯽ ﺟﺪا ﺧﻮاﻫﺪ ﺑﻮد؛ ﻟﺬا در اﯾﻦ‬
‫ﺑﺨﺶ ﺗﻨﻬﺎ ﺑﻪ ﻣﻮاردي ﮐﻪ ﻃﺒﻖ ﺗﺠﺮﺑﮥ اﯾﻨﺠﺎﻧﺐ ﺑﺴﯿﺎر ﮐﺎرﺑﺮدي ﻫﺴﺘﻨﺪ‪ ،‬ﺑﻪ ﻣﻌﺮﻓﯽ ﺣﻀﻮر رﺳﯿﺪهاﻧﺪ‪.‬‬
‫در ﺑﺨﺶ دوم‪ ،‬ﺑﺮاﺳﺎس ﺗﺠﺮﺑﮥ ﭼﻨﺪﯾﻦ ﺳﺎﻟﮥ ﺧﻮدم در اﻣﺮ آﻣﻮزش زﺑﺎن ﭘﺎﯾﺘﻮن‪ ،‬ﮐﺪﻫﺎﯾﯽ دﯾﺪهام ﮐﻪ اﺷﺘﺒﺎﻫﺎت راﯾﺞ‬
‫ﺑﺴﯿﺎري از داﻧﺸﺠﻮﯾﺎﻧﻢ ﺑﻮده اﺳﺖ‪ .‬ﺳﻌﯽ ﮐﺮدهام ﺗﻤﺎم اﯾﻦ اﺷﺘﺒﺎﻫﺎت را در اﯾﻦ ﮐﺘﺎب ﺟﻤﻊآوري ﮐﻨﻢ ﺗﺎ ﺑﻪ ﮔﻨﺠﯿﻨﮥ‬
‫ارزﺷﻤﻨﺪي از اﺷﺘﺒﺎﻫﺎت راﯾﺞ و روش ﺣﻞ آنﻫﺎ ﺗﺒﺪﯾﻞ ﮔﺮدد ﺗﺎ ﺑﺎ ﻣﻄﺎﻟﻌﻪ آن‪ ،‬دﯾﮕﺮ ﺷﻤﺎ ﻫﻢ آن اﺷﺘﺒﺎﻫﺎت را ﺗﮑﺮار‬
‫ﻧﮑﻨﯿﺪ‪ .‬در اﯾﻦ ﺑﺨﺶ ﺑﻪ ﻣﻘﺎﯾﺴﮥ ﮐﺎﻣﻞ ﺑﯿﻦ دو ﺑﺮﻧﺎﻣﻪﻧﻮﯾﺲ ﻣﺒﺘﺪي و ﺣﺮﻓﻪاي ﭘﺮداﺧﺘﻪام‪ .‬از واژة ﺑﺮﻧﺎﻣﻪﻧﻮﯾﺲ ﺑﺪ در‬
‫اﯾﻦ ﻓﺼﻞ اﺳﺘﻔﺎده ﻧﮑﺮدهام‪ ،‬زﯾﺮا اﻋﺘﻘﺎد دارم ﮐﻪ ﺑﺮﻧﺎﻣﻪﻧﻮﯾﺲ ﺑﺪ وﺟﻮد ﻧﺪارد‪ .‬ﺑﺮﻧﺎﻣﻪﻧﻮﯾﺲ ﻣﺒﺘﺪي ﺑﺎﻋﺚ ﺑﻪ‪-‬‬
‫وﺟﻮدآﻣﺪن ﮐﺪﻫﺎي ﺑﺪ ﻣﯽﺷﻮد زﯾﺮا آﻣﻮزشﻫﺎ‪ ،‬ﺑﻪﺻﻮرت درﺳﺖ و اﺻﻮﻟﯽ ﺑﻪ او داده ﻧﺸﺪه اﺳﺖ‪.‬‬
‫در ﺑﺨﺶ ﺳﻮم‪ ،‬ﺑﻪ ﺑﺮرﺳﯽ و ﺗﻮﺿﯿﺢ ﻣﻔﺼﻞ ﯾﮑﯽ از ﭘﺎراداﯾﻢﻫﺎي ﭘﺮﮐﺎرﺑﺮد ﺑﺮﻧﺎﻣﻪﻧﻮﯾﺴﯽ ﯾﻌﻨﯽ ﺑﺮﻧﺎﻣﻪﻧﻮﯾﺴﯽ ﺗﺎﺑﻌﯽ‬
‫ﭘﺮداﺧﺘﻪام‪ .‬از ﺗﻮاﺑﻊ ﮐﻮﭼﮏ ﮔﺮﻓﺘﻪ ﺗﺎ ﻣﻔﺎﻫﯿﻢ ﭘﯿﭽﯿﺪه ﻫﻤﭽﻮن ژﻧﺮاﺗﻮر را ﺑﺎ ﻣﺜﺎلﻫﺎﯾﯽ ﺳﺎده و ﮐﺎرﺑﺮدي ﺑﻪﻃﻮر ﮐﺎﻣﻞ‬
‫آﻣﻮزش دادهام‪.‬‬
‫در ﺑﺨﺶ ﭼﻬﺎرم‪ ،‬ﯾﮑﯽ از ﺟﻨﺒﻪﻫﺎي ﻣﻬﻢ در ﻧﺮماﻓﺰار ﯾﻌﻨﯽ ﻻگ را ﺑﺮرﺳﯽ ﻣﯽﮐﻨﯿﻢ‪ .‬ﻫﻨﻮز ﻫﻢ ﺗﻌﺪاد ﺑﺴﯿﺎر زﯾﺎدي از‬
‫ﺑﺮﻧﺎﻣﻪﻧﻮﯾﺴﺎن ﻫﺴﺘﻨﺪ ﮐﻪ ﺑﺎ دﺳﺘﻮر ‪ print‬ﻻگ را ﻧﻤﺎﯾﺶ ﻣﯽدﻫﻨﺪ‪ .‬در اﯾﻦ ﻓﺼﻞ ﺑﻪ ﺑﺮرﺳﯽ درﺳﺖ و ﮐﺎﻣﻞ‬
‫ﻧﺤﻮة اﺳﺘﻔﺎده از ﻻگ ﭘﺮداﺧﺘﻪام ﺗﺎ ﭘﺮوژهﻫﺎﯾﯽ ﮐﻪ اﻧﺠﺎم ﻣﯽدﻫﯿﺪ ﺣﺎوي ﻻگﻫﺎي دﺳﺘﻪﺑﻨﺪيﺷﺪه و ﮐﺎرﺑﺮدي ﺑﺎﺷﺪ‪.‬‬
‫در ﺑﺨﺶ ﭘﻨﺠﻢ‪ ،‬آزﻣﻮن ﻧﺮماﻓﺰار را ﻣﻮرد ﺑﺤﺚ ﻗﺮار ﻣﯽدﻫﻢ‪ .‬در اﺑﺘﺪاي راه ﺑﺮﻧﺎﻣﻪﻧﻮﯾﺴﯽ ﮐﻪ ﺑﻮدم ﻫﯿﭻ دﯾﺪي ﻧﺴﺒﺖ‬
‫ﺑﻪ ﺗﺴﺖﻫﺎي ﻧﺮماﻓﺰار ﻧﺪاﺷﺘﻢ و اﺻﻼً ﻧﻤﯽداﻧﺴﺘﻢ ﮐﻪ ﭼﻪ ﻟﺰوﻣﯽ در ﻧﻮﺷﺘﻦ و اﻧﺠﺎم ﺗﺴﺖ وﺟﻮد دارد‪ .‬ﺷﺎﯾﺪ ﺷﻤﺎ ﻫﻢ‬
‫در ﺣﺎل ﺣﺎﺿﺮ ﻫﻤﭽﻮن ﻣﻦ ﻓﮑﺮ ﻣﯽﮐﻨﯿﺪ وﻟﯽ ﺑﺎ ﮔﺬﺷﺖ زﻣﺎن و ﮐﺴﺐ ﺗﺠﺮﺑﻪ و ﻣﻄﺎﻟﻌﻪ درﯾﺎﻓﺘﻢ‪ ،‬ﺗﺴﺖ ﯾﮑﯽ از‬
‫زﯾﺒﺎﺗﺮﯾﻦ ﮐﺎرﻫﺎ در ﺑﺮﻧﺎﻣﻪﻧﻮﯾﺴﯽ و ﺳﺎﺧﺖ ﻧﺮماﻓﺰار اﺳﺖ‪ .‬ﻧﻪﺗﻨﻬﺎ ﮐﺎري زﻣﺎنﮔﯿﺮ ﻧﯿﺴﺖ ﺑﻠﮑﻪ ﯾﮏ ﻫﻨﺮ اﺳﺖ ﺗﺎ در‬
‫زﻣﺎن ﺻﺮﻓﻪﺟﻮﯾﯽ ﮐﻨﺪ!‬
‫در ﺑﺨﺶ ﺷﺸﻢ‪ ،‬ﻧﺤﻮة ﺳﺎﺧﺖ ﯾﮏ ﭘﮑﯿﺞ و اﻧﺘﺸﺎر آن را ﺗﻮﺿﯿﺢ دادهام‪ .‬اﻧﺘﺸﺎر ﭘﮑﯿﺞﻫﺎ ﻣﯽﺗﻮاﻧﺪ ﺑﻪ دﯾﮕﺮ‬
‫ﺗﻮﺳﻌﻪدﻫﻨﺪﮔﺎن در ﺟﻠﻮﮔﯿﺮي از ﻧﻮﺷﺘﻦ دوﺑﺎره ﮐﺪﻫﺎ ﮐﻤﮏ ﮐﻨﺪ و ﺑﺮاي ﺷﻤﺎ اﻋﺘﺒﺎر ﺑﻪ ﻫﻤﺮاه داﺷﺘﻪ ﺑﺎﺷﺪ‪.‬‬
‫در اﻧﺘﻬﺎ ﻻزم ﺑﻪ ذﮐﺮ اﺳﺖ ﮐﻪ اﯾﻦ ﮐﺘﺎب ﯾﻘﯿﻨﺎً ﺧﺎﻟﯽ از اﺷﮑﺎل ﻧﯿﺴﺖ‪ .‬از ﺷﻤﺎ ﺧﻮاﻧﻨﺪة ﻋﺰﯾﺰ و ﮔﺮاﻣﯽ ﺧﻮاﻫﺸﻤﻨﺪم‬
‫در ﺻﻮرت داﺷﺘﻦ ﭘﯿﺸﻨﻬﺎد و ﯾﺎ اﻧﺘﻘﺎد ﺣﺘﻤﺎً ﺑﺎ ﺑﻨﺪه در ﻣﯿﺎن ﺑﮕﺬارﯾﺪ‪.‬‬
‫ﺳﯿﺎوش ﮔﻨﺠﯽ‬
‫‪Contact@Siyanew.ir‬‬
‫‪Ahwaz_Hackerz‬‬

‫‪9‬‬ ‫ﺑﺨﺶ اول ‪ /‬ﻓﺼﻞ ‪ / 1‬ﭘﺎﯾﺘﻮن ‪306‬‬

‫ﭘﯿﺸﮕﻔﺘﺎر‬
‫ﻧﺨﺴﺖ‪ ،‬از اﯾﺰد ﯾﮑﺘﺎ ﺳﭙﺎﺳﮕﺰارم ﮐﻪ ﻣﺮا از داﻧﺶ ﺑﯽﮐﺮان ﺧﻮد ﺳﻬﻤﯽ داد و راﻫﯽ را ﺑﺮاﯾﻢ ﻣﺤﻘﻖ ﮐﺮد ﺗﺎ‬
‫ﺑﺘﻮاﻧﻢ داﻧﺶ اﻧﺪك ﺧﻮد را ﺑﻪ اﻧﺴﺎنﻫﺎي ﻓﺮﻫﯿﺨﺘﻪ و ﺟﻮﯾﺎي ﻋﻠﻢ ﻣﻨﺘﻘﻞ ﮐﻨﻢ و ﺳﻬﻤﯽ در ﭘﯿﺸﺮﻓﺖ ﻋﻠﻢ و‬
‫داﻧﺶ ﺑﺸﺮﯾﺖ داﺷﺘﻪ ﺑﺎﺷﻢ‪ .‬از آﻗﺎي ﻓﺮﺳﺎﯾﯽ‪ ،‬ﻣﺪﯾﺮ ﻣﺤﺘﺮم اﻧﺘﺸﺎرات دﯾﺒﺎﮔﺮان و ﻫﻤﭽﻨﯿﻦ ﺧﺎﻧﻢ ﻗﺰﻟﺒﺎش‪،‬‬
‫ﻣﺴﺌﻮل ﺗﺄﻟﯿﻒ و ﺗﺮﺟﻤﮥ اﻧﺘﺸﺎرات دﯾﺒﺎﮔﺮان‪ ،‬ﺻﻤﯿﻤﺎﻧﻪ ﺗﺸﮑﺮ ﻣﯽﮐﻨﻢ؛ ﭼﺮاﮐﻪ ﺑﺪون ﮐﻤﮏﻫﺎي اﯾﺸﺎن‬
‫ﮔﺮدآوري و ﭼﺎپ اﯾﻦ ﮐﺘﺎب ﻏﯿﺮﻣﻤﮑﻦ ﺑﻮد‪.‬‬

‫ﻧﮕﺎرش اﯾﻦ ﮐﺘﺎب در ﺣﺪود ‪ 6‬ﻣﺎه زﻣﺎن ﺑﺮده اﺳﺖ‪ ،‬اﻣﺎ ﻣﻄﺎﻟﺒﯽ ﮐﻪ در آن وﺟﻮد دارد ﺣﺎﺻﻞ ﺳﺎلﻫﺎ‬
‫ﺗﺠﺮﺑﻪ‪ ،‬آﻣﻮزش‪ ،‬ﺗﺤﻠﯿﻞ و ﻣﻄﺎﻟﻌﻪ ﻣﺒﺎﺣﺚ زﺑﺎن ﺷﯿﺮﯾﻦ ﭘﺎﯾﺘﻮن ﺑﻮده اﺳﺖ‪ .‬ﭘﺲ از ﺑﺮﮔﺰاري ﭼﻨﺪ ﺻﺪ ﺳﺎﻋﺖ‬
‫ﮐﻼس ﺣﻀﻮري و ﻏﯿﺮﺣﻀﻮري ﺑﺮﻧﺎﻣﻪﻧﻮﯾﺴﯽ‪ ،‬ﯾﮑﯽ از ﭼﺎﻟﺶﻫﺎي ﺗﺄﻟﯿﻒ اﯾﻦ ﮐﺘﺎب ﺑﺮاﯾﻢ اﻧﺘﻘﺎل ﻣﻔﺎﻫﯿﻢ‬
‫ﺗﻨﻬﺎ ﺑﺎ ﮐﻠﻤﺎت ﺑﻮد‪ .‬ﺗﻤﺎم ﺳﻌﯽ ﺧﻮد را ﮐﺮدهام ﺗﺎ ﻣﺒﺎﺣﺜﯽ ﮐﺎرﺑﺮدي را ﮔﺮدآوري ﮐﻨﻢ ﺗﺎ ﺣﺘﯽ ﭘﺲ از‬
‫ﭼﻨﺪﯾﻦ ﺳﺎل ﮐﺎرآﻣﺪ ﺑﺎﺷﺪ و در ﺗﻤﺎم ﻣﺒﺎﺣﺚ ﻣﺜﺎلﻫﺎي ﮐﺎرﺑﺮدي را ﺑﺮاي ﺷﻤﺎ ﺧﻮاﻧﻨﺪة ﻋﺰﯾﺰ ﺑﺎ ﺻﺒﺮ و‬
‫ﺣﻮﺻﻠﻪ ﺗﺪوﯾﻦ ﮐﻨﻢ‪ .‬اﻣﯿﺪوارم ﮐﻪ ﺑﻪ ﮐﻤﮏ اﯾﻦ ﮐﺘﺎب ﺑﺘﻮاﻧﯿﺪ ﺳﻄﺢ داﻧﺶ ﺧﻮد را در زﺑﺎن ﭘﺎﯾﺘﻮن ﺑﺎﻻﺗﺮ‬
‫ﺑﺮده و ﺑﺎ ﻣﻔﺎﻫﯿﻢ ﭘﯿﺸﺮﻓﺘﻪ ﭘﺎﯾﺘﻮن آﺷﻨﺎ ﺷﻮﯾﺪ‪.‬‬

‫در ﭘﺎﯾﺎن‪ ،‬از ﭘﺪر‪ ،‬ﻣﺎدر و ﺑﺮادرم ﮐﻪ ﺻﻤﯿﻤﺎﻧﻪ در ﺗﻤﺎم ﻃﻮل زﻧﺪﮔﯽام ﻣﺸﻮق و راﻫﻨﻤﺎي دﻟﺴﻮزم ﺑﻮدهاﻧﺪ‪،‬‬
‫ﮐﻤﺎل ﺗﺸﮑﺮ و ﻗﺪرداﻧﯽ را دارم و اﻣﯿﺪوارم ﻫﻤﯿﺸﻪ ﺑﺎﻋﺚ ﺳﺮﺑﻠﻨﺪي و ﺳﺮاﻓﺮازﯾﺸﺎن ﺑﺎﺷﻢ‪.‬‬

‫ﺑﺎ ﻋﺸﻖ‪ ،‬ﺗﻘﺪﯾﻢ ﺑﻪ ﭘﺪر‪ ،‬ﻣﺎدر و ﺑﺮادر ﻋﺰﯾﺰم‬

‫ﺳﯿﺎوش ﮔﻨﺠﯽ‬
‫‪Ahwaz_Hackerz‬‬

‫ﻣﺒﺎﺣﺚ ﭘﯿﺸﺮﻓﺘﻪ در ﭘﺎﯾﺘﻮن ‪ /‬ﻣﻮﺳﺴﻪ ﻓﺮﻫﻨﮕﯽ ﻫﻨﺮي دﯾﺒﺎﮔﺮان ﺗﻬﺮان‬ ‫‪10‬‬

‫ﺑﺨﺶ‪1‬‬

‫وﯾﮋﮔﯽﻫﺎي ﺟﺪﯾﺪ در ﭘﺎﯾﺘﻮن‬


‫‪Ahwaz_Hackerz‬‬

‫‪11‬‬ ‫ﺑﺨﺶ اول ‪ /‬ﻓﺼﻞ ‪ / 1‬ﭘﺎﯾﺘﻮن ‪306‬‬

‫ﻓﺼﻞ‪1‬‬
‫ﭘﺎﯾﺘﻮن ‪3.6‬‬
‫‪Ahwaz_Hackerz‬‬

‫ﻣﺒﺎﺣﺚ ﭘﯿﺸﺮﻓﺘﻪ در ﭘﺎﯾﺘﻮن ‪ /‬ﻣﻮﺳﺴﻪ ﻓﺮﻫﻨﮕﯽ ﻫﻨﺮي دﯾﺒﺎﮔﺮان ﺗﻬﺮان‬ ‫‪12‬‬

‫اﺳﺘﻔﺎده از ﺗﻤﺎﻣﯽ اﻣﮑﺎﻧﺎت ﯾﮏ زﺑﺎن ﺑﻪ ﺷﻤﺎ ﮐﻤﮏ ﻣﯽﮐﻨﺪ ﺗﺎ ﮐﺪﻫﺎي ﺑﺎ ﮐﯿﻔﯿﺖ ﺑﺎﻻﺗﺮي ﺑﻨﻮﯾﺴﯿﺪ‪ .‬در‬
‫ﻓﺼﻞﻫﺎي ﭘﯿﺶرو‪ ،‬ﺑﻪ ﺑﺮرﺳﯽ ﺟﺎﻣﻊ وﯾﮋﮔﯽﻫﺎي ﺟﺪﯾﺪ و اﺿﺎﻓﻪ ﺷﺪه ﺑﻪ زﺑﺎن ﭘﺎﯾﺘﻮن از ﻧﺴﺨﻪ ‪ 3.6‬ﺑﻪ ﺑﻌﺪ‬
‫ﻣﯽﭘﺮدازﯾﻢ‪.‬‬

‫‪Formatted String Literals‬‬


‫در ‪ PEP 498‬ﻧﻮع ﺟﺪﯾﺪي از ﺗﻌﺮﯾﻒ ‪ string‬ﻣﻌﺮﻓﯽ ﺷﺪ ﮐﻪ ﺑﻪ آﻧﻬﺎ ‪ formatted string‬و ﯾﺎ‬
‫ﺑﻪاﺧﺘﺼﺎر ‪ f-string‬ﮔﻔﺘﻪ ﻣﯽﺷﻮد‪ .‬اﯾﻦ ﻧﻮع از ﺗﻌﺮﯾﻒ ‪ string‬ﺑﻪ ﻣﺎ ﮐﻤﮏ ﻣﯽﮐﻨﺪ ﺗﺎ رﺷﺘﻪﻫﺎﯾﯽ ﮐﻪ ﻗﺮار‬
‫اﺳﺖ ﺣﺎوي ﻣﺘﻐﯿﺮ ﺑﺎﺷﺪ را ﺑﻪﺳﺎدﮔﯽ ﺗﻮﻟﯿﺪ ﮐﻨﯿﻢ‪ .‬ﻫﻤﭽﻨﯿﻦ ﻣﯽﺗﻮان ﻋﺒﺎرات ﺳﺎده و ﻓﺮاﺧﻮاﻧﯽﻫﺎي ﺗﻮاﺑﻊ را‬
‫در ﻣﯿﺎن آﮐﻮﻻد ﻧﻮﺷﺖ‪ .‬ﺑﺮاي اﺳﺘﻔﺎده از اﯾﻦ ﻧﻮع ﺗﻌﺮﯾﻒ از ﭘﯿﺸﻮﻧﺪ ‪ f‬اﺳﺘﻔﺎده ﻣﯽﺷﻮد و ﻣﻘﺎدﯾﺮي ﮐﻪ‬
‫ﻣﯽﺧﻮاﻫﯿﻢ در رﺷﺘﮥ اﺻﻠﯽ ﺟﺎﯾﮕﺰﯾﻦ ﺷﻮﻧﺪ را در ﻣﯿﺎن آﮐﻮﻻد ﻗﺮار ﻣﯽدﻫﯿﻢ‪.‬‬

‫ﺟﺎيﮔﺬاري ﻣﺘﻐﯿﺮ در ‪string‬‬


‫اﮔﺮ ﺷﻤﺎ ﯾﮏ ﺑﺮﻧﺎﻣﻪﻧﻮﯾﺲ ﻣﺒﺘﺪي ﺑﺎﺷﯿﺪ اﺣﺘﻤﺎﻻً از روش زﯾﺮ ﺑﺮاي ﭼﺴﺒﺎﻧﺪن ﯾﮏ ﻣﺘﻐﯿﺮ ﺑﻪ ‪string‬‬
‫اﺳﺘﻔﺎده ﻣﯽﮐﻨﯿﺪ‪.‬‬

‫"‪name = "Siavash‬‬
‫‪text = "My name is " + name‬‬
‫)‪print(text‬‬

‫‪>> My name is Siavash‬‬

‫و ﯾﺎ اﮔﺮ ﮐﻤﯽ ﭘﯿﺸﺮﻓﺘﻪﺗﺮ ﺑﺎﺷﯿﺪ از )‪ (str.format‬اﺳﺘﻔﺎده ﻣﯽﮐﻨﯿﺪ‪.‬‬

‫"‪name = "Siavash‬‬
‫)‪text = "My name is {}".format(name‬‬
‫)‪print(text‬‬

‫‪>> My name is Siavash‬‬

‫اﻣﺎ ﺑﺎ اﺳﺘﻔﺎده از وﯾﮋﮔﯽ ﺟﺪﯾﺪ در ﭘﺎﯾﺘﻮن ‪ 3.6‬اﯾﻦ ﻋﻤﻞ را ﻣﯽﺗﻮان ﺳﺎدهﺗﺮ اﻧﺠﺎم داد‪ ،‬ﺗﻨﻬﺎ ﮐﺎﻓﯿﺴﺖ در‬
‫اﺑﺘﺪاي رﺷﺘﻪ ﺣﺮف ‪ f‬را ﻗﺮار داده و ﻫﺮﮐﺠﺎ ﮐﻪ ﻧﯿﺎز ﺑﻪ ﭼﺎپ ﻣﺘﻐﯿﺮ وﺟﻮد دارد آن را ﺑﯿﻦ آﮐﻮﻻد ﻗﺮار داد‪.‬‬

‫"‪name = "Siavash‬‬
‫"}‪text = f"My name is {name‬‬
‫)‪print(text‬‬

‫‪>> My name is Siavash‬‬


‫‪Ahwaz_Hackerz‬‬

‫‪13‬‬ ‫ﺑﺨﺶ اول ‪ /‬ﻓﺼﻞ ‪ / 1‬ﭘﺎﯾﺘﻮن ‪306‬‬

‫ﻋﺒﺎرات در ‪f-string‬‬
‫ﻋﻼوهﺑﺮ ﺟﺎﯾﮕﺬاري ﻣﺘﻐﯿﺮﻫﺎ در رﺷﺘﻪﻫﺎ ﻣﯽﺗﻮان از ﻋﺒﺎرات و ﻋﻤﻠﮕﺮﻫﺎ در ﻣﯿﺎن آﮐﻮﻻد اﺳﺘﻔﺎده ﮐﺮد‪ .‬اﯾﻦ‬
‫ﻋﺒﺎرات در زﻣﺎن اﺟﺮا‪ 1‬ﻣﺤﺎﺳﺒﻪﺷﺪه و در داﺧﻞ رﺷﺘﻪﻫﺎي ﻣﺘﻨﯽ ﺟﺎيﮔﺬاري ﻣﯽﺷﻮﻧﺪ‪.‬‬

‫‪a = 26‬‬
‫‪b = 21‬‬
‫"}‪text = f"A plus B is equal to {a + b‬‬
‫)‪print(text‬‬

‫‪>> A plus B is equal to 47‬‬

‫ﻗﺎﺑﻠﯿﺖ ﻓﺮاﺧﻮاﻧﯽ ﺗﻮاﺑﻊ ﻧﯿﺰ در ‪ f-string‬وﺟﻮد دارد‪ .‬در ﻣﺜﺎل زﯾﺮ ﻣﺘﻐﯿﺮ ‪ name‬را ﺑﺎ اﺳﺘﻔﺎده از ﻓﺮاﺧﻮاﻧﯽ‬
‫ﻣﺘﺪ ‪ upper‬ﺑﻪ ﺣﺮوف ﺑﺰرگ ﺗﺒﺪﯾﻞ ﻣﯽﮐﻨﯿﻢ‪.‬‬

‫"‪name = "python‬‬
‫"‪text = f"{name.upper()} is the best programming language‬‬
‫)‪print(text‬‬

‫‪>> PYTHON is the best programming language‬‬

‫در ﺗﮑﻪ ﮐﺪﻫﺎﯾﯽ ﮐﻪ ﻧﯿﺎز ﺑﻪ ﻓﺮاﺧﻮاﻧﯽ ﺗﻮاﺑﻊ ﺑﺎ ﭘﺎراﻣﺘﺮﻫﺎي رﺷﺘﻪاي اﺳﺖ ﺑﺎﯾﺪ از ‪ quoting‬ﻣﺘﻔﺎوت اﺳﺘﻔﺎده‬
‫ﮐﺮد‪ ،‬ﯾﻌﻨﯽ اﮔﺮ ﺑﺮاي ﮐﻞ رﺷﺘﻪ از ‪ single quote‬اﺳﺘﻔﺎده ﮐﺮدهاﯾﺪ از ‪ double quote‬ﺑﺮاي ﻓﺮاﺧﻮاﻧﯽ‬
‫ﭘﺎراﻣﺘﺮﻫﺎ اﺳﺘﻔﺎده ﮐﻨﯿﺪ و ﺑﺮﻋﮑﺲ‪ .‬اﯾﻦ ﻗﺎﻧﻮن ﺑﺮاي درﯾﺎﻓﺖ ﻣﻘﺎدﯾﺮ از ﯾﮏ دﯾﮑﺸﻨﺮي ﺑﺎ اﺳﺘﻔﺎده از ﮐﻠﯿﺪ‬
‫رﺷﺘﻪاي ﻧﯿﺰ ﺻﺎدق اﺳﺖ‪ .‬در ﻣﺜﺎل اﺷﺘﺒﺎه زﯾﺮ از ﯾﮏ ﻧﻮع ‪ quoting‬اﺳﺘﻔﺎده ﺷﺪه اﺳﺖ‪.‬‬

‫"‪number = "1-000-000‬‬
‫"})"‪text = f"The number is {number.replace("-",",‬‬
‫)‪print(text‬‬

‫‪!> File "<stdin>", line 1‬‬


‫'(' ‪SyntaxError: f-string: unmatched‬‬

‫در ﺗﮑﻪ ﮐﺪﻫﺎي زﯾﺮ از ‪ quoting‬ﻣﺘﻔﺎوت اﺳﺘﻔﺎده ﺷﺪه اﺳﺖ‪.‬‬

‫"‪number = "1-000-000‬‬
‫"})'‪text = f"The number is {number.replace('-',',‬‬
‫)‪print(text‬‬

‫‪>> The number is 1,000,000‬‬

‫‪1‬‬
‫‪Runtime‬‬
‫‪Ahwaz_Hackerz‬‬

‫ﻣﺒﺎﺣﺚ ﭘﯿﺸﺮﻓﺘﻪ در ﭘﺎﯾﺘﻮن ‪ /‬ﻣﻮﺳﺴﻪ ﻓﺮﻫﻨﮕﯽ ﻫﻨﺮي دﯾﺒﺎﮔﺮان ﺗﻬﺮان‬ ‫‪14‬‬

‫}‪car = {"name": "BMW", "max_speed": 280‬‬


‫"‪text = f"{car['name']} has {car['max_speed']}km/h max speed.‬‬
‫)‪print(text‬‬

‫‪>> BMW has 280km/h max speed.‬‬

‫‪ Backslash ‬در ‪f-string‬‬


‫در آﮐﻮﻻدﻫﺎي ‪ f-string‬ﻧﻤﯽﺗﻮان از ﮐﺎراﮐﺘﺮ \ اﺳﺘﻔﺎده ﮐﺮد و ﺑﺮاي اﺳﺘﻔﺎده از ‪ Escape Character‬ﻫﺎ‬
‫ﺑﺎﯾﺪ آﻧﻬﺎ را در ﯾﮏ ﻣﺘﻐﯿﺮ ﻣﻮﻗﺘﯽ ذﺧﯿﺮه ﮐﺮد‪.‬‬

‫"‪line_separated = "line1-line2‬‬
‫"})'‪text = f"{line_separated.replace('-','\n‬‬
‫)‪print(text‬‬

‫‪!> SyntaxError: f-string expression part cannot include a‬‬


‫‪backslash‬‬

‫"‪line_separated = "line1-line2‬‬
‫'‪_ = '\n‬‬

‫"})_‪text = f"{line_separated.replace('-',‬‬
‫)‪print(text‬‬

‫‪>> line1‬‬
‫‪line2‬‬

‫‪ ‬ﻧﻮﺷﺘﻦ آﮐﻮﻻد در ‪f-string‬‬


‫در ﺑﺮﺧﯽ ﻣﻮاﻗﻊ ﻧﯿﺎز اﺳﺖ ﮐﻪ ﮐﺎراﮐﺘﺮ آﮐﻮﻻد را ﻧﯿﺰ در ﻣﺘﻦ ﻗﺮار دﻫﯿﻢ‪ .‬ﺑﺪﯾﻦﻣﻨﻈﻮر ﻣﯽﺗﻮان از دو آﮐﻮﻻد‬
‫اﺳﺘﻔﺎده ﮐﺮد‪ .‬ﻣﺜﺎلﻫﺎي زﯾﺮ ﺑﺮاي روﺷﻦﺷﺪن ﻣﻮﺿﻮع ﺑﺴﯿﺎر ﻣﻔﯿﺪ ﻫﺴﺘﻨﺪ‪.‬‬

‫ﺗﻮﺟﻪ ﮐﻨﯿﺪ ﮐﻪ ﮔﺬاﺷﺘﻦ ﺳﻪ ﻋﺪد آﮐﻮﻻد ﻫﻢ ﻣﺎﻧﻨﺪ دو آﮐﻮﻻد ﺗﻨﻬﺎ ﯾﮑﺒﺎر آن را ﭼﺎپ ﻣﯽﮐﻨﺪ و ﯾﮏ آﮐﻮﻻد‬
‫ﺑﺮاي ﺟﺎيﮔﺬاري ﻣﺘﻐﯿﺮ و ﯾﺎ اﻧﺠﺎم ﻣﺤﺎﺳﺒﺎت در آن ﺑﯿﻦ اﺳﺘﻔﺎده ﻣﯽ ﺷﻮد‪.‬‬

‫)"}}‪print(f"{{2+2‬‬

‫}‪>> {2+2‬‬

‫)"}}}‪print(f"{{{2+2‬‬

‫}‪>> {4‬‬

‫)"}}}}‪print(f"{{{{2+2‬‬

‫}}‪>> {{2+2‬‬
‫‪Ahwaz_Hackerz‬‬

‫‪15‬‬ ‫ﺑﺨﺶ اول ‪ /‬ﻓﺼﻞ ‪ / 1‬ﭘﺎﯾﺘﻮن ‪306‬‬

‫‪ ‬اﺳﺘﻔﺎده از ‪string formatting‬‬


‫ﺑﺎ وﺟﻮد آﻧﮑﻪ ﻗﺎﺑﻠﯿﺖ ‪ formatting‬ﻟﺰوﻣﺎً ﺑﺮاي ﭘﺎﯾﺘﻮن ‪ 3.6‬ﻧﯿﺴﺖ‪ ،‬وﻟﯽ ﻣﯽﺗﻮان از آن در ‪f-string‬‬
‫اﺳﺘﻔﺎده ﮐﺮد زﯾﺮا ﻋﺒﺎرات در ‪ f-string‬در زﻣﺎن اﺟﺮا ﻣﺤﺎﺳﺒﻪ ﺷﺪه و ﺑﺎ اﺳﺘﻔﺎده از ﭘﺮوﺗﮑﻞ‬
‫__‪ __format‬ﯾﺎ )(‪ format‬ﻗﺎﻟﺐﺑﻨﺪي ﻣﯽﺷﻮﻧﺪ‪ .‬ﺑﻪﻋﻨﻮان ﯾﮏ ﺑﺮﻧﺎﻣﻪﻧﻮﯾﺲ ﺣﺮﻓﻪاي داﻧﺴﺘﻦ آﻧﻬﺎ‬
‫ﺑﻪ ﻧﻮﺷﺘﻦ ﮐﺪﻫﺎي ﺑﻬﺘﺮ و ﻫﻤﭽﻨﯿﻦ ﺧﻮاﻧﺪن ﮐﺪﻫﺎي دﯾﮕﺮان ﺑﺴﯿﺎر ﮐﻤﮏ ﻣﯽﮐﻨﺪ‪ .‬ﺑﺎ ﻗﺮاردادن ﮐﺎراﮐﺘﺮ‪:‬‬
‫)دوﻧﻘﻄﻪ( ﺑﻌﺪ از ﻧﺎم ﻣﺘﻐﯿﺮ ﻣﯽﺗﻮان ﻓﺮﻣﺖﻫﺎ را اﻋﻤﺎل ﮐﺮد‪.‬‬

‫ﺗﺼﻮﯾﺮ ‪ -1‬ﺟﺎﯾﮕﺎه ﻣﺘﻐﯿﺮ و ﻓﺮﻣﺖ در ‪f-string‬‬

‫‪ ‬اﺳﺘﻔﺎده از ‪ Padding‬در رﺷﺘﻪﻫﺎي ﺣﺮﻓﯽ‬

‫ﺑﻪﻃﻮر ﭘﯿﺶﻓﺮض ﭘﺎﯾﺘﻮن ﺑﺮاي ﻓﺮﻣﺖﮐﺮدن از ﻫﻤﺎن ﺗﻌﺪاد ﮐﺎراﮐﺘﺮ در ﻣﺤﺘﻮا اﺳﺘﻔﺎده ﻣﯽﮐﻨﺪ وﻟﯽ ﺑﺎ‬
‫اﺳﺘﻔﺎده از ‪ Padding‬ﻣﯽﺗﻮان ﻓﺎﺻﻠﻪ و ﮐﺎراﮐﺘﺮﻫﺎﯾﯽ از ﻃﺮﻓﯿﻦ ﺑﻪ ﯾﮏ رﺷﺘﻪ اﺿﺎﻓﻪ ﮐﺮد‪ .‬در ﻣﺜﺎلﻫﺎي زﯾﺮ‬
‫ﺳﻌﯽ ﺷﺪه ﺗﻤﺎم ﺣﺎﻟﺖﻫﺎي ﻣﻤﮑﻦ ﺑﺮرﺳﯽ ﺷﻮد‪.‬‬

‫ﺑﺮاي ﻣﺘﻐﯿﺮ رﺷﺘﻪاي‪ ،‬ﺑﺎ ﮔﺬاﺷﺘﻦ ﻋﺪد در ﻗﺴﻤﺖ ﻓﺮﻣﺖ ﻣﯽﺗﻮان ﺑﻪ آن ‪ Padding‬داد‪ .‬ﺑﻪ اﻧﺪازة ﺗﻌﺪاد‬
‫ﮐﺎراﮐﺘﺮﻫﺎﯾﯽ ﮐﻪ ﻧﯿﺎز اﺳﺖ ﺗﺎ ﺑﻪ رﺷﺘﮥ اﺻﻠﯽ اﺿﺎﻓﻪ ﺷﺪه ﺗﺎ ﻣﺠﻤﻮع ﻃﻮل آﻧﻬﺎ ﺑﻪ ﻋﺪد درجﺷﺪه ﺑﺮﺳﺪ‪،‬‬
‫ﻓﺎﺻﻠﻪ ﮔﺬاﺷﺘﻪ ﻣﯽﺷﻮد‪ .‬در ﻣﺜﺎل زﯾﺮ ﻋﺪد ‪ 10‬ﻧﻮﺷﺘﻪ ﺷﺪه ﯾﻌﻨﯽ ﻣﺘﻐﯿﺮ ‪ test‬ﭼﺎپ ﺷﻮد و ﺗﻌﺪاد ﻣﮑﺎن‬
‫ﺑﺎﻗﯽﻣﺎﻧﺪه ﺑﺮاي رﺳﯿﺪن ﺑﻪ ﻋﺪد ‪ space ،10‬ﭼﺎپ ﺷﻮد‪ .‬در اﯾﻨﺠﺎ ‪ test‬ﭼﻬﺎر ﺣﺮف دارد و ﺑﺮاي رﺳﯿﺪن‬
‫ﺑﻪ ‪ 10‬ﮐﺎراﮐﺘﺮ‪ 6 ،‬ﮐﺎراﮐﺘﺮ ﮐﻢ اﺳﺖ؛ ﺑﻨﺎﺑﺮاﯾﻦ ‪ 6‬ﮐﺎراﮐﺘﺮ از ﺳﻤﺖ راﺳﺖ ﺑﻪ آن اﺿﺎﻓﻪ ﻣﯽﺷﻮد‪.‬‬

‫"‪test = "test‬‬
‫)"‪print(f"this is {test:10} string.‬‬

‫‪>> this is test‬‬ ‫‪string.‬‬

‫ﺑﺮاي ﮔﺬاﺷﺘﻦ ‪ Padding‬از ﺳﻤﺖ ﭼﭗ‪ ،‬ﮐﺎﻓﯿﺴﺖ ﮐﺎراﮐﺘﺮ > را ﻗﺒﻞ از ﻋﺪد ﻗﺮار دﻫﯿﻢ‪ .‬ﻫﻤﭽﻨﯿﻦ ﻣﯽﺗﻮان‬
‫ﺑﺮاي ‪ Padding‬از ﺳﻤﺖ راﺳﺖ از ﮐﺎراﮐﺘﺮ < اﺳﺘﻔﺎده ﮐﺮد‪ .‬دﻗﺖ ﮐﻨﯿﺪ ﮐﻪ ﺑﻪ ﺻﻮرت ﭘﯿﺶﻓﺮض اﮔﺮ‬
‫ﻫﯿﭻﮐﺪام از اﯾﻦ ﮐﺎراﮐﺘﺮﻫﺎ ﻧﻮﺷﺘﻪ ﻧﺸﻮد‪ ،‬ﭘﺎﯾﺘﻮن از ﺳﻤﺖ راﺳﺖ ﻓﺎﺻﻠﻪﮔﺬاري ﻣﯽﮐﻨﺪ‪.‬‬
‫‪Ahwaz_Hackerz‬‬

‫ﻣﺒﺎﺣﺚ ﭘﯿﺸﺮﻓﺘﻪ در ﭘﺎﯾﺘﻮن ‪ /‬ﻣﻮﺳﺴﻪ ﻓﺮﻫﻨﮕﯽ ﻫﻨﺮي دﯾﺒﺎﮔﺮان ﺗﻬﺮان‬ ‫‪16‬‬

‫"‪test = "test‬‬
‫)"‪print(f"this is {test:>10} string.‬‬

‫‪>> this is‬‬ ‫‪test string.‬‬

‫ﻣﯽﺗﻮان ﮐﺎراﮐﺘﺮ ‪ Padding‬را ﻧﯿﺰ ﺗﻐﯿﯿﺮ داد و از ﮐﺎراﮐﺘﺮ دﯾﮕﺮي ﺑﻪ ﻏﯿﺮ از ﻓﺎﺻﻠﻪ اﺳﺘﻔﺎده ﮐﺮد‪ .‬ﺗﻨﻬﺎ‬
‫ﮐﺎﻓﯿﺴﺖ اﯾﻦ ﮐﺎراﮐﺘﺮ را ﺑﻼﻓﺎﺻﻠﻪ ﭘﺲ از دوﻧﻘﻄﻪ ﻗﺮار دﻫﯿﺪ‪ .‬دﻗﺖ ﮐﻨﯿﺪ ﮐﻪ در اﯾﻦ ﺣﺎﻟﺖ ﺣﺘﻤﺎً ﺑﺎﯾﺪ‬
‫ﮐﺎراﮐﺘﺮﻫﺎي < ﯾﺎ > را ﺑﻨﻮﯾﺴﯿﺪ‪.‬‬

‫"‪test = "test‬‬
‫)"‪print(f"this is {test:+<10} string.‬‬

‫‪>> this is test++++++ string.‬‬

‫ﺑﺮاي آﻧﮑﻪ ﺑﺘﻮاﻧﯿﺪ ﻫﻢ از راﺳﺖ و ﻫﻢ از ﭼﭗ ‪ Padding‬داﺷﺘﻪ ﺑﺎﺷﯿﺪ و رﺷﺘﮥ ﺣﺮﻓﯽ را در وﺳﻂ ﺑﻨﻮﯾﺴﯿﺪ‬
‫ﻣﯽﺗﻮاﻧﯿﺪ از ﮐﺎراﮐﺘﺮ ^ اﺳﺘﻔﺎده ﮐﻨﯿﺪ‪.‬‬

‫"‪test = "test‬‬
‫)"‪print(f"this is {test:-^10} string.‬‬

‫‪>> this is ---test--- string.‬‬

‫و اﮔﺮ ﻓﻀﺎي ﻃﺮﻓﯿﻦ ﺑﺮ دو ﺑﺨﺶﭘﺬﯾﺮ ﻧﺒﺎﺷﺪ‪ .‬اوﻟﻮﯾﺖ ﺑﺎ ﻃﺮف راﺳﺖ اﺳﺖ‪.‬‬

‫"‪test = "test‬‬
‫)"‪print(f"this is {test:-^7} string.‬‬

‫‪>> this is -test-- string.‬‬

‫‪ ‬ﺑﺮﯾﺪن‪ 1‬رﺷﺘﻪﻫﺎي ﺣﺮﻓﯽ‬

‫ﺑﺮﻋﮑﺲ ‪ Padding‬ﻣﯽﺗﻮان رﺷﺘﻪﻫﺎي ﺣﺮﻓﯽ ﻃﻮﻻﻧﯽ را ﮐﻮﺗﺎه ﮐﺮد‪ .‬ﻋﺪد ﭘﺲ از ﮐﺎراﮐﺘﺮ ﻧﻘﻄﻪ ﺗﻌﺪاد‬
‫ﮐﺎراﮐﺘﺮ ﻣﺤﺪودﺷﺪه را ﺑﻪ ﻧﻤﺎﯾﺶ ﻣﯽﮔﺬارد‪.‬‬

‫"‪test = "test‬‬
‫)"‪print(f"this is {test:.2} string.‬‬

‫‪>> this is te string.‬‬

‫ﻣﯽﺗﻮان ﺑﺮﯾﺪن و ‪ Padding‬را ﺑﺎ ﻫﻢ اﺳﺘﻔﺎده ﮐﺮد‪.‬‬

‫‪1‬‬
‫‪Truncate‬‬
‫‪Ahwaz_Hackerz‬‬

‫‪17‬‬ ‫ﺑﺨﺶ اول ‪ /‬ﻓﺼﻞ ‪ / 1‬ﭘﺎﯾﺘﻮن ‪306‬‬

‫"‪test = "test‬‬
‫)"‪print(f"this is {test:+>8.2} string.‬‬

‫‪>> this is ++++++te string.‬‬

‫‪ Width ‬در اﻋﺪاد‬

‫ﻣﺸﺎﺑﻪ ‪ Padding‬در رﺷﺘﻪﻫﺎي ﺣﺮﻓﯽ‪ ،‬در اﻋﺪاد ﻫﻢ ﻣﻔﻬﻮم ﻣﺸﺎﺑﻬﯽ ﺑﺎ ﻧﺎم ‪ Width‬وﺟﻮد دارد و اﯾﻦ‬
‫ﻋﺪد ﺑﻌﺪ از ﮐﺎراﮐﺘﺮ دو ﻧﻘﻄﻪ ﻧﻮﺷﺘﻪ ﻣﯽﺷﻮد‪ .‬ﻓﻘﻂ ﺑﺎﯾﺪ ﺗﻮﺟﻪ داﺷﺖ ﮐﻪ ﺗﻨﻬﺎ ﺗﻔﺎوت آﻧﻬﺎ در ﺟﻬﺖ‬
‫ﭘﯿﺶﻓﺮض آن اﺳﺖ‪ .‬در رﺷﺘﻪﻫﺎي ﻋﺪدي ﺑﻪ ﺻﻮرت ﭘﯿﺶﻓﺮض از ﺳﻤﺖ ﭼﭗ ﻓﺎﺻﻠﻪ ﮔﺬاﺷﺘﻪ ﻣﯽﺷﻮد‪.‬‬

‫‪number = 21‬‬
‫)"‪print(f"The number is {number:6}.‬‬

‫‪>> The number is 21.‬‬

‫ﺑﺮاي ﺗﻐﯿﯿﺮ ﺟﻬﺖ ﻣﯽﺗﻮان از ﮐﺎراﮐﺘﺮ < اﺳﺘﻔﺎده ﮐﺮد‪.‬‬

‫‪number = 21‬‬
‫)"‪print(f"The number is {number:<6}.‬‬

‫‪>> The number is 21.‬‬

‫ﻫﻤﭽﻨﯿﻦ ﺑﺮاي ﺗﻐﯿﯿﺮ ﮐﺎراﮐﺘﺮ ﭘﯿﺶﻓﺮض ﺑﻪﺟﺎي ﻓﺎﺻﻠﻪ‪ ،‬ﻣﯽﺗﻮان ﮐﺎراﮐﺘﺮ را ﻗﺒﻞ از ‪ Width‬ﻧﻮﺷﺖ‪.‬‬
‫ﻧﮑﺘﮥ ﻗﺎﺑﻞ ﺗﻮﺟﻪ آن اﺳﺖ ﮐﻪ ﺗﻨﻬﺎ ﺑﺮاي ﺟﺎﯾﮕﺰﯾﻨﯽ ﺻﻔﺮ ﺑﺎ ﮐﺎراﮐﺘﺮ ﻓﺎﺻﻠﻪ ﻧﯿﺎزي ﺑﻪ ﮐﺎراﮐﺘﺮﻫﺎي < و ﯾﺎ >‬
‫ﻧﯿﺴﺖ‪.‬‬

‫‪number = 21‬‬
‫)"‪print(f"The number is {number:06}.‬‬

‫‪>> The number is 000021.‬‬

‫‪number = 21‬‬
‫)"‪print(f"The number is {number:*6}.‬‬

‫‪!> ValueError: Invalid format specifier‬‬

‫‪number = 21‬‬
‫)"‪print(f"The number is {number:*>6}.‬‬

‫‪>> The number is ****21.‬‬


‫‪Ahwaz_Hackerz‬‬

‫ﻣﺒﺎﺣﺚ ﭘﯿﺸﺮﻓﺘﻪ در ﭘﺎﯾﺘﻮن ‪ /‬ﻣﻮﺳﺴﻪ ﻓﺮﻫﻨﮕﯽ ﻫﻨﺮي دﯾﺒﺎﮔﺮان ﺗﻬﺮان‬ ‫‪18‬‬

‫‪ Precision ‬در اﻋﺪاد‬

‫ﻣﯽﺗﻮان ﻗﺴﻤﺘﯽ از اﻋﺪاد اﻋﺸﺎري )‪ (Float‬را ر‪‬ﻧﺪ ﮐﺮد و ﺗﻨﻬﺎ ﻗﺴﻤﺘﯽ از اﻋﺸﺎر آن را ﮔﺰارش ﮐﺮد‪ .‬ﺑﺮاي‬
‫اﺳﺘﻔﺎده ﺑﺎﯾﺪ ﺗﻌﺪاد ارﻗﺎﻣﯽ ﮐﻪ ﺑﻌﺪ از اﻋﺸﺎر ﻣﯽﺧﻮاﻫﯿﻢ ﺑﺎﻗﯽ ﺑﻤﺎﻧﺪ را ﺑﻌﺪ از ﮐﺎراﮐﺘﺮ ﻧﻘﻄﻪ ﻧﻮﺷﺖ‪ ،‬ﺳﭙﺲ‬
‫ﺑﻌﺪ از آن از ﮐﺎراﮐﺘﺮ ‪ f‬اﺳﺘﻔﺎده ﮐﺮد‪ .‬ﺑﺴﯿﺎر واﺿﺢ اﺳﺖ ﮐﻪ اﺳﺘﻔﺎده از اﯾﻦ ﻋﻤﻠﯿﺎت در اﻋﺪاد ﺻﺤﯿﺢ‬
‫)‪ (Integer‬ﻣﻌﻨﯽ ﻧﺪارد‪.‬‬

‫‪number = 3.1415926535‬‬
‫)"}‪print(f"The Pi number = {number:.2f‬‬

‫‪>> The Pi number = 3.14‬‬

‫‪number = 3.1415926535‬‬
‫)"}‪print(f"The Pi number = {number:.3f‬‬

‫‪>> The Pi number = 3.142‬‬

‫‪ ‬ﺗﺮﮐﯿﺐ ﻓﺮﻣﺖﻫﺎ‬

‫در اداﻣﻪ ﭼﻨﺪﯾﻦ ﻣﺜﺎل از ﺗﺮﮐﯿﺐ و ﻗﺎﺑﻠﯿﺖﻫﺎي دﯾﮕﺮ در ﺟﺪول ‪ 1‬ﺑﺮرﺳﯽ ﺷﺪه اﺳﺖ‪.‬‬

‫ﺟﺪول ‪ - 1‬ﻓﺮﻣﺖﻫﺎ در اﻋﺪاد‬

‫ﻣﺘﻐﯿﺮ‬ ‫ﻓﺮﻣﺖ‬ ‫ﺧﺮوﺟﯽ‬ ‫ﺗﻮﺿﯿﺤﺎت‬


‫ﺳﻪ رﻗﻢ اﻋﺸﺎر ﺟﺪاﺷﺪه‪ ،‬ﻃﻮل ﻋﺪد‬
‫‪3.14159‬‬ ‫"}‪f"{var:*>8.3f‬‬ ‫‪***3.142‬‬ ‫ﭼﺎﭘﯽ ‪ 8‬اﺳﺖ و از ﺳﻤﺖ ﭼﭗ ﺑﺎ‬
‫ﮐﺎراﮐﺘﺮ * ﭘﺮﺷﺪه‬
‫ﻋﺪد ﺑﻪ ﺻﻮرت درﺻﺪي و ﺑﺎ ‪ 3‬رﻗﻢ‬
‫‪2/3‬‬ ‫"}‪f"{var:.3%‬‬ ‫‪66.667%‬‬
‫اﻋﺸﺎر‬
‫ﺟﺪاﮐﺮدن ﺳﻪﺗﺎﯾﯽ اﻋﺪاد ﺑﺎ وﯾﺮﮔﻮل و ‪3‬‬
‫‪1234567.8989‬‬ ‫"}‪f"{var:,.3f‬‬ ‫‪1,234,567.899‬‬
‫رﻗﻢ اﻋﺸﺎر‬
‫ﺗﺒﺪﯾﻞ ﻋﺪد ﺑﻪ ﻓﺮﻣﺖ ﻧﻤﺎد ﻋﻠﻤﯽ ﺑﺎ دو‬
‫‪10000000‬‬ ‫"}‪f"{var:.2e‬‬ ‫‪1.00e+07‬‬
‫رﻗﻢ اﻋﺸﺎر‬
‫‪32‬‬ ‫"}‪f"{var:o‬‬ ‫‪40‬‬ ‫ﺗﺒﺪﯾﻞ ﻋﺪد ﺑﻪ ‪ octal‬ﯾﺎ ﻣﺒﻨﺎي ‪8‬‬
‫ﺗﺒﺪﯾﻞ ﻋﺪد ﺑﻪ ‪ hexadecimal‬ﯾﺎ‬
‫‪32‬‬ ‫"}‪f"{var:x‬‬ ‫‪20‬‬
‫ﻣﺒﻨﺎي ‪16‬‬
‫‪Ahwaz_Hackerz‬‬

‫‪19‬‬ ‫ﺑﺨﺶ اول ‪ /‬ﻓﺼﻞ ‪ / 1‬ﭘﺎﯾﺘﻮن ‪306‬‬

‫‪ ‬ﻓﺮﻣﺖ ﺗﺎرﯾﺦ‬

‫ﻓﺮﻣﺖدﻫﯽ در ‪ f-string‬ﺑﺎﻋﺚ ﻣﯽﺷﻮد از ﻓﺮﻣﺖﻫﺎي اﺷﯿﺎ ﻣﺨﺘﻠﻒ ﻧﯿﺰ ﺑﺘﻮان اﺳﺘﻔﺎده ﮐﺮد‪ .‬ﺑﻪ ﻫﻤﯿﻦ‬
‫ﻣﻨﻈﻮر ﻣﯽﺗﻮان ﺑﺮاي ‪ datetime‬ﻧﯿﺰ ﻓﺮﻣﺖ ﺗﻌﯿﯿﻦ ﮐﺮد‪.‬‬

‫‪from datetime import datetime‬‬

‫)‪date = datetime(2020,1,2,10,10‬‬
‫)"}‪print(f"{date:%Y/%m/%d%H:%M‬‬

‫‪>> 2020/01/02 10:10‬‬

‫‪1‬‬
‫در ﺟﺪول ‪ 2‬ﮐﺪﻫﺎي ﭘﺮ اﺳﺘﻔﺎده ﺑﺮاي ﻓﺮﻣﺖدﻫﯽ در ﺗﺎرﯾﺦ ﮔﺮدآوري ﺷﺪه اﺳﺖ ‪ .‬اﯾﻦ ﻣﺜﺎلﻫﺎ ﺑﺎ ‪Locale‬‬
‫اﻧﮕﻠﯿﺴﯽ ﻣﯽﺑﺎﺷﺪ و ﺑﺮاي ﻫﺮ ‪ Locale‬ﺧﺮوﺟﯽﻫﺎ ﻣﯽﺗﻮاﻧﺪ ﻣﺘﻔﺎوت ﺑﺎﺷﺪ‪.‬‬

‫ﺟﺪول ‪ - 2‬ﻟﯿﺴﺖ دﺳﺘﻮرات ﺑﺮاي ﻓﺮﻣﺖ ﺗﺎرﯾﺦ‬

‫دﺳﺘﻮر‬ ‫ﺗﻮﺿﯿﺢ‬ ‫ﻣﺜﺎل‬


‫‪%a‬‬ ‫… ‪ Sun, Mon,‬روز ﻫﻔﺘﻪ )ﻣﺨﺘﺼﺮ(‬
‫‪%A‬‬ ‫… ‪ Sunday, Monday,‬روز ﻫﻔﺘﻪ )ﺻﻮرت ﮐﺎﻣﻞ(‬
‫‪%b‬‬ ‫… ‪ Jan, Feb,‬ﻣﺎه )ﻣﺨﺘﺼﺮ(‬
‫‪%B‬‬ ‫… ‪ January, February,‬ﻣﺎه )ﮐﺎﻣﻞ(‬
‫‪%d‬‬ ‫‪ 01, 02, 03, …, 31‬ﭼﻨﺪﻣﯿﻦ روز از ﻣﺎه )ﺑﺎ ﺻﻔﺮ(‬
‫‪%H‬‬ ‫‪ 00, 01, …, 23‬ﺳﺎﻋﺖ )‪ 24‬ﺳﺎﻋﺖ ﺑﺎ ﺻﻔﺮ(‬
‫‪%I‬‬ ‫‪ 01, 02, …, 12‬ﺳﺎﻋﺖ )‪ 12‬ﺳﺎﻋﺖ ﺑﺎ ﺻﻔﺮ(‬
‫‪%m‬‬ ‫‪ 01, 02, …, 12‬ﻣﺎه )ﻋﺪدي ﺑﺎ ﺻﻔﺮ(‬
‫‪%M‬‬ ‫‪ 00, 01, …, 59‬دﻗﯿﻘﻪ )ﺑﺎ ﺻﻔﺮ(‬
‫‪%p‬‬ ‫‪ AM AM, PM‬ﯾﺎ ‪PM‬‬
‫‪%S‬‬ ‫‪ 00, 01, …, 59‬ﺛﺎﻧﯿﻪ )ﺑﺎ ﺻﻔﺮ(‬
‫‪%y‬‬ ‫‪ 00, 01, …, 99‬ﺳﺎل )ﺑﺪون ﻗﺮن ﺑﺎ ﺻﻔﺮ(‬
‫‪%Y‬‬ ‫‪ 0001, …, 2020, …, 9999‬ﺳﺎل )ﺑﺎ ﻗﺮن ﺑﺎ ﺻﻔﺮ(‬

‫‪1‬ﺗﻤﺎﻣﯽ زﺑﺎنﻫﺎي ﻣﺒﺘﻨﯽﺑﺮ ‪ C‬از اﯾﻦ ﮐﺪﻫﺎ ﭘﯿﺮوي ﻣﯽﮐﻨﻨﺪ‪.‬‬


‫‪Ahwaz_Hackerz‬‬

‫ﻣﺒﺎﺣﺚ ﭘﯿﺸﺮﻓﺘﻪ در ﭘﺎﯾﺘﻮن ‪ /‬ﻣﻮﺳﺴﻪ ﻓﺮﻫﻨﮕﯽ ﻫﻨﺮي دﯾﺒﺎﮔﺮان ﺗﻬﺮان‬ ‫‪20‬‬

‫‪ ‬ﻣﺜﺎﻟﯽ از ﮐﺎرﺑﺮد ﻓﺮﻣﺖﻫﺎ‬

‫اﺳﺘﻔﺎده از ﻓﺮﻣﺖﻫﺎ ﺑﻪ ﭼﺎپ رﺷﺘﻪﻫﺎ ﺑﻪ ﺻﻮرت ﻣﻨﻈﻢ و راﺣﺖﺗﺮ ﮐﻤﮏ ﻣﯽﮐﻨﺪ‪ .‬در اداﻣﻪ اﻃﻼﻋﺎت اﻓﺮاد‬
‫ﺷﺎﻣﻞ‪ ،‬ﻧﺎم‪ ،‬ﺳﻦ و ﻗﺪ آﻧﻬﺎ در ﯾﮏ دﯾﮑﺸﻨﺮي ﻣﻮﺟﻮد اﺳﺖ و ﻣﺎ ﺑﺎ اﺳﺘﻔﺎده از ﻓﺮﻣﺖﻫﺎ ﻣﯽﺧﻮاﻫﯿﻢ اﯾﻦ‬
‫اﻃﻼﻋﺎت را ﺑﻪﺻﻮرت ﻣﻨﻈﻢ و ﺧﻮاﻧﺎ ﭼﺎپ ﮐﻨﯿﻢ‪.‬‬

‫[ = ‪users‬‬
‫‪{"name": "Siavash", "age": 25, "height": 1.85},‬‬
‫‪{"name": "Soroush", "age": 16, "height": 1.72},‬‬
‫}‪{"name": "Guido", "age": 64, "height": 1.8‬‬
‫]‬

‫‪for user in users:‬‬


‫"|}‪print(f"{user['name']:<8}|{user['age']:<3‬‬
‫)"}‪f"{user['height']:.2f‬‬

‫‪>> Siavash |25 |1.85‬‬


‫‪Soroush |16 |1.72‬‬
‫‪Guido |64 |1.80‬‬

‫در ﻣﺜﺎل ﻗﺒﻞ‪ ،‬ﺑﺮاي ﻧﺎم‪ ،‬ﻓﻀﺎي ‪ 8‬و ﺑﺮاي ﺳﻦ‪ ،‬ﻓﻀﺎي ‪ 3‬ﮐﺎراﮐﺘﺮي درﻧﻈﺮ ﮔﺮﻓﺘﻪ ﺷﺪه اﺳﺖ‪ .‬ﻫﻤﭽﻨﯿﻦ ﻗﺪ‬
‫اﻓﺮاد ﺑﺎ دو رﻗﻢ اﻋﺸﺎر‪ ،‬ﻓﺮﻣﺖدﻫﯽ ﺷﺪه اﺳﺖ‪ .‬ﻧﮑﺘﮥ دﯾﮕﺮي ﮐﻪ در اﯾﻦ ﻣﺜﺎل وﺟﻮد دارد ﻗﺎﺑﻠﯿﺖ ﻧﻮﺷﺘﻦ‬
‫ﭼﻨﺪﺧﻄﯽ رﺷﺘﻪﻫﺎ ﻣﯽﺑﺎﺷﺪ‪ .‬ﺑﻪ اﯾﻦ ﺻﻮرت ﮐﻪ رﺷﺘﻪﻫﺎ را ﺑﺮاي ﺧﻮاﻧﺎﯾﯽ ﺑﯿﺸﺘﺮ ﻣﯽﺗﻮان در ﻫﺮ ﺧﻂ ﻧﻮﺷﺖ‬
‫اﻣﺎ در اﺑﺘﺪاي ﻫﺮ ﺧﻂ ﺑﺎﯾﺪ ﮐﺎراﮐﺘﺮ ‪ f‬ﮔﺬاﺷﺘﻪ ﺷﻮد‪.‬‬

‫‪ Type Annotation‬ﺑﺮاي ﻣﺘﻐﯿﺮﻫﺎ‬


‫در ﭘﺎﯾﺘﻮن ‪ 3.5‬ﻣﯽﺗﻮاﻧﺴﺘﯿﻢ در ﺗﻮاﺑﻊ و ﻣﺘﺪﻫﺎ ﻧﻮع ﻣﺘﻐﯿﺮﻫﺎي ورودي و ﺧﺮوﺟﯽ را ﻣﺸﺨﺺ ﮐﻨﯿﻢ‪ .‬در‬
‫ﻣﺜﺎل زﯾﺮ ﻗﺴﻤﺖﻫﺎﯾﯽ ﮐﻪ ﻫﺎﯾﻼﯾﺖ ﺷﺪه‪ ،‬ﻣﺘﻐﯿﺮﻫﺎي ورودي ﮐﻪ ‪ Integer‬و ﺧﺮوﺟﯽ ﺗﺎﺑﻊ ﮐﻪ ‪ float‬اﺳﺖ‬
‫را ﻧﻤﺎﯾﺶ ﻣﯽدﻫﺪ‪ .‬ﻻزم ﺑﻪ ذﮐﺮ اﺳﺖ ﺑﻪ اﯾﻦ ﻧﺸﺎﻧﻪﮔﺬاري‪ Type Hints ،‬ﻫﻢ ﮔﻔﺘﻪ ﻣﯽﺷﻮد‪.‬‬

‫‪def div(a: int, b: int) -> float:‬‬


‫‪return a / b‬‬

‫اﻣﺎ در ﭘﺎﯾﺘﻮن ‪ 3.6‬ﺑﺮاي ﻣﺘﻐﯿﺮﻫﺎ ﻫﻢ اﯾﻦ اﻣﮑﺎن ﻓﺮاﻫﻢ ﺷﺪه اﺳﺖ‪ .‬ﻣﯽﺗﻮان ﻧﻮع ﻣﺘﻐﯿﺮﻫﺎ را ﻧﯿﺰ ﺗﻌﺮﯾﻒ ﮐﺮد‪.‬‬
‫ﺑﺮاي اﺳﺘﻔﺎده از دﯾﮑﺸﻨﺮي و ﻟﯿﺴﺖ و ﻧﻮعﻫﺎي دﯾﮕﺮ ﺑﺎﯾﺪ از ﻣﺎژول ‪ typing‬اﺳﺘﻔﺎده ﮐﺮد‪.‬‬

‫‪from typing import List, Dict‬‬

‫‪number: int = 20‬‬


‫]‪numbers: List[int] = [1,2,3,4,5‬‬
‫‪Ahwaz_Hackerz‬‬

‫‪21‬‬ ‫ﺑﺨﺶ اول ‪ /‬ﻓﺼﻞ ‪ / 1‬ﭘﺎﯾﺘﻮن ‪306‬‬

‫}‪grades: Dict[str,float] = {"Shayan": 19.8‬‬

‫ﻫﻤﭽﻨﯿﻦ اﮔﺮ ﻣﺘﻐﯿﺮ از دو ﯾﺎ ﭼﻨﺪ ﻧﻮع ﺑﺎﺷﺪ ﺑﺎﯾﺪ از ‪ Union‬اﺳﺘﻔﺎده ﮐﺮد‪.‬‬

‫‪from typing import Union‬‬

‫‪price: Union[str,int] = 2000‬‬


‫"‪price = "2000$‬‬

‫ﻻزم ﺑﻪ ذﮐﺮ اﺳﺖ ﮐﻪ ﻣﻔﺴﺮ ﭘﺎﯾﺘﻮن ‪ Type Hints‬را ﺑﺮرﺳﯽ ﻧﻤﯽﮐﻨﺪ و ﺑﺮاي آن ﻣﻌﻨﯽ ﻧﺪارد و ﺑﺮاي‬
‫اﺳﺘﻔﺎده از اﺑﺰارﻫﺎ و ﮐﺘﺎﺑﺨﺎﻧﻪﻫﺎي دﯾﮕﺮ ﻫﻤﭽﻮن ‪ PyCharm‬و ‪ Mypy‬ﻣﻔﯿﺪ اﺳﺖ‪.‬‬

‫اﺿﺎﻓﻪﺷﺪن ﻣﺎژول ‪Secrets‬‬


‫ﻣﺎژول ‪ secrets‬ﻣﯽﺗﻮاﻧﺪ رﺷﺘﻪﻫﺎ و اﻋﺪاد ﺗﺼﺎدﻓﯽ ﮐﻪ از ﻟﺤﺎظ رﻣﺰﻧﮕﺎري ﻗﻮي ﻫﺴﺘﻨﺪ را ﺗﻮﻟﯿﺪ ﮐﻨﺪ‪.‬‬
‫ﺑﺴﯿﺎري از ﻣﺘﺪﻫﺎي اﯾﻦ ﻣﺎژول ﻫﻤﺎﻧﻨﺪ ﻣﺎژول ‪ random‬ﮐﻪ اﻋﺪاد ﺷﺒﻪ‪-‬ﺗﺼﺎدﻓﯽ‪ 1‬ﺗﻮﻟﯿﺪ ﻣﯽﮐﻨﺪ‪،‬‬
‫ﭘﯿﺎدهﺳﺎزي ﺷﺪه اﺳﺖ‪ .‬ﺑﺎ اﺳﺘﻔﺎده از اﯾﻦ ﻣﺎژول ﻣﯽﺗﻮاﻧﯿﺪ اﻋﺪاد و رﺷﺘﻪﻫﺎي ﺗﺼﺎدﻓﯽ ﺑﻪﻣﻨﻈﻮر اﺣﺮاز‬
‫ﻫﻮﯾﺖ‪ ،‬رﻣﺰ ﻋﺒﻮر و ‪ Token‬ﺗﻮﻟﯿﺪ ﮐﻨﯿﺪ‪.‬‬

‫ﺷﺎﯾﺪ اﯾﻦ ﺳﺆال ﭘﯿﺶ ﺑﯿﺎﯾﺪ ﮐﻪ ﭼﺮا از ﻫﻤﺎن ‪ random‬اﺳﺘﻔﺎده ﻧﮑﻨﯿﻢ؟ ﺟﻮاب آن اﺳﺖ ﮐﻪ ﺑﺮاي ﮐﺎرﻫﺎي‬
‫ﻏﯿﺮﺿﺮوري )ﻣﺜﻼً در ﯾﮏ ﺑﺎزي ﺑﺨﻮاﻫﯿﻢ ﯾﮏ ﻋﺪد ﺗﺼﺎدﻓﯽ ﺗﻮﻟﯿﺪ ﮐﻨﯿﻢ( ﻣﯽﺗﻮان از ‪ random‬اﺳﺘﻔﺎده‬
‫ﮐﺮد‪ .‬در اﯾﻦ ﻣﺎژول از ‪ seed‬اﺳﺘﻔﺎده ﻣﯽﺷﻮد ﮐﻪ اﮔﺮ ﻣﻨﺒﻊ ‪ randomness‬را ﺳﯿﺴﺘﻢﻋﺎﻣﻞ ﻋﺮﺿﻪ ﻧﮑﻨﺪ‪،‬‬
‫از زﻣﺎن ﺳﯿﺴﺘﻢ اﺳﺘﻔﺎده ﻣﯽﺷﻮد‪ .‬ﺑﺎ داﺷﺘﻦ اﻋﺪاد ﺗﺼﺎدﻓﯽ ﺗﻮﻟﯿﺪ ﺷﺪه‪ ،‬ﻣﯽﺗﻮان ‪ seed‬را ﺣﺪس زد و‬
‫رﺷﺘﻪﻫﺎي ﻣﺘﻮاﻟﯽ را در ﻃﻮل زﻣﺎن ﺗﻮﻟﯿﺪ ﮐﺮد‪ .‬اﻣﺎ در ﻣﻮرد ﺗﻮﻟﯿﺪ رﻣﺰ ﻋﺒﻮر ﮐﻪ ﯾﮏ رﺷﺘﻪ ﺑﺴﯿﺎر ﻣﻬﻢ و‬
‫ﺣﯿﺎﺗﯽ اﺳﺖ‪ ،‬ﻣﺴﺌﻠﻪ ﻓﺮق ﻣﯽﮐﻨﺪ و ﺑﺎﯾﺪ ﺣﺘﻤﺎً از ﺳﺨﺖﺑﻮدن رﺷﺘﻪ ﺗﻮﻟﯿﺪﺷﺪه ﻣﻄﻤﺌﻦ ﺑﻮد و اﻣﮑﺎن ﺣﺪس‬
‫و ﺗﻮﻟﯿﺪ آن وﺟﻮد ﻧﺪاﺷﺘﻪ ﺑﺎﺷﺪ‪.‬‬

‫در ﺟﺪول ‪ 3‬ﺗﻮاﺑﻊ ﭘﺮﮐﺎرﺑﺮد اﯾﻦ ﻣﺎژول را ﻣﺮور ﻣﯽﮐﻨﯿﻢ‪.‬‬

‫‪1‬‬
‫‪Pseudo-random‬‬
‫‪Ahwaz_Hackerz‬‬

‫ﻣﺒﺎﺣﺚ ﭘﯿﺸﺮﻓﺘﻪ در ﭘﺎﯾﺘﻮن ‪ /‬ﻣﻮﺳﺴﻪ ﻓﺮﻫﻨﮕﯽ ﻫﻨﺮي دﯾﺒﺎﮔﺮان ﺗﻬﺮان‬ ‫‪22‬‬

‫ﺟﺪول ‪ - 3‬ﺗﻮاﺑﻊ ﭘﺮﮐﺎرﺑﺮد ﻣﺎژول ‪secrets‬‬

‫ﺗﺎﺑﻊ‬ ‫ﺗﻮﺿﯿﺤﺎت‬
‫ﯾﮏ ا‪‬ﻟﻤﺎن از ﯾﮏ دﻧﺒﺎﻟﮥ ﻏﯿﺮﺧﺎﻟﯽ را‬
‫)‪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‬‬

‫‪alphabet = string.ascii_letters + string.digits‬‬

‫‪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

23 306 ‫ ﭘﺎﯾﺘﻮن‬/ 1 ‫ ﻓﺼﻞ‬/ ‫ﺑﺨﺶ اول‬

import secrets

token = secrets.token_urlsafe(16)
print(token)

>> bfaIv7qGAmjt9osV6UOFJA
‫‪Ahwaz_Hackerz‬‬

‫ﻣﺒﺎﺣﺚ ﭘﯿﺸﺮﻓﺘﻪ در ﭘﺎﯾﺘﻮن ‪ /‬ﻣﻮﺳﺴﻪ ﻓﺮﻫﻨﮕﯽ ﻫﻨﺮي دﯾﺒﺎﮔﺮان ﺗﻬﺮان‬ ‫‪24‬‬

‫ﻓﺼﻞ‪2‬‬
‫ﭘﺎﯾﺘﻮن ‪3.7‬‬
‫‪Ahwaz_Hackerz‬‬

‫‪25‬‬ ‫ﺑﺨﺶ اول ‪ /‬ﻓﺼﻞ ‪ / 2‬ﭘﺎﯾﺘﻮن ‪307‬‬

‫ﮐﻼسﻫﺎي داده‬
‫در ﭘﺎﯾﺘﻮن ‪ 3.7‬روش ﺟﺪﯾﺪي ﺑﺮاي ﺗﻮﻟﯿﺪ ﮐﻼسﻫﺎي داده ﻣﻌﺮﻓﯽ ﺷﺪ‪ .‬در اﯾﻦ روش ﻣﺘﺪﻫﺎي وﯾﮋه‪ 1‬از‬
‫ﺟﻤﻠﻪ)(__‪ .__eq__()،.__repr__()،.__init‬و)(__‪ .__hash‬ﺑﻪﺻﻮرت ﺧﻮدﮐﺎر اﺿﺎﻓﻪ‬
‫ﻣﯽﺷﻮد و ﺗﻨﻬﺎ ﮐﺎري ﮐﻪ ﻧﯿﺎز اﺳﺖ اﻧﺠﺎم ﺷﻮد اﺳﺘﻔﺎده از ﯾﮏ ‪ Decorator‬ﺑﺎ ﻧﺎم ‪@dataclass‬‬
‫ﻣﯽﺑﺎﺷﺪ‪ .‬ﺑﺎ ‪ Decorator‬در ﺑﺨﺶﻫﺎي آﯾﻨﺪه آﺷﻨﺎ ﺧﻮاﻫﯿﺪ ﺷﺪ‪.‬‬

‫در ﻣﺜﺎل زﯾﺮ ﮐﻼس ﻣﺎﺷﯿﻦ را ﺑﺎ ﺧﺼﻮﺻﯿﺎﺗﺶ از ﻗﺒﯿﻞ ﻧﺎم‪ ،‬ﻣﺪل‪ ،‬ﺣﺪاﮐﺜﺮ ﺳﺮﻋﺖ و ﻗﯿﻤﺖ و ﯾﮏ ﻣﺘﺪ ﺑﺮاي‬
‫ﺗﺒﺪﯾﻞ ﺳﺮﻋﺖ از ﮐﯿﻠﻮﻣﺘﺮ در ﺳﺎﻋﺖ ﺑﻪ ﻣﺎﯾﻞ در ﺳﺎﻋﺖ ﭘﯿﺎدهﺳﺎزي ﻣﯽﮐﻨﯿﻢ‪.‬‬

‫‪from dataclasses import dataclass, field‬‬

‫)‪@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 = Car("BMW", "328i", 220, 2200‬‬


‫)‪print(bmw.name‬‬

‫‪>> BMW‬‬

‫))(‪print(bmw.max_speed_in_mph‬‬

‫‪>> 136.4‬‬

‫‪1‬در ﭘﺎﯾﺘﻮن اﺳﻢﻫﺎي ﻣﺘﻔﺎوﺗﯽ ﺑﺮاي ﻣﺘﺪﻫﺎي وﯾﮋه‪ ،‬ﻣﺎﻧﻨﺪ ﻣﺘﺪﻫﺎي ﺟﺎدوﯾﯽ وﺟﻮد دارد! ﯾﮑﯽ از اﯾﻦ ﻧﺎمﻫﺎ‬
‫‪ Dunder Method‬ﻫﺎ ﻫﺴﺘﻨﺪ‪ .‬ﺟﺎﻟﺐ اﺳﺖ ﺑﺪاﻧﯿﺪ ‪ Dunder‬از اﺑﺘﺪاي ﮐﻠﻤﺎت ‪ Double Underscore‬ﮐﻪ ﻧﺸﺎﻧﮕﺮ دو‬
‫ﻋﻼﻣﺖ زﯾﺮﯾﻦ ﺧﻂ اﺑﺘﺪا و اﻧﺘﻬﺎي اﯾﻦ ﻣﺘﺪﻫﺎﺳﺖ‪ ،‬ﺗﺸﮑﯿﻞ ﺷﺪه اﺳﺖ‪.‬‬
‫‪Ahwaz_Hackerz‬‬

‫ﻣﺒﺎﺣﺚ ﭘﯿﺸﺮﻓﺘﻪ در ﭘﺎﯾﺘﻮن ‪ /‬ﻣﻮﺳﺴﻪ ﻓﺮﻫﻨﮕﯽ ﻫﻨﺮي دﯾﺒﺎﮔﺮان ﺗﻬﺮان‬ ‫‪26‬‬

‫ﻫﻤﭽﻨﯿﻦ ﻧﻤﺎﯾﺶ ﯾﮏ ﺷﺊ از اﯾﻦ ﮐﻼس ﺑﻪ روش ﺧﻮب و آﺳﺎﻧﯽ ﻗﺎﺑﻞ ﺧﻮاﻧﺪن اﺳﺖ‪ .‬ﻫﻤﺎنﻃﻮري ﮐﻪ در‬
‫ﻣﺜﺎل زﯾﺮ ﻣﺸﺎﻫﺪه ﻣﯽﮐﻨﯿﺪ ﻣﺒﻠﻎ ﻣﺎﺷﯿﻦ ﻧﻤﺎﯾﺶ داده ﻧﺸﺪه اﺳﺖ زﯾﺮا ﻣﻘﺪار ‪ repr=False‬ﺑﺮاي آن‬
‫ﻓﯿﻠﺪ درﻧﻈﺮ ﮔﺮﻓﺘﻪ ﺷﺪه ﺑﻮد‪.‬‬

‫)‪print(bmw‬‬

‫)‪>> Car(name='BMW', model='328i', max_speed=220‬‬

‫ﻣﯽﺗﻮان ﺑﻪراﺣﺘﯽ ﺑﺮاﺑﺮي ﺑﯿﻦ دو ﮐﻼس دادهاي را ﺑﺮرﺳﯽ ﮐﺮد‪ .‬در ﻣﺜﺎل زﯾﺮ ﻣﺒﻠﻎ ﻣﺎﺷﯿﻦ ﺑﻪدﻟﯿﻞ ﻧﻮﺷﺘﻦ‬
‫‪ compare=False‬در ﻓﯿﻠﺪ آن‪ ،‬درﻧﺘﯿﺠﮥ ﺑﺮاﺑﺮي درﻧﻈﺮ ﮔﺮﻓﺘﻪ ﻧﻤﯽﺷﻮد‪.‬‬

‫)‪bmw = Car("BMW", "328i", 220, 2200‬‬


‫)‪bmw2 = Car("BMW", "328i", 220, 1200‬‬

‫)‪print(bmw == bmw2‬‬

‫‪>> True‬‬

‫ﻫﻤﭽﻨﯿﻦ ﺑﺎ ﻗﺮاردادن ‪ order=True‬در ‪ Decorator‬ﮐﻼس‪ ،‬ﻗﺎﺑﻠﯿﺖ ﻣﺮﺗﺐﺳﺎزي ﺑﻪ آن اﺿﺎﻓﻪ ﻣﯽﺷﻮد‪.‬‬


‫ﻣﺮﺗﺐﮐﺮدن ﺑﺮاﺳﺎس ﻓﯿﻠﺪ اول ﯾﻌﻨﯽ ﻧﺎم ﻣﺎﺷﯿﻦ اﻧﺠﺎم ﻣﯽﺷﻮد و اﮔﺮ ﻧﺎمﻫﺎ ﯾﮑﺴﺎن ﺑﺎﺷﺪ‪ ،‬ﺑﺮاﺳﺎس ﻓﯿﻠﺪ دوم‬
‫ﯾﻌﻨﯽ ﻣﺪل ﻣﺎﺷﯿﻦ اﻧﺠﺎم ﻣﯽﺷﻮد و ﺑﻪ ﻫﻤﯿﻦ ﺻﻮرت اداﻣﻪ ﻣﯽﯾﺎﺑﺪ‪ .‬ﻻزم ﺑﻪ ذﮐﺮ اﺳﺖ ﮐﻪ ﻗﯿﻤﺖ ﻣﺎﺷﯿﻦ‬
‫ﺑﻪدﻟﯿﻞ داﺷﺘﻦ ‪ compare=False‬در ﻓﯿﻠﺪ ﺧﻮد‪ ،‬ﺗﺄﺛﯿﺮي در ﻣﺮﺗﺐﺳﺎزي ﻧﺪارد‪.‬‬

‫)‪bmw = Car("BMW", "328i", 220, 2200‬‬


‫)‪benz = Car("BENZ", "CLS 500", 200, 3600‬‬
‫)‪pride = Car("Pride", "132", 180.0, 700‬‬

‫))]‪print(sorted([bmw, benz, pride‬‬

‫[ >>‬
‫‪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‬‬
‫]‬

‫‪ ‬ﻣﻘﺪاردﻫﯽ ﭘﯿﺶﻓﺮض در ﮐﻼسﻫﺎي داده‬


‫ﻣﯽﺗﻮان ﻣﻘﺎدﯾﺮ ﭘﯿﺶﻓﺮض ﺑﺮاي دادهﻫﺎي داﺧﻞ ﮐﻼس ﺗﻌﯿﯿﻦ ﮐﺮد‪ .‬ﻣﺜﺎل زﯾﺮ ﮐﻪ ﻣﺮﺑﻮط ﺑﻪ اﯾﻤﯿﻞﻫﺎي‬
‫درﯾﺎﻓﺘﯽ ﮐﺎرﺑﺮان اﺳﺖ‪ ،‬ﺑﻪوﺿﻮح روش اﻧﺠﺎم اﯾﻦ ﮐﺎر را ﻧﺸﺎن ﻣﯽدﻫﺪ‪.‬‬

‫‪from dataclasses import dataclass, field‬‬


‫‪from typing import List‬‬
‫‪Ahwaz_Hackerz‬‬

‫‪27‬‬ ‫ﺑﺨﺶ اول ‪ /‬ﻓﺼﻞ ‪ / 2‬ﭘﺎﯾﺘﻮن ‪307‬‬

‫‪@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‬اﺳﺘﻔﺎده ﮐﺮدﯾﻢ‪ .‬اﮔﺮ ﻣﯽﺧﻮاﺳﺘﯿﻢ ﻫﻤﺎﻧﻨﺪ‬
‫ﺗﮑﻪ ﮐﺪ زﯾﺮ ﺑﻪﻃﻮر ﻣﺴﺘﻘﯿﻢ اﯾﻦ ﻣﻘﺪاردﻫﯽ را اﻧﺠﺎم دﻫﯿﻢ‪ ،‬ﯾﮏ ﭘﺎد اﻟﮕﻮ در ﭘﺎﯾﺘﻮن ﺑﻮد‪.‬‬

‫‪inbox: List[Email] = get_new_inbox() # Incorrect‬‬

‫در اﯾﻦ ﭘﺎد اﻟﮕﻮ‪ ،‬ﻣﺸﮑﻞ از آﻧﺠﺎ ﻧﺎﺷﯽ ﻣﯽﺷﻮد ﮐﻪ از ﯾﮏ ‪ mutable‬ﺑﻪﻋﻨﻮان ﻣﻘﺪار ﭘﯿﺶﻓﺮض ﺑﺮاي ﯾﮏ‬
‫آرﮔﻮﻣﺎن ﻣﯽﺧﻮاﻫﯿﻢ اﺳﺘﻔﺎده ﮐﻨﯿﻢ‪ .‬ﯾﻌﻨﯽ ﺑﺮاي ﺗﻤﺎم ﻧﻤﻮﻧﻪﻫﺎي ﮐﻼس ‪ ،UserInbox‬ﯾﮏ ﻟﯿﺴﺖ‬
‫ﯾﮑﺴﺎن ﺑﺮاي ﻫﻤﮥ ﻧﻤﻮﻧﻪﻫﺎ ﺳﺎﺧﺘﻪ ﻣﯽﺷﻮد‪ .‬ﺑﻪ زﺑﺎن دﯾﮕﺮ‪ ،‬اﮔﺮ ﯾﮏ اﯾﻤﯿﻞ ﺑﻪ ‪ inbox‬ﯾﮏ ﮐﺎرﺑﺮ اﺿﺎﻓﻪ ﺷﻮد‬
‫ﺑﺮاي ﺗﻤﺎﻣﯽ ﮐﺎرﺑﺮان ﻧﯿﺰ اﺿﺎﻓﻪ ﻣﯽﺷﻮد‪ .‬ﺧﻮﺷﺒﺨﺘﺎﻧﻪ ﮐﻼس داده ﺷﻤﺎ را از ﻣﻘﺪاردﻫﯽ ﭘﯿﺶﻓﺮض‬
‫ﺑﺪﯾﻦﺻﻮرت ﻣﻨﻊ ﻣﯽﮐﻨﺪ‪.‬‬

‫‪ ‬ﺳﺎﺧﺖ ﮐﻼس داده ﺗﻐﯿﯿﺮﻧﺎﭘﺬﯾﺮ‬


‫ﻫﻤﭽﻮن ‪ collections.namedtuple‬ﻣﯽﺗﻮان ﮐﻼس دادهاي ﺳﺎﺧﺖ ﮐﻪ دادهﻫﺎي آن ﺗﻐﯿﯿﺮ‬
‫ﻧﮑﻨﺪ و ﺑﻪاﺻﻄﻼح ‪ immutable‬ﺑﺎﺷﺪ‪ .‬در اﯾﻦ ﻧﻮع ﮐﻼس دادهاي‪ ،‬ﻣﻘﺎدﯾﺮ ﻓﯿﻠﺪﻫﺎ ﭘﺲ از ﻣﻘﺪاردﻫﯽ اوﻟﯿﻪ‪،‬‬
‫ﻗﺎﺑﻞ ﺗﻐﯿﯿﺮ ﻧﻤﯽﺑﺎﺷﺪ‪ .‬ﺑﺪﯾﻦﻣﻨﻈﻮر ﺑﺎﯾﺪ از ‪ frozen=True‬در ‪ Decorator‬اﺳﺘﻔﺎده ﮐﺮد‪.‬‬

‫‪from dataclasses import dataclass‬‬

‫)‪@dataclass(frozen=True‬‬
‫‪Ahwaz_Hackerz‬‬

‫ﻣﺒﺎﺣﺚ ﭘﯿﺸﺮﻓﺘﻪ در ﭘﺎﯾﺘﻮن ‪ /‬ﻣﻮﺳﺴﻪ ﻓﺮﻫﻨﮕﯽ ﻫﻨﺮي دﯾﺒﺎﮔﺮان ﺗﻬﺮان‬ ‫‪28‬‬

‫‪class File:‬‬
‫‪name: str‬‬
‫‪size: float‬‬

‫)‪file = File("test.zip", 22.5‬‬


‫‪file.size = 25.2‬‬

‫'‪!> dataclasses.FrozenInstanceError: cannot assign to field 'size‬‬

‫در ﻣﺜﺎل ﺑﺎﻻ‪ ،‬ﻣﻘﺪار ‪ size‬ﻧﻤﯽﺗﻮاﻧﺪ ﺗﻐﯿﯿﺮ ﭘﯿﺪا ﮐﻨﺪ‪ ،‬زﯾﺮا ﮐﻼس ﻓﺎﯾﻞ از ﻧﻮع ‪ read-only‬ﻣﯽﺑﺎﺷﺪ و ﭘﺲ‬
‫از ﻣﻘﺪاردﻫﯽ اوﻟﯿﻪ دﯾﮕﺮ ﻧﻤﯽﺗﻮان ﻣﻘﺎدﯾﺮ ﻓﯿﻠﺪﻫﺎي آن را ﺗﻐﯿﯿﺮ داد‪.‬‬

‫‪ ‬ﻓﺮاداده در ﻓﯿﻠﺪﻫﺎي ﮐﻼس داده‬


‫در ﮐﻼسﻫﺎي داده ﻣﯽﺗﻮان ﯾﮏ ﻧﮕﺎﺷﺖ‪ 1‬ﺑﺮاي ﻓﺮادادهﻫﺎ‪ 2‬درﻧﻈﺮ ﮔﺮﻓﺖ‪ .‬اﯾﻦ اﻃﻼﻋﺎت اﺿﺎﻓﻪ در ﮐﻼس‬
‫داده اﺳﺘﻔﺎده ﻧﻤﯽﺷﻮد ﺑﻠﮑﻪ ﺑﺮاي ﺷﻤﺎ و ﯾﺎ اﺑﺰارﻫﺎي دﯾﮕﺮ ﻗﺎﺑﻞ اﺳﺘﻔﺎده ﻣﯽﺑﺎﺷﺪ‪ .‬اﯾﻦ ﻓﺮادادهﻫﺎ را ﺑﺎ‬
‫اﺳﺘﻔﺎده از ﺗﺎﺑﻊ ‪ fields‬اﺳﺘﺨﺮاج ﻣﯽﮐﻨﯿﻢ‪ .‬درواﻗﻊ‪ ،‬اﯾﻦ ﺗﺎﺑﻊ ﺑﻪ ﻣﺎ ﮐﻤﮏ ﻣﯽﮐﻨﺪ ﺗﺎ ﻓﯿﻠﺪﻫﺎي داﺧﻠﯽ‬
‫ﯾﮏ ﮐﻼس داده را ﻣﺸﺎﻫﺪه ﮐﺮده و ﻣﻘﺎدﯾﺮ دﻟﺨﻮاه را اﺳﺘﺨﺮاج ﮐﻨﯿﻢ‪.‬‬

‫‪from dataclasses import dataclass, field, fields‬‬

‫‪@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‬‬

‫‪29‬‬ ‫ﺑﺨﺶ اول ‪ /‬ﻓﺼﻞ ‪ / 2‬ﭘﺎﯾﺘﻮن ‪307‬‬

‫‪ ‬ﺑﻬﯿﻨﻪﺑﻮدن ﮐﻼس داده‬


‫ﺑﺎ ‪ slots‬ﻣﯽﺗﻮان ﮐﻼسﻫﺎي ﺳﺮﯾﻊﺗﺮ و ﺑﺎ ﻣﺼﺮف ﺣﺎﻓﻈﮥ ﮐﻤﺘﺮ ﺳﺎﺧﺖ‪ .‬ﺑﺎ اﺿﺎﻓﻪﮐﺮدن ﻣﺘﻐﯿﺮﻫﺎ ﺑﻪ ﻟﯿﺴﺖ‬
‫__‪ __slots‬اﯾﻦ اﻣﺮ ﻣﺤﻘﻖ ﻣﯽﺷﻮد‪.‬‬

‫‪from dataclasses import dataclass‬‬

‫‪@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‬‬

‫‪for k,v in test_dictionary.items():‬‬


‫)'}‪print(f'{k}:{v‬‬

‫‪>> 5:5‬‬
‫‪4:4‬‬
‫‪3:3‬‬
‫‪2:2‬‬
‫‪1:1‬‬

‫دﯾﮕﺮ ﺗﻐﯿﯿﺮات در ﭘﺎﯾﺘﻮن ‪3.7‬‬


‫ﻣﻮاردي ﮐﻪ در ﺑﺎﻻ ذﮐﺮ ﺷﺪ ﮐﺎراﯾﯽ ﺑﺴﯿﺎري در ﮐﺪزدن ﺷﻤﺎ دارد‪ ،‬وﻟﯽ در ﭘﺎﯾﺘﻮن ‪ 3.7‬اﻣﮑﺎﻧﺎت و‬
‫وﯾﮋﮔﯽﻫﺎي ﺑﺴﯿﺎر زﯾﺎدي اﺿﺎﻓﻪ ﺷﺪه اﺳﺖ ﮐﻪ ﺑﺮاي ﻣﻄﺎﻟﻌﮥ ﺑﯿﺸﺘﺮ ﻣﯽﺗﻮاﻧﯿﺪ ﺑﻪ آدرس زﯾﺮ ﻣﺮاﺟﻌﻪ ﻧﻤﺎﯾﯿﺪ‪.‬‬

‫‪https://docs.python.org/3/whatsnew/3.7.html‬‬
‫‪Ahwaz_Hackerz‬‬

‫ﻣﺒﺎﺣﺚ ﭘﯿﺸﺮﻓﺘﻪ در ﭘﺎﯾﺘﻮن ‪ /‬ﻣﻮﺳﺴﻪ ﻓﺮﻫﻨﮕﯽ ﻫﻨﺮي دﯾﺒﺎﮔﺮان ﺗﻬﺮان‬ ‫‪30‬‬

‫ﻓﺼﻞ‪3‬‬
‫ﭘﺎﯾﺘﻮن ‪3.8‬‬
‫‪Ahwaz_Hackerz‬‬

‫‪31‬‬ ‫ﺑﺨﺶ اول ‪ /‬ﻓﺼﻞ ‪ / 3‬ﭘﺎﯾﺘﻮن ‪308‬‬

‫ﻋﺒﺎرت اﻧﺘﺴﺎﺑﯽ‬
‫ﯾﮑﯽ از ﺗﻐﯿﯿﺮات ﻋﻤﺪه و ﮐﺎرآﻣﺪ در ﭘﺎﯾﺘﻮن ‪ 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‬‬

‫ﻣﺒﺎﺣﺚ ﭘﯿﺸﺮﻓﺘﻪ در ﭘﺎﯾﺘﻮن ‪ /‬ﻣﻮﺳﺴﻪ ﻓﺮﻫﻨﮕﯽ ﻫﻨﺮي دﯾﺒﺎﮔﺮان ﺗﻬﺮان‬ ‫‪32‬‬

‫)(‪users = list‬‬

‫‪while (user:= input())!= "end":‬‬


‫)‪users.append(user‬‬

‫آرﮔﻮﻣﺎنﻫﺎي ﻣﮑﺎﻧﯽ‬
‫ﯾﮑﯽ دﯾﮕﺮ از وﯾﮋﮔﯽﻫﺎي ﺟﺪﯾﺪ در ﭘﺎﯾﺘﻮن ‪ 3.8‬ﻣﻌﺮﻓﯽ ﻗﺎﺑﻠﯿﺘﯽ ﺑﺮاي ﺷﻨﺎﺳﺎﻧﺪن آرﮔﻮﻣﺎنﻫﺎ ﺑﻪﺻﻮرت ﻓﻘﻂ‬
‫ﻣﮑﺎﻧﯽ‪ 1‬ﻣﯽﺑﺎﺷﺪ‪ .‬ﺑﺎ ﮔﺬاﺷﺘﻦ ﻋﻼﻣﺖ ‪ /‬در آرﮔﻮﻣﺎنﻫﺎي ﺗﺎﺑﻊ‪ ،‬ﭘﺎﯾﺘﻮن ﻣﺘﻀﻤﻦ ﻣﯽﺷﻮد ﮐﻪ ﻓﺮاﺧﻮاﻧﯽ اﯾﻦ‬
‫ﺗﺎﺑﻊ ﺑﺎ آرﮔﻮﻣﺎنﻫﺎي ﺳﻤﺖ ﭼﭗ اﯾﻦ ﻋﻼﻣﺖ ﺑﻪ ﺻﻮرت ﻓﻘﻂ ‪ -‬ﻣﮑﺎﻧﯽ ﻗﺎﺑﻞ اﻧﺠﺎم اﺳﺖ‪ .‬ﺑﺮاي درك ﺑﻬﺘﺮ ﺑﻪ‬
‫ﻣﺜﺎل زﯾﺮ ﺗﻮﺟﻪ ﮐﻨﯿﺪ‪ .‬در اﯾﻦ ﻣﺜﺎل ﯾﮏ ﺗﺎﺑﻊ ﺳﺎده ﺑﺎ ﻧﺎم ‪ minus‬ﺗﻌﺮﯾﻒ ﮐﺮدهاﯾﻢ ﮐﻪ ﻋﺪد دوم را از ﻋﺪد‬
‫اول ﮐﻢ ﻣﯽﮐﻨﺪ و ﺟﻮاب را ﺑﺮﻣﯽﮔﺮداﻧﺪ‪.‬‬

‫‪def minus(a, b):‬‬


‫‪return a - b‬‬

‫))‪print(minus(10, 6‬‬

‫‪>> 4‬‬

‫ﺣﺎل ﻣﯽﺗﻮان ﺑﻪﺟﺎي ﮔﺬاﺷﺘﻦ ﭘﺎراﻣﺘﺮﻫﺎ ﺑﻪﺗﺮﺗﯿﺐ‪ ،‬از ﮐﻠﯿﺪواژه‪ 2‬آﻧﻬﺎ اﺳﺘﻔﺎده ﮐﺮد‪.‬‬

‫‪def minus(a, b):‬‬


‫‪return a - b‬‬

‫))‪print(minus(b=6, a=10‬‬

‫‪>> 4‬‬

‫اﯾﻦ روش ﻧﻮﺷﺘﻦ درﺣﺎﻟﯽﮐﻪ درﺳﺖ اﺳﺖ وﻟﯽ ﺧﻮاﻧﺎﯾﯽ و ﻓﻬﻢ ﮐﺪ را ﭘﺎﯾﯿﻦ ﻣﯽآورد‪ .‬ﺑﺎ ﮔﺬاﺷﺘﻦ ﻋﻼﻣﺖ‬
‫‪ slash‬ﺑﻌﺪ از آرﮔﻮﻣﺎن ‪ b‬ﺑﻪ ﭘﺎﯾﺘﻮن ﻣﯽﮔﻮﯾﯿﻢ ﮐﻪ آرﮔﻮﻣﺎنﻫﺎي ﺳﻤﺖ ﭼﭗ ‪ /‬را ﺑﻪﺻﻮرت ﻓﻘﻂ ‪-‬ﻣﮑﺎﻧﯽ‬
‫درﯾﺎﻓﺖ ﮐﻨﺪ‪ .‬ﺑﺎ اﯾﻦ ﮐﺎر ﻣﯽﺗﻮان ‪ API‬ﻫﺎي ﺳﺎﺧﺘﺎرﻣﻨﺪﺗﺮي ﺳﺎﺧﺖ‪.‬‬

‫‪def minus(a, b, /):‬‬


‫‪return a - b‬‬

‫))‪print(minus(10, 6‬‬

‫‪1‬‬
‫‪Positional-only‬‬
‫‪2‬‬
‫‪Keyword‬‬
‫‪Ahwaz_Hackerz‬‬

‫‪33‬‬ ‫ﺑﺨﺶ اول ‪ /‬ﻓﺼﻞ ‪ / 3‬ﭘﺎﯾﺘﻮن ‪308‬‬

‫‪>> 4‬‬

‫))‪print(minus(b=6, a=10‬‬

‫‪!> TypeError: minus() got some positional-only arguments‬‬


‫'‪passed as keyword arguments: 'a, b‬‬

‫ﻫﻤﺎنﻃﻮرﮐﻪ در ﻣﺜﺎل ﺑﺎﻻ ﭘﯿﺪاﺳﺖ در ﺗﺎﺑﻊ ‪ minus‬ﮐﻪ ﺳﺎزوﮐﺎر آن ﺑﻪ ﺻﻮرت ﻓﻘﻂ ﻣﮑﺎﻧﯽ ﺑﻬﺘﺮ و‬
‫ﻗﺎﺑﻞدركﺗﺮ اﺳﺖ‪ ،‬اﺳﺘﻔﺎده از ﮐﻠﯿﺪواژهﻫﺎ را ﻣﻨﻊ ﮐﺮدﯾﻢ‪ .‬ﺑﻨﺎﺑﺮاﯾﻦ ﮐﺪﻫﺎﯾﯽ ﮐﻪ ﻗﺮار اﺳﺖ از اﯾﻦ ﺗﺎﺑﻊ‬
‫اﺳﺘﻔﺎده ﮐﻨﻨﺪ ﺑﺴﯿﺎر ﺧﻮاﻧﺎﺗﺮ ﺧﻮاﻫﻨﺪ ﺷﺪ‪.‬‬

‫در ﻣﻮاردي ﮐﻪ ﮔﺬاﺷﺘﻦ آرﮔﻮﻣﺎنﻫﺎ ﺑﻪﺗﺮﺗﯿﺐ‪ ،‬ﻣﻨﻄﻖ ﺧﺎﺻﯽ دارد و اﻧﺘﺨﺎب ﮐﻠﯿﺪواژه ﺑﺮاي آرﮔﻮﻣﺎنﻫﺎ ﮐﺎر‬
‫دﺷﻮاري اﺳﺖ‪ ،‬اﺳﺘﻔﺎده از آرﮔﻮﻣﺎن ﻓﻘﻂ ﻣﮑﺎﻧﯽ ﺑﺴﯿﺎر ﮐﺎرﺑﺮدي اﺳﺖ‪ .‬ﻫﻤﭽﻨﯿﻦ ﺑﺎ ﺗﻌﺮﯾﻒ آرﮔﻮﻣﺎنﻫﺎ ﺑﻪ‬
‫ﺻﻮرت ﻓﻘﻂ ﻣﮑﺎﻧﯽ ﻣﯽﺗﻮان ﺗﻐﯿﯿﺮﭘﺬﯾﺮي را اﻓﺰاﯾﺶ داد‪ .‬ﯾﻌﻨﯽ در آﯾﻨﺪه ﺑﺪون آﻧﮑﻪ ﻧﮕﺮان ﺑﺎﺷﯿﻢ ﻧﺎم‬
‫ﮐﻠﯿﺪواژهﻫﺎ ﭼﯿﺴﺖ‪ ،‬ﻣﯽﺗﻮان آنﻫﺎ را ﺑﻪﺳﺎدﮔﯽ ﺗﻐﯿﯿﺮ داد‪ ،‬زﯾﺮا ﻣﻄﻤﺌﻦ ﻫﺴﺘﯿﻢ ﮐﺴﺎﻧﯽ ﮐﻪ از اﯾﻦ ﺗﻮاﺑﻊ‬
‫اﺳﺘﻔﺎده ﮐﺮدهاﻧﺪ ﻓﻘﻂ ﺑﻪﺻﻮرت ﻣﮑﺎﻧﯽ ﭘﺎراﻣﺘﺮﻫﺎ را ﺑﻪ ﺗﺎﺑﻊ ﭘﺎس دادهاﻧﺪ و از ﮐﻠﯿﺪواژه اﺳﺘﻔﺎده ﻧﮑﺮدهاﻧﺪ‪.‬‬
‫ﭘﺲ ﺗﻐﯿﯿﺮ ﻧﺎم آﻧﻬﺎ ﮐﺎري اﻣﻦ اﺳﺖ‪.‬‬

‫‪ ‬آرﮔﻮﻣﺎنﻫﺎي ﻓﻘﻂ ﮐﻠﯿﺪواژهاي‬


‫درﺳﺖ اﺳﺖ ﮐﻪ اﯾﻦ ﻗﺎﺑﻠﯿﺖ در ﺗﻤﺎم ﻧﺴﺨﻪﻫﺎي ﭘﺎﯾﺘﻮن ‪ 3‬ﻗﺎﺑﻞ اﺳﺘﻔﺎده اﺳﺖ‪ ،‬وﻟﯽ ﯾﺎدآوري آن ﭘﺲ از‬
‫آرﮔﻮﻣﺎنﻫﺎي ﻓﻘﻂ ﻣﮑﺎﻧﯽ ﺧﺎﻟﯽ از ﻟﻄﻒ ﻧﯿﺴﺖ‪ .‬آرﮔﻮﻣﺎنﻫﺎي ﻓﻘﻂ ﮐﻠﯿﺪواژهاي‪ 1‬ﻣﺘﻀﻤﻦ آن ﻫﺴﺘﻨﺪ ﮐﻪ‬
‫ﺗﻤﺎم آرﮔﻮﻣﺎنﻫﺎي ﺳﻤﺖ راﺳﺖ آن‪ ،‬ﺗﻨﻬﺎ ﺑﻪﺻﻮرت ﮐﻠﯿﺪواژهاي ﻓﺮاﺧﻮاﻧﯽ ﺷﻮﻧﺪ‪ .‬ﺑﺎ ﮔﺬاﺷﺘﻦ ﻋﻼﻣﺖ *‪،‬‬
‫ﭘﺎﯾﺘﻮن ﻣﺘﻀﻤﻦ ﻣﯽﺷﻮد ﮐﻪ ﻓﺮاﺧﻮاﻧﯽ ﺗﺎﺑﻊ ﭘﺲ از اﯾﻦ ﻋﻼﻣﺖ ﺗﻨﻬﺎ ﺑﺎ ﮐﻠﯿﺪواژهﻫﺎ اﻧﺠﺎم ﺷﻮد‪ .‬ﺑﺮاي روﺷﻦ‬
‫ﺷﺪن ﻣﻮﺿﻮع‪ ،‬ﺗﺮﮐﯿﺒﯽ از ﺗﻤﺎم اﯾﻦ اﻣﮑﺎﻧﺎت در ﻣﺜﺎل زﯾﺮ آورده ﺷﺪه اﺳﺖ‪.‬‬

‫در اﯾﻦ ﻣﺜﺎل ﯾﮏ ﺗﺎﺑﻊ ﻓﺮﺿﯽ دارﯾﻢ ﮐﻪ ﭘﯿﺎﻣﯽ را ﺑﻪ ﮐﺎرﺑﺮي ارﺳﺎل ﻣﯽﮐﻨﺪ‪ .‬در اﯾﻦ ﻣﺜﺎل آرﮔﻮﻣﺎن ‪ to‬ﺑﻪ‬
‫ﺻﻮرت ﻓﻘﻂ‪ -‬ﻣﮑﺎﻧﯽ اﺳﺖ‪ ،‬آرﮔﻮﻣﺎن ‪ title‬ﻫﻢ ﻣﯽﺗﻮاﻧﺪ ﺑﻪﺻﻮرت ﻣﮑﺎﻧﯽ و ﻫﻢ ﺑﻪﺻﻮرت ﮐﻠﯿﺪواژه‬
‫اﺳﺘﻔﺎده ﺷﻮد و ﺑﺮاي آرﮔﻮﻣﺎن ‪ ،body‬ﻓﻘﻂ ﻓﺮاﺧﻮاﻧﯽ ﺑﻪﻫﻤﺮاه ﮐﻠﯿﺪواژة آن ﺻﻮرتﭘﺬﯾﺮ اﺳﺖ‪.‬‬

‫‪def send_message(to, /, title, *, body):‬‬


‫)"}‪print(f"to = {to‬‬
‫)"}‪print(f"title = {title‬‬
‫)"}‪print(f"body = {body‬‬

‫‪send_message("Siavash", "Welcome",‬‬
‫)"‪body="Welcome to our site‬‬

‫‪1‬‬
‫‪Keyword-only‬‬
‫‪Ahwaz_Hackerz‬‬

‫ﻣﺒﺎﺣﺚ ﭘﯿﺸﺮﻓﺘﻪ در ﭘﺎﯾﺘﻮن ‪ /‬ﻣﻮﺳﺴﻪ ﻓﺮﻫﻨﮕﯽ ﻫﻨﺮي دﯾﺒﺎﮔﺮان ﺗﻬﺮان‬ ‫‪34‬‬

‫‪>> to = Siavash‬‬
‫‪title = Welcome‬‬
‫‪body = Welcome to our site‬‬

‫ﻻزم ﺑﻪ ذﮐﺮ اﺳﺖ ﺗﺎﺑﻊ ‪ send_message‬را ﻣﯽﺗﻮاﻧﺴﺘﯿﻢ ﺑﻪﺻﻮرت زﯾﺮ ﻫﻢ ﻓﺮاﺧﻮاﻧﯽ ﮐﻨﯿﻢ‪.‬‬

‫‪send_message("Siavash", title="Welcome",‬‬
‫)"‪body="Welcome to our site‬‬

‫اﺳﺘﻔﺎده از ﻣﺴﺎوي در ‪f-string‬‬


‫ﯾﮑﯽ از ﻗﺎﺑﻠﯿﺖﻫﺎي ﺧﻮب ﭘﺎﯾﺘﻮن ‪ ،3.8‬راﺣﺖﺗﺮﮐﺮدن ﻓﺮاﯾﻨﺪ دﯾﺒﺎگ ﺑﻪوﺳﯿﻠﻪ ‪ f-string‬اﺳﺖ‪ .‬در ﻣﺜﺎل ﺑﺎﻻ‬
‫ﺑﺮاي ‪ send_message‬ﻣﯽﺗﻮاﻧﯿﻢ ﺑﺎ ﮔﺬاﺷﺘﻦ ﻣﺴﺎوي در اﻧﺘﻬﺎي ﻧﺎم ﻣﺘﻐﯿﺮ ﻧﺎم آن و ﻣﻘﺪار آن را ﭼﺎپ‬
‫ﮐﻨﯿﻢ‪ .‬اﯾﻦ ﻓﺮاﯾﻨﺪ ﺑﻪ دﯾﺒﺎگﮐﺮدن و ﻧﻮﺷﺘﻦ ﮐﺪﻫﺎي ﺗﻤﯿﺰﺗﺮ ﮐﻤﮏ ﻣﯽﮐﻨﺪ‪.‬‬

‫‪def send_message(to, /, title, *, body):‬‬


‫)"}=‪print(f"{to‬‬
‫)"}=‪print(f"{title‬‬
‫)"}=‪print(f"{body‬‬

‫‪send_message("Siavash", "Welcome",‬‬
‫)"‪body="welcome to our site‬‬

‫'‪>> to='Siavash‬‬
‫'‪title='Welcome‬‬
‫'‪body='welcome to our site‬‬

‫دﯾﮕﺮ ﺗﻐﯿﯿﺮات در ﭘﺎﯾﺘﻮن ‪3.8‬‬


‫در اﯾﻦ ﻓﺼﻞ ﻣﺮوري ﺑﺮ ﭘﺮاﺳﺘﻔﺎدهﺗﺮﯾﻦ وﯾﮋﮔﯽﻫﺎي ﺟﺪﯾﺪ در ﭘﺎﯾﺘﻮن ‪ 3.8‬ﮐﺮدﯾﻢ‪ .‬ﺗﻐﯿﯿﺮات و وﯾﮋﮔﯽﻫﺎي‬
‫دﯾﮕﺮي در اﯾﻦ ﻧﺴﺨﻪ ﭘﺎﯾﺘﻮن ﺑﻪوﺟﻮد آﻣﺪه ﮐﻪ ﮐﻤﺘﺮ ﻣﻮرد اﺳﺘﻔﺎده ﻗﺮار ﻣﯽﮔﯿﺮﻧﺪ‪ ،‬وﻟﯽ داﻧﺴﺘﻦ آﻧﻬﺎ ﺧﺎﻟﯽ‬
‫از ﻟﻄﻒ ﻧﯿﺴﺖ‪ .‬در ﻣﺴﺘﻨﺪات رﺳﻤﯽ ﭘﺎﯾﺘﻮن ﺑﻪ ﻧﺸﺎﻧﯽ زﯾﺮ ﻣﯽﺗﻮان آﻧﻬﺎ را ﯾﺎﻓﺖ‪.‬‬

‫‪https://docs.python.org/3/whatsnew/3.8.html‬‬
‫‪Ahwaz_Hackerz‬‬

‫‪35‬‬ ‫ﺑﺨﺶ اول ‪ /‬ﻓﺼﻞ ‪ / 4‬ﭘﺎﯾﺘﻮن ‪309‬‬

‫ﻓﺼﻞ‪4‬‬
‫ﭘﺎﯾﺘﻮن ‪3.9‬‬
‫‪Ahwaz_Hackerz‬‬

‫ﻣﺒﺎﺣﺚ ﭘﯿﺸﺮﻓﺘﻪ در ﭘﺎﯾﺘﻮن ‪ /‬ﻣﻮﺳﺴﻪ ﻓﺮﻫﻨﮕﯽ ﻫﻨﺮي دﯾﺒﺎﮔﺮان ﺗﻬﺮان‬ ‫‪36‬‬

‫ﻋﻤﻠﮕﺮ ادﻏﺎم و ﺑﺮوزرﺳﺎن‬


‫ﯾﮑﯽ از ﻋﻤﻠﮕﺮﻫﺎي ﮐﺎرﺑﺮدي در ﭘﺎﯾﺘﻮن ‪ 3.9‬ﻋﻤﻠﮕﺮ ادﻏﺎم‪ 1‬اﺳﺖ‪ .‬ﺑﺎ اﺳﺘﻔﺎده از اﯾﻦ ﻋﻤﻠﮕﺮ ﻣﯽﺗﻮان دو‬
‫دﯾﮑﺸﻨﺮي را ﺑﺎ ﻫﻢ ادﻏﺎم ﮐﺮد‪ .‬اﯾﻦ ﻋﻤﻠﮕﺮ ﺑﺎ ﮐﺎراﮐﺘﺮ | ﻗﺎﺑﻞ اﺳﺘﻔﺎده اﺳﺖ‪.‬‬

‫}"‪a = {1: "One", 2: "Two‬‬


‫}"‪b = {3: "Three", 4: "Four‬‬

‫‪c = a | b‬‬

‫)‪print(c‬‬

‫}"‪>> {1: "One", 2: "Two", 3: "Three", 4: "Four‬‬

‫ﺳﺆاﻟﯽ ﮐﻪ ﻣﻤﮑﻦ اﺳﺖ ﭘﯿﺶ ﺑﯿﺎﯾﺪ اﯾﻦ اﺳﺖ ﮐﻪ اﮔﺮ ﮐﻠﯿﺪي در ﻫﺮ دو دﯾﮑﺸﻨﺮي وﺟﻮد داﺷﺖ ﭼﻪ‬
‫ﻣﯽﺷﻮد؟ در اﯾﻦ ﺻﻮرت ﮐﻠﯿﺪ و ﻣﻘﺪاري ﮐﻪ در دﯾﮑﺸﻨﺮي دوم وﺟﻮد دارد‪ ،‬در ﻧﺘﯿﺠﻪ ﻇﺎﻫﺮ ﻣﯽﺷﻮد‪.‬‬

‫}"‪a = {1: "One", 2: "Two‬‬


‫}"‪b = {2: "Do", 3: "Se", 4: "Chahar‬‬

‫‪c = a | b‬‬

‫)‪print(c‬‬

‫}"‪>> {1: "One", 2: "Do", 3: "Se", 4: "Chahar‬‬

‫ﺑﺎ اﺳﺘﻔﺎده از =| ﮐﻪ ﺑﻪ آن ﻋﻤﻠﮕﺮ ﺑﺮوزرﺳﺎن‪ 2‬ﻧﯿﺰ ﮔﻮﯾﻨﺪ‪ ،‬ﻣﯽﺗﻮان دﯾﮑﺸﻨﺮي را ﺑﻪراﺣﺘﯽ ﺑﺮوزﺳﺎﻧﯽ ﮐﺮد‪.‬‬

‫}"‪a = {1: "One", 2: "Two‬‬

‫}"‪a |= {1: "Yek‬‬

‫)‪print(a‬‬

‫}"‪>> {1: "Yek", 2: "Two‬‬

‫ﻫﻤﭽﻨﯿﻦ ﺑﺎ ﻋﻤﻠﮕﺮ ﺑﺮوزرﺳﺎن ﻣﯽﺗﻮان از ژﻧﺮاﺗﻮرﻫﺎ ﮐﻪ در ﺑﺨﺶﻫﺎي ﺑﻌﺪ ﺑﺎ آن آﺷﻨﺎ ﻣﯽﺷﻮﯾﺪ ﻫﻢ اﺳﺘﻔﺎده‬
‫ﮐﺮد و دﯾﮑﺸﻨﺮي ﺟﺪﯾﺪ ﺳﺎﺧﺖ‪.‬‬

‫}"‪a = {1: "1‬‬


‫))‪b = ((i, str(i)) for i in range(2,4‬‬

‫‪a |= b‬‬

‫‪1‬‬
‫‪Merge operator‬‬
‫‪2‬‬
‫‪Update operator‬‬
‫‪Ahwaz_Hackerz‬‬

‫‪37‬‬ ‫ﺑﺨﺶ اول ‪ /‬ﻓﺼﻞ ‪ / 4‬ﭘﺎﯾﺘﻮن ‪309‬‬

‫)‪print(a‬‬

‫}"‪>> {1: "1", 2: "2", 3: "3‬‬

‫ﺣﺬف ﭘﯿﺸﻮﻧﺪ و ﭘﺴﻮﻧﺪ رﺷﺘﻪﻫﺎ‬


‫در ﭘﺎﯾﺘﻮن ‪ 3.9‬دو ﺗﺎﺑﻊ ﮐﺎرﺑﺮدي ‪ removeprefix‬و ‪ removesuffix‬ﺑﻪ ﮐﻼس ‪ str‬اﺿﺎﻓﻪ ﺷﺪه‬
‫ﮐﻪ ﻣﯽﺗﻮاﻧﺪ ﭘﯿﺸﻮﻧﺪ و ﭘﺴﻮﻧﺪ ﯾﮏ رﺷﺘﻪ را ﺣﺬف ﮐﻨﺪ‪ .‬اﮔﺮ رﺷﺘﮥ ﺣﺮﻓﯽ ﺑﺎ ﯾﮏ ﮐﻠﻤﻪ ﺷﺮوع و ﯾﺎ ﭘﺎﯾﺎن ﯾﺎﺑﺪ‪،‬‬
‫ﺑﺎ اﯾﻦ ﺗﻮاﺑﻊ ﻣﯽﺗﻮان آﻧﻬﺎ را ﺣﺬف ﮐﺮد و اﮔﺮ ﺑﺎ ﭘﯿﺸﻮﻧﺪ و ﯾﺎ ﭘﺴﻮﻧﺪ ﻣﻮرد ﻧﻈﺮ ﺷﺮوع و ﯾﺎ ﭘﺎﯾﺎن ﻧﯿﺎﺑﺪ‪ ،‬رﺷﺘﮥ‬
‫اﺻﻠﯽ ﺑﺮﮔﺮداﻧﺪه ﻣﯽﺷﻮد‪.‬‬

‫"‪text = "price:5000Toman‬‬

‫))"‪print(text.removeprefix("price:‬‬

‫‪>> 5000Toman‬‬

‫))"‪print(text.removesuffix("Toman‬‬

‫‪>> price:5000‬‬

‫))"‪print(text.removesuffix("Rial‬‬

‫‪>> price:5000Toman‬‬

‫‪Type Hints‬‬
‫از اﯾﻦ ورژن ﺑﻪ ﺑﻌﺪ ﻣﯽﺗﻮان از ﺗﺎﯾﭗﻫﺎي ﺧﻮدﺳﺎﺧﺘﻪ ﺑﻪﺟﺎي ﺗﺎﯾﭗﻫﺎي ﺟﻨﺮﯾﮏ اﺳﺘﻔﺎده ﮐﺮد‪ .‬دﯾﮕﺮ ﻧﯿﺎزي‬
‫ﺑﻪ ‪ import‬ﺗﺎﯾﭗﻫﺎﯾﯽ ﻫﻤﭽﻮن ﻟﯿﺴﺖ و دﯾﮑﺸﻨﺮي ﻧﯿﺴﺖ‪ .‬ﺑﻪﻋﻨﻮان ﻣﺜﺎل ﺑﻪﺟﺎي آﻧﮑﻪ ﺑﻨﻮﯾﺴﯿﻢ‪:‬‬

‫‪from typing import List‬‬

‫‪def print_sum(numbers: List[int]) -> None:‬‬


‫))‪print(sum(numbers‬‬

‫ﻣﯽﺗﻮاﻧﯿﻢ از ﺧﻮد ﺗﺎﯾﭗﻫﺎي دادهاي ﭘﺎﯾﺘﻮن اﺳﺘﻔﺎده ﮐﻨﯿﻢ‪:‬‬

‫‪def print_sum(numbers: list[int]) -> None:‬‬


‫))‪print(sum(numbers‬‬
‫‪Ahwaz_Hackerz‬‬

‫ﻣﺒﺎﺣﺚ ﭘﯿﺸﺮﻓﺘﻪ در ﭘﺎﯾﺘﻮن ‪ /‬ﻣﻮﺳﺴﻪ ﻓﺮﻫﻨﮕﯽ ﻫﻨﺮي دﯾﺒﺎﮔﺮان ﺗﻬﺮان‬ ‫‪38‬‬

‫ب‪.‬م‪.‬م و ك‪.‬م‪.‬م ﭼﻨﺪ ﻋﺪدي‬


‫ﺗﺎﺑﻊ ب‪.‬م‪.‬م )ﺑﺰرﮔﺘﺮﯾﻦ ﻣﻘﺴﻮمﻋﻠﯿﻪ ﻣﺸﺘﺮك( ‪ math.gcd‬در اﯾﻦ ﻧﺴﺨﻪ ﭼﻨﺪﯾﻦ ﻋﺪد را ﻗﺒﻮل ﻣﯽﮐﻨﺪ و‬
‫ك‪.‬م‪.‬م )ﮐﻮﭼﮏﺗﺮﯾﻦ ﻣﻀﺮب ﻣﺸﺘﺮك( ‪ math.lcm‬در ﻣﺎژول ‪ math‬ﺑﻪﺗﺎزﮔﯽ اﺿﺎﻓﻪ ﺷﺪه اﺳﺖ‪.‬‬

‫‪import math‬‬

‫)‪g = math.gcd(8, 20, 24‬‬

‫)‪print(g‬‬

‫‪>> 4‬‬

‫)‪l = math.lcm(8, 20, 24‬‬

‫)‪print(l‬‬

‫‪>> 120‬‬

‫دﯾﮕﺮ ﺗﻐﯿﯿﺮات در ﭘﺎﯾﺘﻮن ‪3.9‬‬


‫ﭘﺎﯾﺘﻮن ‪ 3.9‬ﺗﻐﯿﯿﺮات ﭘﺮﮐﺎرﺑﺮد ﺑﺴﯿﺎري داﺷﺘﻪ اﺳﺖ‪ ،‬ﺑﺎ ﺗﻌﻮﯾﺾ ﺗﺠﺰﯾﻪﮔﺮ ﺑﺮ ﭘﺎﯾﮥ )‪ LL(1‬ﺑﻪ ﺗﺠﺰﯾﻪﮔﺮ ﺑﺮ ﭘﺎﯾﻪ‬
‫‪ PEG‬در ﻧﺴﺨﻪ ﺟﺪﯾﺪ‪ ،‬ﺑﺴﯿﺎري از اﻣﮑﺎﻧﺎت در آﯾﻨﺪه ﺑﻪ ﭘﺎﯾﺘﻮن اﺿﺎﻓﻪ ﺧﻮاﻫﺪ ﺷﺪ و اﯾﻦ زﺑﺎن ﺑﯿﺶ از ﭘﯿﺶ‬
‫اﻧﻌﻄﺎفﭘﺬﯾﺮ ﺷﺪه اﺳﺖ‪ .‬ﺳﺮﻋﺖ و ﮐﺎراﯾﯽ در اﯾﻦ ﻧﺴﺨﻪ ﻧﯿﺰ ﺑﺎﻻﺗﺮ رﻓﺘﻪ اﺳﺖ‪ .‬ﺗﻤﺎﻣﯽ ﺟﺰﺋﯿﺎت اﺿﺎﻓﻪﺷﺪه ﺑﻪ‬
‫اﯾﻦ ﻧﺴﺨﻪ را در ﻟﯿﻨﮏ زﯾﺮ ﻣﯽﺗﻮاﻧﯿﺪ ﻣﺸﺎﻫﺪه ﮐﻨﯿﺪ‪.‬‬

‫‪https://docs.python.org/3.9/whatsnew/3.9.html‬‬
‫‪Ahwaz_Hackerz‬‬

‫‪39‬‬ ‫ﺑﺨﺶ اول ‪ /‬ﻓﺼﻞ ‪ / 4‬ﭘﺎﯾﺘﻮن ‪309‬‬

‫ﺑﺨﺶ‪2‬‬

‫اﺷﺘﺒﺎﻫﺎت راﯾﺞ در ﮐﺪزدن و راهﺣﻞ ﺑﻬﺒﻮد آﻧﻬﺎ‬


‫‪Ahwaz_Hackerz‬‬

‫ﻣﺒﺎﺣﺚ ﭘﯿﺸﺮﻓﺘﻪ در ﭘﺎﯾﺘﻮن ‪ /‬ﻣﻮﺳﺴﻪ ﻓﺮﻫﻨﮕﯽ ﻫﻨﺮي دﯾﺒﺎﮔﺮان ﺗﻬﺮان‬ ‫‪40‬‬

‫ﻓﺼﻞ‪1‬‬
‫ﺳﺒﮏﻫﺎي وﯾﮋة زﺑﺎن‬
‫‪Ahwaz_Hackerz‬‬

‫‪41‬‬ ‫ﺑﺨﺶ دوم ‪ /‬ﻓﺼﻞ ‪ / 1‬ﺳﺒﮏﻫﺎي وﯾﮋه زﺑﺎن‬

‫ﺳﺒﮏﻫﺎي وﯾﮋة ﺑﺮﻧﺎﻣﻪﻧﻮﯾﺴﯽ‪ 1‬ﺑﯿﺎنﮐﻨﻨﺪة وﯾﮋﮔﯽﻫﺎي ﻣﺨﺼﻮﺻﯽ اﺳﺖ ﮐﻪ در ﯾﮏ زﺑﺎن ﺗﮑﺮار ﻣﯽﺷﻮد و‬
‫ﻣﯽﺗﻮان ﺑﻪ آﻧﻬﺎ ﺑﻪﻋﻨﻮان ﻣﻔﻬﻮم ﺑﻨﯿﺎدﯾﻦ در ﻣﺒﺤﺚ اﻟﮕﻮﻫﺎ ﻧﮕﺎه ﮐﺮد‪ Code Smell .‬ﺑﻪ ﻫﺮ ﺧﺼﯿﺼﻪاي در‬
‫ﮐﺪ ﺑﺮﻧﺎﻣﻪ ﻣﯽﮔﻮﯾﻨﺪ ﮐﻪ اﺣﺘﻤﺎﻻً در آﯾﻨﺪه ﺑﺎﻋﺚ ﻣﺸﮑﻼت ﻋﻤﯿﻖﺗﺮي ﻣﯽﺷﻮد‪ .2‬ﺑﺴﯿﺎري از ﺑﺮﻧﺎﻣﻪﻧﻮﯾﺲﻫﺎ‬
‫ﺑﺪون داﺷﺘﻦ اﻃﻼﻋﯽ از اﯾﻦ ﻣﻮارد ﮐﺪﻫﺎﯾﯽ ﺗﻮﻟﯿﺪ ﻣﯽﮐﻨﻨﺪ ﮐﻪ در ﻇﺎﻫﺮ ﺧﻮب و ﮐﺎرآﻣﺪ اﺳﺖ وﻟﯽ ﻣﻤﮑﻦ‬
‫اﺳﺖ در آﯾﻨﺪه ﻣﺸﮑﻞﺳﺎز ﺑﺎﺷﺪ‪ .‬ﻣﻨﻈﻮر از ﻣﺸﮑﻼت‪ ،‬ﻟﺰوﻣﺎً وﺟﻮد ﺑﺎگﻫﺎي ﻧﺮماﻓﺰاري ﻧﯿﺴﺖ ﺑﻠﮑﻪ ﻣﻮاردي‬
‫ﻫﻤﭽﻮن ﺿﻌﻒ در ﺗﻐﯿﯿﺮﭘﺬﯾﺮي‪ ،3‬ﻣﻘﯿﺎسﭘﺬﯾﺮي‪ ،4‬ﮐﺎراﯾﯽ‪ 5‬و دﯾﮕﺮ ﺻﻔﺎت ﮐﯿﻔﯽ ﻣﯽﺷﻮد‪ .‬ﺑﺎزآراﯾﯽ‪ 6‬روﺷﯽ‬
‫ﺑﺮاي ﺣﻞ اﯾﻦ ﻣﺸﮑﻼت اﺳﺖ‪ .‬ﺑﺎزآراﯾﯽ ﺑﺮاﺳﺎس ﺗﻌﺮﯾﻒ ‪ ،Fowler‬ﺗﮑﻨﯿﮏ ﻣﻨﺴﺠﻤﯽ ﺑﺮاي ﺑﺎزﺳﺎزي ﮐﺪ‪ ،‬ﺑﺎ‬
‫ﺗﻐﯿﯿﺮ ﺳﺎﺧﺘﺎر دروﻧﯽ آن اﺳﺖ ﮐﻪ رﻓﺘﺎر ﺧﺎرﺟﯽ آن را ﺗﻐﯿﯿﺮ ﻧﻤﯽدﻫﺪ‪.‬‬

‫در اﯾﻦ ﻓﺼﻞ ﺳﻌﯽ دارﯾﻢ در ﻗﺎﻟﺐ ﻣﺜﺎلﻫﺎﯾﯽ‪ ،‬ﮐﺪﻫﺎي ﺑﺪ را ﺗﻮﺿﯿﺢ داده و روش ﺑﻬﺒﻮد آﻧﻬﺎ را ﺷﺮح دﻫﯿﻢ‪.‬‬
‫در اﯾﻦ ﻣﺜﺎلﻫﺎ‪ ،‬اﺑﺘﺪا ﮐﺪﻫﺎي ﺑﺮﻧﺎﻣﻪﻧﻮﯾﺲ ﻣﺒﺘﺪي و ﺳﭙﺲ ﮐﺪﻫﺎي ﺑﺮﻧﺎﻣﻪﻧﻮﯾﺲ ﺣﺮﻓﻪاي را ﺑﺎ ﻫﻢ ﻣﻘﺎﯾﺴﻪ‬
‫ﻣﯽﮐﻨﯿﻢ‪.‬‬

‫اﺳﺘﻔﺎده از ‪ index‬در ﺣﻠﻘﻪﻫﺎي ‪for‬‬

‫‪ ‬ﺻﻮرتﻣﺴﺌﻠﻪ‪ :‬ﮐﺪي ﺑﻨﻮﯾﺴﯿﺪ ﮐﻪ ﻟﯿﺴﺘﯽ از ﻧﺎم ﮐﺎرﺑﺮان درﯾﺎﻓﺖ ﮐﻨﺪ و ﻫﺮ ﮐﺎرﺑﺮ را ﺑﻪ ﻫﻤﺮاه‬
‫ﺟﺎﯾﮕﺎﻫﺶ در ﻟﯿﺴﺖ ﭼﺎپ ﮐﻨﺪ‪.‬‬

‫‪ ‬ﺑﺮﻧﺎﻣﻪﻧﻮﯾﺲ ﻣﺒﺘﺪي‬
‫]'‪users = ['Siavash', 'Soroush', 'Shayan', 'Sara‬‬

‫‪index = 1‬‬

‫‪for user in users:‬‬


‫)"}‪print(f"{index}. {user‬‬
‫‪index += 1‬‬

‫‪>> 1.‬‬ ‫‪Siavash‬‬


‫‪2.‬‬ ‫‪Soroush‬‬
‫‪3.‬‬ ‫‪Shayan‬‬
‫‪4.‬‬ ‫‪Sara‬‬

‫‪1‬‬
‫‪Programming Idiom‬‬
‫‪2‬اﯾﻦ ﺗﻌﺮﯾﻒ اﺑﺘﺪا ﺗﻮﺳﻂ ‪ Beck‬در اواﺧﺮ ﺳﺎل ‪ 1990‬ﺑﻪوﺟﻮد آﻣﺪ و ﺑﻪﻃﻮر وﯾﮋهاي در ﺳﺎل ‪ 1999‬در ﮐﺘﺎب ﺑﺎزآراﯾﯽ‬
‫‪ Fowler‬اﺳﺘﻔﺎده ﺷﺪ‪.‬‬
‫‪3‬‬
‫‪Modifiability‬‬
‫‪4‬‬
‫‪Scalability‬‬
‫‪5‬‬
‫‪Performance‬‬
‫‪6‬‬
‫‪Refactoring‬‬
‫‪Ahwaz_Hackerz‬‬

‫ﻣﺒﺎﺣﺚ ﭘﯿﺸﺮﻓﺘﻪ در ﭘﺎﯾﺘﻮن ‪ /‬ﻣﻮﺳﺴﻪ ﻓﺮﻫﻨﮕﯽ ﻫﻨﺮي دﯾﺒﺎﮔﺮان ﺗﻬﺮان‬ ‫‪42‬‬

‫‪ ‬ﺑﺮﻧﺎﻣﻪﻧﻮﯾﺲ ﺣﺮﻓﻪاي‬
‫]'‪users = ['Siavash', 'Soroush', 'Shayan', 'Sara‬‬

‫‪for index, user in enumerate(users, 1):‬‬


‫)"}‪print(f"{index}. {user‬‬

‫‪>> 1.‬‬ ‫‪Siavash‬‬


‫‪2.‬‬ ‫‪Soroush‬‬
‫‪3.‬‬ ‫‪Shayan‬‬
‫‪4.‬‬ ‫‪Sara‬‬

‫‪ ‬ﺗﻮﺿﯿﺤﺎت‪ :‬ﺗﺎﺑﻊ)‪ enumerate(iterable, start=0‬ﯾﮏ ‪ iterable‬ﻣﯽﮔﯿﺮد و ﯾﮏ‬


‫ﺷﻤﺎرﻧﺪه ﺑﻪ آن اﺿﺎﻓﻪ ﻣﯽﮐﻨﺪ و در ﻗﺎﻟﺐ ﯾﮏ ﺷﺊ ‪ enumerate‬ﺑﺮﻣﯽﮔﺮداﻧﺪ‪ .‬ﻣﯽﺗﻮان ﺑﺎ )(‪list‬‬
‫ﻣﻘﺎدﯾﺮ آن را در ﻣﺜﺎل ﻗﺒﻞ ﻣﺸﺎﻫﺪه ﮐﺮد‪.‬‬

‫]'‪users = ['Siavash', 'Soroush', 'Shayan', 'Sara‬‬

‫))‪enum_obj = list(enumerate(users‬‬
‫)‪print(enum_obj‬‬

‫‪>> [(0, 'Siavash'), (1, 'Soroush'),‬‬


‫])'‪(2, 'Shayan'), (3, 'Sara‬‬

‫ﻫﻤﺎنﻃﻮرﮐﻪ ﻣﺸﺎﻫﺪه ﻣﯽﮐﻨﯿﺪ ‪ enumerate‬ﺑﻪ ﻟﯿﺴﺘﯽ از ﺗﺎﭘﻞﻫﺎ ﺗﺒﺪﯾﻞ ﺷﺪ و درك آن راﺣﺖﺗﺮ ﺷﺪ‪ .‬ﺑﺎ‬
‫اﺳﺘﻔﺎده از ‪ for‬دو ﻣﺘﻐﯿﺮه ﻣﯽﺗﻮان از ﺷﻤﺎرﻧﺪه و ﺷﺊ داﺧﻞ آن اﺳﺘﻔﺎده ﮐﺮد‪ .‬آرﮔﻮﻣﺎن ‪ start‬ﺑﺮاي آن‬
‫اﺳﺖ ﮐﻪ ﺑﮕﻮﯾﯿﻢ ﺷﻤﺎرﻧﺪه از ﭼﻪ ﺷﻤﺎرهاي ﺷﺮوع ﺑﻪ ﺷﻤﺎرش ﮐﻨﺪ ﮐﻪ ﻣﻘﺪار ﭘﯿﺶﻓﺮض آن ﺻﻔﺮ اﺳﺖ‪.‬‬

‫ﻋﻤﻠﮕﺮﻫﺎي ﻣﻘﺎﯾﺴﻪاي زﻧﺠﯿﺮﺷﺪه‬

‫‪ ‬ﺻﻮرتﻣﺴﺌﻠﻪ‪ :‬اﮔﺮ دﻣﺎي ﻫﻮا ﺑﯿﻦ ‪ 20‬ﺗﺎ ‪ 25‬درﺟﻪ ﺑﻮد‪ ،‬ﻣﺘﻨﯽ را ﭼﺎپ ﮐﻨﺪ‪.‬‬

‫‪ ‬ﺑﺮﻧﺎﻣﻪﻧﻮﯾﺲ ﻣﺒﺘﺪي‬
‫‪temperature = 21‬‬

‫‪if 20 < temperature and 25 > temperature:‬‬


‫)"!‪print("It's Ok‬‬

‫!‪>> It's Ok‬‬


‫‪Ahwaz_Hackerz‬‬

‫‪43‬‬ ‫ﺑﺨﺶ دوم ‪ /‬ﻓﺼﻞ ‪ / 1‬ﺳﺒﮏﻫﺎي وﯾﮋه زﺑﺎن‬

‫‪ ‬ﺑﺮﻧﺎﻣﻪﻧﻮﯾﺲ ﺣﺮﻓﻪاي‬
‫‪temperature = 21‬‬

‫‪if 20 < temperature < 25:‬‬


‫)"!‪print("It's Ok‬‬

‫!‪>> It's Ok‬‬

‫‪ ‬ﺗﻮﺿﯿﺤﺎت‪ :‬از زﻧﺠﯿﺮﮐﺮدن ﻋﻤﻠﮕﺮﻫﺎي ﻣﻘﺎﯾﺴﻪاي ﻧﺘﺮﺳﯿﺪ‪ .‬ﭘﺎﯾﺘﻮن ﻗﺎدر اﺳﺖ درﺳﺘﯽ آن را ﻣﺤﺎﺳﺒﻪ‬
‫ﮐﻨﺪ‪ .‬ﻣﺜﻼً ﻣﯽﺗﻮان ﻧﻮﺷﺖ ‪ 20 < 30 < 45 == 45!= 50‬ﮐﻪ ﻣﻘﺪار ‪ True‬ﺑﺮاي آن درﻧﻈﺮ‬
‫ﮔﺮﻓﺘﻪ ﻣﯽﺷﻮد‪ .‬ﭘﺎﯾﺘﻮن ﺑﺮاي ﻣﺤﺎﺳﺒﮥ درﺳﺘﯽ‪ ،‬از ﺳﻤﺖ ﭼﭗ دوﺑﻪدو ﻣﻘﺎﯾﺴﻪﻫﺎ را اﻧﺠﺎم ﻣﯽدﻫﺪ‪.‬‬

‫ﻟﯿﺴﺖ ﺧﺎﻟﯽ‬
‫‪ ‬ﺻﻮرتﻣﺴﺌﻠﻪ‪ :‬اﮔﺮ ﻟﯿﺴﺘﯽ ﺧﺎﻟﯽ ﺑﻮد‪ empty ،‬را ﭼﺎپ ﮐﻨﺪ‪.‬‬

‫‪ ‬ﺑﺮﻧﺎﻣﻪﻧﻮﯾﺲ ﻣﺒﺘﺪي‬
‫][ = ‪cars‬‬

‫‪if len(cars) == 0:‬‬


‫)"‪print("empty‬‬

‫‪>> empty‬‬

‫‪ ‬ﺑﺮﻧﺎﻣﻪﻧﻮﯾﺲ ﺣﺮﻓﻪاي‬
‫][ = ‪cars‬‬

‫‪if not cars:‬‬


‫)"‪print("empty‬‬

‫‪>> empty‬‬

‫‪ ‬ﺗﻮﺿﯿﺤﺎت‪ :‬ﯾﮑﯽ از ﻣﺒﺎﺣﺚ ﻣﻬﻢ در ﭘﺎﯾﺘﻮن ﺑﺤﺚ درﺳﺘﯽ اﺳﺖ‪ .‬در ﭘﺎﯾﺘﻮن ﺗﻨﻬﺎ ‪ True‬ﯾﺎ ‪False‬‬
‫درﺳﺘﯽ را ﺑﯿﺎن ﻧﻤﯽﮐﻨﻨﺪ‪ ،‬ﺑﻠﮑﻪ ﺷﺊﻫﺎي دﯾﮕﺮي ﻧﯿﺰ درﺳﺘﯽ را ﺑﯿﺎن ﻣﯽﮐﻨﻨﺪ‪ .‬در ﺟﺪول ‪ 4‬ﻧﻤﻮﻧﻪاي از اﯾﻦ‬
‫ﻣﻮارد را ﺑﺮاﺳﺎس ﻧﻮع ﻣﺘﻐﯿﺮ ﻣﯽﺗﻮاﻧﯿﺪ ﻣﺸﺎﻫﺪه ﮐﻨﯿﺪ‪ .‬در ﺳﺘﻮن "ﻣﻘﺪار ‪ "False‬ﺣﺎﻟﺖﻫﺎﯾﯽ ﮐﻪ ﻣﺘﻐﯿﺮ‬
‫ﻣﺮﺑﻮﻃﻪ ‪ False‬درﻧﻈﺮ ﮔﺮﻓﺘﻪ ﻣﯽﺷﻮد را ﻣﯽﺗﻮاﻧﯿﺪ ﻣﺸﺎﻫﺪه ﮐﻨﯿﺪ‪.‬‬
‫‪Ahwaz_Hackerz‬‬

‫ﻣﺒﺎﺣﺚ ﭘﯿﺸﺮﻓﺘﻪ در ﭘﺎﯾﺘﻮن ‪ /‬ﻣﻮﺳﺴﻪ ﻓﺮﻫﻨﮕﯽ ﻫﻨﺮي دﯾﺒﺎﮔﺮان ﺗﻬﺮان‬ ‫‪44‬‬

‫ﺟﺪول ‪ - 4‬درﺳﺘﯽ ﺑﺮاﺳﺎس ﻧﻮع ﻣﺘﻐﯿﺮ‬

‫ﻧﻮع ﻣﺘﻐﯿﺮ‬ ‫ﻣﻘﺪار ‪False‬‬ ‫ﻣﻘﺪار ‪True‬‬


‫‪Integer‬‬ ‫ﻋﺪد ﺻﻔﺮ‬ ‫ﻫﺮ ﻋﺪدي ﺑﻪ ﻏﯿﺮ از ﺻﻔﺮ‬
‫‪Float‬‬ ‫ﻋﺪد ﺻﻔﺮ‬ ‫ﻫﺮ ﻋﺪدي ﺑﻪ ﻏﯿﺮ از ﺻﻔﺮ‬
‫‪String‬‬ ‫"" )رﺷﺘﻪ ﺧﺎﻟﯽ(‬ ‫ﻫﺮ رﺷﺘﻪاي ﮐﻪ ﺣﺪاﻗﻞ ﻃﻮل آن ﯾﮏ ﺑﺎﺷﺪ‪.‬‬
‫‪List‬‬ ‫][ )ﻟﯿﺴﺖ ﺧﺎﻟﯽ(‬ ‫ﻫﺮ ﻟﯿﺴﺘﯽ ﮐﻪ ﺣﺪاﻗﻞ ﻃﻮل آن ﯾﮏ ﺑﺎﺷﺪ‪.‬‬
‫‪Dictionary‬‬ ‫}{ )دﯾﮑﺸﻨﺮي ﺧﺎﻟﯽ(‬ ‫ﻫﺮ دﯾﮑﺸﻨﺮي ﮐﻪ ﺣﺪاﻗﻞ ﻃﻮل آن ﯾﮏ ﺑﺎﺷﺪ‪.‬‬
‫‪Tuple‬‬ ‫)( )ﺗﺎﭘﻞ ﺧﺎﻟﯽ(‬ ‫ﻫﺮ ﺗﺎﭘﻠﯽ ﮐﻪ ﺣﺪاﻗﻞ ﻃﻮل آن ﯾﮏ ﺑﺎﺷﺪ‪.‬‬
‫‪Set‬‬ ‫)(‪) set‬ﻣﺠﻤﻮﻋﻪ ﺧﺎﻟﯽ(‬ ‫ﻫﺮ ﻣﺠﻤﻮﻋﻪاي ﮐﻪ ﺣﺪاﻗﻞ ﻃﻮل آن ﯾﮏ ﺑﺎﺷﺪ‪.‬‬

‫در رﺷﺘﻪﻫﺎي ﺣﺮﻓﯽ‪ ،‬رﺷﺘﻪاي ﮐﻪ ﺣﺘﯽ ﯾﮏ ‪ space‬ﻫﻢ داﺷﺘﻪ ﺑﺎﺷﺪ ﺑﺎز ﯾﮏ رﺷﺘﻪ ﺑﺎ ﻃﻮل ﯾﮏ ﺣﺴﺎب‬
‫ﻣﯽﺷﻮد و درﻧﺘﯿﺠﻪ درﺳﺖ اﺳﺖ‪ .‬ﺑﺮاي ﺑﺮرﺳﯽ درﺳﺘﯽ ﻣﯽﺗﻮاﻧﯿﺪ ﺷﯽء ﻣﻮرد ﻧﻈﺮ ﺧﻮد را ﺑﻪ ‪Boolean‬‬
‫ﺗﺒﺪﯾﻞ ﮐﻨﯿﺪ‪.‬‬

‫))""(‪print(bool‬‬

‫‪>> False‬‬

‫))" "(‪print(bool‬‬

‫‪>> True‬‬

‫‪ None‬ﻫﻤﯿﺸﻪ ‪ False‬درﻧﻈﺮ ﮔﺮﻓﺘﻪ ﻣﯽﺷﻮد‪ .‬ﺑﺮاي درك درﺳﺖ ﺗﻔﺎوت ﺑﯿﻦ ‪ ،None‬رﺷﺘﮥ ﺧﺎﻟﯽ و‬
‫رﺷﺘﮥ ﺣﺮﻓﯽ ﻏﯿﺮﺧﺎﻟﯽ ﻣﺜﺎل ﻓﻨﺠﺎن ﻗﻬﻮه زﯾﺮ را ﺑﻪ ﺧﺎﻃﺮ ﺑﺴﭙﺎرﯾﺪ‪.‬‬

‫ﺗﺼﻮﯾﺮ ‪ - 2‬ﻣﺜﺎل ﻓﻨﺠﺎن‬

‫ﻫﺮﮔﺎه ﻓﻨﺠﺎن ﺣﺎوي ﺣﺘﯽ ﯾﮑﯽ ‪ space‬ﻗﻬﻮه ﺑﺎﺷﺪ‪ True ،‬اﺳﺖ!‬


‫‪Ahwaz_Hackerz‬‬

‫‪45‬‬ ‫ﺑﺨﺶ دوم ‪ /‬ﻓﺼﻞ ‪ / 1‬ﺳﺒﮏﻫﺎي وﯾﮋه زﺑﺎن‬

‫ﺷﻤﺎرش اﺟﺰاي ﻟﯿﺴﺖ‬

‫‪ ‬ﺻﻮرتﻣﺴﺌﻠﻪ‪ :‬ﻟﯿﺴﺘﯽ از اﺟﺰاي ﺗﮑﺮاري دارﯾﻢ‪ ،‬ﺑﺮﻧﺎﻣﻪاي ﺑﻨﻮﯾﺴﯿﺪ ﮐﻪ اﯾﻦ اﺟﺰا را ﺑﺎ ﺗﻌﺪاد ﺗﮑﺮارﺷﺎن‬
‫در ﯾﮏ دﯾﮑﺸﻨﺮي ﺟﻤﻊآوري ﮐﻨﺪ‪.‬‬

‫‪ ‬ﺑﺮﻧﺎﻣﻪﻧﻮﯾﺲ ﻣﺒﺘﺪي‬
‫]'‪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‬‬

‫}‪>> {'a': 3, 'b': 2, 'c': 5, 'd': 3‬‬

‫‪ ‬ﺑﺮﻧﺎﻣﻪﻧﻮﯾﺲ ﺣﺮﻓﻪاي‬
‫‪import collections‬‬

‫]'‪chars = ['a','a','a','b','b','c','c','c','c','c','d','d','d‬‬

‫)‪freq = collections.Counter(chars‬‬

‫)‪print(freq‬‬

‫}‪>> {'a': 3, 'b': 2, 'c': 5, 'd': 3‬‬

‫‪ ‬ﺗﻮﺿﯿﺤﺎت‪ :‬در ﮐﺪ ﻧﻮﺷﺘﻪﺷﺪه ﺗﻮﺳﻂ ﺑﺮﻧﺎﻣﻪﻧﻮﯾﺲ ﻣﺒﺘﺪي‪ ،‬در اﺑﺘﺪا ﯾﮏ دﯾﮑﺸﻨﺮي ﺧﺎﻟﯽ ﺗﻌﺮﯾﻒﺷﺪه‬
‫و ﺑﺮاي ﻫﺮ ﻋﻀﻮ ﻟﯿﺴﺖ‪ ،‬اﮔﺮ ﻗﺒﻼً در دﯾﮑﺸﻨﺮي وﺟﻮد داﺷﺖ‪ ،‬ﻣﻘﺪارش ﯾﮑﯽ زﯾﺎد ﻣﯽﺷﻮد و اﮔﺮ وﺟﻮد‬
‫ﻧﺪاﺷﺖ ﺑﺎ ﻣﻘﺪار ‪ 1‬در دﯾﮑﺸﻨﺮي اﺿﺎﻓﻪ ﻣﯽﺷﻮد‪ .‬ﭘﺎﯾﺘﻮن داراي ﻣﺎژول ‪ collections‬اﺳﺖ ﮐﻪ ﺣﺎوي‬
‫ﮐﻼسﻫﺎ و ﺗﻮاﺑﻊ ﺑﻪﺷﺪت ﮐﺎرﺑﺮدي اﺳﺖ‪ .‬ﯾﮑﯽ از اﯾﻦ ﮐﻼسﻫﺎ ‪ Counter‬ﻣﯽﺑﺎﺷﺪ ﮐﻪ ﺑﺎ درﯾﺎﻓﺖ ﯾﮏ‬
‫‪ iterable‬ﺗﻌﺪاد ﺗﮑﺮار اﺟﺰاي آن را ﺑﺮﻣﯽﮔﺮداﻧﺪ‪ .‬اﯾﻦ روش ﺳﺮﯾﻊ‪ ،‬ﺧﻼﺻﻪ و ﺧﻮاﻧﺎﺗﺮ از روش اول اﺳﺖ‪.‬‬
‫‪Ahwaz_Hackerz‬‬

‫ﻣﺒﺎﺣﺚ ﭘﯿﺸﺮﻓﺘﻪ در ﭘﺎﯾﺘﻮن ‪ /‬ﻣﻮﺳﺴﻪ ﻓﺮﻫﻨﮕﯽ ﻫﻨﺮي دﯾﺒﺎﮔﺮان ﺗﻬﺮان‬ ‫‪46‬‬

‫ﻣﻘﺎﯾﺴﻪ ﺑﺎ ﭼﻨﺪﯾﻦ ﻣﻘﺪار‬

‫‪ ‬ﺻﻮرتﻣﺴﺌﻠﻪ‪ :‬اﮔﺮ ﻧﺎم ﮐﺎرﺑﺮي ﯾﮑﯽ از ﻣﻘﺎدﯾﺮ "‪ "Siavash‬ﯾﺎ "‪ "Shayan‬ﯾﺎ "‪"Saman‬ﺑﻮد‬
‫ﭘﯿﻐﺎم ‪ ok‬ﭼﺎپ ﺷﻮد‪.‬‬

‫‪ ‬ﺑﺮﻧﺎﻣﻪﻧﻮﯾﺲ ﻣﺒﺘﺪي‬
‫"‪user = "Shayan‬‬

‫‪if user == "Siavash" or user == "Shayan" or user == "Saman":‬‬


‫)"‪print("ok‬‬

‫‪>> ok‬‬

‫‪ ‬ﺑﺮﻧﺎﻣﻪﻧﻮﯾﺲ ﺣﺮﻓﻪاي‬
‫"‪user = "Shayan‬‬

‫‪if user in ['Siavash', 'Shayan', 'Saman']:‬‬


‫)"‪print("ok‬‬

‫‪>> ok‬‬

‫‪ ‬ﺗﻮﺿﯿﺤﺎت‪ :‬از ﻣﺘﻐﯿﺮ ﺑﺮاي ﭼﮏﮐﺮدن ﻣﻘﺪار آن ﭼﻨﺪﯾﻦ ﺑﺎر اﺳﺘﻔﺎده ﻧﮑﻨﯿﺪ‪ .‬ﺑﻪراﺣﺘﯽ ﻣﯽﺗﻮاﻧﯿﺪ ﺑﺎ‬
‫ﻋﻤﻠﮕﺮ ‪ in‬ﮐﺪ ﮐﻤﺘﺮ و ﺧﻮاﻧﺎﺗﺮي ﺑﻨﻮﯾﺴﯿﺪ‪.‬‬

‫اﺳﺘﻔﺎده از ‪ if‬و ‪ else‬ﯾﮏ ﺧﻄﯽ‬

‫‪ ‬ﺻﻮرتﻣﺴﺌﻠﻪ‪ :‬ﻣﻘﺪار ﻣﺘﻐﯿﺮ ‪ check‬را ﺑﺮﻋﮑﺲ ﮐﻨﯿﺪ‪ .‬ﯾﻌﻨﯽ اﮔﺮ ‪ True‬ﺑﻮد‪ False ،‬ﺷﻮد و ﯾﺎ‬
‫ﺑﺮﻋﮑﺲ‪.‬‬

‫‪ ‬ﺑﺮﻧﺎﻣﻪﻧﻮﯾﺲ ﻣﺒﺘﺪي‬
‫‪if check:‬‬
‫‪check = False‬‬
‫‪else:‬‬
‫‪check = True‬‬

‫‪ ‬ﺑﺮﻧﺎﻣﻪﻧﻮﯾﺲ ﺣﺮﻓﻪاي‬
‫‪check = False if check else True‬‬
‫‪Ahwaz_Hackerz‬‬

‫‪47‬‬ ‫ﺑﺨﺶ دوم ‪ /‬ﻓﺼﻞ ‪ / 1‬ﺳﺒﮏﻫﺎي وﯾﮋه زﺑﺎن‬

‫‪ ‬ﺗﻮﺿﯿﺤﺎت‪ :‬درﺳﺖ اﺳﺖ ﮐﻪ ﭘﺎﯾﺘﻮن ﻋﻤﻠﮕﺮ ‪ Ternary‬ﻧﺪارد‪ ،‬وﻟﯽ ﻣﯽﺗﻮان از ‪ if‬و ‪ else‬ﯾﮏ ﺧﻄﯽ‬
‫اﺳﺘﻔﺎده ﮐﺮد‪ .‬ﺑﺮاي آﺷﻨﺎﯾﯽ ﺑﺎ ﻧﺤﻮة ﮐﺎر اﯾﻦ دﺳﺘﻮر ﺑﻪ ﺗﺼﻮﯾﺮ ‪ 3‬دﻗﺖ ﮐﻨﯿﺪ‪.‬‬

‫ﺗﺼﻮﯾﺮ ‪ - 3‬دﺳﺘﻮر ‪ if‬و ‪ else‬ﺗﮏﺧﻄﯽ‬

‫ﺑﻪﺟﺎي ‪ comp‬ﻣﯽﺗﻮاﻧﺪ ﻣﺘﻐﯿﺮ‪ ،‬ﻋﺒﺎرت ﺷﺮﻃﯽ و ﯾﺎ ﯾﮏ ﻣﻘﺪار ‪ boolean‬ﻗﺮار ﺑﮕﯿﺮد‪ .‬اﮔﺮ ‪ comp‬درﺳﺖ‬
‫ﺑﺎﺷﺪ اوﻟﯿﻦ ﻣﻘﺪار و اﮔﺮ ﻏﻠﻂ ﺑﺎﺷﺪ دوﻣﯿﻦ ﻣﻘﺪار ﮐﻪ ﺑﻌﺪ از ‪ else‬اﺳﺖ در ﻣﺘﻐﯿﺮ ‪ var‬ﻗﺮار ﻣﯽﮔﯿﺮد‪.‬‬
‫ﺑﺎﯾﺪ ﺗﻮﺟﻪ داﺷﺖ اﯾﻦ روش‪ ،‬ﮐﺪ را ﮐﻮﺗﺎهﺗﺮ ﻣﯽﮐﻨﺪ وﻟﯽ ﺷﺎﯾﺪ ﺧﻮاﻧﺎﯾﯽ را ﺑﺎﻻﺗﺮ ﻧﺒﺮد و ﺑﺎ ﻣﻼﺣﻈﻪ ﺑﺎﯾﺪ از‬
‫آن اﺳﺘﻔﺎده ﮐﺮد‪.‬‬

‫اﺳﺘﻔﺎده از ‪ else‬در ﺣﻠﻘﻪ‬


‫‪ ‬ﺻﻮرتﻣﺴﺌﻠﻪ‪ :‬ﻟﯿﺴﺘﯽ از اﻋﺪاد وﺟﻮد دارد‪ ،‬اﮔﺮ ﻫﻤﮥ اﻋﺪاد ﻟﯿﺴﺖ زوج ﺑﻮد ‪ even‬را ﭼﺎپ و در ﻏﯿﺮ‬
‫اﯾﻦﺻﻮرت ‪ mixed‬را ﭼﺎپ ﮐﻨﯿﺪ‪.‬‬

‫‪ ‬ﺑﺮﻧﺎﻣﻪﻧﻮﯾﺲ ﻣﺒﺘﺪي‬
‫]‪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‬‬

‫‪for number in numbers:‬‬


‫‪if number% 2!= 0:‬‬
‫)"‪print("mixed‬‬
‫‪break‬‬
‫‪Ahwaz_Hackerz‬‬

‫ﻣﺒﺎﺣﺚ ﭘﯿﺸﺮﻓﺘﻪ در ﭘﺎﯾﺘﻮن ‪ /‬ﻣﻮﺳﺴﻪ ﻓﺮﻫﻨﮕﯽ ﻫﻨﺮي دﯾﺒﺎﮔﺮان ﺗﻬﺮان‬ ‫‪48‬‬

‫‪else:‬‬
‫)"‪print("even‬‬

‫‪ ‬ﺗﻮﺿﯿﺤﺎت‪ :‬اﺳﺘﻔﺎده از ‪ else‬در ﺣﻠﻘﻪﻫﺎﯾﯽ ﮐﻪ در آﻧﻬﺎ از ‪ break‬اﺳﺘﻔﺎده ﺷﺪه ﺑﺎﺷﺪ‪ ،‬ﺑﺴﯿﺎر‬


‫ﮐﺎرﺑﺮدي اﺳﺖ‪ .‬ﻫﺮﮔﺎه ﺣﻠﻘﻪ ﺑﻪدرﺳﺘﯽ ﮐﺎر ﺧﻮد را ﺑﻪ ﭘﺎﯾﺎن رﺳﺎﻧﺪ و ﺑﻪ ‪ break‬ﻧﺮﺳﺪ وارد ﻗﺴﻤﺖ ‪else‬‬
‫ﻣﯽﺷﻮد‪ .‬اﻟﺒﺘﻪ ﻻزم ﺑﻪ ذﮐﺮ اﺳﺖ اﯾﻦ ﮐﺪ را ﻣﯽﺗﻮان ﺑﺎ ﺗﺎﺑﻊ ‪ all‬ﺑﻪ ﺻﻮرت ﺑﻬﺘﺮي ﻧﻮﺷﺖ ﮐﻪ ﺑﻌﺪاً در ﻓﺼﻞ‬
‫ﺑﺮﻧﺎﻣﻪﻧﻮﯾﺴﯽ ﺗﺎﺑﻌﯽ آن را ﺑﺮرﺳﯽ ﻣﯽﮐﻨﯿﻢ‪.‬‬

‫اﺳﺘﻔﺎده از ﺗﻮاﺑﻊ ﺑﻪ ﺻﻮرت زﻧﺠﯿﺮوار‬

‫‪ ‬ﺻﻮرتﻣﺴﺌﻠﻪ‪ :‬ﺗﺎﺑﻌﯽ ﺑﻨﻮﯾﺴﯿﺪ ﮐﻪ رﺷﺘﻪاي را درﯾﺎﻓﺖ ﮐﻨﺪ و ﺳﭙﺲ آن را ﺑﻪ ﺣﺮوف ﺑﺰرگ ﺗﺒﺪﯾﻞ‬
‫ﮐﻨﺪ‪ .‬ﺗﻤﺎم ﺧﻂﻫﺎي ﻓﺎﺻﻠﻪ را ﺑﻪ دو ﻧﻘﻄﻪ و ﻫﻤﭽﻨﯿﻦ ﺗﻤﺎم اﻋﺪاد ﺻﻔﺮ را ﭘﺎك ﮐﻨﺪ‪.‬‬

‫‪ ‬ﺑﺮﻧﺎﻣﻪﻧﻮﯾﺲ ﻣﺒﺘﺪي‬
‫‪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‬‬

‫‪49‬‬ ‫ﺑﺨﺶ دوم ‪ /‬ﻓﺼﻞ ‪ / 1‬ﺳﺒﮏﻫﺎي وﯾﮋه زﺑﺎن‬

‫‪ ‬ﺑﺮﻧﺎﻣﻪﻧﻮﯾﺲ ﺣﺮﻓﻪاي‬
‫"‪file_name = "document.pdf‬‬

‫)]‪print(file_name[-3:‬‬

‫‪ ‬ﺗﻮﺿﯿﺤﺎت‪ :‬اﺳﺘﻔﺎده از ‪ index‬ﻣﻨﻔﯽ دﻗﯿﻘﺎً ﻫﻤﺎن ﻧﺘﯿﺠﻪ ‪ len() – index‬را دارد و از اﻧﺘﻬﺎي‬
‫ﻟﯿﺴﺖ و ﯾﺎ رﺷﺘﻪ‪ ،‬ﺷﺮوع ﺑﻪ ﺷﻤﺎرش ﻣﯽﮐﻨﺪ‪ .‬در ﺗﺼﻮﯾﺮ ‪ 4‬ﻧﻤﻮﻧﻪاي از اﯾﻦ ‪ 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‬‬

‫ﻣﺒﺎﺣﺚ ﭘﯿﺸﺮﻓﺘﻪ در ﭘﺎﯾﺘﻮن ‪ /‬ﻣﻮﺳﺴﻪ ﻓﺮﻫﻨﮕﯽ ﻫﻨﺮي دﯾﺒﺎﮔﺮان ﺗﻬﺮان‬ ‫‪50‬‬

‫]‪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‬‬
‫}‬

‫))‪max_length = max(users.values(), key=lambda name: len(name‬‬

‫)‪print(max_length‬‬

‫‪>> Siavash‬‬
‫‪Ahwaz_Hackerz‬‬

‫‪51‬‬ ‫ﺑﺨﺶ دوم ‪ /‬ﻓﺼﻞ ‪ / 1‬ﺳﺒﮏﻫﺎي وﯾﮋه زﺑﺎن‬

‫در ﻣﺜﺎل ﺑﺎﻻ در ﺑﯿﻦ ﻣﻘﺎدﯾﺮ دﯾﮑﺸﻨﺮي دﻧﺒﺎل ﻋﻨﺼﺮي ﻣﯽﮔﺮدﯾﻢ ﮐﻪ ‪ 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‬‬

‫‪head, *middle, tail = numbers‬‬

‫‪ ‬ﺗﻮﺿﯿﺤﺎت‪ :‬اﺳﺘﻔﺎده از * اﯾﻦ اﻣﺮ را ﺳﺮﻋﺖ ﻣﯽﺑﺨﺸﺪ‪ .‬ﺑﺮاي اﻧﺘﺴﺎب ﺑﻘﯿﮥ اﺟﺰاي ﻟﯿﺴﺖ‪ ،‬از * ﻣﯽﺗﻮان‬
‫اﺳﺘﻔﺎده ﮐﺮد‪ .‬ﻣﺜﻼً ﺑﺮاي ﮔﺮﻓﺘﻦ ﺳﻪ ﻣﻘﺪار اول ﻟﯿﺴﺖ و دو ﻣﻘﺪار اﻧﺘﻬﺎﯾﯽ ﻟﯿﺴﺖ ﺑﺪﯾﻦﮔﻮﻧﻪ ﻋﻤﻞ ﻣﯽﮐﻨﯿﻢ‪:‬‬

‫]‪numbers = [1, 2, 3, 4, 5, 6, 7, 8‬‬

‫‪head1, head2, head3, *middle, tail1, tail2 = numbers‬‬

‫)‪print(head2‬‬

‫‪>> 2‬‬

‫)‪print(tail1‬‬

‫‪>> 7‬‬

‫)‪print(middle‬‬

‫]‪>> [4, 5, 6‬‬


Ahwaz_Hackerz

‫ ﻣﻮﺳﺴﻪ ﻓﺮﻫﻨﮕﯽ ﻫﻨﺮي دﯾﺒﺎﮔﺮان ﺗﻬﺮان‬/ ‫ﻣﺒﺎﺣﺚ ﭘﯿﺸﺮﻓﺘﻪ در ﭘﺎﯾﺘﻮن‬ 52

namedtuple ‫اﺳﺘﻔﺎده از‬

‫ ﻋﻨﺼﺮ‬،‫ ﻋﻨﺼﺮ اول ﻧﺎم‬.‫ ﻓﺮض ﮐﻨﯿﺪ اﻃﻼﻋﺎت ﯾﮏ ﮐﺎرﺑﺮ در ﻟﯿﺴﺘﯽ ذﺧﯿﺮه ﺷﺪه ﺑﺎﺷﺪ‬:‫ ﺻﻮرتﻣﺴﺌﻠﻪ‬
‫ ﺗﺎﺑﻌﯽ ﺑﻨﻮﯾﺴﯿﺪ ﮐﻪ اﯾﻦ‬.‫ ﻋﻨﺼﺮ ﺳﻮم ﺳﻦ و درﻧﻬﺎﯾﺖ ﻋﻨﺼﺮ ﭼﻬﺎرم اﯾﻤﯿﻞ ﮐﺎرﺑﺮ ﺑﺎﺷﺪ‬،‫دوم ﻧﺎم ﺧﺎﻧﻮادﮔﯽ‬
.‫ﻟﯿﺴﺖ را ﺑﮕﯿﺮد و ﻣﺸﺨﺼﺎت ﮐﺎرﺑﺮ را ﭼﺎپ ﮐﻨﺪ‬

‫ ﺑﺮﻧﺎﻣﻪﻧﻮﯾﺲ ﻣﺒﺘﺪي‬
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]}")

user_sample = ['shayan', 'soroushpour', 35,


'shayan@gmail.com']
print_user_detail(user_sample)

>> 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=}")

user_sample = ['shayan', 'soroushpour', 35,


'shayan@gmail.com']

print_user_detail(user_sample)
‫‪Ahwaz_Hackerz‬‬

‫‪53‬‬ ‫ﺑﺨﺶ دوم ‪ /‬ﻓﺼﻞ ‪ / 1‬ﺳﺒﮏﻫﺎي وﯾﮋه زﺑﺎن‬

‫'‪>> user.first_name='shayan‬‬
‫'‪user.last_name='soroushpour‬‬
‫‪user.age=35‬‬
‫'‪user.email='shayan@gmail.com‬‬

‫‪ ‬ﺗﻮﺿﯿﺤﺎت‪ :‬ﻗﺒﻼً ﺑﺎ ﮐﻼس ‪ Counter‬آﺷﻨﺎ ﺷﺪه ﺑﻮدﯾﻢ‪ .‬ﯾﮑﯽ دﯾﮕﺮ از ﮐﻼسﻫﺎي ﺑﺴﯿﺎر ﮐﺎرﺑﺮدي در‬
‫آن ﭘﮑﯿﺞ‪ namedtuple ،‬اﺳﺖ‪ .‬ﺑﺎ اﺳﺘﻔﺎده از اﯾﻦ ﮐﻼس ﻣﯽﺗﻮان دادهﻫﺎ را ﺑﻪدرﺳﺘﯽ دﺳﺘﻪﺑﻨﺪي ﮐﺮد‪.‬‬
‫در ﺑﺴﯿﺎري از ﻣﺎژولﻫﺎي ارﺗﺒﺎط ﺑﺎ ﭘﺎﯾﮕﺎه داده‪ ،‬اﻃﻼﻋﺎت در ﻗﺎﻟﺐ ﺳﻄﺮي و ﺑﻪﺻﻮرت ﻟﯿﺴﺖ ﺑﺮﮔﺮداﻧﺪه‬
‫ﻣﯽﺷﻮد‪ .‬ﻣﻤﮑﻦ اﺳﺖ داﻧﺴﺘﻦ اﯾﻨﮑﻪ ﮐﺪام دادة ﻣﺎ ﭼﻨﺪﻣﯿﻦ دادة ﻟﯿﺴﺖ ﺑﺎﺷﺪ ﺑﻌﺪ از ﻣﺪﺗﯽ ﺳﺨﺖ ﺷﻮد‪،‬‬
‫ﺑﻨﺎﺑﺮاﯾﻦ ﻧﻮﺷﺘﻦ ‪ user.last_name‬ﺧﯿﻠﯽ ﺑﻬﺘﺮ از ﻧﻮﺷﺘﻦ ]‪ user_list[0‬اﺳﺖ‪.‬‬

‫‪ namedtuple‬ﺧﻮاﻧﺎﯾﯽ و ﺗﻐﯿﯿﺮﭘﺬﯾﺮي ﮐﺪ را ﺑﺎﻻ ﻣﯽﺑﺮد‪ .‬اﺳﺘﻔﺎده از آن ﺑﺴﯿﺎر راﺣﺖ اﺳﺖ‪ ،‬اوﻟﯿﻦ‬


‫ﭘﺎراﻣﺘﺮ ﺑﺎﯾﺪ رﺷﺘﻪاي ﺑﺎﺷﺪ ﮐﻪ ﺗﺎﯾﭗ را ﻣﺸﺨﺺ ﻣﯽﮐﻨﺪ‪.‬‬

‫)‪user = User._make(user_list‬‬
‫))‪print(type(user‬‬

‫>'‪>> <class '__main__.User‬‬

‫ﺳﭙﺲ ﯾﮏ ‪ iterable‬ﺣﺎوي ﻓﯿﻠﺪﻫﺎي ﮐﻼس ﺑﻪ آن ﭘﺎس داده ﻣﯽﺷﻮد‪ .‬درﻧﻬﺎﯾﺖ ﺑﺎ ﺗﺎﺑﻊ ‪ _make‬ﻟﯿﺴﺖ را‬
‫ﺑﻪ ‪ namedtuple‬ﺗﺒﺪﯾﻞ ﻣﯽﮐﻨﯿﻢ و ﺑﺪﯾﻦﺗﺮﺗﯿﺐ دﺳﺘﺮﺳﯽ ﺑﻪ ﻓﯿﻠﺪﻫﺎي آن راﺣﺖﺗﺮ ﻣﯽﺷﻮد‪ .‬اﻟﺒﺘﻪ‬
‫ﻣﯽﺗﻮاﻧﺴﺘﯿﻢ ﺑﺎ اﺳﺘﻔﺎده از ﮐﻼس داده ﻫﻢ اﯾﻦ ﮐﺎر را اﻧﺠﺎم دﻫﯿﻢ‪.‬‬
‫‪Ahwaz_Hackerz‬‬

‫ﻣﺒﺎﺣﺚ ﭘﯿﺸﺮﻓﺘﻪ در ﭘﺎﯾﺘﻮن ‪ /‬ﻣﻮﺳﺴﻪ ﻓﺮﻫﻨﮕﯽ ﻫﻨﺮي دﯾﺒﺎﮔﺮان ﺗﻬﺮان‬ ‫‪54‬‬

‫ﻓﺼﻞ‪2‬‬
‫ﻧﮑﺎت ﮐﻠﯽ‬
‫‪Ahwaz_Hackerz‬‬

‫‪55‬‬ ‫ﺑﺨﺶ دوم ‪ /‬ﻓﺼﻞ ‪ / 2‬ﻧﮑﺎت ﮐﻠﯽ‬

‫در اﯾﻦ ﻓﺼﻞ ﺑﻪ ﻧﮑﺎت ﮐﻠﯽ درﻣﻮرد ﻣﻮاردي ﮐﻪ ﺑﻬﺘﺮ اﺳﺖ در ﮐﺪزدن رﻋﺎﯾﺖ ﮐﻨﯿﻢ‪ ،‬ﻣﯽﭘﺮدازﯾﻢ‪.‬‬

‫‪ .1‬ﭘﯿﺮوي از ﭘﯿﺸﻨﻬﺎدات ‪PEP8‬‬


‫در ‪ PEP8‬ﻣﻮاردي ذﮐﺮﺷﺪه ﮐﻪ ﺑﻪ ﮐﺪﻧﻮﯾﺴﯽ ﺑﺎ ﺷﯿﻮة ﺗﻤﯿﺰﺗﺮ ﮐﻤﮏ ﻣﯽﮐﻨﺪ و ﻫﻤﭽﻨﯿﻦ ﺗﻤﺎم ﻗﺮاردادﻫﺎي‬
‫ﻻزم در آن ذﮐﺮ ﺷﺪه اﺳﺖ‪ .‬ﺑﻪﻋﻨﻮان ﻣﺜﺎل‪ ،‬ﺑﺮاي اﻧﺘﺨﺎب ﻧﺎم ﻣﺘﻐﯿﺮﻫﺎ و ﯾﺎ ﺗﻮاﺑﻊ ﺑﺎﯾﺪ ﺑﻪﺻﻮرت‬
‫‪ snake_case‬ﻧﺎمﻫﺎ ﻧﻮﺷﺘﻪ ﺷﻮد‪.‬‬
‫ﻣﻄﺎﻟﻌﮥ ﺑﯿﺸﺘﺮ در‪:‬‬

‫‪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‬‬

‫ﭘﺎﯾﺘﻮن اﺑﺘﺪا ﺑﻪدﻧﺒﺎل ﻣﺎژول ﯾﺎ ﭘﮑﯿﺠﯽ ﺑﺎ ﻧﺎم ‪ dibagaran‬در ‪3sys.modules‬ﻣﯽﮔﺮدد‪.‬‬

‫‪1‬‬
‫)‪Version Control System (VCS‬‬
‫‪2‬‬
‫)‪Don't Repeat Yourself (DRY‬‬
‫‪3‬ﺟﺎﯾﯽ ﮐﻪ ﺗﻤﺎم ﻣﺎژولﻫﺎﯾﯽ ﮐﻪ ﻗﺒﻼً وارد ﺑﺮﻧﺎﻣﻪ ﺷﺪهاﻧﺪ‪ ،‬ﮐﺶ ﺷﺪه اﺳﺖ‪،‬‬
‫‪Ahwaz_Hackerz‬‬

‫ﻣﺒﺎﺣﺚ ﭘﯿﺸﺮﻓﺘﻪ در ﭘﺎﯾﺘﻮن ‪ /‬ﻣﻮﺳﺴﻪ ﻓﺮﻫﻨﮕﯽ ﻫﻨﺮي دﯾﺒﺎﮔﺮان ﺗﻬﺮان‬ ‫‪56‬‬

‫اﮔﺮ ﻣﻮﻓﻖ ﺑﻪ ﭘﯿﺪاﮐﺮدن آن ﻧﺸﺪ‪ ،‬ﺑﻪ دﻧﺒﺎل اﯾﻦ ﻧﺎم در ﻣﺎژولﻫﺎي ﺧﻮدﺳﺎﺧﺘﻪ‪ 1‬ﭘﺎﯾﺘﻮن ﻣﯽﮔﺮدد‪ .‬درﻧﻬﺎﯾﺖ‬
‫در آدرسﻫﺎي ‪ sys.path‬ﺑﻪ دﻧﺒﺎل اﯾﻦ ﻧﺎم ﻣﯽﮔﺮدد‪ .‬اوﻟﯿﻦ ﻣﻘﺪار ﻟﯿﺴﺖ ‪ sys.path‬ﺑﻪ داﯾﺮﮐﺘﻮري‬
‫‪2‬‬
‫ﺟﺎري اﺷﺎره دارد‪ .‬اﮔﺮ در ﻫﯿﭻﮐﺪام از اﯾﻦ ﻣﺴﯿﺮﻫﺎ ﻣﺎژول ﻣﻮرد ﻧﻈﺮ ﭘﯿﺪا ﻧﺸﺪ‪ ،‬اﺳﺘﺜﻨﺎ‬
‫‪ ModuleNotFoundError‬رخ ﻣﯽدﻫﺪ‪.‬‬

‫‪ .11‬اﺳﺘﻔﺎده از ‪ Absolute Import‬ﺑﻪﺟﺎي ‪Relative Import‬‬


‫‪ .12‬اﺳﺘﻔﺎده از ‪ f-string‬ﺑﻪﺟﺎي ‪c-style format‬‬
‫‪ .13‬اﺳﺘﻔﺎده از ‪ zip‬ﺑﺮاي ﮐﺎر ﺑﺮ روي ﭼﻨﺪ ‪iterator‬‬

‫ﺑﺮاي اﺳﺘﻔﺎده از ﭼﻨﺪﯾﻦ ‪ iterable‬ﻫﻢاﻧﺪازه ﺑﻪﺻﻮرت ﻫﻢزﻣﺎن ﻣﯽﺗﻮان از ‪ zip‬اﺳﺘﻔﺎده ﮐﺮد‪ .‬ﻓﺮض ﮐﻨﯿﺪ‬
‫دو ﻟﯿﺴﺖ ﻣﺠﺰا ﯾﮑﯽ ﺣﺎوي اﺳﻢ و دﯾﮕﺮي ﺣﺎوي ﺳﻦ اﻓﺮاد دارﯾﻢ و ﻣﯽﺧﻮاﻫﯿﻢ روي ﺟﻔﺖ اﯾﻦ ﻟﯿﺴﺖﻫﺎ‬
‫ﺑﻪ ﺻﻮرت ﻫﻢزﻣﺎن ﮔﺮدش ﮐﻨﯿﻢ‪ .‬اﺳﺘﻔﺎده از ‪ zip‬ﻫﻤﺎﻧﻨﺪ زﯾﭗ ﻟﺒﺎس اﯾﻦ دو ﻟﯿﺴﺖ را ﺑﻪ ﻫﻢ ﮔﺮه ﻣﯽزﻧﺪ‪.‬‬
‫ﻣﺜﺎل ﺳﺎدة زﯾﺮ روش اﺳﺘﻔﺎده از اﯾﻦ ﺗﺎﺑﻊ را ﻧﺸﺎن ﻣﯽدﻫﺪ‪.‬‬

‫]'‪first_names = ['Siavash', 'Shayan', 'Peyman', 'Saman‬‬

‫]‪ages = [25, 22, 29, 27‬‬

‫‪for first_name, age in zip(first_names,ages):‬‬


‫)"‪print(f"{first_name} is {age} years old.‬‬

‫‪>> Siavash is 25 years old.‬‬


‫‪Shayan is 22 years old.‬‬
‫‪Peyman is 29 years old.‬‬
‫‪Saman is 27 years old.‬‬

‫‪ .14‬اﺳﺘﻔﺎده از ‪ .get‬ﺑﺮاي درﯾﺎﻓﺖ ﻣﻘﺪار در دﯾﮑﺸﻨﺮي‬


‫‪ .15‬ﺑﻪﺟﺎي ﺑﺮﮔﺮداﻧﺪن ‪ None‬و ﯾﺎ ‪ Exception ،-1‬ﺑﻔﺮﺳﺘﯿﺪ‬
‫‪ .16‬اﺳﺘﻔﺎدهﻧﮑﺮدن از ‪global‬‬

‫‪ .17‬ﻧﻮﺷﺘﻦ ‪Test Case‬‬


‫‪ .18‬ﺑﺎزآراﯾﯽ ﮐﺪ ﻫﺮ ﭼﻨﺪوﻗﺖ ﯾﮑﺒﺎر‬

‫‪1‬‬
‫‪Built-in‬‬
‫‪2‬‬
‫‪Exception‬‬
‫‪Ahwaz_Hackerz‬‬

‫‪57‬‬ ‫ﺑﺨﺶ دوم ‪ /‬ﻓﺼﻞ ‪ / 2‬ﻧﮑﺎت ﮐﻠﯽ‬

‫ﺑﺨﺶ‪3‬‬

‫ﺑﺮﻧﺎﻣﻪﻧﻮﯾﺴﯽ ﺗﺎﺑﻌﯽ‬
‫‪Ahwaz_Hackerz‬‬

‫ﻣﺒﺎﺣﺚ ﭘﯿﺸﺮﻓﺘﻪ در ﭘﺎﯾﺘﻮن ‪ /‬ﻣﻮﺳﺴﻪ ﻓﺮﻫﻨﮕﯽ ﻫﻨﺮي دﯾﺒﺎﮔﺮان ﺗﻬﺮان‬ ‫‪58‬‬

‫ﻓﺼﻞ‪1‬‬
‫ﻣﻘﺪﻣﻪ‬
‫‪Ahwaz_Hackerz‬‬

‫‪59‬‬ ‫ﺑﺨﺶ ﺳﻮم ‪ /‬ﻓﺼﻞ ‪ / 1‬ﻣﻘﺪﻣﻪ‬

‫ﺑﺮﻧﺎﻣﻪﻧﻮﯾﺴﯽ ﺗﺎﺑﻌﯽ‪ ،1‬ﯾﮏ ﭘﺎراداﯾﻢ ﺑﺮﻧﺎﻣﻪﻧﻮﯾﺴﯽ اﺳﺖ ﮐﻪ در آن ﺑﺮﻧﺎﻣﻪ ﺗﻮﺳﻂ ﺗﻮاﺑﻌﯽ ﺗﺸﮑﯿﻞ ﺷﺪه ﮐﻪ ﺑﺎ‬
‫اﺟﺮا و ﺗﺮﮐﯿﺐ ﯾﮑﺪﯾﮕﺮ ﻫﺪف ﺑﺮﻧﺎﻣﻪ را ﻣﺤﻘﻖ ﻣﯽﺳﺎزﻧﺪ‪ .‬در ﺑﺮﻧﺎﻣﻪﻧﻮﯾﺴﯽ ﺗﺎﺑﻌﯽ‪ ،‬ﺗﻮاﺑﻊ ﺑﻪﻋﻨﻮان ﺟﺰ اﺻﻠﯽ‬
‫ﺑﺮﻧﺎﻣﻪ ﺑﻪﺣﺴﺎب ﻣﯽآﯾﻨﺪ و ﻣﯽﺗﻮان آﻧﻬﺎ را در ﻣﺘﻐﯿﺮﻫﺎ ذﺧﯿﺮه ﮐﺮد‪ ،‬ﺑﻪﻋﻨﻮان آرﮔﻮﻣﺎن ﺑﻪ ﺗﻮاﺑﻊ دﯾﮕﺮ ﭘﺎس‬
‫داد و ﺣﺘﯽ از ﯾﮏ ﺗﺎﺑﻊ ﺑﺮﮔﺸﺖ داد‪ .‬ﭘﺎﯾﺘﻮن از ﺑﺮﻧﺎﻣﻪﻧﻮﯾﺴﯽ ﺗﺎﺑﻌﯽ ﭘﺸﺘﯿﺒﺎﻧﯽ ﻣﯽﮐﻨﺪ‪ ،‬اﻣﺎ ﺑﺴﯿﺎري از‬
‫ﺑﺮﻧﺎﻣﻪﻧﻮﯾﺴﺎن ﭘﺎﯾﺘﻮن از اﯾﻦ ﻗﺎﺑﻠﯿﺖﻫﺎ اﺳﺘﻔﺎده ﻧﻤﯽﮐﻨﻨﺪ‪.‬‬

‫ﺑﺮﻧﺎﻣﻪﻧﻮﯾﺴﯽ ﺗﺎﺑﻌﯽ ﺑﺎﻋﺚ‪:‬‬

‫‪ ‬ﻣﺎژوﻻر ﺷﺪن‪ :‬ﺑﺮﻧﺎﻣﻪﻧﻮﯾﺴﯽ ﺑﻪﺻﻮرت ﺗﺎﺑﻌﯽ ﺑﺎﻋﺚ ﻣﺎژوﻻرﺷﺪن ﻣﯽﺷﻮد‪ .‬ﻫﺮ ﺗﺎﺑﻊ ﮐﺎر ﺑﻪﺧﺼﻮﺻﯽ را‬
‫اﻧﺠﺎم ﻣﯽدﻫﺪ و اﮔﺮ ﺧﻮاﺳﺘﯿﻢ ﻗﺴﻤﺘﯽ را ﺗﻐﯿﯿﺮ دﻫﯿﻢ ﮐﺎﻓﯿﺴﺖ ﺗﻨﻬﺎ ﺗﺎﺑﻊ ﻣﺮﺑﻮﻃﻪ را ﺗﻐﯿﯿﺮ دﻫﯿﻢ‪.‬‬
‫‪ ‬اﺧﺘﺼﺎر‪ :‬ﺑﺮﻧﺎﻣﻪﻧﻮﯾﺴﯽ ﺗﺎﺑﻌﯽ ﻣﻌﻤﻮﻻً از ﭘﺎراداﯾﻢﻫﺎي دﯾﮕﺮ ﻧﯿﺎز ﺑﻪ ﻧﻮﺷﺘﻦ ﮐﺪﻫﺎي ﮐﻤﺘﺮي دارد‪.‬‬
‫‪ ‬ﻫﻤﺮوﻧﺪي‪ :‬ﺑﺮﻧﺎﻣﻪﻧﻮﯾﺴﯽ ﺗﺎﺑﻌﯽ از آﻧﺠﺎﮐﻪ ﺗﻮاﺑﻊ از ﻫﻢ ﺟﺪا ﻫﺴﺘﻨﺪ‪ ،‬ﻣﯽﺗﻮاﻧﺪ ﺑﻪﺻﻮرت ﻫﻤﺮوﻧﺪ اﻧﺠﺎم‬
‫ﺷﻮﻧﺪ ﮐﻪ ﺑﻪ ﮐﺎراﯾﯽ و ﻣﻘﯿﺎسﭘﺬﯾﺮي ﮐﻤﮏ ﻣﯽﮐﻨﻨﺪ‪.‬‬
‫‪ ‬ﻗﺎﺑﻠﯿﺖ ﺗﺴﺖ‪ :‬از آﻧﺠﺎﯾﯽﮐﻪ ﺑﺮﻧﺎﻣﻪ ﻣﺎ را ﯾﮏ ﺳﺮي ﺗﺎﺑﻊ ﺗﺸﮑﯿﻞ داده ﮐﻪ ورودي و ﺧﺮوﺟﯽ آﻧﻬﺎ‬
‫ﻣﺸﺨﺺ اﺳﺖ؛ ﺑﻨﺎﺑﺮاﯾﻦ ﻧﻮﺷﺘﻦ ﺗﺴﺖ ﺑﺮاي آﻧﻬﺎ ﺑﺴﯿﺎر ﺳﺎده اﺳﺖ‪.‬‬

‫ﺗﺎﺑﻊ ﺑﻪﻋﻨﻮان ﭘﺎراﻣﺘﺮ‬


‫در ﭘﺎﯾﺘﻮن ﻣﯽﺗﻮاﻧﯿﻢ ﺗﺎﺑﻌﯽ را ﺑﻪﻋﻨﻮان ورودي ﺑﻪ ﺗﺎﺑﻊ دﯾﮕﺮي ﺑﺪﻫﯿﻢ‪ .‬ﻓﻬﻢ درﺳﺖ اﯾﻦ ﻣﺴﺌﻠﻪ ﺑﻪ درك ﻣﺎ‬
‫از ﺑﺮﻧﺎﻣﻪﻧﻮﯾﺴﯽ ﺗﺎﺑﻌﯽ ﺑﺴﯿﺎر ﮐﻤﮏ ﻣﯽﮐﻨﺪ‪ .‬ﺗﻮاﺑﻊ ﻣﯽﺗﻮاﻧﻨﺪ ﺣﺎوي ﻧﺎم ﺑﺎﺷﻨﺪ وﻟﯽ ﺗﺎ وﻗﺘﯽ ﺻﺪاﯾﺸﺎن ﻧﮑﻨﯿﻢ‬
‫ﮐﺎري ﻧﻤﯽﮐﻨﻨﺪ‪ .‬ﺗﻮاﺑﻊ را ﺑﻪﻋﻨﻮان ﯾﮏ آدم ﺑﺎ ﻣﻬﺎرت ﺧﺎص ﻓﺮض ﮐﻨﯿﺪ‪ .‬ﻣﺜﻼً ﻓﺮدي ﺑﺎ ﻧﺎم ‪ Tim‬وﺟﻮد دارد‬
‫و ﮐﺎر ﺧﺎص او آن اﺳﺖ ﮐﻪ ﻫﺮ اﺳﻤﯽ را ﺑﻪ او ﺑﺪﻫﯿﺪ او ﭘﯿﺎم ﺧﻮشآﻣﺪﮔﻮﯾﯽ ﺑﻪ آن اﺳﻢ را ﭼﺎپ ﻣﯽﮐﻨﺪ‪.‬‬

‫‪def tim(name):‬‬
‫)"}‪print(f"Welcome, {name‬‬

‫‪ tim‬ﯾﮑﯽ از دوﺳﺘﺎن ﻣﺎ اﺳﺖ ﮐﻪ ﻣﯽﺗﻮاﻧﯿﻢ ﺧﻮدﻣﺎن او را ﺻﺪا ﺑﺰﻧﯿﻢ ﮐﻪ ﮐﺎرش را اﻧﺠﺎم دﻫﺪ و ﯾﺎ ﺑﻪ‬
‫دوﺳﺖ دﯾﮕﺮي ﺑﺪﻫﯿﻢ و او ﻫﺮﮔﻮﻧﻪ ﮐﻪ دﻟﺶ ﺧﻮاﺳﺖ ﻣﯽﺗﻮاﻧﺪ ﺑﺎ ﺗﯿﻢ رﻓﺘﺎر ﮐﻨﺪ‪ .‬درواﻗﻊ‪ ،‬ﻫﺮﮔﺎه ﺑﻌﺪ از‬
‫‪ tim‬ﭘﺮاﻧﺘﺰ ﺑﺎز ﺷﻮد ﯾﻌﻨﯽ ﺻﺪازدن و ﯾﺎ ﻓﺮاﺧﻮاﻧﯽ ﺗﺎﺑﻊ اﻧﺠﺎم ﺷﺪه اﺳﺖ و ‪ tim‬ﺑﻼﻓﺎﺻﻠﻪ ﺷﺮوع ﺑﻪ اﻧﺠﺎم‬
‫ﮐﺎرش ﻣﯽﮐﻨﺪ‪ .‬ﺗﺼﻮﯾﺮ ‪ 5‬ﻧﺸﺎندﻫﻨﺪة آن اﺳﺖ ﮐﻪ زﻣﺎﻧﯽ ﺗﯿﻢ را ﺑﺎ اﺳﻤﯽ ﺻﺪا ﻣﯽزﻧﯿﻢ ﺑﻪ او ﻣﯽﮔﻮﯾﯿﻢ‪:‬‬
‫»ﺗﯿﻢ ﻓﻮراً ﺑﻪ اﯾﻦ اﺳﻢ ﺧﻮشآﻣﺪﮔﻮﯾﯽ ﮐﻦ« وﻟﯽ وﻗﺘﯽ ﻓﻘﻂ ‪ tim‬را ﺑﻨﻮﯾﺴﯿﻢ ﯾﻌﻨﯽ ﺑﺎ ﺧﻮد ‪ Tim‬ﮐﺎر‬
‫دارﯾﻢ و ﺑﻪ او ﻧﮕﻔﺘﯿﻢ ﮐﺎري اﻧﺠﺎم ﺑﺪﻫﺪ!‬

‫‪1‬‬
‫‪Functional Programming‬‬
‫‪Ahwaz_Hackerz‬‬

‫ﻣﺒﺎﺣﺚ ﭘﯿﺸﺮﻓﺘﻪ در ﭘﺎﯾﺘﻮن ‪ /‬ﻣﻮﺳﺴﻪ ﻓﺮﻫﻨﮕﯽ ﻫﻨﺮي دﯾﺒﺎﮔﺮان ﺗﻬﺮان‬ ‫‪60‬‬

‫ﺗﺼﻮﯾﺮ ‪ - 5‬ﺗﻔﺎوت ﺑﯿﻦ ﺻﺪازدن و ﻧﺰدن در ﺗﺎﺑﻊ‬

‫ﺑﯿﺎﯾﯿﺪ ﺑﻪ ﺗﺎﯾﭗ ‪ tim‬وﻗﺘﯽﮐﻪ آن را ﺻﺪا ﻣﯽزﻧﯿﻢ و وﻗﺘﯽﮐﻪ ﺻﺪا ﻧﻤﯽزﻧﯿﻢ‪ ،‬ﻧﮕﺎﻫﯽ ﺑﯿﺎﻧﺪازﯾﻢ‪.‬‬

‫))‪print(type(tim‬‬

‫>'‪>> <class 'function‬‬

‫)))"‪print(type(tim("Siavash‬‬

‫‪>> Welcome, Siavash‬‬


‫>'‪<class 'NoneType‬‬

‫ﺑﺴﯿﺎر واﺿﺢ اﺳﺖ ﮐﻪ وﻗﺘﯽ ‪ 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‬‬

‫‪61‬‬ ‫ﺑﺨﺶ ﺳﻮم ‪ /‬ﻓﺼﻞ ‪ / 1‬ﻣﻘﺪﻣﻪ‬

‫‪def manager(name, person1, person2):‬‬


‫)‪person1(name‬‬
‫)(‪person2‬‬

‫)‪manager("Siavash", tim, mike‬‬

‫‪>> Welcome, Siavash‬‬


‫!‪Bye‬‬

‫در ﻣﺜﺎل ﺑﺎﻻ دﯾﺪﯾﻢ ﮐﻪ ﻣﺎ ﻧﺎﻣﯽ را ﺑﻪ ﻣﺪﯾﺮ ﺑﺮﻧﺎﻣﻪ دادﯾﻢ ﺗﺎ ﺑﺮﻧﺎﻣﻪ ﺧﻮشآﻣﺪﮔﻮﯾﯽ و ﺧﺪاﺣﺎﻓﻈﯽ را اﻧﺠﺎم‬
‫دﻫﺪ و ﻫﻤﭽﻨﯿﻦ اﺧﺘﯿﺎر ‪ tim‬و ‪ mike‬را ﻫﻢ ﺑﻪ آن دادﯾﻢ ﺗﺎ ﻫﺮﮔﺎه ﺧﻮاﺳﺖ آﻧﻬﺎ را ﺻﺪا ﺑﺰﻧﺪ‪ .‬ﺑﺪﯾﻦﮔﻮﻧﻪ‬
‫ﻣﯽﺗﻮاﻧﯿﻢ ﺗﻮاﺑﻊ را ﺑﻪ ﯾﮑﺪﯾﮕﺮ ﭘﺎس دﻫﯿﻢ و از ﺗﺎﺑﻌﯽ در ﺗﺎﺑﻊ دﯾﮕﺮ اﺳﺘﻔﺎده ﮐﻨﯿﻢ‪ .‬ﻓﻬﻢ درﺳﺖ اﯾﻦ ﻗﻀﯿﻪ در‬
‫ﺑﺮﻧﺎﻣﻪﻧﻮﯾﺴﯽ ﺗﺎﺑﻌﯽ ﺑﺴﯿﺎر ﮐﻤﮏ ﻣﯽﮐﻨﺪ ‪.‬‬

‫ﺗﻮاﺑﻊ ‪Lambda‬‬
‫ﻫﻤﭽﻮن زﺑﺎنﻫﺎي ﺑﺮﻧﺎﻣﻪﻧﻮﯾﺴﯽ ﺳﯽ‪ ،‬ﺟﺎوا و‪ ...‬ﭘﺎﯾﺘﻮن ﻧﯿﺰ از ﺗﻮاﺑﻊ ‪ Lambda‬ﭘﺸﺘﯿﺒﺎﻧﯽ ﻣﯽﮐﻨﺪ‪ .‬اﯾﻦ ﺗﻮاﺑﻊ‬
‫ﺑﯽﻧﺎم ﻫﺴﺘﻨﺪ و از ﺗﻮاﺑﻌﯽ ﮐﻪ ﻗﺒﻼً ﻣﯽﻧﻮﺷﺘﯿﻢ ﻣﺤﺪودﺗﺮ و ﻣﺨﺘﺼﺮﺗﺮ ﻫﺴﺘﻨﺪ‪ .‬ﺟﺒﺮ ‪ Lambda‬ﭘﺎﯾﮥ‬
‫ﺑﺮﻧﺎﻣﻪﻧﻮﯾﺴﯽ ﺗﺎﺑﻌﯽ اﺳﺖ ﮐﻪ ﺗﻮﺳﻂ ﭼﺮچ در دﻫﮥ ‪ 1930‬ﻣﻌﺮﻓﯽ ﺷﺪ‪ .‬ﭘﺎﯾﺘﻮن زﺑﺎﻧﯽ ﺑﻪﻃﻮر ذاﺗﯽ ﺗﺎﺑﻌﯽ‬
‫ﻧﯿﺴﺖ وﻟﯽ ﺗﻮاﺑﻌﯽ ﻫﻤﭽﻮن ‪ filter ،map‬و ‪ reduce‬دارد ﮐﻪ از ﻫﻤﺎن ﻣﻔﺎﻫﯿﻢ ﺑﺮﻧﺎﻣﻪﻧﻮﯾﺴﯽ ﺗﺎﺑﻌﯽ‬
‫ﭘﯿﺮوي ﻣﯽﮐﻨﺪ‪.‬‬

‫روش ﮐﻠﯽ ﻧﻮﺷﺘﻦ ﺗﺎﺑﻊ ‪ Lambda‬در ﺗﺼﻮﯾﺮ ‪ 6‬آﻣﺪه اﺳﺖ‪ .‬ﭘﺲ از ﻧﻮﺷﺘﻦ ‪ ،lambda‬وروديﻫﺎي ﺗﺎﺑﻊ را‬
‫ﻣﯽﻧﻮﯾﺴﯿﻢ و ﺑﺎ ﻋﻼﻣﺖ دوﻧﻘﻄﻪ ﺧﺮوﺟﯽ را از ورودي ﺟﺪا ﻣﯽﮐﻨﯿﻢ‪.‬‬

‫ﺗﺼﻮﯾﺮ ‪ - 6‬ﻧﺤﻮه ﻧﻮﺷﺘﻦ ﺗﺎﺑﻊ ‪Lambda‬‬

‫ﺗﺎﺑﻌﯽ دارﯾﻢ ﮐﻪ ﯾﮏ ورودي ﻣﯽﮔﯿﺮد و آن را ﺑﻼﻓﺎﺻﻠﻪ ﺑﺮﻣﯽﮔﺮداﻧﺪ‪ .‬ﻫﺮ دو ﻣﺪل ﻧﻮﺷﺘﻦ اﯾﻦ ﺗﺎﺑﻊ در ﻣﺜﺎل‬
‫زﯾﺮ آﻣﺪه اﺳﺖ ﮐﻪ ﻣﻌﺎدل ﯾﮑﺪﯾﮕﺮ ﻫﺴﺘﻨﺪ و ﺗﻨﻬﺎ ﺗﻔﺎوت ﻣﯿﺎن آﻧﻬﺎ اﯾﻦ اﺳﺖ ﮐﻪ ﺗﺎﺑﻊ ‪ Lambda‬ﻧﺎم ﻧﺪارد‪.‬‬
‫اﯾﻦ ﺣﺎﻟﺖ ﺳﺎدهﺗﺮﯾﻦ ﺣﺎﻟﺖ ﻧﻮﺷﺘﻦ ﯾﮏ ﺗﺎﺑﻊ ‪ Lambda‬اﺳﺖ‪.‬‬
‫‪Ahwaz_Hackerz‬‬

‫ﻣﺒﺎﺣﺚ ﭘﯿﺸﺮﻓﺘﻪ در ﭘﺎﯾﺘﻮن ‪ /‬ﻣﻮﺳﺴﻪ ﻓﺮﻫﻨﮕﯽ ﻫﻨﺮي دﯾﺒﺎﮔﺮان ﺗﻬﺮان‬ ‫‪62‬‬

‫‪def simple(x):‬‬
‫‪return x‬‬

‫‪lambda x: x‬‬

‫ﺣﺎل ﻣﯽﺧﻮاﻫﯿﻢ ﺗﺎﺑﻌﯽ ﺑﻨﻮﯾﺴﯿﻢ ﮐﻪ ﻋﺪدي را درﯾﺎﻓﺖ ﮐﺮده و آن را ﺑﺎ ‪ 5‬ﺟﻤﻊ ﮐﻨﺪ و ﺑﺮﮔﺮداﻧﺪ‪.‬‬

‫‪lambda x: x + 5‬‬

‫ﺑﺮاي اﺳﺘﻔﺎده از آن‪ ،‬ﭘﺲ از ﺻﺪازدن ﺗﺎﺑﻊ ﺑﺎ ﻋﺪد ‪ ،7‬ﺟﻮاب را در ﻣﺘﻐﯿﺮي ﻣﯽرﯾﺰﯾﻢ‪.‬‬

‫)‪result = (lambda x: x + 5)(7‬‬

‫)‪print(result‬‬

‫‪>> 12‬‬

‫ﺗﺎﺑﻊ ﺑﻌﺪي‪ ،‬ﺗﺎﺑﻊ ﻣﯿﺎﻧﮕﯿﻦ اﺳﺖ ﮐﻪ ﺑﺎ ‪ *args‬ﻣﯽﺗﻮان آن را ﻧﻮﺷﺖ‪.‬‬

‫)‪avg = (lambda *args: sum(args)/len(args))(12, 15, 21‬‬

‫)‪print(avg‬‬

‫‪>> 16.0‬‬

‫در ﻣﺜﺎل زﯾﺮ ﺗﺎﺑﻊ ﺳﺎﺧﺘﻪﺷﺪه را در ﯾﮏ ﻣﺘﻐﯿﺮ ﻣﯽﮔﺬارﯾﻢ‪ ،‬ﺑﺎ اﯾﻦ ﮐﺎر ﻧﺎﻣﯽ ﺑﻪ ﺗﺎﺑﻊ ﻣﯽدﻫﯿﻢ و ﺑﻌﺪاً از آن‬
‫اﺳﺘﻔﺎده ﻣﯽﮐﻨﯿﻢ‪ .‬ﻫﻤﺎنﻃﻮرﮐﻪ ﻣﺸﺎﻫﺪه ﻣﯽﮐﻨﯿﺪ در آرﮔﻮﻣﺎنﻫﺎي ورودي ﺗﺎﺑﻊ‪ ،‬ﻣﺠﺎز ﺑﻪ اﺳﺘﻔﺎده از ﺗﻤﺎم‬
‫ﺣﺎﻟﺖﻫﺎﯾﯽ ﮐﻪ ﻗﺒﻼً اﺳﺘﻔﺎده ﻣﯽﮐﺮدﯾﻢ‪ ،‬ﻫﺴﺘﯿﻢ‪.‬‬

‫\ ‪sum_num = lambda x, /, y=0, *, z=0, **kwargs:‬‬


‫)‪x + y + z + kwargs.get('k', 0‬‬

‫))‪print(sum_num(1, 2, z=2, k=3‬‬

‫‪>> 8‬‬

‫درﺳﺖ اﺳﺖ ﮐﻪ ﻧﻮﺷﺘﻦ ﺗﻮاﺑﻊ ‪ Lambda‬در ﯾﮏ ﺧﻂ ﺑﺴﯿﺎر ﺟﺎﻟﺐ اﺳﺖ وﻟﯽ ﺑﺎﯾﺪ از آﻧﻬﺎ ﮐﻢ و ﺑﺎ اﺣﺘﯿﺎط‬
‫اﺳﺘﻔﺎده ﺷﻮد‪.‬‬
‫‪Ahwaz_Hackerz‬‬

‫‪63‬‬ ‫ﺑﺨﺶ ﺳﻮم ‪ /‬ﻓﺼﻞ ‪ / 1‬ﻣﻘﺪﻣﻪ‬

‫ﻧﮑﺎﺗﯽ ﮐﻪ ﺑﺎﯾﺪ در ﻣﻮرد آنﻫﺎ درﻧﻈﺮ ﺑﮕﯿﺮﯾﻢ‪:‬‬

‫‪ ‬اﯾﻦ ﺗﻮاﺑﻊ ﻣﻌﻤﻮﻻً ﯾﮏ ﺧﻄﯽ و ﺑﯽﻧﺎم ﻫﺴﺘﻨﺪ‪.‬‬


‫‪ ‬ﻧﻮﺷﺘﻦ آنﻫﺎ ﺣﺘﯽ در ﺑﻌﻀﯽ ﻣﻮارد ﺑﺎﻋﺚ ﭘﯿﭽﯿﺪﮔﯽ و ﮐﺎﻫﺶ ﺧﻮاﻧﺎﯾﯽ ﻣﯽﺷﻮد‪.‬‬
‫‪ ‬ﻣﻘﺪار ﺑﺮﮔﺸﺘﯽ در آن ﺿﻤﻨﯽ اﺳﺖ‪.‬‬
‫‪ ‬ﺑﻬﺘﺮ اﺳﺖ در ﻣﻮاﻗﻌﯽ ﮐﻪ ﺑﻪ ﯾﮏ ﺗﺎﺑﻊ ﺳﺎده ﺑﺪون ﻧﺎم ﻧﯿﺎز دارﯾﻢ‪ ،‬از اﯾﻦ ﺗﻮاﺑﻊ اﺳﺘﻔﺎده ﮐﻨﯿﻢ‪.‬‬

‫ﺗﺎﺑﻊ ‪Map‬‬
‫ﺗﺎﺑﻊ )‪ map(func, *iterables‬ﺗﺎﺑﻌﯽ ﮐﺎرﺑﺮدي اﺳﺖ ﮐﻪ ﺑﺎ درﯾﺎﻓﺖ ﯾﮏ ﺗﺎﺑﻊ و ﯾﮏ ﯾﺎ ﭼﻨﺪ‬
‫‪ iterable‬ﻣﺎﻧﻨﺪ ﻟﯿﺴﺖ‪ ،‬ﺗﺎﺑﻊ را ﺑﺮاي ﺗﻤﺎم ﻋﻨﺎﺻﺮ داﺧﻞ ‪ iterable‬ﻫﺎ اﺟﺮا ﻣﯽﮐﻨﺪ‪ .‬اﯾﻦ ﺗﺎﺑﻊ دﻗﯿﻘﺎً ﮐﺎري‬
‫را ﮐﻪ در رﯾﺎﺿﯿﺎت ﺑﺎ ﻣﻔﻬﻮم ﻧﮕﺎﺷﺖ از آن ﯾﺎد ﻣﯽﮐﻨﯿﻢ را اﻧﺠﺎم ﻣﯽدﻫﺪ‪ .‬ﺧﺮوﺟﯽ اﯾﻦ ﺗﺎﺑﻊ ﯾﮏ ژﻧﺮاﺗﻮر‬
‫اﺳﺖ و ﺑﺮاي اﯾﻨﮑﻪ ﺑﺘﻮاﻧﯿﻢ اﺟﺰاي آن را ﺑﺮرﺳﯽ ﮐﻨﯿﻢ ﺑﺎ )(‪ list‬آن را ﺑﻪ ﻟﯿﺴﺖ ﺗﺒﺪﯾﻞ ﻣﯽﮐﻨﯿﻢ‪.‬‬

‫ﻓﺮض ﮐﻨﯿﺪ ﻟﯿﺴﺘﯽ از اﻋﺪاد دارﯾﻢ و ﻣﯽﺧﻮاﻫﯿﻢ ﺗﻤﺎم اﻋﺪاد اﯾﻦ ﻟﯿﺴﺖ را ﺑﻪ ﺗﻮان ‪ 2‬ﺑﺮﺳﺎﻧﯿﻢ‪ .‬اﮔﺮ از ‪map‬‬
‫اﺳﺘﻔﺎده ﻧﮑﻨﯿﻢ اﺣﺘﻤﺎﻻً ﮐﺪ ﻣﺎ ﺑﻪ اﯾﻦ ﺷﮑﻞ ﺧﻮاﻫﺪ ﺑﻮد‪:‬‬

‫]‪numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9‬‬

‫][ = ‪power_two‬‬

‫‪for number in numbers:‬‬


‫)‪power_two.append(number ** 2‬‬

‫)‪print(power_two‬‬

‫]‪>> [1, 4, 9, 16, 25, 36, 49, 64, 81‬‬

‫اﻣﺎ ﻫﻤﯿﻦ ﮐﺪ را ﮐﻮﺗﺎهﺗﺮ ﻫﻢ ﻣﯽﺗﻮان ﻧﻮﺷﺖ‪:‬‬

‫]‪numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9‬‬

‫‪def pow2(x):‬‬
‫‪return x ** 2‬‬

‫))‪power_two = list(map(pow2, numbers‬‬


‫)‪print(power_two‬‬

‫]‪>> [1, 4, 9, 16, 25, 36, 49, 64, 81‬‬


‫‪Ahwaz_Hackerz‬‬

‫ﻣﺒﺎﺣﺚ ﭘﯿﺸﺮﻓﺘﻪ در ﭘﺎﯾﺘﻮن ‪ /‬ﻣﻮﺳﺴﻪ ﻓﺮﻫﻨﮕﯽ ﻫﻨﺮي دﯾﺒﺎﮔﺮان ﺗﻬﺮان‬ ‫‪64‬‬

‫ﻧﮑﺘﮥ ﻗﺎﺑﻞ ﺗﻮﺟﻪ اﯾﻦ اﺳﺖ ﮐﻪ ‪ map‬ﻫﻢ ﻣﺜﻞ ﺗﺎﺑﻊ ﻣﺪﯾﺮ ﺑﺮﻧﺎﻣﻪ )‪(manager‬ﮐﮫ ﻗﺒﻼً ﺗﻮﺿﯿﺢ دادﯾﻢ‪ ،‬ﺗﺎﺑﻊ‬
‫را ﻣﯽﮔﯿﺮد و ﺧﻮدش آن را ﺻﺪا ﻣﯽزﻧﺪ‪ .‬ﻫﻤﺎنﻃﻮرﮐﻪ ﻣﯽﺑﯿﻨﯿﻢ ‪ map‬اﻋﺪاد را ﺑﻪﺗﺮﺗﯿﺐ ﮔﺮﻓﺘﻪ و ﺗﺎﺑﻊ ﺗﻮان‬
‫دو را ﺑﺮاي ﻫﺮﮐﺪام ﺻﺪا ﻣﯽزﻧﺪ و ﻧﺘﯿﺠﻪ را ﺑﺮﻣﯽﮔﺮداﻧﺪ‪ .‬ﺣﺎل ﺑﺎ ﺗﻮﺟﻪ ﺑﻪ آﻣﻮزش ﻗﺒﻠﯽ ﻣﯽﺗﻮاﻧﯿﻢ ﺗﺎﺑﻊ‬
‫ﺳﺎده و اﺑﺘﺪاﯾﯽ ﺗﻮان رﺳﺎﻧﺪن را ﺑﻪﺻﻮرت ‪ Lambda‬ﺑﻨﻮﯾﺴﯿﻢ ﺗﺎ ﮐﺪ ﮐﻮﺗﺎهﺗﺮ ﺷﺪه و ﺧﻮاﻧﺎﯾﯽ آن ﺑﺎﻻﺗﺮ‬
‫ﺑﺮود‪ .‬ﻫﻤﺎنﻃﻮرﮐﻪ ﻗﺒﻼً اﺷﺎره ﮐﺮدﯾﻢ ﺑﺎﯾﺪ ﺑﻪ اﺳﺘﻔﺎده از ﺗﻮاﺑﻊ ﯾﮏ ﺧﻄﯽ ﺗﻮﺟﻪ داﺷﺖ‪ .‬اﯾﻦ ﮐﺪ‪ ،‬ﻣﺜﺎل‬
‫ﺧﻮﺑﯽ اﺳﺖ ﮐﻪ ﺑﺒﯿﻨﯿﻢ ﻧﻮﺷﺘﻦ آﻧﻬﺎ در اﯾﻨﺠﺎ ﺧﻮب اﺳﺖ‪ ،‬زﯾﺮا ﺗﺎﺑﻊ ﺳﺎده اﺳﺖ و ﻣﯽﺧﻮاﻫﯿﻢ ﺳﺮﯾﻌﺎً ﻧﻮﺷﺘﻪ‬
‫و از آن اﺳﺘﻔﺎده ﮐﻨﯿﻢ‪.‬‬

‫]‪numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9‬‬

‫))‪power_two = list(map(lambda x: x ** 2, numbers‬‬


‫)‪print(power_two‬‬

‫]‪>> [1, 4, 9, 16, 25, 36, 49, 64, 81‬‬

‫در ﻣﺜﺎل ﺑﻌﺪي ﻓﺮض ﮐﻨﯿﺪ ﻣﺎ ﺳﻪ ﻟﯿﺴﺖ از اﻋﺪاد ﺑﻪ ﻃﻮل ﯾﮑﺴﺎن دارﯾﻢ و ﻣﯽﺧﻮاﻫﯿﻢ ﻟﯿﺴﺖ ﺟﺪﯾﺪي‬
‫درﺳﺖ ﮐﻨﯿﻢ ﮐﻪ در ﻫﺮ ﺧﺎﻧﻪ ﺑﺰرﮔﺘﺮﯾﻦ ﻋﺪد ﻫﻤﺎن ﺧﺎﻧﻪ در ﯾﮑﯽ از ﺳﻪ ﻟﯿﺴﺖ ﺑﺎﺷﺪ‪ .‬ﺗﺼﻮﯾﺮ ‪7‬‬
‫ﺻﻮرتﻣﺴﺌﻠﻪ را ﺷﻔﺎفﺗﺮ ﺗﻮﺿﯿﺢ ﻣﯽدﻫﺪ‪.‬‬

‫ﺗﺼﻮﯾﺮ ‪ - 7‬ﺑﯿﺸﯿﻨﮥ ﻫﺮ ﻟﯿﺴﺖ‬

‫در ﮐﺪ زﯾﺮ ﺗﺎﺑﻊ ‪ max‬ﺑﺮاي ﺳﻪ ﻋﻨﺼﺮ اول ﻟﯿﺴﺖ اﺟﺮاﺷﺪه و ﻧﺘﯿﺠﻪ را در ﺧﺎﻧﮥ اول ﺟﻮاب ﻣﯽﮔﺬارد‪.‬‬

‫]‪numbers1 = [1, 2, 3, 4, 5, 6, 7, 8, 9‬‬


‫]‪numbers2 = [9, 8, 7, 6, 5, 4, 3, 2, 1‬‬
‫]‪numbers3 = [1, 4, 9, 5, 8, 2, 4, 2, 0‬‬

‫))‪max_number = list(map(max, numbers1, numbers2, numbers3‬‬


‫)‪print(max_number‬‬

‫]‪>> [9, 8, 9, 6, 8, 6, 7, 8, 9‬‬


‫‪Ahwaz_Hackerz‬‬

‫‪65‬‬ ‫ﺑﺨﺶ ﺳﻮم ‪ /‬ﻓﺼﻞ ‪ / 1‬ﻣﻘﺪﻣﻪ‬

‫ﺗﺎﺑﻊ ‪Filter‬‬
‫ﺗﺎﺑﻊ )‪ filter(func, iterable‬ﺑﺮاي ﻓﯿﻠﺘﺮﮐﺮدن ﯾﮏ ‪ iterable‬اﺳﺘﻔﺎده ﻣﯽﺷﻮد‪ .‬ﺑﺮﻋﮑﺲ‬
‫ﺗﺎﺑﻊ ‪ ،map‬ﻓﻘﻂ ﯾﮏ ‪ iterable‬درﯾﺎﻓﺖ ﻣﯽﮐﻨﺪ و ﺗﮏﺗﮏ ﻋﻨﺎﺻﺮ ‪ iterable‬را ﺑﻪ ﺗﺎﺑﻊ ورودي ﺧﻮد‬
‫ﻣﯽدﻫﺪ‪ ،‬اﮔﺮ ﻣﻘﺪار ‪ True‬درﯾﺎﻓﺖ ﮐﻨﺪ آن را در ﺧﺮوﺟﯽ ﻣﯽﮔﺬارد و اﮔﺮ ‪ False‬ﺑﻮد از آن ﻣﯽﮔﺬرد‪ .‬در‬
‫ﻣﺜﺎل زﯾﺮ ﺗﻤﺎم اﻋﺪاد زوج ﯾﮏ ﻟﯿﺴﺖ را ﻓﯿﻠﺘﺮ ﻣﯽﮐﻨﯿﻢ‪ .‬ﻻزم ﺑﻪ ذﮐﺮ اﺳﺖ ﮐﻪ ﻫﻤﭽﻮن ﺗﺎﺑﻊ ‪ ،map‬اﯾﻦ ﺗﺎﺑﻊ‬
‫ﻧﯿﺰ ژﻧﺮاﺗﻮر ﺑﺮﻣﯽﮔﺮداﻧﺪ‪ ،‬ﺑﻨﺎﺑﺮاﯾﻦ ﺑﺮاي ﻣﺸﺎﻫﺪه اﺟﺰاي آن ﺑﺎﯾﺪ آن را ﺑﻪ ﻟﯿﺴﺖ ﺗﺒﺪﯾﻞ ﮐﺮد‪ .‬در ﻓﺼﻞﻫﺎي‬
‫آﯾﻨﺪه در ﻣﻮرد ژﻧﺮاﺗﻮر ﺻﺤﺒﺖ ﺧﻮاﻫﯿﻢ ﮐﺮد و ﺑﺮاي اﺳﺘﻔﺎده از آن ﻫﻤﯿﺸﻪ ﻧﯿﺎزي ﺑﻪ ﺗﺒﺪﯾﻞ ﺑﻪ ﻟﯿﺴﺖ‬
‫ﻧﺪارﯾﻢ‪.‬‬

‫]‪numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9‬‬

‫))‪even_number = list(filter(lambda x: x% 2 == 0, numbers‬‬


‫)‪print(even_number‬‬

‫]‪>> [2, 4, 6, 8‬‬

‫ﻓﺮض ﮐﻨﯿﺪ ﻟﯿﺴﺘﯽ از رﺷﺘﻪﻫﺎﯾﯽ دارﯾﻢ ﮐﻪ در آن اﻓﺮاد ﺑﺎ ﻧﺎم و ﺳﻦ ﺑﺎ ﮐﺎراﮐﺘﺮ – ﺟﺪا ﺷﺪهاﻧﺪ‪ .‬ﺣﺎل‬
‫ﻣﯽﺧﻮاﻫﯿﻢ ﻓﻘﻂ ﮐﺎرﺑﺮاﻧﯽ را اﻧﺘﺨﺎب ﮐﻨﯿﻢ ﮐﻪ ﻧﺎﻣﺸﺎن ﺑﺎ ‪ s‬ﺷﺮوع ﺷﺪه و ﺳﻦ آﻧﻬﺎ ﺑﯿﺸﺘﺮ از ‪ 30‬ﺑﺎﺷﺪ‪.‬‬

‫]"‪users = ["Shayan-35", "Siavash-22", "Peyman-40", "Saman-33‬‬

‫‪def is_qualify(user_str: str) -> bool:‬‬


‫)'‪name, age = user_str.split('-‬‬
‫)‪age = int(age‬‬
‫‪return name.lower().startswith('s') and age > 30‬‬

‫))‪qualified_user = list(filter(is_qualify, users‬‬

‫)‪print(qualified_user‬‬

‫]'‪>> ['Shayan-35', 'Saman-33‬‬

‫در ﻣﺜﺎل ﺑﺎﻻ ﻣﺸﺎﻫﺪه ﻣﯽﮐﻨﯿﻢ ﮐﻪ ﺗﺎﺑﻊ ﻓﯿﻠﺘﺮﮐﻨﻨﺪه ﭘﯿﭽﯿﺪه ﺑﻮده و ﻧﻮﺷﺘﻦ آن ﺑﺎ ‪ Lambda‬اﺻﻼً ﺗﻮﺻﯿﻪ‬
‫ﻧﻤﯽﺷﻮد‪.‬‬
‫‪Ahwaz_Hackerz‬‬

‫ﻣﺒﺎﺣﺚ ﭘﯿﺸﺮﻓﺘﻪ در ﭘﺎﯾﺘﻮن ‪ /‬ﻣﻮﺳﺴﻪ ﻓﺮﻫﻨﮕﯽ ﻫﻨﺮي دﯾﺒﺎﮔﺮان ﺗﻬﺮان‬ ‫‪66‬‬

‫ﺗﺎﺑﻊ ‪Reduce‬‬
‫ﺗﺎﺑﻊ )]‪ reduce(func, iterable[, initial‬ﯾﮏ ﺗﺎﺑﻊ ﺑﺎ دو آرﮔﻮﻣﺎن را درﯾﺎﻓﺖ‬
‫ﻣﯽﮐﻨﺪ و ﻋﻨﺎﺻﺮ ﯾﮏ ‪ iterable‬را ﺑﻪ ﺻﻮرت ﺗﺠﻤﻌﯽ ﺑﺎ ﻫﻤﺎن ﺗﺎﺑﻊ ﺣﺴﺎب ﻣﯽﮐﻨﺪ‪ .‬ﺑﺮاي ﻣﺜﺎل ﻓﺮض ﮐﻨﯿﺪ‬
‫ﺗﺎﺑﻌﯽ ﺑﻪ ﻧﺎم ‪ add‬دارﯾﻢ ﮐﻪ دو ﻋﺪد ﻣﯽﮔﯿﺮد و آنﻫﺎ را ﺑﺎ ﻫﻢ ﺟﻤﻊ ﻣﯽﮐﻨﺪ‪ .‬ﺗﺎﺑﻊ ‪ reduce‬ﺑﺎ درﯾﺎﻓﺖ‬
‫اﯾﻦ ﺗﺎﺑﻊ و ﻟﯿﺴﺘﯽ از اﻋﺪاد‪ ،‬ﺑﻪﺻﻮرت ﺗﺠﻤﻌﯽ اﯾﻦ اﻋﺪاد را ﺟﻤﻊ ﻣﯽﮐﻨﺪ‪ .‬ﻣﻨﻈﻮر از ﺗﺠﻤﻌﯽ آن اﺳﺖ ﮐﻪ‬
‫ﻧﺨﺴﺖ ﻋﺪد اول و دوم ﺑﺎ ﻫﻢ ﺟﻤﻊ ﻣﯽﺷﻮد‪ ،‬ﺳﭙﺲ ﺟﻮاب آﻧﻬﺎ ﺑﺎ ﻋﺪد ﺳﻮم ﺟﻤﻊ ﻣﯽﺷﻮد‪ .‬ﺑﻪ ﻫﻤﯿﻦ‬
‫ﺗﺮﺗﯿﺐ اﯾﻦ ﮐﺎر را ﺗﺎ ﺟﺎﯾﯽ ﮐﻪ ﺗﻤﺎم ﻟﯿﺴﺖ ﻃﯽ ﺷﻮد‪ ،‬ﺗﮑﺮار ﻣﯽﮐﻨﺪ‪ .‬ﺗﺼﻮﯾﺮ ‪ 8‬ﻧﻤﺎﯾﯽ از اﯾﻦ ﻋﻤﻠﯿﺎت را ﺑﻪ‬
‫ﺗﺮﺗﯿﺐ از ﭘﺎﯾﯿﻦ ﺑﻪ ﺑﺎﻻ ﻧﻤﺎﯾﺶ ﻣﯽدﻫﺪ‪.‬‬

‫ﺗﺼﻮﯾﺮ ‪ - 8‬ﺟﻤﻊ ﺗﺠﻤﻌﯽ ﻟﯿﺴﺘﯽ از اﻋﺪاد‬

‫ﺑﺮاي اﺳﺘﻔﺎده از ﺗﺎﺑﻊ ‪ reduce‬ﺑﺎﯾﺪ آن را ‪ import‬ﮐﻨﯿﺪ‪.‬‬

‫‪from functools import reduce‬‬

‫‪def add(num1, num2):‬‬


‫‪return num1 + num2‬‬

‫]‪numbers = [1, 2, 3, 4‬‬

‫)‪result = reduce(add, numbers‬‬

‫)‪print(result‬‬

‫‪>> 10‬‬
‫‪Ahwaz_Hackerz‬‬

‫‪67‬‬ ‫ﺑﺨﺶ ﺳﻮم ‪ /‬ﻓﺼﻞ ‪ / 1‬ﻣﻘﺪﻣﻪ‬

‫ﺗﺎﺑﻊ ‪ reduce‬ﯾﮏ ﻣﻘﺪار اوﻟﯿﻪ ﺑﻪﺻﻮرت اﺧﺘﯿﺎري ﻫﻢ درﯾﺎﻓﺖ ﻣﯽﮐﻨﺪ‪ .‬ﻣﺜﺎل ﺑﻌﺪي را ﺑﺎ ﭘﺎراﻣﺘﺮ ﻣﻘﺪار‬
‫اوﻟﯿﻪ ‪ 8‬اﺟﺮا ﮐﺮدﯾﻢ‪.‬‬

‫)‪result = reduce(add, numbers, 8‬‬


‫)‪print(result‬‬

‫‪>> 18‬‬
‫ﻣﻘﺪار اوﻟﯿﻪ اﯾﻦﮔﻮﻧﻪ ﻋﻤﻞ ﻣﯽﮐﻨﺪ ﮐﻪ در اﺑﺘﺪا ﺑﻪﺟﺎي آﻧﮑﻪ ﺗﺎﺑﻊ ‪ reduce‬ﻋﻨﺼﺮ اول و دوم را ﺑﻪ ﺗﺎﺑﻊ‬
‫ﺑﺪﻫﺪ‪ ،‬ﻧﺨﺴﺖ ﻣﻘﺪار اوﻟﯿﻪ و ﻋﻨﺼﺮ اول را ﺑﻪ ﺗﺎﺑﻊ ﻣﯽدﻫﺪ و ﻣﺮاﺣﻞ را ﻣﺎﻧﻨﺪ ﻗﺒﻞ اداﻣﻪ ﻣﯽدﻫﺪ‪.‬‬

‫‪List Comprehension‬‬
‫در اﯾﻦ ﻓﺼﻞ دﯾﺪﯾﻢ ﮐﻪ ﺑﺮاي ﺳﺎﺧﺖ ﻟﯿﺴﺖ ﻣﯽﺗﻮان از ﺣﻠﻘﻪ ‪ for‬و ﻫﻤﭽﻨﯿﻦ از ﺗﺎﺑﻊ ‪ map‬اﺳﺘﻔﺎده ﮐﺮد‪.‬‬
‫وﻟﯽ ﺗﻮﺻﯿﻪ ﻣﯽﺷﻮد ﻟﯿﺴﺖﻫﺎ را ﺑﺎ ﺗﮑﻨﯿﮏ ‪ List Comprehension‬ﺑﺴﺎزﯾﺪ‪ .‬روش ﮐﺎر ﺑﺴﯿﺎر ﺳﺎده اﺳﺖ‪،‬‬
‫ﺑﻪ ﻧﻤﻮﻧﻪ ﻧﻮﺷﺘﺎر آن دﻗﺖ ﮐﻨﯿﺪ‪:‬‬

‫ﺗﺼﻮﯾﺮ ‪ - 9‬ﻧﺤﻮة اﺳﺘﻔﺎده از ‪List Comprehension‬‬

‫‪ :expression ‬ﻋﺒﺎرﺗﯽ اﺳﺖ ﮐﻪ ﻣﯽﺧﻮاﻫﯿﻢ وارد ﻟﯿﺴﺖ ﺷﻮد‪ .‬ﻣﯽﺗﻮاﻧﺪ ﯾﮏ ﻣﻘﺪار ﺛﺎﺑﺖ‪ ،‬ﺗﺎﺑﻊ‪ ،‬ﺷﺊ و ﯾﺎ‬
‫ﯾﮏ ﻋﺒﺎرت رﯾﺎﺿﯽ ﺑﺎﺷﺪ‪.‬‬
‫‪ :member ‬ﻋﻀﻮي از ‪ iterable‬اﺳﺖ ﮐﻪ در ﻫﺮ ﻣﺮﺣﻠﻪ ﺑﻪ اﯾﻦ ﻣﺘﻐﯿﺮ ﻣﻨﺴﻮب ﻣﯽﺷﻮد‪.‬‬
‫‪ :iterable ‬ﯾﮏ ﻟﯿﺴﺖ‪ ،‬ﻣﺠﻤﻮﻋﻪ‪ ،‬ژﻧﺮاﺗﻮر ﯾﺎ ﻫﺮ ﮐﻼﺳﯽ ﮐﻪ ﺑﺘﻮان ﻋﻨﺎﺻﺮ آن را در ﯾﮏ زﻣﺎن ﻣﺸﺨﺺ‬
‫درﯾﺎﻓﺖ ﮐﺮد‪.‬‬
‫‪ :conditional ‬ﯾﮏ ﻋﺒﺎرت ﺷﺮﻃﯽ ﻣﯽﺗﻮاﻧﺪ ﺑﺎﺷﺪ و اﮔﺮ اﯾﻦ ﻋﺒﺎرت درﺳﺖ ﺑﺎﺷﺪ ‪ expression‬در‬
‫ﻟﯿﺴﺖ ﻗﺮار ﻣﯽﮔﯿﺮد‪.‬‬

‫اﯾﻦ ﻋﺒﺎرت را ﺑﻪﺳﺎدﮔﯽ ﻣﯽﺗﻮان ﺑﺎ ﺣﻠﻘﻪ ‪ for‬ﻧﻮﺷﺖ‪ .‬ﺑﻪ ﺗﺼﻮﯾﺮ ‪ 10‬دﻗﺖ ﮐﻨﯿﺪ و اﯾﻦ دو روش را ﺑﺎ ﻫﻢ‬
‫ﻣﻘﺎﯾﺴﻪ ﮐﻨﯿﺪ‪.‬‬
‫‪Ahwaz_Hackerz‬‬

‫ﻣﺒﺎﺣﺚ ﭘﯿﺸﺮﻓﺘﻪ در ﭘﺎﯾﺘﻮن ‪ /‬ﻣﻮﺳﺴﻪ ﻓﺮﻫﻨﮕﯽ ﻫﻨﺮي دﯾﺒﺎﮔﺮان ﺗﻬﺮان‬ ‫‪68‬‬

‫ﺗﺼﻮﯾﺮ ‪ - 10‬ﻣﻘﺎﯾﺴﻪ ﺗﻌﺮﯾﻒ ﻟﯿﺴﺖ ﺑﺎ ‪ for‬و ‪list comprehension‬‬

‫اﯾﻦ روش ﺧﻮاﻧﺎﯾﯽ ﮐﺪ را اﻓﺰاﯾﺶ داده و ﻧﯿﺎز ﺑﻪ ﮐﺪ ﮐﻤﺘﺮي دارد‪ .‬در اداﻣﻪ ﺑﺎ ﻣﺜﺎلﻫﺎي ﻣﺘﻨﻮع ﺳﻌﯽ ﺷﺪه‬
‫ﺗﺎ ﻃﺮﯾﻘﻪ اﺳﺘﻔﺎده از اﯾﻦ روش ﺑﻪﺧﻮﺑﯽ آﻣﻮزش داده ﺷﻮد‪.‬‬

‫‪ ‬در اوﻟﯿﻦ ﻣﺜﺎل ﺳﻌﯽ دارﯾﻢ ﻟﯿﺴﺘﯽ درﺳﺖ ﮐﻨﯿﻢ ﮐﻪ در آن ‪ 10‬ﻋﺪد ﺻﻔﺮ ﺑﺎﺷﺪ‪.‬‬

‫])‪my_list = [0 for i in range(10‬‬

‫)‪print(my_list‬‬

‫]‪>> [0, 0, 0, 0, 0, 0, 0, 0, 0, 0‬‬

‫اﯾﻦ ﻋﺒﺎرت را اﯾﻦﮔﻮﻧﻪ ﻣﯽﺧﻮاﻧﯿﻢ‪:‬‬


‫»در ﻟﯿﺴﺖ‪ ،‬ﻋﺪد ﺻﻔﺮ ﻗﺮار ﺑﺪه‪ ،‬ﺑﻪ ازاء ﻫﺮ ‪ i‬در )‪«range(10‬‬

‫‪ ‬ﻣﯽﺧﻮاﻫﯿﻢ ﻟﯿﺴﺘﯽ از ﺗﻮان دوم اﻋﺪاد ‪ 3‬ﺗﺎ ‪ 7‬را ﺑﺴﺎزﯾﻢ‪.‬‬

‫])‪my_list = [i ** 2 for i in range(3, 7‬‬

‫)‪print(my_list‬‬

‫]‪>> [9, 16, 25, 36‬‬

‫اﯾﻦ ﻋﺒﺎرت را اﯾﻦﮔﻮﻧﻪ ﻣﯽﺧﻮاﻧﯿﻢ‪:‬‬


‫»در ﻟﯿﺴﺖ‪ ،‬ﻋﺪد ‪ i‬ﺑﻪ ﺗﻮان دو ﻗﺮار ﺑﺪه‪ ،‬ﺑﻪ ازاء ﻫﺮ ‪ i‬در )‪«range(3,7‬‬

‫‪ ‬ﻣﯽﺧﻮاﻫﯿﻢ ﻟﯿﺴﺘﯽ از ﺗﻘﺴﯿﻢ اﻋﺪاد زوج ﺑﯿﻦ ‪ 10‬ﺗﺎ ‪ 30‬را ﺑﺮ ‪ 5‬ﺑﺴﺎزﯾﻢ‪.‬‬

‫]‪my_list = [i / 5 for i in range(10, 30) if i% 2 == 0‬‬

‫)‪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‬‬

‫‪69‬‬ ‫ﺑﺨﺶ ﺳﻮم ‪ /‬ﻓﺼﻞ ‪ / 1‬ﻣﻘﺪﻣﻪ‬

‫اﯾﻦ ﻋﺒﺎرت را اﯾﻦﮔﻮﻧﻪ ﻣﯽﺧﻮاﻧﯿﻢ‪:‬‬


‫»در ﻟﯿﺴﺖ‪ ،‬ﻋﺪد ‪ i‬ﺗﻘﺴﯿﻢﺑﺮ ‪ 5‬را ﻗﺮار ﺑﺪه‪ ،‬ﺑﻪ ازاء ﻫﺮ ‪ i‬در )‪ range(10,30‬اﮔﺮ ﺑﺎﻗﯿﻤﺎﻧﺪه آن ﺑﻪ دو‬
‫ﺻﻔﺮ ﺷﻮد )ﻋﺪد ﻣﻮرد ﻧﻈﺮ زوج ﺑﺎﺷﺪ(«‬

‫‪ ‬ﻣﯽﺧﻮاﻫﯿﻢ ﻟﯿﺴﺘﯽ از رﺷﺘﮥ اﻋﺪاد ‪ 0‬ﺗﺎ ‪ 10‬را ﺑﺴﺎزﯾﻢ‪.‬‬

‫])‪my_list = [str(i) for i in range(10‬‬

‫)‪print(my_list‬‬

‫]'‪>> ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9‬‬

‫اﯾﻦ ﻋﺒﺎرت را اﯾﻦﮔﻮﻧﻪ ﻣﯽﺧﻮاﻧﯿﻢ‪:‬‬


‫»در ﻟﯿﺴﺖ‪ str(i) ،‬ﻗﺮار ﺑﺪه‪ ،‬ﺑﻪ ازاء ﻫﺮ ‪ i‬در )‪«range(10‬‬

‫‪ ‬ﻣﯽﺧﻮاﻫﯿﻢ ﻟﯿﺴﺘﯽ ﺑﺴﺎزﯾﻢ ﮐﻪ ﺗﻨﻬﺎ ﮐﻠﻤﺎﺗﯽ ﮐﻪ در ﻟﯿﺴﺖ دﯾﮕﺮي ﺣﺎوي ﺣﺮف ‪ s‬ﻫﺴﺘﻨﺪ را در ﺧﻮد‬
‫ﺟﺎي دﻫﺪ‪.‬‬

‫]'‪names = ['siavash', 'sara', 'shayan', 'nazanin‬‬


‫]‪my_list = [name for name in names if 's' in name‬‬

‫)‪print(my_list‬‬

‫]'‪>> ['siavash', 'sara', 'shayan‬‬

‫اﯾﻦ ﻋﺒﺎرت را اﯾﻦﮔﻮﻧﻪ ﻣﯽﺧﻮاﻧﯿﻢ‪:‬‬


‫»در ﻟﯿﺴﺖ‪ name ،‬ﻗﺮار ﺑﺪه‪ ،‬ﺑﻪ ازاء ﻫﺮ ‪ name‬در ﻟﯿﺴﺖ ‪ names‬اﮔﺮ ﺣﺮف ‪ s‬داﺧﻞ ‪name‬‬
‫ﺑﻮد«‬

‫‪ ‬ﻟﯿﺴﺘﯽ از ﺗﺎﭘﻞﻫﺎي ﻧﺎم و ﺳﻦ وﺟﻮد دارد‪ ،‬ﻣﯽﺧﻮاﻫﯿﻢ آﻧﻬﺎ را ﺑﺎ ﮐﺎراﮐﺘﺮ – ﺑﻪ ﻫﻢ ﺑﭽﺴﺒﺎﻧﯿﻢ و در‬
‫ﻟﯿﺴﺘﯽ ﺟﺪﯾﺪ ﺑﮕﺬارﯾﻢ‪.‬‬

‫‪names = [('siavash',25), ('soroush',16), ('shayan',23),‬‬


‫])‪('nazanin',26‬‬

‫]‪my_list = [f"{name}-{age}" for name, age in names‬‬

‫)‪print(my_list‬‬

‫]'‪>> ['siavash-25', 'soroush-16', 'shayan-23', 'nazanin-26‬‬


‫‪Ahwaz_Hackerz‬‬

‫ﻣﺒﺎﺣﺚ ﭘﯿﺸﺮﻓﺘﻪ در ﭘﺎﯾﺘﻮن ‪ /‬ﻣﻮﺳﺴﻪ ﻓﺮﻫﻨﮕﯽ ﻫﻨﺮي دﯾﺒﺎﮔﺮان ﺗﻬﺮان‬ ‫‪70‬‬

‫اﯾﻦ ﻋﺒﺎرت را اﯾﻦﮔﻮﻧﻪ ﻣﯽﺧﻮاﻧﯿﻢ‪:‬‬


‫»در ﻟﯿﺴﺖ‪ f"{name}-{age}" ،‬ﻗﺮار ﺑﺪه‪ ،‬ﺑﻪ ازاء ﻫﺮ ‪ name‬و ‪ age‬در ﻟﯿﺴﺖ ‪«names‬‬

‫‪ ‬ﻓﺮض ﮐﻨﯿﺪ ﺗﺎﺑﻌﯽ ﺑﺎ ﻧﺎم ‪ dice‬دارﯾﻢ ﮐﻪ ﺑﻪ ﺻﻮرت ﻣﺠﺎزي ﺗﺎس ﻣﯽاﻧﺪازد‪ .‬ﻣﯽﺧﻮاﻫﯿﻢ ﻟﯿﺴﺘﯽ درﺳﺖ‬
‫ﮐﻨﯿﻢ ﮐﻪ ‪ 10‬ﺑﺎر ﺗﺎس ﺑﯿﺎﻧﺪازد و اﮔﺮ ﻣﻘﺪار ﺗﺎس ﺑﺰرﮔﺘﺮ از ‪ 3‬ﺑﻮد در ﻟﯿﺴﺖ ﻗﺮار دﻫﺪ‪.‬‬

‫‪from random import randint‬‬

‫‪def dice():‬‬
‫)‪return randint(1, 6‬‬

‫]‪my_list = [temp for _ in range(10) if (temp:= dice()) > 3‬‬

‫)‪print(my_list‬‬

‫]‪>> [5, 5, 6, 4, 5‬‬

‫ﻋﻤﻠﮕﺮ ‪ Walrus‬را ﺑﻪ ﺧﺎﻃﺮ ﺑﯿﺎورﯾﺪ‪ .‬در ﺗﻮﺻﯿﻒ ﻟﯿﺴﺖ ﻣﯽﺗﻮان از اﯾﻦ ﻋﺒﺎرت اﺳﺘﻔﺎده ﮐﺮد‪ .‬در ﻗﺴﻤﺖ‬
‫ﺷﺮﻃﯽ‪ ،‬ﺗﺎﺑﻊ ﺗﺎس را ﻓﺮاﺧﻮاﻧﯽ ﮐﺮدﯾﻢ و ﻣﻘﺪار آن را در ﻣﺘﻐﯿﺮ ‪ temp‬ذﺧﯿﺮه ﮐﺮدﯾﻢ و ﺷﺮط آﻧﮑﻪ ﺑﯿﺸﺘﺮ‬
‫از ‪ 3‬ﺑﺎﺷﺪ را ﺑﺮرﺳﯽ ﮐﺮدﯾﻢ‪ ،‬ﺳﭙﺲ ﻣﺘﻐﯿﺮ ‪ temp‬را در ﻟﯿﺴﺖ ﻗﺮار دادﯾﻢ‪.‬‬

‫‪Set Comprehension‬‬
‫ﺳﺎﺧﺖ ﻣﺠﻤﻮﻋﻪﻫﺎ ﺑﻪﺻﻮرت ﺗﮏﺧﻄﯽ دﻗﯿﻘﺎً ﻣﺸﺎﺑﻪ ﺳﺎﺧﺖ ﻟﯿﺴﺖ اﺳﺖ ﺑﺎ اﯾﻦ ﺗﻔﺎوت ﮐﻪ ﺑﻪﺟﺎي دو‬
‫ﮐﺎراﮐﺘﺮ [ و ] در اﺑﺘﺪاي آن از { و } اﺳﺘﻔﺎده ﻣﯽﺷﻮد‪.‬‬

‫در ﻣﺜﺎل زﯾﺮ ﻣﯽﺧﻮاﻫﯿﻢ ﺣﺮوف ﯾﮏ ﺟﻤﻠﻪ را در ﻣﺠﻤﻮﻋﻪاي ﺑﺮﯾﺰﯾﻢ‪ .‬ﺣﺘﻤﺎً ﺑﻪ ﺧﺎﻃﺮ دارﯾﺪ ﮐﻪ در ﻣﺠﻤﻮﻋﻪ‬
‫ﻋﻀﻮ ﺗﮑﺮاري وﺟﻮد ﻧﺪارد‪.‬‬

‫"‪sentence = "This is an example sentence‬‬

‫}‪my_set = {character for character in sentence‬‬

‫)‪print(my_set‬‬

‫‪>> {'T', 'i', 'n', 'x', 'c', 'h', 'e', 's', 'l', 't', 'p',‬‬
‫}'‪'m', ' ', 'a‬‬

‫))‪print(len(my_set‬‬

‫‪>> 14‬‬
‫‪Ahwaz_Hackerz‬‬

‫‪71‬‬ ‫ﺑﺨﺶ ﺳﻮم ‪ /‬ﻓﺼﻞ ‪ / 1‬ﻣﻘﺪﻣﻪ‬

‫‪Dictionary Comprehension‬‬
‫ﺳﺎﺧﺖ دﯾﮑﺸﻨﺮي ﻫﻤﺎﻧﻨﺪ ﺳﺎﺧﺖ ﻣﺠﻤﻮﻋﻪ اﺳﺖ ﺑﺎ اﯾﻦ ﺗﻔﺎوت ﮐﻪ ﺳﺎﺧﺖ دﯾﮑﺸﻨﺮي ﻧﯿﺎز ﺑﻪ ﮐﻠﯿﺪ و ﻣﻘﺪار‬
‫دارد و ﺑﺎﯾﺪ ﺑﻪﺻﻮرت ‪ key:value‬در دﯾﮑﺸﻨﺮي ﻗﺮار ﺑﮕﯿﺮد‪ .‬اﮔﺮ از اﯾﻦ روش ﻧﻮﺷﺘﻦ‪ ،‬ﯾﮏ ﻋﻨﺼﺮ‬
‫ﺑﮕﺬارﯾﻢ‪ ،‬ﻣﺠﻤﻮﻋﻪ ﺳﺎﺧﺘﻪ ﻣﯽﺷﻮد و اﮔﺮ از ﺗﺮﮐﯿﺐ ﮐﻠﯿﺪ‪-‬ﻣﻘﺪار اﺳﺘﻔﺎده ﮐﻨﯿﻢ‪ ،‬دﯾﮑﺸﻨﺮي ﺗﺤﻮﯾﻞ‬
‫ﻣﯽﮔﯿﺮﯾﻢ‪.‬‬

‫ﻣﯽﺧﻮاﻫﯿﻢ دﯾﮑﺸﻨﺮي ﺑﺴﺎزﯾﻢ ﮐﻪ اﻋﺪاد ‪ 1‬ﺗﺎ ‪ 10‬را ﺑﻪ ﺗﻮان ﺳﻮم آﻧﻬﺎ ﻧﮕﺎﺷﺖ ﮐﻨﺪ‪.‬‬

‫})‪my_dict = {i: i ** 3 for i in range(1, 11‬‬

‫)‪print(my_dict‬‬

‫‪>> {1: 1, 2: 8, 3: 27, 4: 64, 5: 125, 6: 216,‬‬


‫}‪7: 343, 8: 512, 9: 729, 10: 1000‬‬
‫‪Ahwaz_Hackerz‬‬

‫ﻣﺒﺎﺣﺚ ﭘﯿﺸﺮﻓﺘﻪ در ﭘﺎﯾﺘﻮن ‪ /‬ﻣﻮﺳﺴﻪ ﻓﺮﻫﻨﮕﯽ ﻫﻨﺮي دﯾﺒﺎﮔﺮان ﺗﻬﺮان‬ ‫‪72‬‬

‫ﻓﺼﻞ‪2‬‬
‫ژﻧﺮاﺗﻮر‬
‫‪Ahwaz_Hackerz‬‬

‫‪73‬‬ ‫ﺑﺨﺶ ﺳﻮم ‪ /‬ﻓﺼﻞ ‪ / 2‬ژﻧﺮاﺗﻮر‬

‫ژﻧﺮاﺗﻮر ﭼﯿﺴﺖ؟‬
‫ﻫﻤﺎنﻃﻮرﮐﻪ در ‪ 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‬‬

‫ﻣﺒﺎﺣﺚ ﭘﯿﺸﺮﻓﺘﻪ در ﭘﺎﯾﺘﻮن ‪ /‬ﻣﻮﺳﺴﻪ ﻓﺮﻫﻨﮕﯽ ﻫﻨﺮي دﯾﺒﺎﮔﺮان ﺗﻬﺮان‬ ‫‪74‬‬

‫دﻗﺖ ﮐﻨﯿﺪ ﮐﻪ ﺑﺎ ﺻﺪازدن اﯾﻦ ﺗﺎﺑﻊ ﯾﮏ ﺷﯽ ژﻧﺮاﺗﻮر ﺗﻮﻟﯿﺪ ﻣﯽﺷﻮد ﮐﻪ ﺑﺎﯾﺪ از آن اﺳﺘﻔﺎده ﮐﺮد‪ .‬اﯾﻦ ﯾﮏ‬
‫روش ﻫﻮﺷﻤﻨﺪ ﺑﺮاي اﺳﺘﻔﺎده از ژﻧﺮاﺗﻮرﻫﺎ اﺳﺖ زﯾﺮا اﮔﺮ ﺑﻪ اﯾﻦ ﺷﮑﻞ ﻧﺒﻮد ﺑﺮاي ﻫﺮ ژﻧﺮاﺗﻮر ﺑﺎﯾﺪ ﯾﮏ ﺗﺎﺑﻊ‬
‫ﺟﺪﯾﺪ ﻣﯽﺳﺎﺧﺘﯿﻢ و ژﻧﺮاﺗﻮرﻫﺎ درواﻗﻊ ﯾﮑﺒﺎر ﻣﺼﺮف ﻣﯽﺑﻮدﻧﺪ‪ .‬ﺑﺎ ﻫﺮ ﺑﺎر ﺻﺪازدن ﺗﺎﺑﻊ ‪ ،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‬‬

‫‪>> before a‬‬


‫‪a‬‬

‫))‪print(next(generator_object‬‬

‫‪>> before b‬‬


‫‪b‬‬

‫ﻫﻤﺎنﻃﻮرﮐﻪ ﻣﺸﺎﻫﺪه ﻣﯽﮐﻨﯿﺪ ژﻧﺮاﺗﻮر وﻗﺘﯽ ﺻﺪا زده ﻣﯽﺷﻮد ﮐﺎرﻫﺎﯾﯽ را اﻧﺠﺎم ﻣﯽدﻫﺪ ﺗﺎ ﺑﻪ اوﻟﯿﻦ‬
‫‪ yield‬ﺑﺮﺳﺪ‪ ،‬ﺳﭙﺲ ﻗﻔﻞ ﻣﯽﺷﻮد و ﺑﺎر دﯾﮕﺮ ﮐﻪ ﺻﺪا ﺑﺸﻮد روﻧﺪ ﮐﻠﯽ را ﺗﺎ رﺳﯿﺪن ﺑﻪ ‪ yield‬ﺑﻌﺪي‬
‫ﻃﯽ ﻣﯽﮐﻨﺪ‪.‬‬

‫ژﻧﺮاﺗﻮرﻫﺎ ﺑﺮاي دادهﻫﺎي ﺣﺠﯿﻢ و زﯾﺎدي ﮐﻪ در ﺣﺎﻓﻈﻪ ﺟﺎ ﻧﻤﯽﺷﻮﻧﺪ ﻣﻨﺎﺳﺐ اﺳﺖ‪ .‬ﺑﯿﺎﯾﯿﺪ ﺑﺎ‬
‫‪ sys.getsizeof‬ﻣﻘﺪار ﺣﺎﻓﻈﮥ ﻣﺼﺮﻓﯽ ﯾﮏ ﻟﯿﺴﺖ از اﻋﺪاد ‪ 0‬ﺗﺎ ‪ 50‬ﻣﯿﻠﯿﻮن را اﻧﺪازهﮔﯿﺮي ﮐﻨﯿﻢ‪.‬‬
‫‪Ahwaz_Hackerz‬‬

‫‪75‬‬ ‫ﺑﺨﺶ ﺳﻮم ‪ /‬ﻓﺼﻞ ‪ / 2‬ژﻧﺮاﺗﻮر‬

‫‪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‬‬

‫ﻣﺒﺎﺣﺚ ﭘﯿﺸﺮﻓﺘﻪ در ﭘﺎﯾﺘﻮن ‪ /‬ﻣﻮﺳﺴﻪ ﻓﺮﻫﻨﮕﯽ ﻫﻨﺮي دﯾﺒﺎﮔﺮان ﺗﻬﺮان‬ ‫‪76‬‬

‫‪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‬ﺑﺮاي ﺳﺎﺧﺖ ﻟﯿﺴﺖ‪ ،‬ﻣﯽﺗﻮان ژﻧﺮاﺗﻮرﻫﺎي ﺗﮏﺧﻄﯽ ﻧﯿﺰ ﺳﺎﺧﺖ‪.‬‬
‫ﻧﻮﺷﺘﻦ آﻧﻬﺎ دﻗﯿﻘﺎً ﻣﺎﻧﻨﺪ ﻟﯿﺴﺖ اﺳﺖ وﻟﯽ ﺑﺎ اﯾﻦ ﺗﻔﺎوت ﮐﻪ ﺑﻪﺟﺎي اﺳﺘﻔﺎده از ﮐﺎراﮐﺘﺮ [ و ] در اﺑﺘﺪا و‬
‫اﻧﺘﻬﺎي آن ﺑﺎﯾﺪ از ﭘﺮاﻧﺘﺰ اﺳﺘﻔﺎده ﻧﻤﻮد‪.‬‬

‫ژﻧﺮاﺗﻮر زﯾﺮ اﻋﺪاد زوج را ﺗﺎ ‪ 1‬ﻣﯿﻠﯿﺎرد ﺗﻮﻟﯿﺪ ﻣﯽﮐﻨﺪ‪.‬‬

‫)‪numbers = (i for i in range(1000000000) if not i% 2‬‬

‫))‪print(next(numbers‬‬

‫‪>> 0‬‬

‫))‪print(next(numbers‬‬

‫‪>> 2‬‬

‫))‪print(next(numbers‬‬

‫‪>> 4‬‬

‫ﺗﺎﺑﻊ ‪send‬‬
‫ژﻧﺮاﺗﻮرﻫﺎ در ﻃﻮل ﺣﯿﺎت ﺧﻮد ﻣﯽﺗﻮاﻧﻨﺪ ﻣﻘﺎدﯾﺮي را از ﺑﯿﺮون درﯾﺎﻓﺖ ﮐﻨﻨﺪ‪ ،‬اﯾﻦ ﻋﻤﻞ ﺗﻮﺳﻂ ﺗﺎﺑﻊ ‪send‬‬
‫اﻧﺠﺎم ﻣﯽﺷﻮد‪ .‬ﺑﻪ ﻣﺜﺎل زﯾﺮ دﻗﺖ ﮐﻨﯿﺪ‪ ،‬ﯾﮏ ژﻧﺮاﺗﻮر دارﯾﻢ ﮐﻪ ﯾﮏ ﺷﻤﺎرﺷﮕﺮ ﻣﻌﮑﻮس اﺳﺖ‪ .‬ﮐﺎري‬
‫ﻣﯽﮐﻨﯿﻢ ﮐﻪ اﯾﻦ ﺷﻤﺎرﺷﮕﺮ ﺑﺎ درﯾﺎﻓﺖ ﻋﺪد ﺟﺪﯾﺪ ﻣﻘﺪار ﺧﻮد را ﺑﻪروزرﺳﺎﻧﯽ ﮐﻨﺪ و ﺷﻤﺎرش را از آن ﻋﺪد‬
‫درﯾﺎﻓﺘﯽ اداﻣﻪ دﻫﺪ‪.‬‬
‫‪Ahwaz_Hackerz‬‬

‫‪77‬‬ ‫ﺑﺨﺶ ﺳﻮم ‪ /‬ﻓﺼﻞ ‪ / 2‬ژﻧﺮاﺗﻮر‬

‫‪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‬ﻧﯿﺎورده اﺳﺖ‪ ،‬ﻋﺪد ﭼﺎپ ﮐﻨﺪ‪.‬‬

‫‪from random import randint‬‬


‫‪def dice():‬‬
‫‪while True:‬‬
‫)‪yield randint(1,6‬‬
‫)(‪dice_gen = dice‬‬
‫‪Ahwaz_Hackerz‬‬

‫ﻣﺒﺎﺣﺚ ﭘﯿﺸﺮﻓﺘﻪ در ﭘﺎﯾﺘﻮن ‪ /‬ﻣﻮﺳﺴﻪ ﻓﺮﻫﻨﮕﯽ ﻫﻨﺮي دﯾﺒﺎﮔﺮان ﺗﻬﺮان‬ ‫‪78‬‬

‫‪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‬‬

‫‪79‬‬ ‫ﺑﺨﺶ ﺳﻮم ‪ /‬ﻓﺼﻞ ‪ / 2‬ژﻧﺮاﺗﻮر‬

‫اﮔﺮ ﺑﺎر دﯾﮕﺮ ﺑﺮاي اﯾﻦ ژﻧﺮاﺗﻮر ﺗﺎﺑﻊ ‪ next‬را ﺻﺪا ﺑﺰﻧﯿﻢ ‪ StopIteration‬درﯾﺎﻓﺖ ﻣﯽﮐﻨﯿﻢ‪.‬‬

‫)‪next(numbers_gen‬‬

‫‪!> StopIteration‬‬

‫در اﺻﻞ ﺳﺎزوﮐﺎر ﺣﻠﻘﻪ ‪ for‬ﺑﻪ اﯾﻦ ﺻﻮرت اﺳﺖ ﮐﻪ آﻧﻘﺪر ﺗﺎﺑﻊ ‪ next‬را ﺻﺪا ﻣﯽزﻧﺪ ﺗﺎ ﺑﻪ‬
‫‪ StopIteration‬ﺑﺮﺳﺪ‪ .‬ﺗﻮاﺑﻊ ‪ for‬را ﻣﯽﺗﻮان ﺑﺎ ‪ while‬ﺑﻪ ﺻﻮرت زﯾﺮ ﺷﺒﯿﻪﺳﺎزي ﮐﺮد‪ .‬اﯾﻦ ﻣﺜﺎل‬
‫دﯾﺪ وﺳﯿﻌﯽ ﺑﻪ ﺷﻤﺎ از ﻧﺤﻮة اﺳﺘﻔﺎده از ژﻧﺮاﺗﻮرﻫﺎ‪ next ،‬و ‪ StopIteration‬ﻣﯽدﻫﺪ‪.‬‬

‫))‪gen = (i*i for i in range(1,4‬‬

‫‪while True:‬‬
‫‪try:‬‬
‫)‪i = next(gen‬‬
‫)‪print(i‬‬
‫‪except StopIteration:‬‬
‫‪break‬‬

‫‪>> 1‬‬
‫‪4‬‬
‫‪9‬‬

‫ﻣﺰاﯾﺎي اﺳﺘﻔﺎده از ژﻧﺮاﺗﻮر‬

‫‪ ‬ﮐﺪ ﺳﺎده‪ :‬ﺑﺎ ﻣﺸﺎﻫﺪه ﻣﺜﺎلﻫﺎي ﺑﺎﻻ ﺣﺘﻤﺎً درﯾﺎﻓﺘﻪاﯾﺪ ﮐﻪ ﻧﻮﺷﺘﻦ و ﺳﺎﺧﺖ ﯾﮏ ژﻧﺮاﺗﻮر ﺑﺴﯿﺎر ﺳﺎده‬
‫اﺳﺖ و در ﺑﺴﯿﺎري از ﻣﻮاﻗﻊ ﻣﯽﺗﻮان از آن اﺳﺘﻔﺎده ﮐﺮد‪.‬‬
‫‪ ‬ﺑﻬﯿﻨﮕﯽ‪ :‬ژﻧﺮاﺗﻮرﻫﺎ ﺑﻪﺻﻮرت ﺗﻨﺒﻞ ﮐﺎر ﻣﯽﮐﻨﻨﺪ‪ ،‬ﯾﻌﻨﯽ ﻫﺮﮔﺎه از آﻧﻬﺎ درﺧﻮاﺳﺘﯽ ﺷﻮد‪ ،‬ﺑﻪدﻧﺒﺎل ﻧﺘﯿﺠﮥ‬
‫آن ﻣﯽروﻧﺪ‪ .‬اﯾﻦ ﻋﻤﻞ ﺑﺎﻋﺚ ﻣﯽﺷﻮد ﺗﺎ در ﻣﺼﺮف ﺣﺎﻓﻈﻪ ﺑﻪﺷﺪت ﺻﺮﻓﻪﺟﻮﯾﯽ ﺷﻮد‪.‬‬
‫‪Ahwaz_Hackerz‬‬

‫ﻣﺒﺎﺣﺚ ﭘﯿﺸﺮﻓﺘﻪ در ﭘﺎﯾﺘﻮن ‪ /‬ﻣﻮﺳﺴﻪ ﻓﺮﻫﻨﮕﯽ ﻫﻨﺮي دﯾﺒﺎﮔﺮان ﺗﻬﺮان‬ ‫‪80‬‬

‫ﻓﺼﻞ‪3‬‬
‫‪Decorator‬‬
‫‪Ahwaz_Hackerz‬‬

‫‪81‬‬ ‫ﺑﺨﺶ ﺳﻮم ‪ /‬ﻓﺼﻞ ‪Decorator / 3‬‬

‫ﺗﺎﺑﻊ داﺧﻠﯽ‬
‫ﭘﯿﺶ از آﻧﮑﻪ در ﻣﻮرد ‪ Decorator‬ﺻﺤﺒﺖ ﮐﻨﯿﻢ‪ ،‬ﻧﺨﺴﺖ ﻣﯽﺑﺎﯾﺴﺖ درك درﺳﺘﯽ از ﺗﺎﺑﻊ داﺧﻠﯽ داﺷﺘﻪ‬
‫ﺑﺎﺷﯿﻢ‪ .‬ﺗﺎﺑﻊ داﺧﻠﯽ‪ 1‬ﺗﺎﺑﻌﯽ اﺳﺖ ﮐﻪ در ﯾﮏ ﺗﺎﺑﻊ دﯾﮕﺮ ﺗﻌﺮﯾﻒ ﺷﺪه و ﻣﯽﺗﻮاﻧﺪ ﻫﻤﺎنﺟﺎ ﺻﺪا زده ﺷﻮد و ﯾﺎ‬
‫ﺑﺮﮔﺮداﻧﺪه ﺷﻮد‪.‬‬

‫‪def root():‬‬
‫)"‪print("/‬‬

‫‪def boot():‬‬
‫)"‪print("/boot‬‬

‫‪def proc():‬‬
‫)"‪print("/proc‬‬

‫)(‪boot‬‬
‫)(‪proc‬‬

‫)(‪root‬‬

‫‪>> /‬‬
‫‪/boot‬‬
‫‪/proc‬‬

‫در ﻣﺜﺎل ﺑﺎﻻ دو ﺗﺎﺑﻊ ‪ boot‬و ‪ proc‬در ﺗﺎﺑﻊ ‪ root‬ﺗﻌﺮﯾﻒ ﺷﺪهاﻧﺪ و در ﻫﻤﺎنﺟﺎ از آﻧﻬﺎ اﺳﺘﻔﺎده ﺷﺪ‪.‬‬
‫اﮔﺮ ﺑﺨﻮاﻫﯿﻢ ﺗﺎﺑﻊ ‪ boot‬را در ﺑﯿﺮون از ﺗﺎﺑﻊ ‪ root‬اﺳﺘﻔﺎده ﮐﻨﯿﻢ ﺑﺎ ﺧﻄﺎ ﻣﻮاﺟﻪ ﻣﯽﺷﻮﯾﻢ‪.‬‬

‫)(‪boot‬‬

‫‪!> NameError: name 'boot' is not defined‬‬

‫ﺗﺎﺑﻊ اﺻﻠﯽ ﻣﯽﺗﻮاﻧﺪ ﺗﺎﺑﻊﻫﺎي داﺧﻞ ﺧﻮد را ﻧﯿﺰ ﺑﺮﮔﺮداﻧﺪ‪ .‬ﻓﺮض ﮐﻨﯿﺪ ﺗﺎﺑﻌﯽ ﺑﺎ ﻋﻨﻮان ﺗﺎﺑﻊ ﻋﻤﻠﮕﺮ دارﯾﻢ ﮐﻪ‬
‫ﻋﻤﻠﮕﺮ ﻣﺜﺒﺖ ﯾﺎ ﻣﻨﻔﯽ را ﺑﺮﻣﯽﮔﺮداﻧﺪ و ﻣﯽﺗﻮاﻧﯿﻢ از ﺧﻮد ﺗﺎﺑﻊﻫﺎي ﺑﺮﮔﺮداﻧﺪهﺷﺪه اﺳﺘﻔﺎده ﮐﻨﯿﻢ‪.‬‬

‫‪def get_operator(name):‬‬

‫‪def add(a, b, /):‬‬


‫‪return a + b‬‬

‫‪def sub(a, b, /):‬‬


‫‪return a - b‬‬

‫‪1‬‬
‫‪Inner-function‬‬
‫‪Ahwaz_Hackerz‬‬

‫ﻣﺒﺎﺣﺚ ﭘﯿﺸﺮﻓﺘﻪ در ﭘﺎﯾﺘﻮن ‪ /‬ﻣﻮﺳﺴﻪ ﻓﺮﻫﻨﮕﯽ ﻫﻨﺮي دﯾﺒﺎﮔﺮان ﺗﻬﺮان‬ ‫‪82‬‬

‫‪if name == "add":‬‬


‫‪return add‬‬

‫‪elif name == "sub":‬‬


‫‪return sub‬‬

‫‪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‬‬

‫‪83‬‬ ‫ﺑﺨﺶ ﺳﻮم ‪ /‬ﻓﺼﻞ ‪Decorator / 3‬‬

‫ﺗﺼﻮﯾﺮ ‪Decorator - 11‬‬

‫در اﺑﺘﺪا ﺳﺎدهﺗﺮﯾﻦ ‪ Decorator‬ﻣﻤﮑﻦ را ﻣﯽﺳﺎزﯾﻢ ﮐﻪ ﻗﺒﻞ از اﺟﺮاي ﺗﺎﺑﻊ ﻣﺎ‪ ،‬ﮐﻠﻤﻪ ‪ Hello‬را ﭼﺎپ‬
‫ﻣﯽﮐﻨﺪ‪ .‬در اﯾﻦ ﻣﺜﺎل ﻣﺎ ﭼﯿﺰ ﺟﺪﯾﺪي ﯾﺎد ﻧﻤﯽﮔﯿﺮﯾﻢ ﺑﻠﮑﻪ از ﻫﻤﺎن ﻣﻔﺎﻫﯿﻤﯽ ﮐﻪ در اﯾﻦ ﺑﺨﺶ آﻣﻮﺧﺘﯿﻢ‪،‬‬
‫اﺳﺘﻔﺎده ﺧﻮاﻫﯿﻢ ﮐﺮد‪.‬‬

‫‪def hello(func):‬‬
‫‪def wrapper():‬‬
‫)"‪print("Hello‬‬
‫)(‪func‬‬

‫‪return wrapper‬‬

‫‪def sample():‬‬
‫)"‪print("sample‬‬

‫)‪sample = hello(sample‬‬

‫)(‪sample‬‬

‫‪>> Hello‬‬
‫‪sample‬‬
‫‪Ahwaz_Hackerz‬‬

‫ﻣﺒﺎﺣﺚ ﭘﯿﺸﺮﻓﺘﻪ در ﭘﺎﯾﺘﻮن ‪ /‬ﻣﻮﺳﺴﻪ ﻓﺮﻫﻨﮕﯽ ﻫﻨﺮي دﯾﺒﺎﮔﺮان ﺗﻬﺮان‬ ‫‪84‬‬

‫ﺗﺎﺑﻊ ‪ hello‬ﯾﮏ ﺗﺎﺑﻊ ﺑﻪﻋﻨﻮان ورودي درﯾﺎﻓﺖ ﻣﯽﮐﻨﺪ و ﯾﮏ ﺗﺎﺑﻊ داﺧﻠﯽ ﺑﺎ ﻧﺎم ‪ wrapper‬ﻧﯿﺰ دارد ﮐﻪ‬
‫ﮐﺎر ﺑﺴﺘﻪﺑﻨﺪي ﺗﺎﺑﻊ ورودي را ﺑﺮﻋﻬﺪه دارد‪ .‬ﺗﺎﺑﻊ ‪ wrapper‬ﻗﺒﻞ از اﺟﺮاي ﺗﺎﺑﻊ ورودي )‪ ،(func‬ﮐﻠﻤﻪ‬
‫‪ Hello‬را ﭼﺎپ ﻣﯽﮐﻨﺪ‪ .‬ﺗﺎﺑﻊ اﺻﻠﯽ ﯾﻌﻨﯽ ‪ hello‬ﺗﺎﺑﻊ داﺧﻠﯽ ‪ wrapper‬را ﺑﺮﻣﯽﮔﺮداﻧﺪ‪ .‬ﺣﺎل ﺗﺎﺑﻊ‬
‫‪ sample‬را ﺑﺎ )‪ hello(sample‬ﺟﺎﯾﮕﺰﯾﻦ ﻣﯽﮐﻨﯿﻢ‪ .‬در اﺻﻞ ﮐﺎري ﮐﻪ در اﯾﻨﺠﺎ اﻧﺠﺎم ﺷﺪ اﯾﻦ ﺑﻮد‬
‫ﮐﻪ ﻣﺎ ﺗﺎﺑﻊ اﺻﻠﯽ را ﺑﻪ ﺗﺎﺑﻊ ‪ hello‬دادﯾﻢ و ﺑﺴﺘﻪﺑﻨﺪيﺷﺪه آن را ﺗﺤﻮﯾﻞ ﮔﺮﻓﺘﯿﻢ‪.‬‬

‫‪ ‬ﺑﻪ زﺑﺎن ﺳﺎده‪ Decorator :‬ﯾﮏ ﺗﺎﺑﻊ را درﯾﺎﻓﺖ ﻣﯽﮐﻨﺪ‪ ،‬در آن ﺗﻐﯿﯿﺮاﺗﯽ اﻧﺠﺎم ﻣﯽدﻫﺪ و آن را‬
‫ﺑﺮﻣﯽﮔﺮداﻧﺪ‪.‬‬

‫روش ﻧﻮﺷﺘﻦ ﺑﻬﺘﺮ‬


‫در ﻣﺜﺎل ﻗﺒﻞ روش ﺑﺴﺘﻪﺑﻨﺪي ﺗﺎﺑﻊ ‪ sample‬ﮐﻤﯽ زﻧﻨﺪه و اﺿﺎﻓﯽ اﺳﺖ وﻟﯽ ﺑﺮاي ﺷﺮوع ﻣﻄﻠﺐ و درك‬
‫ﭼﮕﻮﻧﮕﯽ ﮐﺎر ﺑﺎ ‪ Decorator‬ﮐﺎﻣﻼً اﻟﺰاﻣﯽ اﺳﺖ‪ .‬ﻣﯽﺗﻮاﻧﯿﻢ از @ ﺑﺮاي ‪ Decorator‬اﺳﺘﻔﺎده ﮐﻨﯿﻢ ﺑﻪ اﯾﻦ‬
‫ﺻﻮرت ﮐﻪ ﻧﺎم آن را ﺑﺎ ﻋﻼﻣﺖ @ ﻗﺒﻞ از ﺗﺎﺑﻊ ﺑﯿﺎورﯾﻢ‪ ،‬ﻣﺜﺎل ﻗﺒﻞ را ﺑﻪ اﯾﻦ ﺻﻮرت ﺑﺎزﻧﻮﯾﺴﯽ ﻣﯽﮐﻨﯿﻢ‪.‬‬

‫‪def hello(func):‬‬
‫‪def wrapper():‬‬
‫)"‪print("Hello‬‬
‫)(‪func‬‬

‫‪return wrapper‬‬

‫‪@hello‬‬
‫‪def sample():‬‬
‫)"‪print("sample‬‬

‫)(‪sample‬‬

‫‪>> Hello‬‬
‫‪sample‬‬

‫ﮔﺬاﺷﺘﻦ ‪ @hello‬ﻗﺒﻞ از ﺗﺎﺑﻊ‪ ،‬ﻫﻤﺎﻧﻨﺪ آن اﺳﺖ ﮐﻪ ﺑﻌﺪ از آن ﺑﻨﻮﯾﺴﯿﻢ‪:‬‬

‫)‪sample = hello(sample‬‬

‫از ‪ Decorator‬ﻣﯽﺗﻮاﻧﯿﻢ ﭼﻨﺪﯾﻦ ﺑﺎر اﺳﺘﻔﺎده ﮐﻨﯿﻢ و اﯾﻦ ﮐﺎر ﺑﺎﻋﺚ ﺟﻠﻮﮔﯿﺮي از ﻧﻮﺷﺘﻦ ﮐﺪﻫﺎي ﺗﮑﺮاري‬
‫ﻣﯽﺷﻮد‪ .‬ﻣﺜﺎﻟﯽ را ﺑﺎ ﻫﻢ ﺑﺮرﺳﯽ ﻣﯽﮐﻨﯿﻢ و ﺳﭙﺲ ﻣﺸﮑﻼت آن را رﻓﻊ ﻣﯽﮐﻨﯿﻢ‪ .‬ﻣﯽﺧﻮاﻫﯿﻢ ﯾﮏ‬
‫‪ Decorator‬ﺟﺪﯾﺪ ﺑﺴﺎزﯾﻢ ﮐﻪ ﻫﺮ ﺗﺎﺑﻌﯽ را ‪ 3‬ﺑﺎر اﺟﺮا ﻣﯽﮐﻨﺪ‪.‬‬
‫‪Ahwaz_Hackerz‬‬

‫‪85‬‬ ‫ﺑﺨﺶ ﺳﻮم ‪ /‬ﻓﺼﻞ ‪Decorator / 3‬‬

‫‪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‬‬

‫ﻣﺒﺎﺣﺚ ﭘﯿﺸﺮﻓﺘﻪ در ﭘﺎﯾﺘﻮن ‪ /‬ﻣﻮﺳﺴﻪ ﻓﺮﻫﻨﮕﯽ ﻫﻨﺮي دﯾﺒﺎﮔﺮان ﺗﻬﺮان‬ ‫‪86‬‬

‫‪@three_times‬‬
‫‪def say_name(name):‬‬
‫)'}‪print(f'you are {name‬‬

‫)"‪say_name("siavash‬‬

‫‪>> you are siavash‬‬


‫‪you are siavash‬‬
‫‪you are siavash‬‬

‫ﺗﻐﯿﯿﺮ ﺧﺮوﺟﯽ ﺗﺎﺑﻊ ﺑﺎ اﺳﺘﻔﺎده از ‪Decorator‬‬


‫ﻣﯽﺧﻮاﻫﯿﻢ ﺧﺮوﺟﯽﻫﺎي ﺗﻮاﺑﻊ را ﺗﻐﯿﯿﺮ دﻫﯿﻢ‪ ،‬ﮐﺎﻓﯿﺴﺖ زﻣﺎﻧﯽﮐﻪ ﺗﺎﺑﻊ را در ‪ wrapper‬ﺻﺪا ﻣﯽزﻧﯿﻢ‬
‫ﻫﻤﺎنﺟﺎ ﻫﻢ ﺧﺮوﺟﯽ را ﺗﻐﯿﯿﺮ داده و ﺑﺮﮔﺮداﻧﯿﻢ‪ Decorator .‬ﺟﻮاب ﻧﻬﺎﯾﯽ را ﺑﺎ ﻃﻮل ‪ 20‬در وﺳﻂ‬
‫ﮐﺎراﮐﺘﺮ * ﭼﺎپ ﻣﯽﮐﻨﺪ‪.‬‬

‫‪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‬‬

‫ﺑﺎز ﻫﻢ ﺗﺄﮐﯿﺪ ﻣﯽﺷﻮد ﮐﻪ ﭘﺲ از اﺳﺘﻔﺎده از ‪ Decorator‬ﺗﺎﺑﻊ ﻣﺎ ﻫﻤﺎن ﺗﺎﺑﻊ ‪ wrapper‬ﻣﯽﺷﻮد و اﯾﻨﮑﻪ‬


‫ﻫﻤﺎن ﻣﻘﺪار را ﺑﺮﮔﺮداﻧﯿﻢ‪ ،‬ﺗﻐﯿﯿﺮ دﻫﯿﻢ و ﯾﺎ اﺻﻼً ﭼﯿﺰي ﺑﺮﻧﮕﺮداﻧﯿﻢ‪ ،‬ﮐﺎﻣﻼً ﺑﻪ آن ﺗﺎﺑﻊ ﺑﺴﺘﮕﯽ دارد‪.‬‬

‫ﺑﻪ ﻧﺘﺎﯾﺞ ﮐﺪﻫﺎي زﯾﺮ دﻗﺖ ﮐﻨﯿﺪ‪.‬‬

‫)‪print(add‬‬

‫>‪>> <function border.<locals>.wrapper at 0x02F18148‬‬

‫)__‪print(add.__name‬‬

‫‪>> wrapper‬‬
‫‪Ahwaz_Hackerz‬‬

‫‪87‬‬ ‫ﺑﺨﺶ ﺳﻮم ‪ /‬ﻓﺼﻞ ‪Decorator / 3‬‬

‫ﻫﻤﺎنﻃﻮريﮐﻪ ﻣﯽﺑﯿﻨﯿﺪ اﯾﻦ ﺗﻮاﺑﻊ ﺑﻪﮐﻠﯽ ﺗﻐﯿﯿﺮ ﭘﯿﺪا ﮐﺮدﻧﺪ و ﻫﻮﯾﺖ آﻧﻬﺎ دﻗﯿﻘﺎً ﻫﻤﺎن ﺗﺎﺑﻊ ‪ wrapper‬در‬
‫‪ Decorator‬ﺷﺪه اﺳﺖ‪ .‬ﺑﺮاي اﯾﻨﮑﻪ ﻧﺎم و ﻣﺸﺨﺼﺎﺗﺸﺎن ﺗﻐﯿﯿﺮ ﻧﮑﻨـﺪ از ﯾـﮏ ‪ Decorator‬دﯾﮕـﺮ در اﯾـﻦ‬
‫‪ Decorator‬اﺳــﺘﻔﺎده ﻣــﯽﮐﻨــﯿﻢ! زﻣــﺎﻧﯽﮐــﻪ ‪ @wraps‬از ﻣــﺎژول ‪ functools‬را در ﺑــﺎﻻي ﺗــﺎﺑﻊ‬
‫‪ wrapper‬ﻣــﯽﮔــﺬارﯾﻢ‪ ،‬اﯾــﻦ اﻃﻤﯿﻨــﺎن را ﻣــﯽدﻫــﯿﻢ ﮐــﻪ ﺗــﺎﺑﻊ اﺻــﻠﯽ ﻣﺸﺨﺼــﺎت ﺧــﻮد از ﻗﺒﯿــﻞ‬
‫__‪ __doc__ ،__module‬و __‪ __name‬را ﺣﻔﻆ ﮐﻨﺪ‪.‬‬

‫‪from functools import wraps‬‬

‫‪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‬‬

‫>‪>> <function add at 0x03AA8418‬‬

‫)__‪print(add.__name‬‬

‫‪>> add‬‬

‫ﺷﺎﯾﺪ ﺑﺮاﯾﺘﺎن ﺳﺆال ﭘﯿﺶ ﺑﯿﺎﯾﺪ ﮐﻪ ‪ wraps‬ﭼﮕﻮﻧﻪ ورودي درﯾﺎﻓﺖ ﻣﯽﮐﻨﺪ‪ .‬در ﻗﺴﻤﺖ ﺑﻌﺪ درﯾﺎﻓﺖ ورودي‬
‫ﺗﻮﺳﻂ ‪ Decorator‬را ﻣﻮرد ﺑﺤﺚ ﻗﺮار ﻣﯽدﻫﯿﻢ‪.‬‬

‫‪ Decorator‬ﺑﺎ ورودي‬
‫‪ Decorator‬ﺑﺎ ورودي درواﻗﻊ ﺗﺎﺑﻌﯽ اﺳﺖ ﮐﻪ ورودي درﯾﺎﻓﺖ ﮐﺮده و ﯾﮏ ‪ Decorator‬ﺑﺮﻣﯽﮔﺮداﻧﺪ‪.‬‬
‫ﻣﺜﺎﻟﯽ ﮐﻪ ﺗﺎﺑﻌﯽ را ﺳﻪ ﻣﺮﺗﺒﻪ ﺻﺪا ﻣﯽزد را ﺑﻪ ﺧﺎﻃﺮ ﺑﯿﺎورﯾﺪ‪ .‬ﻣﯽﺧﻮاﻫﯿﻢ آن را ﺗﻐﯿﯿﺮﭘﺬﯾﺮ ﮐﻨﯿﻢ و ﺑﮕﻮﯾﯿﻢ‬
‫ﭼﻨﺪﺑﺎر ﺗﮑﺮار را اﻧﺠﺎم دﻫﺪ‪ .‬ﭘﺲ ﺑﺎﯾﺪ ﺗﺎﺑﻌﯽ داﺷﺘﻪ ﺑﺎﺷﯿﻢ ﮐﻪ وﻗﺘﯽ آن را ﺻﺪا زدﯾﻢ ﯾﮏ ‪Decorator‬‬
‫ﻣﺜﻞ ﻗﺒﻞ ﺑﻪ ﻣﺎ ﺑﺮﮔﺮداﻧﺪ‪.‬‬
Ahwaz_Hackerz

‫ ﻣﻮﺳﺴﻪ ﻓﺮﻫﻨﮕﯽ ﻫﻨﺮي دﯾﺒﺎﮔﺮان ﺗﻬﺮان‬/ ‫ﻣﺒﺎﺣﺚ ﭘﯿﺸﺮﻓﺘﻪ در ﭘﺎﯾﺘﻮن‬ 88

from functools import wraps

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 ‫ﯾﮏ‬

Decorator ‫ﻧﮕﻬﺪاري اﻃﻼﻋﺎت در‬


‫ ﺑﺮاي ﻣﺜﺎل ﻣﯽﺧﻮاﻫﯿﻢ آﺧﺮﯾﻦ‬.‫ ﻣﯽﺗﻮان ﺣﺎﻟﺖﻫﺎﯾﯽ را ذﺧﯿﺮه ﮐﺮد‬Decorator ‫در ﻣﺸﺨﺼﺎت ﺗﺎﺑﻊ‬
.‫ ﻧﮕﻬﺪاري ﮐﻨﯿﻢ‬Decorator ‫اﺳﺘﻔﺎده از ﯾﮏ ﺗﺎﺑﻊ را ﺑﺎ اﺳﺘﻔﺎده از ﯾﮏ‬

from functools import wraps


from datetime import datetime

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

‫‪89‬‬ ‫ﺑﺨﺶ ﺳﻮم ‪ /‬ﻓﺼﻞ ‪Decorator / 3‬‬

‫‪@last_time‬‬
‫‪def test():‬‬
‫)"‪print("test‬‬

‫)(‪test‬‬

‫‪>> test‬‬

‫)‪print(test.last_time‬‬

‫‪>> 2020-01-01 00:00:00.000000‬‬

‫ﺑﺎ ﺗﻌﺮﯾﻒ ﻣﻘﺪار ‪ last_time‬در ﺗﺎﺑﻊ ‪ wrapper‬درواﻗﻊ ﺑﻪ ﺗﺎﺑﻊ ‪ test‬اﯾﻦ ﻣﻘﺪار را اﺿﺎﻓﻪ ﮐﺮدﯾﻢ‪،‬‬
‫ﮐﻪ ﻫﺮﮔﺎه اﯾﻦ ﺗﺎﺑﻊ ﺻﺪا زده ﺷﺪ‪ ،‬ﻣﻘﺪار آن ﺑﺎ ﺗﺎرﯾﺦ ﮐﻨﻮﻧﯽ ﺑﻪروزرﺳﺎﻧﯽ ﺷﻮد ﺗﺎ از آن ﺑﻌﺪاً اﺳﺘﻔﺎده ﮐﻨﯿﻢ‪.‬‬

‫‪ Decorator‬در ﮐﻼس‬
‫ﺳﺎﺧﺖ ‪ Decorator‬ﺑﺮاي ﮐﻼسﻫﺎ دﻗﯿﻘﺎً ﻣﺸﺎﺑﻪ آن ﭼﯿﺰي اﺳﺖ ﮐﻪ ﺑﺮاي ﺗﺎﺑﻊ ﻣﯽﺳﺎﺧﺘﯿﻢ ﺑﺎ اﯾﻦ ﺗﻔﺎوت‬
‫ﮐﻪ ﺑﻪﺟﺎي ورودي ‪ func‬از ‪ cls‬اﺳﺘﻔﺎده ﻣﯽﮐﻨﯿﻢ‪ .‬اﯾﻦ ﺗﻐﯿﯿﺮ ﻧﺎم ﻓﻘﻂ ﺑﻪ ﺧﺎﻃﺮ آن اﺳﺖ ﮐﻪ ﻣﺘﻮﺟﻪ‬
‫ﺷﻮﯾﻢ اﯾﻦ ﯾﮏ ‪ Decorator‬ﺑﺮاي ﮐﻼس اﺳﺖ‪ .‬در اﯾﻦ ﺣﺎﻟﺖ ﺳﺎﺧﺖ ﯾﮏ ﻧﻤﻮﻧﻪ از ﯾﮏ ﮐﻼس در ﺗﺎﺑﻊ‬
‫‪ wrapper‬اﻧﺠﺎم ﻣﯽﺷﻮد‪.‬‬

‫ﻣﯽﺧﻮاﻫﯿﻢ ﯾﮏ ‪ Decorator‬ﮐﻤﯽ ﭘﯿﭽﯿﺪه ﺑﺴﺎزﯾﻢ ﮐﻪ وﻗﺘﯽ در ﮐﻼﺳﯽ اﻋﻤﺎل ﻣﯽﺷﻮد‪ ،‬ﺗﺎﺑﻊ‬


‫__‪ __repr‬آن را ﺑﺎزﻧﻮﯾﺴﯽ ﻣﯽﮐﻨﺪ‪ .‬در اﯾﻦ ﻣﺜﺎل ﻣﺎ ﺗﺎﺑﻌﯽ ﻣﯽﺳﺎزﯾﻢ ﮐﻪ ﮐﻼس اﺻﻠﯽ را ﻣﯽﮔﯿﺮد و‬
‫ﯾﮏ ﮐﻼس دﯾﮕﺮي ﮐﻪ از ﮐﻼس اﺻﻠﯽ ارث ﻣﯽﺑﺮد‪ ،‬را ﻣﯽﺳﺎزد و ﺳﭙﺲ اﯾﻦ ﮐﻼس را ﺑﺎ ﺗﺎﺑﻊ‬
‫__‪ __repr‬ﺗﻐﯿﯿﺮ داده ﺷﺪه ﺑﺮﻣﯽﮔﺮداﻧﺪ‪.‬‬

‫‪from functools import wraps‬‬

‫‪def modify_repr(cls):‬‬

‫)‪@wraps(cls‬‬
‫‪def wrapper(*args, **kwargs):‬‬

‫‪class child(cls):‬‬

‫‪def __repr__(self):‬‬
‫'‪return f'{cls.__name__.upper()} object‬‬

‫)‪return child(*args, **kwargs‬‬

‫‪return wrapper‬‬
Ahwaz_Hackerz

‫ ﻣﻮﺳﺴﻪ ﻓﺮﻫﻨﮕﯽ ﻫﻨﺮي دﯾﺒﺎﮔﺮان ﺗﻬﺮان‬/ ‫ﻣﺒﺎﺣﺚ ﭘﯿﺸﺮﻓﺘﻪ در ﭘﺎﯾﺘﻮن‬ 90

@modify_repr
class Siavash:
pass

@modify_repr
class Mehran:
pass

print(Siavash())

>> SIAVASH object

print(Mehran())

>> MEHRAN object

‫ﻣﻄﺎﻟﻌﮥ ﺑﯿﺸﺘﺮ‬
.‫ را ﻣﻄﺎﻟﻌﻪ ﮐﻨﯿﺪ‬PEP318 ‫ ﻣﯽﺗﻮاﻧﯿﺪ‬Decorator ‫ﺑﺮاي ﻣﻄﺎﻟﻌﻪ درﻣﻮرد ﭘﯿﺶزﻣﯿﻨﻪ‬

:‫ ﺑﺎ اﺳﺘﻔﺎده از اﯾﻦ ﻣﺎژول ﺑﺴﯿﺎر ﺳﺎدهﺗﺮ اﺳﺖ‬Decorator ‫ﺳﺎﺧﺖ‬

https://github.com/micheles/decorator
:‫ ﻫﺎي ﮐﺎرﺑﺮدي‬Decorator ‫ﻟﯿﺴﺘﯽ از‬

https://github.com/lord63/awesome-python-decorator
Ahwaz_Hackerz

91 Context Manager / 4 ‫ ﻓﺼﻞ‬/ ‫ﺑﺨﺶ ﺳﻮم‬

4‫ﻓﺼﻞ‬
Context Manager
‫‪Ahwaz_Hackerz‬‬

‫ﻣﺒﺎﺣﺚ ﭘﯿﺸﺮﻓﺘﻪ در ﭘﺎﯾﺘﻮن ‪ /‬ﻣﻮﺳﺴﻪ ﻓﺮﻫﻨﮕﯽ ﻫﻨﺮي دﯾﺒﺎﮔﺮان ﺗﻬﺮان‬ ‫‪92‬‬

‫ﻣﻘﺪﻣﻪ‬
‫اﯾﻦ ﻓﺼﻞ رﺑﻂ ﺧﺎﺻﯽ ﺑﻪ ﺑﺮﻧﺎﻣﻪﻧﻮﯾﺴﯽ ﺗﺎﺑﻌﯽ ﻧﺪارد وﻟﯽ ﺑﻪ ﻣﺒﺤﺚ ژﻧﺮاﺗﻮرﻫﺎ ﻣﺮﺑﻮط اﺳﺖ و ﻣﯽﺗﻮان‬
‫‪ Context Manager‬را ﺑﺎ ژﻧﺮاﺗﻮر ﭘﯿﺎدهﺳﺎزي ﮐﺮد‪ .‬ﻓﺮض ﮐﻨﯿﺪ در ﻣﯿﺎن ﯾﮏ اﻟﮕﻮرﯾﺘﻢ ﭘﯿﭽﯿﺪه ﻧﯿﺎز‬
‫دارﯾﺪ ﺗﺎ ﻣﻨﺎﺑﻌﯽ را از ﺟﻤﻠﻪ ﻓﺎﯾﻞ و ﯾﺎ ﭘﺎﯾﮕﺎه داده اﺳﺘﻔﺎده ﮐﻨﯿﺪ و در اﻧﺘﻬﺎ ﺣﻮاﺳﺘﺎن ﺑﺎﺷﺪ ﮐﻪ اﯾﻦ ﻣﻨﺎﺑﻊ را‬
‫آزاد ﮐﻨﯿﺪ‪ Context Manager .‬ﺑﻪ ﻣﺎ ﮐﻤﮏ ﻣﯽﮐﻨﺪ ﺗﺎ از ﺗﮑﺮار ﮐﺪﻫﺎﯾﯽ ﻗﺒﻞ و ﺑﻌﺪ از ﮐﺪ اﺻﻠﯽ‬
‫ﺟﻠﻮﮔﯿﺮي ﮐﻨﯿﻢ‪ .‬ﺑﺮاي ﻣﺜﺎل ﻓﺮض ﮐﻨﯿﺪ ﻣﯽﺧﻮاﻫﯿﻢ ﻓﺎﯾﻠﯽ را ﺑﺎز ﮐﻨﯿﻢ و ﻣﺘﻨﯽ در آن ﻧﻮﺷﺘﻪ و آن را‬
‫ﺑﺒﻨﺪﯾﻢ‪.‬‬

‫)"‪f = open("file.txt", "w‬‬


‫‪try:‬‬
‫)"‪f.write("Test‬‬
‫‪finally:‬‬
‫)(‪f.close‬‬

‫اﯾﻦ ﮐﺎر را ﺑﻪراﺣﺘﯽ ﺑﺎ ‪ Context Manager‬ﻣﯽﺗﻮاﻧﯿﻢ اﻧﺠﺎم دﻫﯿﻢ‪ .‬ﺑﺎ دﺳﺘﻮر ‪ with‬ﯾﮏ‬
‫‪ Context Manager‬را ﺑﻪﻋﻨﻮان ﯾﮏ ﻣﺘﻐﯿﺮ درﻧﻈﺮ ﻣﯽﮔﯿﺮﯾﻢ‪ .‬در اﯾﻦ ﺻﻮرت اﯾﻦ ﻣﺘﻐﯿﺮ ﺗﺎ اﻧﺘﻬﺎي‬
‫ﻣﺤﺪوده ﻗﺎﺑﻞ اﺳﺘﻔﺎده اﺳﺖ و وﻗﺘﯽ از ﺑﻼك ‪ with‬ﺧﺎرج ﺷﻮﯾﻢ ﺑﻪ ﺻﻮرت ﺧﻮدﮐﺎر ﻓﺎﯾﻞ ﺑﺴﺘﻪ ﻣﯽﺷﻮد‪.‬‬

‫‪with open("file.txt", "w") as f:‬‬


‫)"‪f.write("Test‬‬

‫وﻗﺘﯽ اﯾﻦ ﻣﺜﺎل را ﺑﺎ ﻣﺜﺎل اول ﻣﻘﺎﯾﺴﻪ ﻣﯽﮐﻨﯿﺪ ﻣﺘﻮﺟﻪ ﻣﯽﺷﻮﯾﺪ ﮐﻪ ﻧﯿﺎزي ﺑﻪ ﻧﻮﺷﺘﻦ ﮐﺪﻫﺎي اﺿﺎﻓﻪ‬
‫ﻧﯿﺴﺖ‪ .‬ﺑﺰرگﺗﺮﯾﻦ ﻣﺰﯾﺖ ﻧﻮﺷﺘﻦ ﮐﺪﻫﺎ ﺑﺎ ‪ with‬اﻃﻤﯿﻨﺎن ﺧﺎﻃﺮ از ﺑﺴﺘﻪﺷﺪن ﻓﺎﯾﻞ اﺳﺖ و دﯾﮕﺮ ﻧﮕﺮاﻧﯽ در‬
‫ﻣﻮرد ﻓﺮاﻣﻮﺷﯽ از ﺑﺴﺘﻦ ﻣﻨﺎﺑﻊ ﭘﯿﺶ ﻧﻤﯽآﯾﺪ‪.‬‬

‫ﭘﯿﺎدهﺳﺎزي ‪ Context Manager‬ﺑﺎ ﮐﻼس‬


‫ﺳﺎﺧﺖ ﯾﮏ ‪ Context Manager‬ﺑﻪوﺳﯿﻠﻪ ﮐﻼس ﺑﺴﯿﺎر ﺳﺎده اﺳﺖ و ﺗﻨﻬﺎ ﻧﯿﺎز ﺑﻪ ﭘﯿﺎدهﺳﺎزي دو ﺗﺎﺑﻊ‬
‫__‪ __enter‬و __‪ __exit‬دارﯾﻢ‪ .‬ﺑﺮاي ﻣﺜﺎل ﯾﮏ ‪ Context Manager‬ﭘﯿﺎدهﺳﺎزي ﻣﯽﮐﻨﯿﻢ ﮐﻪ‬
‫در اﺑﺘﺪاي ﮐﺪ ﭼﺎپ ﮐﻨﺪ ﻣﻨﺒﻌﯽ را در اﺧﺘﯿﺎر ﮔﺮﻓﺘﻪ‪ ،‬ﺳﭙﺲ ﺑﻌﺪ از اﺳﺘﻔﺎده ﭼﺎپ ﮐﻨﺪ ﮐﻪ آن را آزاد ﮐﺮده‬
‫اﺳﺖ‪ .‬در واﻗﻌﯿﺖ ﻣﯽﺗﻮاﻧﯿﻢ ﺑﻪﺟﺎي ﭼﺎپ از دﺳﺘﻮرات اﺧﺘﺼﺎص ﻣﻨﺒﻊ اﺳﺘﻔﺎده ﮐﻨﯿﻢ و اﯾﻦ ﻣﺜﺎل ﺗﻨﻬﺎ‬
‫ﺟﻨﺒﮥ آﻣﻮزﺷﯽ دارد‪.‬‬

‫‪class Resource(object):‬‬

‫‪def __init__(self):‬‬
‫‪self.number = 1‬‬
‫‪Ahwaz_Hackerz‬‬

‫‪93‬‬ ‫ﺑﺨﺶ ﺳﻮم ‪ /‬ﻓﺼﻞ ‪Context Manager / 4‬‬

‫‪def __enter__(self):‬‬
‫)"‪print("Allocating a recourse ...‬‬
‫‪return self‬‬

‫‪def __exit__(self, exc_type, exc_value, traceback):‬‬


‫)"‪print("The resource is free now.‬‬

‫‪with Resource() as res:‬‬


‫)‪print(res.number‬‬
‫)"!‪print("Do Something‬‬

‫‪>> Allocating a recourse ...‬‬


‫‪1‬‬
‫!‪Do Something‬‬
‫‪The resource is free now.‬‬

‫در اﺑﺘﺪاي دﺳﺘﻮر ‪ with‬ﺗﺎﺑﻊ __‪ __enter‬ﺻﺪا زده ﻣﯽﺷﻮد و دﺳﺘﻮرات داﺧﻞ آن اﺟﺮا ﻣﯽﺷﻮد و‬
‫اﮔﺮ ﻧﯿﺎز ﺑﺎﺷﺪ ﺷﯽ ﯾﺎ ﻣﻘﺪاري ﺑﺮﮔﺮداﻧﺪه ﻣﯽﺷﻮد و ﺑﺎ ﺷﻨﺎﺳﻪاي ﮐﻪ ﺑﻌﺪ از ‪ as‬ﻣﯽآﯾﺪ ﻗﺎﺑﻞ اﺳﺘﻔﺎده اﺳﺖ‪.‬‬

‫زﻣﺎﻧﯽ ﮐﻪ از ﺑﻼك ‪ with‬ﺑﻪﻃﻮر ﻋﺎدي و ﯾﺎ ﺑﺎ ﺧﻄﺎ ﺧﺎرج ﺷﻮﯾﻢ ﺗﺎﺑﻊ __‪ __exit‬ﻓﺮاﺧﻮاﻧﯽ ﻣﯽﺷﻮد و‬
‫در اﯾﻦ ﺗﺎﺑﻊ ﺑﺎﯾﺪ دﺳﺘﻮرات آزادﺳﺎزي ﻣﻨﺎﺑﻊ را اﻧﺠﺎم دﻫﯿﻢ‪ .‬ﺗﺎﺑﻊ __‪ __exit‬ﺑﻪﺟﺰ ‪ self‬ﺳﻪ آرﮔﻮﻣﺎن‬
‫دﯾﮕﺮ ﻧﯿﺰ ﻣﯽﮔﯿﺮد ﮐﻪ درﺻﻮرﺗﯽﮐﻪ ﮐﺪ داﺧﻞ ﺑﻼك ‪ with‬ﺑﺎ ﺧﻄﺎ ﻣﻮاﺟﻪ ﺷﻮد اﺳﺘﺜﻨﺎ ﺗﻮﻟﯿﺪﺷﺪه ﺑﻪ اﯾﻦ‬
‫ﺗﺎﺑﻊ ﭘﺎس داده ﻣﯽﺷﻮد‪.‬‬

‫‪ exc_type ‬ﻧﻮع اﺳﺘﺜﻨﺎ را ﻣﺸﺨﺺ ﻣﯽﮐﻨﺪ ﺑﻪﻋﻨﻮان ﻣﺜﺎل‪<class 'ValueError'> :‬‬


‫‪ exc_value ‬ﭘﯿﺎم ﺧﻄﺎ را ﻣﺸﺨﺺ ﻣﯽﮐﻨﺪ ﻣﺎﻧﻨﺪ‪:‬‬

‫'‪invalid literal for int() with base 10: 'Something‬‬

‫‪ traceback ‬ﺷﯽ ‪ traceback‬را ﺑﺮﻣﯽﮔﺮداﻧﺪ ﮐﻪ ﺣﺎوي ﻣﺮاﺣﻞ ﺗﻮﻟﯿﺪ ﺧﻄﺎ اﺳﺖ‪.‬‬

‫ﭘﯿﺎدهﺳﺎزي ‪ Context Manager‬ﺑﺎ ژﻧﺮاﺗﻮر‬


‫ﺑﺎ اﺳﺘﻔﺎده از ‪ Decorator‬ﺑﺎ ﻧﺎم ‪ contextmanager‬ﻣﯽﺗﻮاﻧﯿﻢ ژﻧﺮاﺗﻮر را ﺗﺒﺪﯾﻞ ﺑﻪ ﯾﮏ‬
‫‪ Context Manager‬ﮐﻨﯿﻢ‪ .‬ﺑﺎ ﻫﻤﺎن ﻣﺜﺎل ﻓﺎﯾﻞ ﺷﺮوع ﻣﯽﮐﻨﯿﻢ و ﻣﯽﺧﻮاﻫﯿﻢ از ﻧﻮﺷﺘﻦ‬
‫‪ try-except-finally‬ﺟﻠﻮﮔﯿﺮي ﮐﻨﯿﻢ‪ .‬ﻓﻘﻂ ﮐﺎﻓﯿﺴﺖ اﯾﻦ ‪ Decorator‬را در ﯾﮏ ژﻧﺮاﺗﻮر اﺳﺘﻔﺎده ﮐﻨﯿﻢ‪.‬‬
‫زﻣﺎﻧﯽﮐﻪ ژﻧﺮاﺗﻮر در دﺳﺘﻮر ‪ with‬ﺻﺪا زده ﺷﻮد‪ ،‬دﺳﺘﻮراﺗﺶ اﺟﺮا ﻣﯽﺷﻮد ﺗﺎ ﺑﻪ دﺳﺘﻮر ‪ yield‬ﺑﺮﺳﺪ‪،‬‬
‫اﮔﺮ ‪ yield‬ﭼﯿﺰي را ﺑﺮﮔﺮداﻧﺪ ﺑﻪﻋﻨﻮان ﻣﺘﻐﯿﺮ ﺑﻌﺪ از دﺳﺘﻮر ‪ as‬درﻧﻈﺮ ﮔﺮﻓﺘﻪ ﻣﯽﺷﻮد و دﺳﺘﻮرات‬
Ahwaz_Hackerz

‫ ﻣﻮﺳﺴﻪ ﻓﺮﻫﻨﮕﯽ ﻫﻨﺮي دﯾﺒﺎﮔﺮان ﺗﻬﺮان‬/ ‫ﻣﺒﺎﺣﺚ ﭘﯿﺸﺮﻓﺘﻪ در ﭘﺎﯾﺘﻮن‬ 94

‫ ژﻧﺮاﺗﻮر ﮐﺎر ﺧﻮد را از دﺳﺘﻮر‬،‫ ﭘﺲ از اﺗﻤﺎم دﺳﺘﻮرات و ﯾﺎ ﺑﺮوز ﺧﻄﺎ‬.‫ اﻧﺠﺎم ﻣﯽﺷﻮد‬with ‫داﺧﻞ ﺑﻼك‬
.‫ ﺑﻪ ﺑﻌﺪ اﻧﺠﺎم ﻣﯽدﻫﺪ‬yield

from contextlib import contextmanager

@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

95 Context Manager / 4 ‫ ﻓﺼﻞ‬/ ‫ﺑﺨﺶ ﺳﻮم‬

4‫ﺑﺨﺶ‬

‫ﻻگ‬
‫‪Ahwaz_Hackerz‬‬

‫ﻣﺒﺎﺣﺚ ﭘﯿﺸﺮﻓﺘﻪ در ﭘﺎﯾﺘﻮن ‪ /‬ﻣﻮﺳﺴﻪ ﻓﺮﻫﻨﮕﯽ ﻫﻨﺮي دﯾﺒﺎﮔﺮان ﺗﻬﺮان‬ ‫‪96‬‬

‫ﻓﺼﻞ‪1‬‬
‫ﻣﻘﺪﻣﻪ‬
‫‪Ahwaz_Hackerz‬‬

‫‪97‬‬ ‫ﺑﺨﺶ ﭼﻬﺎرم ‪ /‬ﻓﺼﻞ ‪ / 1‬ﻣﻘﺪﻣﻪ‬

‫ﻣﻘﺪﻣﻪ‬
‫ﻣﺎﻧﻨﺪ ﺑﺴﯿﺎري از زﺑﺎنﻫﺎي ﺑﺮﻧﺎﻣﻪﻧﻮﯾﺴﯽ‪ ،‬زﺑﺎن ﭘﺎﯾﺘﻮن ﻫﻢ ﻗﺎﺑﻠﯿﺖ ﻻگ‪ 1‬را دارد و ﻣﺎژول ‪ logging‬ﺟﺰو‬
‫ﻣﺎژولﻫﺎي ﺧﻮدﺳﺎﺧﺘﻪ ﭘﺎﯾﺘﻮن ﺑﻮده و ﻣﻨﺎﺳﺐ ذﺧﯿﺮة ﻻگ اﺳﺖ‪.‬‬

‫در اداﻣﻪ ﻣﯽﺧﻮاﻫﯿﻢ ﺑﻪ ﻣﻔﺎﻫﯿﻢ ﻣﺮﺑﻮط ﺑﻪ ﻻگ‪ ،‬اﯾﻨﮑﻪ ﭼﺮا ﻻگ ﻣﯽﮐﻨﯿﻢ‪ ،‬ﭼﻪ ﭼﯿﺰﻫﺎﯾﯽ را ﻻگ ﮐﻨﯿﻢ و‬
‫ﻣﻌﺮﻓﯽ ﻣﺎژول ‪ logging‬ﺑﭙﺮدازﯾﻢ‪ .‬اﮔﺮ ﺷﻤﺎ ﻫﻢ ﺑﺮاي ﻻگاﻧﺪاﺧﺘﻦ از دﺳﺘﻮر ‪ print‬اﺳﺘﻔﺎده ﻣﯽﮐﻨﯿﺪ‪،‬‬
‫ﭘﺲ ﺣﺘﻤﺎً اﯾﻦ ﺑﺨﺶ را ﻣﻄﺎﻟﻌﻪ ﮐﻨﯿﺪ‪.‬‬

‫ﻻگ ﭼﯿﺴﺖ؟‬
‫در ﺑﺮﻧﺎﻣﻪﻧﻮﯾﺴﯽ ﻻگ ﺑﻪ ﻣﻌﻨﯽ ﺿﺒﻂ دادهﻫﺎي ورودي‪ ،‬ﻓﺮاﯾﻨﺪﻫﺎي اﺟﺮاﺷﺪه و دادهﻫﺎي ﺧﺮوﺟﯽ اﺳﺖ‪.‬‬
‫ﺑﺮاي ﻣﺜﺎل‪ ،‬ﮐﺎرﺑﺮي ﺑﺮﻧﺎﻣﻪ ﻣﺎﺷﯿﻦ ﺣﺴﺎب ﺷﻤﺎ را ﺑﺎز ﮐﺮده و ﻣﯽﺧﻮاﻫﺪ ﻋﺪد ‪ 4‬و ‪ 8‬را ﺟﻤﻊ ﮐﻨﺪ‪.‬‬
‫دادهﻫﺎﯾﯽ ﮐﻪ ﻻگ ﻣﯽﺷﻮد ﻣﯽﺗﻮاﻧﺪ ﺑﻪ اﯾﻦ ﺻﻮرت ﺑﺎﺷﺪ‪:‬‬

‫‪ ‬ﮐﺎرﺑﺮ از ﮐﺎﻣﭙﯿﻮﺗﺮ ﺷﻤﺎرة ‪ 2‬در ﺗﺎرﯾﺦ ﻣﺸﺨﺼﯽ وارد ﺳﯿﺴﺘﻢ ﺷﺪه اﺳﺖ‪.‬‬
‫‪ ‬ﮐﺎرﺑﺮ ﺑﺮﻧﺎﻣﻪ ﻣﺎﺷﯿﻦﺣﺴﺎب را ﺑﺎز ﮐﺮد و ﺑﺮﻧﺎﻣﻪ ﭘﺲ از ‪ 200‬ﻣﯿﻠﯽﺛﺎﻧﯿﻪ ﻧﻤﺎﯾﺶ داده ﺷﺪ‪.‬‬
‫‪ ‬ﮐﺎرﺑﺮ ﺑﺮ روي ﻋﺪد ‪ 4‬ﮐﻠﯿﮏ ﮐﺮد‪.‬‬
‫‪ ‬ﮐﺎرﺑﺮ ﺑﺮ روي ﺟﻤﻊ ﮐﻠﯿﮏ ﮐﺮد‪.‬‬
‫‪ ‬ﮐﺎرﺑﺮ ﺑﺮ روي ﻋﺪد ‪ 8‬ﮐﻠﯿﮏ ﮐﺮد‪.‬‬
‫‪ ‬ﮐﺎرﺑﺮ ﮐﻠﯿﺪ ‪ enter‬را زد‪.‬‬
‫‪ ‬ﺟﻮاب ‪ 12‬ﺑﺮاي ﮐﺎرﺑﺮ ﺑﻪ ﻧﻤﺎﯾﺶ در آﻣﺪ‪.‬‬
‫‪ ‬ﮐﺎرﺑﺮ ﺑﺮﻧﺎﻣﻪ را ﺑﺴﺖ‪ .‬ﮐﺪ ﺧﺮوج ‪ 0‬اﺳﺖ‪.‬‬

‫اﯾﻦ ﺑﺎور اﺷﺘﺒﺎﻫﯽ اﺳﺖ ﮐﻪ ﻓﻘﻂ ﺧﻄﺎﻫﺎي ﺑﺮﻧﺎﻣﻪ ﺟﺰو ﻻگ ﻣﺤﺴﻮب ﻣﯽﺷﻮﻧﺪ‪ ،‬ﺑﻠﮑﻪ ﺗﻤﺎم دادهﻫﺎي‬
‫ورودي‪ ،‬ﻓﺮاﯾﻨﺪﻫﺎي ﻃﯽﺷﺪه ﺗﻮﺳﻂ ﮐﺎرﺑﺮ‪ ،‬ﺗﻌﺎﻣﻼت ﮐﺎرﺑﺮ‪ ،‬ﻧﺘﺎﯾﺞ ﺧﺮوﺟﯽ‪ ،‬ﺧﻄﺎﻫﺎي درﯾﺎﻓﺖ ﺷﺪه و‬
‫دادهﻫﺎي ﺧﺮوﺟﯽ ﻣﯽﺗﻮاﻧﺪ ﺑﺎﺷﺪ‪ .‬ﭘﺲ ﻻگ را اﯾﻦﮔﻮﻧﻪ ﺗﻌﺮﯾﻒ ﻣﯽﮐﻨﯿﻢ‪:‬‬

‫»ﻻگ ﺑﻪ ﻓﺮاﯾﻨﺪ ﺿﺒﻂ اﻋﻤﺎل و ﺣﺎﻟﺖﻫﺎي ﺑﺮﻧﺎﻣﻪ در ﯾﮏ راﺑﻂ‪ 2‬ﺛﺎﻧﻮﯾﻪ ﮔﻔﺘﻪ ﻣﯽﺷﻮد‪«.‬‬

‫‪1‬‬
‫‪Log‬‬
‫‪2‬‬
‫‪Interface‬‬
‫‪Ahwaz_Hackerz‬‬

‫ﻣﺒﺎﺣﺚ ﭘﯿﺸﺮﻓﺘﻪ در ﭘﺎﯾﺘﻮن ‪ /‬ﻣﻮﺳﺴﻪ ﻓﺮﻫﻨﮕﯽ ﻫﻨﺮي دﯾﺒﺎﮔﺮان ﺗﻬﺮان‬ ‫‪98‬‬

‫ﭼﺮا ﻻگ ﮐﻨﯿﻢ؟‬
‫ﺷﺎﯾﺪ اﯾﻦ ﺳﺆال ﺑﺮاي ﺷﻤﺎ ﻫﻢ ﭘﯿﺶ ﺑﯿﺎﯾﺪ ﮐﻪ اﺻﻼً ﭼﺮا ﺑﺎﯾﺪ از ﻻگ اﺳﺘﻔﺎده ﮐﻨﯿﻢ‪ .‬ﻋﻤﻠﯿﺎت ﻻگ ﻋﻤﻠﯽ‬
‫اﺳﺖ ﮐﻪ ﺑﺎﯾﺪ از روز اول ﺳﺎﺧﺖ ﯾﮏ ﭘﺮوژه ﺑﻪ ﻓﮑﺮ آن ﺑﺎﺷﯿﺪ و آن را وﻇﯿﻔﮥ ﻣﻬﻤﯽ ﺗﻠﻘﯽ ﮐﻨﯿﺪ و ﻧﻪ‬
‫وﻇﯿﻔﻪاي وﻗﺖﮔﯿﺮ و ﻧﺎﮐﺎرآﻣﺪ‪ ،‬اﻣﺎ ﭼﺮا؟‬

‫ﻻگﻫﺎ ﺑﻪ ﺗﻮﺳﻌﻪدﻫﻨﺪﮔﺎن ﭼﺸﻢ ﺳﻮﻣﯽ ﻣﯽدﻫﺪ ﺗﺎ ﻣﻮاردي را ﮐﻪ در ﺗﻮﺳﻌﻪ ﺑﻪ آن ﻓﮑﺮ ﻧﮑﺮدهاﻧﺪ را ﻣﺘﻮﺟﻪ‬
‫ﺷﻮﻧﺪ‪ .‬ﻻگﻫﺎ ﻧﻪﺗﻨﻬﺎ ﻣﺸﮑﻼت ﺑﺮﻧﺎﻣﻪ را ﮔﺰارش ﻣﯽﮐﻨﻨﺪ ﺑﻠﮑﻪ اﻃﻼﻋﺎت ﺑﺴﯿﺎر ارزﺷﻤﻨﺪي را ﺑﺮاي ﻣﻄﺎﻟﻌﻪ‬
‫و ارزﯾﺎﺑﯽ از ﯾﮏ ﺳﯿﺴﺘﻢ در اﺧﺘﯿﺎر ﻣﺎ ﻗﺮار ﻣﯽدﻫﻨﺪ‪ .‬ﻻگﻫﺎ اﻃﻼﻋﺎﺗﯽ ﻫﻤﭽﻮن آﻧﺎﻟﯿﺰ ﮐﺴﺐوﮐﺎر‪،‬‬
‫ﻣﻘﯿﺎسﭘﺬﯾﺮي ﺳﯿﺴﺘﻢ و ﮐﺎراﯾﯽ ﺑﺮﻧﺎﻣﻪ را در اﺧﺘﯿﺎر ﻣﺎ ﻗﺮار ﻣﯽدﻫﻨﺪ‪ .‬در اداﻣﻪ ﭼﻨﺪﯾﻦ ﻓﺎﯾﺪة ﻻگ را ﺑﺎ ﻫﻢ‬
‫ﺑﺮرﺳﯽ ﻣﯽﮐﻨﯿﻢ‪.‬‬

‫‪ ‬ﺗﺸﺨﯿﺺ رﻓﺘﺎرﻫﺎي ﻏﯿﺮﻣﻨﺘﻈﺮه‪ :‬ﺑﺴﯿﺎري از رﻓﺘﺎرﻫﺎي ﺑﺮﻧﺎﻣﻪ ﻣﻤﮑﻦ اﺳﺖ از ﭼﺸﻢ ﺗﻮﺳﻌﻪدﻫﻨﺪﮔﺎن‬
‫در ﻃﻮل ﺗﻮﺳﻌﻪ دور ﺑﻤﺎﻧﺪ‪ .‬ﻻگﻫﺎ ﮐﻤﮏ ﻣﯽﮐﻨﻨﺪ ﺗﺎ اﮔﺮ رﻓﺘﺎرﻫﺎي ﻏﯿﺮﻣﻨﺘﻈﺮهاي رخ داد‪ ،‬ﮔﺰارشﻫﺎي‬
‫ﻣﺮﺑﻮط ﺑﻪ آن‪ ،‬در اﺧﺘﯿﺎر ﺗﻮﺳﻌﻪدﻫﻨﺪﮔﺎن ﺑﺮاي ﺗﻮﻟﯿﺪ ﻣﺠﺪد آن رﻓﺘﺎر ﻗﺮار ﺑﮕﯿﺮد و ﻣﺸﮑﻞزداﯾﯽ از ﺑﺮﻧﺎﻣﻪ‬
‫را ﺳﺮﻋﺖ ﺑﺒﺨﺸﺪ‪.‬‬
‫‪ ‬ﻋﯿﺐﯾﺎﺑﯽ ﺧﻄﺎﻫﺎ‪ :‬ﻫﻤﺎﻧﻨﺪ ﺗﺼﻮر ﺑﯿﺸﺘﺮ اﻓﺮاد ﻻگﻫﺎ ﺑﺮاي ﺧﻄﺎﯾﺎﺑﯽ و ﻋﯿﺐﯾﺎﺑﯽ ﺳﯿﺴﺘﻢ ﺑﺴﯿﺎر ﮐﺎرآﻣﺪ‬
‫و ﻣﻔﯿﺪ ﻫﺴﺘﻨﺪ و ﻣﯽﺗﻮاﻧﻨﺪ ﻣﺸﮑﻞ رخداده ﺷﺪه را ﺑﺎ ﺟﺰﺋﯿﺎت و ﻣﮑﺎن ﮐﺪ و ﺗﺎرﯾﺦ اﯾﺠﺎد را ﮔﺰارش ﮐﻨﻨﺪ‪.‬‬
‫‪ ‬ﺷﻨﺎﺳﺎﯾﯽ رﻓﺘﺎرﻫﺎي ﻣﺨﺮب‪ :‬ﻻگﻫﺎ ﻫﻤﭽﻨﯿﻦ ﮐﻤﮏ ﻣﯽﮐﻨﻨﺪ ﺗﺎ رﻓﺘﺎرﻫﺎي ﻣﺨﺮب را ﺷﻨﺎﺳﺎﯾﯽ و‬
‫ﻣﺴﺪود ﮐﻨﯿﺪ‪ .‬ﺑﺮاي ﻣﺜﺎل در ﯾﮏ وب ﺳﺮوﯾﺲ‪ ،‬ﺷﻤﺎ ﻣﺘﻮﺟﻪ ﻣﯽﺷﻮﯾﺪ ﮐﻪ ﯾﮏ ‪ IP‬ﺧﺎص ﻣﺸﻐﻮل ﺧﺮاﺑﮑﺎري‬
‫در ﺳﯿﺴﺘﻢ ﺷﻤﺎ اﺳﺖ‪ ،‬ﭘﺲ آن ‪ IP‬را در ‪ Firewall‬ﺧﻮد ﻣﺴﺪود ﻣﯽﮐﻨﯿﺪ‪.‬‬
‫‪ ‬ﻣﺒﺎﺣﺚ ﻗﺎﻧﻮﻧﯽ و ﺣﻘﻮﻗﯽ‪ :‬در ﺑﺮﺧﯽ ﻣﻮارد ﻧﯿﺎز اﺳﺖ ﮐﻪ دادهﻫﺎ ﺗﺎ ﭼﻨﺪﯾﻦ ﺳﺎل و ﯾﺎ ﺗﺎ ﻫﻤﯿﺸﻪ ﺿﺒﻂ‬
‫ﺷﻮﻧﺪ ﺗﺎ در ﻣﻮارد ﻧﯿﺎز از ﺳﻤﺖ ﻗﺎﻧﻮن ﻣﻮرد ﺑﺮرﺳﯽ ﻗﺮار ﺑﮕﯿﺮﻧﺪ‪.‬‬
‫‪ ‬ﺗﺼﻤﯿﻢ ﺑﺮاي ﮐﺎراﯾﯽ و ﻣﻘﯿﺎسﭘﺬﯾﺮي‪ :‬در ﺑﺮﺧﯽ ﻣﻮارد ﺑﺎ ﻻگﮐﺮدن ﻣﺸﺨﺼﻪﻫﺎي‬
‫‪ Non-Functional‬ﻣﺎﻧﻨﺪ ﮐﺎراﯾﯽ ﻣﯽﺗﻮاﻧﯿﻢ ﺗﺼﻤﯿﻤﯽ ﺑﺮاي ﺑﯿﺸﺘﺮﮐﺮدن ﻣﻨﺎﺑﻊ ﺧﻮد داﺷﺘﻪ ﺑﺎﺷﯿﻢ‪ .‬ﺑﺮاي‬
‫ﻣﺜﺎل در ﻻگﻫﺎي ﺧﻮد ﻣﺘﻮﺟﻪ ﻣﯽﺷﻮﯾﺪ در ﺑﺎزة ‪ 9‬ﺗﺎ ‪ 12‬ﺷﺐ ﺗﺮاﻓﯿﮏ ﻣﺼﺮﻓﯽ ﮐﺎرﺑﺮان زﯾﺎد و ﺳﺮﻋﺖ‬
‫ﭘﺎﺳﺦﮔﻮﯾﯽ ﺑﺮﻧﺎﻣﻪ ﮐﻢ ﺷﺪه اﺳﺖ‪ ،‬ﺑﻨﺎﺑﺮاﯾﻦ در اﯾﻦ ﺳﺎﻋﺎت ﻣﻨﺎﺑﻊ ﺧﻮد را اﻓﺰاﯾﺶ ﻣﯽدﻫﯿﻢ‪.‬‬
‫‪Ahwaz_Hackerz‬‬

‫‪99‬‬ ‫ﺑﺨﺶ ﭼﻬﺎرم ‪ /‬ﻓﺼﻞ ‪ / 1‬ﻣﻘﺪﻣﻪ‬

‫ﻫﺪف از ﻻگ‬
‫در ﺣﺎﻟﺖ ﮐﻠﯽ ﻻگﮐﺮدن دو ﻫﺪف ﻣﻬﻢ را ﺑﻪدﻧﺒﺎل دارد‪.‬‬

‫‪ -1‬ﻋﯿﺐﯾﺎﺑﯽ‪ :‬وﻗﺘﯽ روﻧﺪ ﮐﻠﯽ ﺑﺮﻧﺎﻣﻪ را ﻻگ ﻣﯽﮐﻨﯿﻢ و ﺑﺮﻧﺎﻣﻪ دﭼﺎر ﻣﺸﮑﻞ ﻣﯽﺷﻮد‪ ،‬ﻣﯽﺗﻮاﻧﯿﻢ آن ﺧﻄﺎ‬
‫را دوﺑﺎره ﺗﻮﻟﯿﺪ‪ ،‬ﺑﺮرﺳﯽ و در ﻧﻬﺎﯾﺖ رﻓﻊ ﮐﻨﯿﻢ‪ .‬ﺑﺪون ﻻگﮐﺮدن اﯾﻦ اﻃﻼﻋﺎت‪ ،‬ﭼﻨﯿﻦ ﭼﯿﺰي ﻣﻤﮑﻦ‬
‫ﻧﯿﺴﺖ‪ .‬درﺳﺖ اﺳﺖ ﮐﻪ زﻣﺎﻧﯽﮐﻪ ﻣﺎ ﮐﺪي را ﻣﯽﻧﻮﯾﺴﯿﻢ و ﺑﺎ ﺧﻄﺎﯾﯽ ﻣﻮاﺟﻪ ﻣﯽﺷﻮﯾﻢ ‪ IDE‬ﻫﺎي اﻣﺮوزه از‬
‫ﺟﻤﻠﻪ ‪ PyCharm‬ﮐﻪ ﺑﺴﯿﺎر ﻫﻮﺷﻤﻨﺪ ﺷﺪهاﻧﺪ‪ ،‬ﻻگﻫﺎﯾﯽ را ﺑﺮاي ﻣﺎ ﺑﻪ ﻧﻤﺎﯾﺶ ﻣﯽﮔﺬارﻧﺪ‪ ،‬اﻣﺎ در‬
‫ﻣﺤﯿﻂﻫﺎي اﺳﺘﻘﺮار ﺑﺪﯾﻦ ﺷﮑﻞ ﻧﯿﺴﺖ و ﺑﺎﯾﺪ ﺗﻤﺎم اﯾﻦ ﻣﻮارد ﺑﻪﺧﻮﺑﯽ ﺛﺒﺖ ﺷﻮﻧﺪ‪ .‬ﻓﺮض ﮐﻨﯿﺪ ﺑﺮﻧﺎﻣﻪاي‬
‫دارﯾﺪ ﮐﻪ دﭼﺎر ﻣﺸﮑﻞ ﺷﺪه اﺳﺖ و ﺗﻌﺪادي از ﮐﺎرﺑﺮان در اﺟﺮاي آن ﺑﻪ ﻣﺸﮑﻞ ﺑﺮﻣﯽﺧﻮرﻧﺪ‪ .‬ﺣﺎل اﮔﺮ‬
‫ﻻﮔﯽ ﺛﺒﺖ ﻧﮑﺮده ﺑﺎﺷﯿﺪ‪ ،‬ﭼﮕﻮﻧﻪ ﻣﯽﺧﻮاﻫﯿﺪ ﺑﻔﻬﻤﯿﺪ ﻣﺸﮑﻞ ﺑﺮﻧﺎﻣﻪ از ﮐﺪام ﻗﺴﻤﺖ ﺑﻮده و ﭼﻪ رﻓﺘﺎر و‬
‫ﺣﺎﻻﺗﯽ ﺑﺎﻋﺚ ﺑﻪوﺟﻮدآﻣﺪن آن ﺷﺪه اﺳﺖ؟‬

‫‪ -2‬ﺣﺴﺎﺑﺮﺳﯽ‪ :‬اﯾﻦ ﻻگﻫﺎ ﺑﺮاي ﻣﻘﺎﺻﺪ ﺗﺠﺎري و ﻗﺎﻧﻮﻧﯽ ﺛﺒﺖ ﻣﯽﺷﻮد‪ .‬ﺑﺮاي ﻣﺜﺎل در ﯾﮏ ﺳﯿﺴﺘﻢ ﺑﺎﻧﮑﯽ‬
‫ﺗﻤﺎم اﻃﻼﻋﺎﺗﯽ ﮐﻪ ﭘﻮﻟﯽ از ﮐﺠﺎ ﺑﻪ ﭼﻪ ﮐﺴﯽ و ﺗﻮﺳﻂ ﮐﺪام ﮐﺎرﻣﻨﺪ ﻣﻨﺘﻘﻞ ﺷﺪه ﺿﺒﻂ و ذﺧﯿﺮه ﻣﯽﺷﻮد‪.‬‬
‫ﻣﻌﻤﻮﻻً در اﯾﻦ ﻧﻮع ﻻگ‪ ،‬اﻃﻼﻋﺎﺗﯽ از ﻗﺒﯿﻞ اﯾﻨﮑﻪ ﭼﻪ ﮐﺴﯽ ﭼﻪ ﮐﺎري را در ﭼﻪ زﻣﺎﻧﯽ اﻧﺠﺎم داده ﺛﺒﺖ‬
‫ﻣﯽﺷﻮد‪.‬‬

‫داﻧﺴﺘﻦ ﺗﻔﺎوت اﯾﻦ دو ﺑﻪ اﯾﻦ ﻣﻌﻨﺎ ﻧﯿﺴﺖ ﮐﻪ ﯾﮑﯽ اﺧﺘﯿﺎري و دﯾﮕﺮي ﺿﺮوري اﺳﺖ‪ ،‬ﺑﻠﮑﻪ ﺑﺎ ﺗﻮﺟﻪ ﺑﻪ‬
‫ﻧﯿﺎزﻣﻨﺪي ﻧﺮماﻓﺰار ﺗﺼﻤﯿﻢﮔﯿﺮي ﺻﻮرت ﻣﯽﮔﯿﺮد ﮐﻪ ﺑﺎ ﭼﻪ ﻫﺪﻓﯽ ﻻگ ﻧﻮﺷﺘﻪ ﺷﻮد‪ .‬ﻻزم ﺑﻪ ذﮐﺮ اﺳﺖ ﮐﻪ‬
‫ﻣﻤﮑﻦ اﺳﺖ در ﻧﺮماﻓﺰاري ﻫﺮ دو ﻫﺪف ﻧﯿﺎز ﺑﻪ ﺗﺤﻘﻖ داﺷﺘﻪ ﺑﺎﺷﺪ و ﻣﺎژولﻫﺎي ﻻگ ﺑﻪﺧﻮﺑﯽ از ﭘﺲ‬
‫ﺑﺮآوردهﮐﺮدن اﯾﻦ دو ﻫﺪف ﺑﺮﻣﯽآﯾﻨﺪ‪.‬‬

‫ﭼﻪ ﭼﯿﺰي را ﻻگ ﮐﻨﯿﻢ؟‬


‫ﭘﺲ از آﻧﮑﻪ ﺑﺎ ﻣﻔﻬﻮم ﻻگ آﺷﻨﺎ ﺷﺪﯾﻢ‪ ،‬ﻧﻮﺑﺖ ﺑﻪ آن ﻣﯽرﺳﺪ ﮐﻪ ﺑﻪ اﯾﻦ ﺳﺆال ﻣﻬﻢ ﭘﺎﺳﺦ دﻫﯿﻢ ﮐﻪ ﭼﻪ‬
‫ﭼﯿﺰﻫﺎﯾﯽ را ﺑﺎﯾﺪ ﻻگ ﮐﻨﯿﻢ؟ ﻻگﮐﺮدن ﺑﺎﯾﺪ ﺷﺎﻣﻞ ﻣﻮاردي ﺑﺎﺷﺪ ﮐﻪ ﺑﻌﺪاً ﺗﻮﺳﻂ اﻓﺮادي ﮐﻪ ﻗﺮار اﺳﺖ آن‬
‫را ﺑﺮرﺳﯽ ﮐﻨﻨﺪ ﻣﻔﯿﺪ ﺑﺎﺷﺪ‪ .‬ﺷﺎﯾﺪ اﯾﻦ ﺟﻤﻠﻪ ﺑﺴﯿﺎر ﺑﺪﯾﻬﯽ ﺑﺎﺷﺪ اﻣﺎ در ﻋﻤﻞ ﻣﻤﮑﻦ اﺳﺖ در ﺑﺮﺧﯽ ﻣﻮاﻗﻊ‬
‫ﻣﻮﺟﺐ ﺳﺮدرﮔﻤﯽ اﻓﺮاد ﺷﻮد‪ .‬ﻣﺜﻼً ﺑﺮاي ﻻگﮐﺮدن ﯾﮏ ﺗﺎﺑﻊ ﻣﻘﺪار ورودي و ﺧﺮوﺟﯽ ﻣﯽﺗﻮاﻧﺪ ﻣﻔﯿﺪ ﺑﺎﺷﺪ‪،‬‬
‫زﯾﺮا در آﯾﻨﺪه ﻣﯽﺗﻮاﻧﯿﻢ در ﺗﺴﺖﻫﺎ از آﻧﻬﺎ اﺳﺘﻔﺎده ﮐﻨﯿﻢ و ﺷﺮاﯾﻂ ﺗﺎﺑﻊ را ﺑﺎ وروديﻫﺎي ﻣﺨﺘﻠﻒ ﺑﺴﻨﺠﯿﻢ‪.‬‬
‫در ﻣﻮرد ﻣﻮارد ﺣﻘﻮﻗﯽ ﺑﺎﯾﺪ ﺑﺎ ﻣﺪﯾﺮ ﺧﻮد ﻣﺸﻮرت ﮐﻨﯿﻢ و ﻧﮑﺎت ﻗﺎﻧﻮﻧﯽ را رﻋﺎﯾﺖ و ﺿﺒﻂ ﮐﻨﯿﻢ‪.‬‬

‫در ﺑﺮﻧﺎﻣﻪ ﺑﻬﺘﺮ اﺳﺖ ﺗﻤﺎم ﺧﻄﺎﻫﺎ را ﺿﺒﻂ ﮐﻨﯿﻢ‪ ،‬زﯾﺮا ﺧﻄﺎﯾﯽ ﮐﻪ رخ داده ﻣﻤﮑﻦ اﺳﺖ ﺑﻌﺪاً ﻣﺸﮑﻞﺳﺎز‬
‫ﺷﻮد و ﺑﻬﺘﺮ اﺳﺖ ﺳﺮﯾﻌﺎً رﻓﻊ ﺷﻮد‪ .‬داﻧﺴﺘﻦ ﻣﻨﺒﻊ ﺧﻄﺎ‪ ،‬زﻣﺎن اﺗﻔﺎق‪ ،‬ﺣﺎﻟﺖﻫﺎي ﺑﺮﻧﺎﻣﻪ‪ ،‬ﺗﺎﺑﻊ رﺧﺪاد‪ ،‬ﺧﻂ‬
‫‪Ahwaz_Hackerz‬‬

‫ﻣﺒﺎﺣﺚ ﭘﯿﺸﺮﻓﺘﻪ در ﭘﺎﯾﺘﻮن ‪ /‬ﻣﻮﺳﺴﻪ ﻓﺮﻫﻨﮕﯽ ﻫﻨﺮي دﯾﺒﺎﮔﺮان ﺗﻬﺮان‬ ‫‪100‬‬

‫رﺧﺪاد و ﭘﯿﺎم ﺧﻄﺎي آن ﻣﯽﺗﻮاﻧﺪ ﺑﺴﯿﺎر ﻣﻔﯿﺪ ﺑﺎﺷﺪ‪ ،‬ﭘﺲ ﺑﺴﺘﻪ ﺑﻪ ﻧﯿﺎز ﺧﻮد ﻣﯽﺗﻮاﻧﯿﻢ اﯾﻦ اﻣﮑﺎﻧﺎت را در‬
‫ﻻگﻫﺎي ﺧﻮد ﻟﺤﺎظ ﮐﻨﯿﻢ‪.‬‬

‫ﯾﮑﯽ دﯾﮕﺮ از ﻣﻮاردي ﮐﻪ ﺧﻮب اﺳﺖ آنﻫﺎ را ﻻگ ﮐﻨﯿﻢ‪ ،‬ارﺗﺒﺎط ﺑﺎ اﺑﺰارﻫﺎي ﺛﺎﻟﺚ اﺳﺖ‪ .‬ﺑﺮاي ﻣﺜﺎل ﺷﻤﺎ از‬
‫ﯾﮏ ‪ API‬ﺗﺸﺨﯿﺺ دﺳﺖﺧﻂ در ﺑﺮﻧﺎﻣﻪ ﺧﻮد اﺳﺘﻔﺎده ﻣﯽﮐﻨﯿﺪ و ﺑﺮﻧﺎﻣﻪ ﺷﻤﺎ ﻣﺪام ﺑﺎ اﯾﻦ ‪ API‬ارﺗﺒﺎط دارد‪،‬‬
‫ﺑﻨﺎﺑﺮاﯾﻦ ﺿﺒﻂ ﻻگﻫﺎي ارﺗﺒﺎط و ﻧﺘﺎﯾﺞ ارﺗﺒﺎﻃﺎت ﺑﺎ اﯾﻦ ‪ API‬ﻣﯽﺗﻮاﻧﺪ ﺑﺴﯿﺎر ﻣﻔﯿﺪ ﺑﺎﺷﺪ‪.‬‬

‫از دﯾﺪ ﺑﺎﻻﺗﺮ اﯾﻦ ‪ 5‬ﻣﻮرد را ﻣﯽﺗﻮان در ﻻگﮐﺮدن ﻣﻮرد ﺗﻮﺟﻪ ﻗﺮار داد‪:‬‬

‫‪ -1‬ﺗﻐﯿﯿﺮات‪ :‬ﻫﺮ ﺗﻐﯿﯿﺮي در دادهﻫﺎي ﺑﺮﻧﺎﻣﻪ اﻧﺠﺎم ﻣﯽﺷﻮد را ﺛﺒﺖ ﮐﻨﯿﻢ‪.‬‬

‫‪ -2‬دﺳﺘﺮﺳﯽ‪ :‬ﻫﺮﮔﻮﻧﻪ دﺳﺘﺮﺳﯽ و اﺣﺮازﻫﻮﯾﺖ ﮐﺎرﺑﺮان را ﺿﺒﻂ ﮐﻨﯿﻢ‪.‬‬

‫‪ -3‬دﺳﺘﺮسﭘﺬﯾﺮي‪ :‬ﺗﻤﺎم اﻃﻼﻋﺎت ﺳﺮور را ﺿﺒﻂ ﮐﻨﯿﻢ‪ .‬اﯾﻨﮑﻪ در ﭼﻪ زﻣﺎﻧﯽ ﺧﺎﻣﻮش ﺑﻮده‪ ،‬ﭼﻪ زﻣﺎﻧﯽ‬
‫دوﺑﺎره ﮐﺎر ﺧﻮد را ﺷﺮوع ﮐﺮده اﺳﺖ و ﺳﺮوﯾﺲ ﻣﺎ در ﭼﻪ زﻣﺎنﻫﺎﯾﯽ ﭘﺎﺳﺨﮕﻮ ﺑﻮده اﺳﺖ‪.‬‬

‫‪ -4‬ﺗﻬﺪﯾﺪﻫﺎ‪ :‬ﺗﻤﺎم ﺗﻬﺪﯾﺪات از ﺟﻤﻠﻪ ﺗﻬﺪﯾﺪات اﻣﻨﯿﺘﯽ و دادهﻫﺎي ورودي اﺷﺘﺒﺎه را ﻻگ ﮐﻨﯿﻢ‪.‬‬

‫‪ -5‬ﻣﻨﺎﺑﻊ‪ :‬اﻃﻼﻋﺎت ﻣﻨﺎﺑﻊ‪ ،‬ﻣﯿﺰان اﺳﺘﻔﺎده از ﻣﻨﺎﺑﻊ و ارﺗﺒﺎﻃﺎت را ﻻگ ﮐﻨﯿﻢ‪.‬‬

‫ﭼﻪ ﭼﯿﺰي را ﻻگ ﻧﮑﻨﯿﻢ؟‬


‫در ﺑﺮﺧﯽ ﻣﻮارد از ﻻگﮐﺮدن ﻣﻮاردي از ﺟﻤﻠﻪ اﻃﻼﻋﺎت ﺷﺨﺼﯽ و ﻣﺤﺮﻣﺎﻧﻪ ﮐﺎرﺑﺮان ﮐﻪ ﺑﻪ آن ‪ PII1‬ﯾﺎ‬
‫اﻃﻼﻋﺎت ﺷﻨﺎﺳﺎﯾﯽ ﺷﺨﺼﯽ ﻣﯽﮔﻮﯾﻨﺪ‪ ،‬ﭘﺮﻫﯿﺰ ﮐﻨﯿﻢ‪ .‬اﻃﻼﻋﺎﺗﯽ ﻫﻤﭽﻮن‪:‬‬

‫‪ ‬ﻧﺎم ﮐﺎرﺑﺮي و رﻣﺰ ﻋﺒﻮر‬


‫‪ ‬ﺷﻤﺎره ﺷﻨﺎﺳﻨﺎﻣﻪ و ﮐﺪ ﻣﻠﯽ‬
‫‪ ‬ﺗﺎرﯾﺦ و ﻣﺤﻞ ﺗﻮﻟﺪ‬
‫‪ ‬اﯾﻤﯿﻞ و آدرس ﺷﺒﮑﻪﻫﺎي اﺟﺘﻤﺎﻋﯽ‬
‫‪ ‬اﻃﻼﻋﺎت ﻓﯿﺰﯾﮑﯽ ﻣﺜﻞ آدرس‪ ،‬ﮐﺪ ﭘﺴﺘﯽ و ﻣﺤﻞ ﮐﺎر‬
‫‪ ‬اﻃﻼﻋﺎت ﺑﯿﻮﻣﺘﺮﯾﮏ ﻣﺜﻞ ﺳﻦ و وزن‬

‫ﺗﻤﺎم ﻣﻮارد ﮔﻔﺘﻪﺷﺪه ﻧﺒﺎﯾﺪ ﻻگ ﺷﻮد وﻟﯽ اﯾﻦ ﺑﺪان ﻣﻌﻨﺎ ﻧﯿﺴﺖ ﮐﻪ اﮔﺮ ﮐﺎرﺑﺮي وارد ﺳﯿﺴﺘﻢ ﺷﺪ ورود‬
‫اﯾﺸﺎن را ﺿﺒﻂ ﻧﮑﻨﯿﻢ ﺑﻠﮑﻪ ﺑﺎﯾﺪ ﺑﺎ ﻣﻮاردي و ﯾﺎ ﺑﺎ ﯾﮏ آﯾﺪي ﺧﺎص آن را ﺿﺒﻂ ﮐﻨﯿﻢ ﮐﻪ اﮔﺮ ﻓﺮد ﻏﯿﺮي‬
‫اﯾﻦ اﻃﻼﻋﺎت را ﻣﻄﺎﻟﻌﻪ ﮐﺮد ﻧﺘﻮاﻧﺪ ﮐﺎرﺑﺮ اﺻﻠﯽ را ﭘﯿﺪا ﮐﻨﺪ‪.‬‬

‫‪1‬‬
‫‪Personally Identification Information‬‬
‫‪Ahwaz_Hackerz‬‬

‫‪101‬‬ ‫ﺑﺨﺶ ﭼﻬﺎرم ‪ /‬ﻓﺼﻞ ‪ / 1‬ﻣﻘﺪﻣﻪ‬

‫ﻧﮑﺘﮥ دﯾﮕﺮ اﯾﻦ اﺳﺖ ﮐﻪ ﻻگﻫﺎ درواﻗﻊ اﺷﮑﺎلزدا ﻧﯿﺴﺘﻨﺪ و ﺗﻤﺎم اﺗﻔﺎقﻫﺎي رخ داده ﺷﺪه در اﯾﺠﺎد آن‬
‫ﺧﻄﺎ را ﺑﻬﺘﺮ اﺳﺖ ﮐﻪ ذﺧﯿﺮه ﻧﮑﻨﻨﺪ و ﻓﺎﯾﻞ ﻻگ را ﺑﯿﺨﻮد ﺷﻠﻮغ ﻧﮑﻨﻨﺪ‪ .‬ﻫﻤﯿﻦﮐﻪ در ﭼﻪ زﻣﺎﻧﯽ و ﺑﻪ ﭼﻪ‬
‫ﺧﺎﻃﺮ اﯾﻦ ﺧﻄﺎ رخ داده اﺳﺖ‪ ،‬ﮐﺎﻓﯽ اﺳﺖ‪.‬‬

‫ﻣﻮرد دﯾﮕﺮي ﮐﻪ ﺑﺎﯾﺪ ﺑﻪ آن ﺗﻮﺟﻪ ﮐﺮد اﯾﻦ اﺳﺖ ﮐﻪ ﻧﺒﺎﯾﺪ ﻫﻤﻪﭼﯿﺰ را ﻻگ ﮐﺮد‪ .‬اﮔﺮ ﺑﺨﻮاﻫﯿﺪ ﻫﻤﻪﭼﯿﺰ را‬
‫ﻻگ ﮐﻨﯿﺪ ﺑﻪ اﻣﯿﺪ آﻧﮑﻪ ﺷﺎﯾﺪ روزي ﺑﻪ ﮐﺎرﺗﺎن ﺧﻮاﻫﺪ آﻣﺪ‪ ،‬ﮐﺎر اﺷﺘﺒﺎﻫﯽ اﺳﺖ زﯾﺮا ﻓﺎﯾﻞ ﻻگ ﻣﻘﺪاري از‬
‫ﻻگﻫﺎ را ﻧﮕﻪ ﻣﯽدارد‪ .‬ﺑﯿﺎﯾﯿﺪ ﻣﺜﺎﻟﯽ را ﺑﺮرﺳﯽ ﮐﻨﯿﻢ ﮐﻪ ﺷﻤﺎ ﺳﺎﯾﺘﯽ دارﯾﺪ ﮐﻪ ﮐﺎرﺑﺮ ﻫﺮ ﺗﻌﺎﻣﻠﯽ در آن‬
‫ﻣﯽﮐﻨﺪ را در ﻓﺎﯾﻞ ﻻﮔﯽ ذﺧﯿﺮه ﻣﯽﮐﻨﯿﺪ‪ .‬ﻣﺜﻼً ﮐﺴﯽ ﮐﻪ ﯾﮏ ﺻﻔﺤﻪ را رﻓﺮش ﻣﯽﮐﻨﺪ ﺷﻤﺎ در ﻓﺎﯾﻞ ﺧﻮد‬
‫ﻣﯽﻧﻮﯾﺴﯿﺪ‪:‬‬

‫‪User XXX Refreshed Main Page At 2020.01.01 10:10 From Iran‬‬

‫ﺷﻤﺎ ﺗﻨﻈﯿﻤﺎت را ﺟﻮري اﻧﺠﺎم دادهاﯾﺪ ﮐﻪ اﯾﻦ ﻓﺎﯾﻞ ﺗﺎ ‪ 100‬ﻣﮕﺎﺑﺎﯾﺖ داده را ﻧﮕﻬﺪاري ﮐﻨﺪ ﺣﺎل ﻓﺮض‬
‫ﮐﻨﯿﺪ ﺷﺨﺼﯽ ﻣﺪاوم اﯾﻦ ﺻﻔﺤﻪ را رﻓﺮش ﮐﻨﺪ و ﺷﻤﺎ در ﻓﺎﯾﻞ ﺧﻮد ﻓﻘﻂ ﻻگﻫﺎي اﯾﻦ ﻓﺮد را دارﯾﺪ و‬
‫ﻧﻤﯽﺗﻮاﻧﯿﺪ دﯾﮕﺮ ﺑﻪ ﻻگﻫﺎي ﺧﻄﺎﻫﺎي ﺧﻮد دﺳﺘﺮﺳﯽ داﺷﺘﻪ ﺑﺎﺷﯿﺪ‪ .‬اﯾﻦ ﺳﻨﺎرﯾﻮ ﯾﮑﯽ از ﺣﻤﻼﺗﯽ اﺳﺖ ﮐﻪ‬
‫ﻣﻤﮑﻦ اﺳﺖ ﺑﻪ ﺑﺮﻧﺎﻣﻪ ﺷﻤﺎ وارد ﺷﻮد‪.‬‬

‫ﻧﮑﺘﮥ ﻣﻬﻢ دﯾﮕﺮ آن اﺳﺖ ﮐﻪ ﺗﻤﺎم اﻃﻼﻋﺎت را در ﯾﮏ ﻓﺎﯾﻞ ﻻگ ﻧﮑﻨﯿﺪ‪ ،‬اﻃﻼﻋﺎت را ﺑﺎ ﺗﻔﮑﯿﮏ ﻧﻮع در‬
‫ﻓﺎﯾﻞﻫﺎي ﺟﺪا ذﺧﯿﺮه ﮐﻨﯿﺪ‪ .‬ﺑﺮاي ﻣﺜﺎل اﻃﻼﻋﺎت ﻋﻤﻠﮑﺮدي ﮐﺎرﺑﺮ ﻣﺜﻞ ﮐﻠﯿﮏﻫﺎ را ﺑﺎ ﻓﺎﯾﻞ ﺧﻄﺎﻫﺎي‬
‫ﺳﯿﺴﺘﻢ ﺟﺪا ﮐﻨﯿﺪ‪ .‬ﺑﻪ اﯾﻦ ﺻﻮرت ﻣﺸﮑﻞ ﻗﺒﻠﯽ را ﺗﺎ ﻗﺴﻤﺘﯽ ﺣﻞ ﻣﯽﮐﻨﯿﺪ و ﺧﻮاﻧﺪن ﻻگﻫﺎ ﺑﺮاي ﺷﻤﺎ‬
‫راﺣﺖﺗﺮ ﻣﯽﺷﻮد‪.‬‬
‫‪Ahwaz_Hackerz‬‬

‫ﻣﺒﺎﺣﺚ ﭘﯿﺸﺮﻓﺘﻪ در ﭘﺎﯾﺘﻮن ‪ /‬ﻣﻮﺳﺴﻪ ﻓﺮﻫﻨﮕﯽ ﻫﻨﺮي دﯾﺒﺎﮔﺮان ﺗﻬﺮان‬ ‫‪102‬‬

‫ﻓﺼﻞ‪2‬‬
‫ﻣﺎژول ‪Logging‬‬
‫‪Ahwaz_Hackerz‬‬

‫‪103‬‬ ‫ﺑﺨﺶ ﭼﻬﺎرم ‪ /‬ﻓﺼﻞ ‪ / 2‬ﻣﺎژول ‪Logging‬‬

‫ﻣﻌﺮﻓﯽ‬
‫ﻣﺎژول ‪ logging‬ﯾﮏ اﺑﺰار ﻗﺪرﺗﻤﻨﺪ ﺑﺮاي ﺿﺒﻂ ﻻگﻫﺎ در ﭘﺎﯾﺘﻮن اﺳﺖ ﮐﻪ ﻧﯿﺎزﻫﺎي ﯾﮏ ﺑﺮﻧﺎﻣﻪﻧﻮﯾﺲ‬
‫ﻣﺒﺘﺪي ﺗﺎ ﯾﮏ ﺑﺮﻧﺎﻣﻪ ﺑﺰرگ ﺷﺮﮐﺘﯽ را ﺑﺮآورده ﻣﯽﮐﻨﺪ‪ .‬اﺿﺎﻓﻪﮐﺮدن اﯾﻦ ﻣﺎژول ﺑﺴﯿﺎر ﺳﺎده اﺳﺖ‪.‬‬

‫‪import logging‬‬

‫ﺑﻪﻃﻮر ﭘﯿﺶﻓﺮض ‪ 5‬ﺳﻄﺢ ﻣﺘﻔﺎوت ﺑﺎ درﺟﻪ اﻫﻤﯿﺖ ﻣﺨﺘﻠﻒ در ﭘﺎﯾﺘﻮن وﺟﻮد دارد ﮐﻪ ﻫﺮﮐﺪام ﺷﻤﺎرهاي از‬
‫ﺟﻬﺖ اﻫﻤﯿﺖ دارﻧﺪ‪ .‬در ﺟﺪول ‪ 5‬ﺳﻄﺢ اﻫﻤﯿﺖ ﻻگﻫﺎ ﺑﻪ ﺗﺮﺗﯿﺐ از ﺑﺎﻻ ﺑﻪ ﭘﺎﯾﯿﻦ ﻧﻮﺷﺘﻪ ﺷﺪه اﺳﺖ‪.‬‬

‫ﺟﺪول ‪ - 5‬ﺳﻄﺢ اﻫﻤﯿﺖ ﻻگﻫﺎ‬

‫ﻧﺎم ﺳﻄﺢ‬ ‫ﺷﻤﺎره‬ ‫ﺗﻮﺿﯿﺤﺎت‬


‫‪CRITICAL‬‬ ‫‪50‬‬ ‫ﺑﺎﻻﺗﺮﯾﻦ درﺟﻪ‪ ،‬ﺧﻄﺎﻫﺎﯾﯽ ﮐﻪ ﺑﺮﻧﺎﻣﻪ ﻧﻤﯽﺗﻮاﻧﺪ دﯾﮕﺮ ﺑﻪ ﺣﯿﺎت ﺧﻮد اداﻣﻪ دﻫﺪ‪.‬‬
‫‪ERROR‬‬ ‫‪40‬‬ ‫ﺧﻄﺎﻫﺎﯾﯽ ﮐﻪ ﺑﺮﻧﺎﻣﻪ ﻧﻤﯽﺗﻮاﻧﺪ ﻣﺪﯾﺮﯾﺖ ﮐﻨﺪ و رﻓﺘﺎر ﻋﺎدي ﺧﻮد را داﺷﺘﻪ ﺑﺎﺷﺪ‪.‬‬
‫‪WARNING‬‬ ‫‪30‬‬ ‫رﺧﺪادﻫﺎي ﻏﯿﺮﻣﻨﺘﻈﺮه ﮐﻪ ﻧﯿﺎزﻣﻨﺪ دﻗﺖ ﺑﯿﺸﺘﺮي اﺳﺖ‪.‬‬
‫‪INFO‬‬ ‫‪20‬‬ ‫اﻃﻼﻋﺎت ﮐﻠﯽ ﺑﻪﻃﻮري ﮐﻪ ﺑﺮﻧﺎﻣﻪ رﻓﺘﺎر درﺳﺖ ﺧﻮد را دارد‪.‬‬
‫‪DEBUG‬‬ ‫‪10‬‬ ‫اﻃﻼﻋﺎت ﺟﺰﺋﯽ و دﻗﯿﻖ ﺑﺮاي ﻋﯿﺐﯾﺎﺑﯽ‪.‬‬
‫‪NOTSET‬‬ ‫‪0‬‬ ‫ﻫﯿﭻ ﻻﮔﯽ ﺛﺒﺖ ﻧﻤﯽﺷﻮد و درواﻗﻊ ﻻگ ﺧﺎﻣﻮش اﺳﺖ‪.‬‬

‫ﻻگﻫﺎ ﺑﺮاﺳﺎس ﺳﻄﺤﯽ ﮐﻪ ﺗﻨﻈﯿﻢ ﺷﺪه ﺑﺎﺷﺪ ﻧﻤﺎﯾﺶ داده ﻣﯽﺷﻮﻧﺪ‪ ،‬ﺑﺮاي ﻣﺜﺎل اﮔﺮ ﺳﻄﺢ ‪ INFO‬را‬
‫اﻧﺘﺨﺎب ﮐﻨﯿﻢ‪ ،‬از ﻫﻤﺎن ﺳﻄﺢ و ﺳﻄﺢﻫﺎي ﺑﺎﻻﺗﺮ ﯾﻌﻨﯽ ‪ ERROR ،WARNING‬و ‪ CRITICAL‬ﻻگ‬
‫ﻣﯽﺷﻮد‪.‬‬

‫اﯾﻦ ﻣﺎژول ﯾﮏ ﻻﮔﺮ‪ 1‬در اﺧﺘﯿﺎر ﻣﺎ ﻗﺮار ﻣﯽدﻫﺪ ﮐﻪ ﻣﯽﺗﻮاﻧﯿﻢ ﺑﺎ )(‪ getLogger‬آن را درﯾﺎﻓﺖ ﮐﻨﯿﻢ‪.‬‬
‫اﯾﻦ ﺗﺎﺑﻊ ﻓﻘﻂ ﯾﮏ ﻧﻤﻮﻧﻪ از ﻻﮔﺮ را ﺑﺎز ﻣﯽﮔﺮداﻧﺪ؛ ﺑﻨﺎﺑﺮاﯾﻦ ﻧﯿﺎزي ﻧﯿﺴﺖ آن را ﺑﯿﻦ ﺗﻮاﺑﻊ و ﻣﺎژولﻫﺎ ﭘﺎس‬
‫دﻫﯿﻢ و ﮐﺎﻓﯿﺴﺖ ﻫﺮﮐﺠﺎ ﺑﻪ آن ﻧﯿﺎز داﺷﺘﯿﻢ ﺗﻨﻬﺎ ﺗﻮﺳﻂ )(‪ getLogger‬آن را درﯾﺎﻓﺖ ﮐﻨﯿﻢ‪.‬‬

‫ﺑﺎ ﻻﮔﺮ ﺗﻤﺎم ﺳﻄﻮح ﺑﺎﻻ را ﻣﯽﺗﻮاﻧﯿﻢ ﻻگ ﮐﻨﯿﻢ‪ .‬ﺗﻤﺎم ﺗﻮاﺑﻊ ﺑﺮاي ﻻگ ﺑﺎ ﻫﻤﺎن ﻧﺎﻣﯽ ﮐﻪ در ﺟﺪول ‪5‬‬
‫ﻧﻮﺷﺘﻪ ﺷﺪه‪ ،‬ﻗﺎﺑﻞ اﺳﺘﻔﺎده اﺳﺖ )ﺑﻪﺟﺰ ‪ .(NOTSET‬ﻧﺎم اﯾﻦ ﺗﺎﺑﻊ ‪ PEP8‬را ﻧﻘﺾ ﻣﯽﮐﻨﺪ وﻟﯽ اﯾﻦ ﻧﺎم از‬
‫ﻣﺎژول ‪ log4j‬در زﺑﺎن ﺟﺎوا ﮔﺮﻓﺘﻪ ﺷﺪه و ﻣﯽداﻧﯿﻢ ﻗﻮاﻧﯿﻦ ﻧﺎمﮔﺬاري در ﺟﺎوا ﺑﻪﺻﻮرت ‪CamelCase‬‬
‫اﺳﺖ‪.‬‬

‫‪1‬‬
‫‪logger‬‬
‫‪Ahwaz_Hackerz‬‬

‫ﻣﺒﺎﺣﺚ ﭘﯿﺸﺮﻓﺘﻪ در ﭘﺎﯾﺘﻮن ‪ /‬ﻣﻮﺳﺴﻪ ﻓﺮﻫﻨﮕﯽ ﻫﻨﺮي دﯾﺒﺎﮔﺮان ﺗﻬﺮان‬ ‫‪104‬‬

‫‪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‬‬

‫‪>> WARNING:root:This is warning‬‬


‫‪ERROR:root:This is error‬‬
‫‪CRITICAL:root:This is critical‬‬

‫از ﭘﯿﺎمﻫﺎي ﺑﺎﻻ ﺗﻨﻬﺎ ‪ 3‬ﻻگ ﭼﺎپ ﺷﺪ‪ ،‬زﯾﺮا ﺑﻪﺻﻮرت ﭘﯿﺶﻓﺮض ﺳﻄﺢ ﻻﮔﺮ ‪ WARNING‬ﺗﻨﻈﯿﻢ ﺷﺪه‬
‫اﺳﺖ‪ .‬ﻓﺮﻣﺖ ﭘﯿﺶﻓﺮض ﻧﯿﺰ ﺑﻪ اﯾﻦ ﺻﻮرت اﺳﺖ ﮐﻪ اﺑﺘﺪا ﺳﻄﺢ ﻻگ ﺳﭙﺲ ‪) root‬ﻧﺎﻣﯿﺴﺖ ﮐﻪ ﺑﻪ‬
‫ﺻﻮرت ﭘﯿﺶﻓﺮض ﺑﻪ ﻻﮔﺮ ﻣﺎ داده ﻣﯽﺷﻮد( و درﻧﻬﺎﯾﺖ ﭘﯿﺎم ﻻگ ﭼﺎپ ﻣﯽﺷﻮد‪.‬‬

‫ﺑﺎ اﺳﺘﻔﺎده از )‪ basicConfig(**kwargs‬ﻣﯽﺗﻮاﻧﯿﻢ ﺗﻨﻈﯿﻤﺎت ﻻﮔﺮ را ﺗﻐﯿﯿﺮ دﻫﯿﻢ‪ .‬ﭘﺎراﻣﺘﺮﻫﺎي‬


‫ﮐﺎرﺑﺮدي اﯾﻦ ﺗﺎﺑﻊ ﻋﺒﺎرﺗﻨﺪ از‪:‬‬

‫‪ :level‬ﺳﻄﺢ ﻻﮔﺮ را ﻣﺸﺨﺺ ﻣﯽﮐﻨﺪ‪.‬‬


‫‪ :filename‬ﻓﺎﯾﻠﯽ را ﻣﺸﺨﺺ ﻣﯽﮐﻨﺪ ﺗﺎ ﻻگﻫﺎ در آن ﻗﺮار ﺑﮕﯿﺮﻧﺪ‪.‬‬
‫‪ :filemode‬اﮔﺮ ‪ filename‬ﻣﺸﺨﺺ ﺷﻮد ﺣﺎﻟﺖ ﺑﺎزﮐﺮدن ﻓﺎﯾﻞ را ﺗﻨﻈﯿﻢ ﻣﯽﮐﻨﺪ‪.‬‬

‫‪ :a‬ﺑﺮاي آﻧﮑﻪ از اداﻣﻪ ﻓﺎﯾﻞ ﺷﺮوع ﺑﻪ ﻧﻮﺷﺘﻦ ﺻﻮرت ﮔﯿﺮد‪.‬‬


‫‪ :w‬ﺑﺮاي آﻧﮑﻪ ﻓﺎﯾﻞ ﺟﺪﯾﺪ ﺳﺎﺧﺘﻪ ﺷﻮد و از اﺑﺘﺪاي اﺟﺮا ﺷﺮوع ﺑﻪ ﻻگﮔﺮﻓﺘﻦ ﮐﻨﺪ‪.‬‬

‫‪ :format‬ﻓﺮﻣﺖ ﻧﻮﺷﺘﻦ ﻻگ را ﻣﺸﺨﺺ ﻣﯽﮐﻨﺪ ﮐﻪ درﻣﻮرد آن ﺑﻌﺪاً ﺻﺤﺒﺖ ﺧﻮاﻫﯿﻢ ﮐﺮد‪.‬‬

‫‪ :datefmt‬ﻓﺮﻣﺖ ﺗﺎرﯾﺦ را ﻣﺸﺨﺺ ﻣﯽﮐﻨﺪ‪) .‬درﻣﻮرد ﻓﺮﻣﺖ ﺗﺎرﯾﺦ ﺑﻪ ﺟﺪول ‪ 2‬ﻣﺮاﺟﻌﻪ ﮐﻨﯿﺪ‪(.‬‬

‫‪import logging‬‬

‫‪logging.basicConfig(filename='test.log', filemode='w',‬‬
‫)‪level=logging.INFO‬‬

‫)(‪logger = logging.getLogger‬‬

‫)'‪logger.info('This is INFO log‬‬


‫‪Ahwaz_Hackerz‬‬

‫‪105‬‬ ‫ﺑﺨﺶ ﭼﻬﺎرم ‪ /‬ﻓﺼﻞ ‪ / 2‬ﻣﺎژول ‪Logging‬‬

‫در اﯾﻦ ﻣﺜﺎل ﻓﺎﯾﻠﯽ ﺑﺎ ﻧﺎم ‪ test.log‬ﺳﺎﺧﺘﻪ ﻣﯽﺷﻮد ﮐﻪ ﺳﻄﺢ ﻻگ آن ‪ INFO‬اﺳﺖ‪ ،‬ﯾﻌﻨﯽ ﺧﻮد آن و‬
‫ﺳﻄﻮح ﺑﺎﻻﺗﺮ آن ﻻگ ﻣﯽﺷﻮﻧﺪ‪ .‬ﻓﺎﯾﻞ را ﺑﺎ ﻣﻮد ‪ w‬ﺑﺎزﮐﺮدهاﯾﻢ ﯾﻌﻨﯽ ﻫﺮﮔﺎه ﺑﺮﻧﺎﻣﻪ از اول ﺷﺮوع ﺷﻮد ﻓﺎﯾﻞ‬
‫ﻻگ ﻗﺒﻠﯽ ﭘﺎك ﻣﯽﺷﻮد‪.‬‬

‫ﻓﺮﻣﺖ ﺧﺮوﺟﯽ ﻻگ‬


‫در ﻣﺜﺎل ﻗﺒﻞ دﯾﺪﯾﻢ ﮐﻪ ﻻﮔﯽ ﮐﻪ در ﻓﺎﯾﻞ ذﺧﯿﺮه ﻣﯽﺷﻮد ﺗﻨﻬﺎ ﺣﺎوي ﺳﻄﺢ ﻻگ‪ ،‬ﻧﺎم و ﭘﯿﺎم آن ﺑﻮد اﻣﺎ‬
‫اﻃﻼﻋﺎت ﺑﺴﯿﺎر زﯾﺎدي را ﻣﯽﺗﻮاﻧﯿﻢ در ﻻگ اﺿﺎﻓﻪ ﮐﻨﯿﻢ‪ .‬در ﺟﺪول ‪ 6‬ﻓﺮﻣﺖﻫﺎي ﻗﺎﺑﻞ اﺳﺘﻔﺎده در رﺷﺘﮥ‬
‫ﺧﺮوﺟﯽ را ﻣﯽﺗﻮاﻧﯿﺪ ﻣﺸﺎﻫﺪه ﮐﻨﯿﺪ‪ .‬ﺑﺎ ﻗﺮاردادن اﯾﻦ ﻓﺮﻣﺖﻫﺎ در رﺷﺘﮥ ﻓﺮﻣﺖ‪ ،‬ﻣﺘﻦ ﻻگ ﻋﻮض ﻣﯽﺷﻮد‪.‬‬

‫ﺟﺪول ‪ - 6‬ﻓﺮﻣﺖ ﺧﺮوﺟﯽ ﻻگ‬

‫ﺗﻮﺿﯿﺤﺎت‬ ‫ﻓﺮﻣﺖ‬ ‫ﻧﺎم‬


‫‪ %(asctime)s‬ﺗﺎرﯾﺦ و زﻣﺎﻧﯽ ﮐﻪ ﻻگ ﺳﺎﺧﺘﻪ ﺷﻮد را ﭼﺎپ ﻣﯽﮐﻨﺪ‪.‬‬ ‫‪asctime‬‬
‫‪ %(filename)s‬ﻧﺎم ﻓﺎﯾﻠﯽ ﮐﻪ ﻻگ در آن رخ داده را ﭼﺎپ ﻣﯽﮐﻨﺪ‪.‬‬ ‫‪filename‬‬
‫‪ %(funcName)s‬ﻧﺎم ﺗﺎﺑﻌﯽ ﮐﻪ ﻻگ در آن اﺳﺖ را ﭼﺎپ ﻣﯽﮐﻨﺪ‪.‬‬ ‫‪funcName‬‬
‫‪ %(levelname)s‬ﺳﻄﺢ ﻻگ را ﭼﺎپ ﻣﯽﮐﻨﺪ‪.‬‬ ‫‪levelname‬‬
‫ﺷﻤﺎرة ﺧﻄﯽ ﮐﻪ ﻻگ ﻓﺮاﺧﻮاﻧﯽ ﺷﺪه را ﭼﺎپ‬
‫‪%(lineno)d‬‬ ‫‪lineno‬‬
‫ﻣﯽﮐﻨﺪ‪.‬‬
‫‪ %(message)s‬ﭘﯿﺎم ﻻگ را ﭼﺎپ ﻣﯽﮐﻨﺪ‪.‬‬ ‫‪message‬‬
‫‪ %(module)s‬ﻧﺎم ﻣﺎژول را ﭼﺎپ ﻣﯽﮐﻨﺪ‪.‬‬ ‫‪module‬‬
‫‪ %(name)s‬ﻧﺎم ﻻﮔﺮ را ﭼﺎپ ﻣﯽﮐﻨﺪ‪.‬‬ ‫‪name‬‬
‫آدرس ﮐﺎﻣﻞ ﻓﺎﯾﻠﯽ ﮐﻪ ﻻگ در آن ﻓﺮاﺧﻮاﻧﯽ ﺷﺪه را‬
‫‪%(pathname)s‬‬ ‫‪pathname‬‬
‫ﭼﺎپ ﻣﯽﮐﻨﺪ‪.‬‬
‫‪ Process ID %(process)d‬را در ﺻﻮرت وﺟﻮد ﭼﺎپ ﻣﯽﮐﻨﺪ‪.‬‬ ‫‪process‬‬
‫‪ %(processName)s processName‬ﻧﺎم ‪ Process‬را در ﺻﻮرت وﺟﻮد ﭼﺎپ ﻣﯽﮐﻨﺪ‪.‬‬
‫‪ Thread ID %(thread)d‬را در ﺻﻮرت وﺟﻮد ﭼﺎپ ﻣﯽﮐﻨﺪ‪.‬‬ ‫‪thread‬‬
‫‪ %(threadName)s‬ﻧﺎم ‪ Thread‬را در ﺻﻮرت وﺟﻮد ﭼﺎپ ﻣﯽﮐﻨﺪ‪.‬‬ ‫‪threadName‬‬
Ahwaz_Hackerz

‫ ﻣﻮﺳﺴﻪ ﻓﺮﻫﻨﮕﯽ ﻫﻨﺮي دﯾﺒﺎﮔﺮان ﺗﻬﺮان‬/ ‫ﻣﺒﺎﺣﺚ ﭘﯿﺸﺮﻓﺘﻪ در ﭘﺎﯾﺘﻮن‬ 106

‫ ﺑﺪﯾﻦﺻﻮرت ﺑﺎﯾﺪ‬.‫ ﺷﻤﺎره ﺧﻂ و ﭘﯿﺎم ﻻگ را در ﻓﺎﯾﻠﯽ ﭼﺎپ ﮐﻨﯿﻢ‬،‫ ﻧﺎم ﻓﺎﯾﻞ‬،‫ ﺳﻄﺢ ﻻگ‬،‫ﻣﯽﺧﻮاﻫﯿﻢ ﺗﺎرﯾﺦ‬
.‫ﺗﻨﻈﯿﻤﺎت را اﻧﺠﺎم دﻫﯿﻢ‬

import logging

log_format = ("%(asctime)s - %(levelname)s – "


"%(filename)s:%(lineno)d - %(message)s")

logging.basicConfig(filename='test.log', format=log_format)
logger = logging.getLogger()

logger.warning('You should update this module')

‫ﺑﺎ اﯾﻦ روش ﻓﺮﻣﺖ ﻻگﻫﺎي ﺧﺮوﺟﯽ را ﺗﻐﯿﯿﺮ ﻣﯽدﻫﯿﻢ اﻣﺎ ﺑﺮاي ﺗﻐﯿﯿﺮ ﻓﺮﻣﺖ ﺗﺎرﯾﺦ ﺑﺎﯾﺪ از ﭘﺎراﻣﺘﺮ‬
.‫ اﺳﺘﻔﺎده ﮐﻨﯿﻢ‬datefmt

import logging

log_format = ("%(asctime)s - %(levelname)s – "


"%(filename)s:%(lineno)d - %(message)s")

date_format = "day:%d%H:%M:%S"

logging.basicConfig(filename='test.log', format=log_format,
datefmt=date_format)

logger = logging.getLogger()

logger.warning('You should update this module')


Ahwaz_Hackerz

107 Logging ‫ ﻣﺎژول‬/ 2 ‫ ﻓﺼﻞ‬/ ‫ﺑﺨﺶ ﭼﻬﺎرم‬

‫ از اﺳﺘﺜﻨﺎ‬Stack Traces ‫ﮔﺰارش‬


‫ ﺑﺪﯾﻦﻣﻨﻈﻮر ﻧﯿﺎز اﺳﺖ ﻣﻘﺪار‬،‫ آن در ﻻگﻫﺎ ﺑﯿﺎورﯾﻢ‬Stack Trace ‫ﻣﯽﺗﻮاﻧﯿﻢ اﻃﻼﻋﺎت ﺧﻄﺎﻫﺎ را ﺑﻪ ﻫﻤﺮاه‬
.‫ ﻗﺮار دﻫﯿﻢ‬True ‫ را ﺑﺮاﺑﺮ‬exc_info

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

‫ﻣﺒﺎﺣﺚ ﭘﯿﺸﺮﻓﺘﻪ در ﭘﺎﯾﺘﻮن ‪ /‬ﻣﻮﺳﺴﻪ ﻓﺮﻫﻨﮕﯽ ﻫﻨﺮي دﯾﺒﺎﮔﺮان ﺗﻬﺮان‬ ‫‪108‬‬

‫ﻧﺎم ﻻﮔﺮ‬
‫در ﻗﺴﻤﺖ ﻗﺒﻞ دﯾﺪﯾﻢ ﮐﻪ ﻻﮔﺮ ﮐﻼﺳﯽ اﺳﺖ ﮐﻪ ﻣﺎ از ﺷﯽﻫﺎي آن اﺳﺘﻔﺎده ﻣﯽﮐﻨﯿﻢ و ﻣﺘﺪﻫﺎي ﻧﻤﺎﯾﺶ‬
‫ﻻگ را ﻓﺮاﺧﻮاﻧﯽ ﻣﯽﮐﻨﯿﻢ‪ .‬ﻫﻤﺎنﻃﻮريﮐﻪ ﻗﺒﻼً ﻫﻢ ﮔﻔﺘﻪ ﺷﺪ ﺗﺎﺑﻊ )(‪ getLogger‬ﯾﮏ ﺷﯽ از اﯾﻦ‬
‫ﮐﻼس را ﺑﺮﻣﯽﮔﺮداﻧﺪ و ﺧﻮﺑﯽ آن اﯾﻦ اﺳﺖ ﮐﻪ در ﻣﺎژولﻫﺎي ﻣﺨﺘﻠﻒ ﻣﯽﺗﻮان ﺳﺮﯾﻊ از آن اﺳﺘﻔﺎده ﮐﺮد‪.‬‬
‫اﻣﺎ در ﺑﻌﻀﯽ ﻣﻮاﻗﻊ ﻧﯿﺎز ﺑﻪ ﭼﻨﺪﯾﻦ ﻻﮔﺮ دارﯾﻢ‪ .‬اﯾﻦ ﺗﺎﺑﻊ ﻣﯽﺗﻮاﻧﺪ ﺑﺎ درﯾﺎﻓﺖ ﻧﺎم‪ ،‬ﻻﮔﺮ ﻣﺮﺑﻮط ﺑﻪ آن ﻧﺎم را‬
‫ﺑﺮﮔﺮداﻧﺪ‪.‬‬

‫‪import logging‬‬

‫)'‪logger = logging.getLogger('our_logger‬‬

‫در اﯾﻦ ﻣﺜﺎل ﯾﮏ ﻻﮔﺮ ﺑﺎ ﻧﺎم ‪ our_logger‬ﺳﺎﺧﺘﯿﻢ و اﮔﺮ در ﻓﺎﯾﻞﻫﺎ و ﻣﺎژولﻫﺎي دﯾﮕﺮ ﺑﻪ اﯾﻦ ﻻﮔﺮ‬
‫ﻧﯿﺎز داﺷﺘﯿﻢ ﮐﺎﻓﯿﺴﺖ ﺑﺎ ﻫﻤﯿﻦ ﻧﺎم آن را درﯾﺎﻓﺖ ﮐﻨﯿﻢ‪ .‬اﮔﺮ ﺑﺮاي ﻻﮔﺮ ﻧﺎم اﻧﺘﺨﺎب ﻧﺸﻮد‪ ،‬ﻻﮔﺮي ﺑﺎ ﻧﺎم‬
‫ﭘﯿﺶﻓﺮض '‪ 'root‬ﺳﺎﺧﺘﻪ ﻣﯽﺷﻮد‪.‬‬

‫ﭘﯿﺸﻨﻬﺎد ﻣﯽﺷﻮد ﮐﻪ در ﺳﺎﺧﺖ ﻻﮔﺮﻫﺎ از ﻧﺎم ﻫﻤﺎن ﻣﺎژوﻟﯽ ﮐﻪ ﻻﮔﺮ در آن ﻗﺮار دارد اﺳﺘﻔﺎده ﺷﻮد‪،‬‬
‫ﺑﻨﺎﺑﺮاﯾﻦ ﻣﯽﺗﻮان از ﻣﺘﻐﯿﺮ ‪ __name__1‬اﺳﺘﻔﺎده ﮐﺮد؛ زﯾﺮا در اﯾﻦ ﺻﻮرت ﻣﯽﺗﻮاﻧﯿﻢ درﯾﺎﺑﯿﻢ ﮐﻪ ﻻگﻫﺎ‬
‫از ﮐﺪام ﻣﺎژول ﮔﺰارش ﻣﯽﺷﻮﻧﺪ‪.‬‬

‫اﺳﺘﻔﺎده از ‪Handler‬‬
‫زﻣﺎﻧﯽﮐﻪ ﻧﯿﺎز ﺑﻪ اﺳﺘﻔﺎده از ﭼﻨﺪﯾﻦ ﻻﮔﺮ دارﯾﻢ و ﯾﺎ ﻣﯽﺧﻮاﻫﯿﻢ ﻻﮔﺮ ﺧﻮدﻣﺎن را ﺗﻨﻈﯿﻢ ﮐﻨﯿﻢ‪ ،‬ﭘﺎي‬
‫‪ Handler‬ﯾﺎ ﮐﻨﺘﺮلﮐﻨﻨﺪه ﺑﻪ ﻣﯿﺎن ﺑﺎز ﻣﯽﺷﻮد‪ .‬ﯾﮏ ﻻﮔﺮ ﻣﯽﺗﻮاﻧﺪ ﭼﻨﺪﯾﻦ ﮐﻨﺘﺮلﮐﻨﻨﺪه داﺷﺘﻪ ﺑﺎﺷﺪ‪ ،‬در‬
‫اداﻣﻪ ﺑﺎ ﭼﻨﺪﯾﻦ ﮐﻨﺘﺮلﮐﻨﻨﺪه آﺷﻨﺎ ﻣﯽﺷﻮﯾﻢ‪:‬‬

‫‪‬‬ ‫‪logging.StreamHandler‬‬

‫ﻻگﻫﺎ را ﺑﻪ ﺧﺮوﺟﯽﻫﺎﯾﯽ ﻫﻤﭽﻮن ‪ stdout‬و ‪ stderr‬ﻣﯽﻓﺮﺳﺘﺪ‪.‬‬

‫‪‬‬ ‫‪logging.FileHandler‬‬

‫ﻻگﻫﺎ را در ﯾﮏ ﻓﺎﯾﻞ ذﺧﯿﺮه ﻣﯽﮐﻨﺪ‪.‬‬

‫‪‬‬ ‫‪logging.handlers.RotatingFileHandler‬‬

‫__‪ 1 __name‬ﯾﮏ ﻣﺘﻐﯿﺮ ﺧﻮدﺳﺎﺧﺘﻪ وﯾﮋه اﺳﺖ ﮐﻪ ﻧﺎم ﻣﺎژول ﺣﺎل ﺣﺎﺿﺮ را در ﺧﻮد ﺣﻔﻆ ﻣﯽﮐﻨﺪ‪.‬‬
‫‪Ahwaz_Hackerz‬‬

‫‪109‬‬ ‫ﺑﺨﺶ ﭼﻬﺎرم ‪ /‬ﻓﺼﻞ ‪ / 2‬ﻣﺎژول ‪Logging‬‬

‫ﻻگﻫﺎ را در ﻓﺎﯾﻞ ذﺧﯿﺮه ﻣﯽﮐﻨﺪ‪ ،‬ﺑﺎ اﯾﻦ ﺗﻔﺎوت ﮐﻪ اﮔﺮ ﺣﺠﻢ ﻓﺎﯾﻞ از ﯾﮏ ﻣﻘﺪاري ﻓﺮاﺗﺮ رود‪ ،‬ﻻگﻫﺎي‬
‫ﺟﺪﯾﺪ را ﺑﺮ روي ﻻگﻫﺎي ﻗﺪﯾﻢ ﻣﯽﻧﻮﯾﺴﺪ‪.‬‬

‫‪‬‬ ‫‪logging.handlers.TimeRotatingFileHandler‬‬

‫ﻻگﻫﺎ را در ﻓﺎﯾﻞ ذﺧﯿﺮه ﻣﯽﮐﻨﺪ‪ ،‬ﺑﺎ اﯾﻦ ﺗﻔﺎوت ﮐﻪ در زﻣﺎن ﻣﻘﺮر ﻓﺎﯾﻞ ﻻگ را ﺗﻌﻮﯾﺾ ﻣﯽﮐﻨﺪ‪.‬‬

‫‪‬‬ ‫‪logging.handlers.SocketHandler‬‬

‫ﺑﺮاي ارﺳﺎل ﻻگ ﺑﻪ ﯾﮏ ﺳﻮﮐﺖ ‪ TCP/IP‬اﺳﺘﻔﺎده ﻣﯽﺷﻮد‪.‬‬

‫‪‬‬ ‫‪logging.handlers.SMTPHandler‬‬

‫ﺑﺮاي ارﺳﺎل ﻻگﻫﺎ ﺑﻪ اﯾﻤﯿﻞ اﺳﺘﻔﺎده ﻣﯽﺷﻮد‪.‬‬

‫‪‬‬ ‫‪logging.handlers.HTTPHandler‬‬

‫ﺑﺮاي ارﺳﺎل ﻻگ ﺑﻪ ﯾﮏ وبﺳﺮوﯾﺲ اﺳﺘﻔﺎده ﻣﯽﺷﻮد‪.‬‬

‫‪‬‬ ‫‪logging.handlers.SysLogHandler‬‬

‫ﺑﺮاي ارﺳﺎل ﻻگ ﺑﻪ ﺑﺮﻧﺎﻣﻪ ‪ syslog‬در ﺳﯿﺴﺘﻢﻫﺎي ﺑﺮ ﭘﺎﯾﮥ ﯾﻮﻧﯿﮑﺲ ﻣﯽﺑﺎﺷﺪ‪.‬‬

‫‪‬‬ ‫‪logging.handlers.NTEventLogHandler‬‬

‫ﺑﺮاي ارﺳﺎل ﻻگ ﺑﻪ ‪ event log‬در وﯾﻨﺪوز ﻣﯽﺑﺎﺷﺪ‪.‬‬

‫‪‬‬ ‫‪logging.NullHandler‬‬

‫در ﻋﻤﻞ ﻫﯿﭻﮐﺎري ﻧﻤﯽﮐﻨﺪ و ﺑﯿﺸﺘﺮ ﺗﻮﺳﻂ ﺗﻮﺳﻌﻪدﻫﻨﺪﮔﺎن دﯾﮕﺮ در ﮐﺘﺎﺑﺨﺎﻧﻪﻫﺎﯾﺸﺎن اﺳﺘﻔﺎده ﻣﯽﺷﻮد ﺗﺎ‬
‫از ﻻﮔﺮ اﺳﺘﻔﺎده ﮐﻨﻨﺪ و ﮐﺴﺎﻧﯽ ﮐﻪ ﻣﯽﺧﻮاﻫﻨﺪ ﻻگ ﻧﻤﺎﯾﺶ دﻫﻨﺪ از ﻻﮔﺮ ﺧﻮد ﺑﻪﺟﺎي آن اﺳﺘﻔﺎده ﮐﻨﻨﺪ‪.‬‬

‫ﻣﯽﺧﻮاﻫﯿﻢ در ﯾﮏ ﺗﮑﻪ ﮐﺪ از دو ﮐﻨﺘﺮلﮐﻨﻨﺪه ‪ RotatingFileHandler‬و ‪StreamHandler‬‬


‫اﺳﺘﻔﺎده ﮐﻨﯿﻢ‪ .‬ﮐﺎﻓﯿﺴﺖ از ﻫﺮﮐﺪام ﯾﮏ ﺷﯽ ﺳﺎﺧﺘﻪ و ﺑﻪ ﻻﮔﺮ اﺿﺎﻓﻪ ﮐﻨﯿﻢ‪ .‬ﻣﺎﻧﻨﺪ ﻻﮔﺮ ﻣﯽﺗﻮان ﺑﺮاي ﻫﺮ‬
‫ﮐﻨﺘﺮلﮐﻨﻨﺪه ﻓﺮﻣﺖ و ﺳﻄﺢ ﻻگ ﻣﺨﺼﻮص ﺑﻪ ﺧﻮدش را ﻣﺸﺨﺺ ﮐﺮد‪.‬‬

‫‪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‬‬

‫ﻣﺒﺎﺣﺚ ﭘﯿﺸﺮﻓﺘﻪ در ﭘﺎﯾﺘﻮن ‪ /‬ﻣﻮﺳﺴﻪ ﻓﺮﻫﻨﮕﯽ ﻫﻨﺮي دﯾﺒﺎﮔﺮان ﺗﻬﺮان‬ ‫‪110‬‬

‫' ‪f_format = logging.Formatter('%(asctime)s%(name)s‬‬


‫)'‪'%(levelname)s%(message)s‬‬
‫)'‪s_format = logging.Formatter('%(asctime)s %(message)s‬‬

‫)‪f_handler.setFormatter(f_format‬‬
‫)‪s_handler.setFormatter(s_format‬‬

‫)‪logger.addHandler(f_handler‬‬
‫)‪logger.addHandler(s_handler‬‬

‫‪while True:‬‬
‫)"‪logger.warning("Warning Log‬‬

‫در اﯾــــﻦ ﻣﺜــــﺎل دو ﮐﻨﺘــــﺮلﮐﻨﻨــــﺪه ﺑــــﺎ ﻧــــﺎمﻫــــﺎي ‪ f_handler‬و ‪ s_handler‬از ﻧــــﻮع‬


‫‪ RotatingFileHandler‬و ‪ StreamHandler‬ﺳــﺎﺧﺘﯿﻢ‪ .‬ﻣﻘــﺪار ‪ maxBytes‬را ﺑﺮاﺑــﺮ ﺑــﺎ‬
‫‪ 5‬ﮐﯿﻠﻮﺑﺎﯾﺖ و ﻣﻘﺪار ‪ backupCount‬را ﺑﺮاﺑﺮ ‪ 4‬ﻗﺮار دادﯾﻢ‪ .‬در اﯾﻦ ﺻﻮرت زﻣﺎﻧﯽﮐﻪ ﻓﺎﯾﻞ ‪test.log‬‬
‫ﺑﻪ ‪ 5‬ﮐﯿﻠﻮﺑﺎﯾﺖ ﺑﺮﺳﺪ‪ ،‬ﻓﺎﯾﻞ ﺟﺪﯾﺪي ﺑـﺎ ﭘﺴـﻮﻧﺪ ‪ .1‬ﻣـﯽﺳـﺎزد و اﮔـﺮ دوﺑـﺎره ﻓﺎﯾـﻞ ‪ test.log‬ﺑـﻪ ‪5‬‬
‫ﮐﯿﻠﻮﺑﺎﯾﺖ ﺑﺮﺳﺪ‪ ،‬ﻻگ را ﺑﻪ ﻓﺎﯾﻞ ‪ test.log.1‬ﻣﻨﺘﻘﻞ ﮐﺮده و ﻓﺎﯾﻞﻫﺎي ﻗﺒﻠﯽ ﯾﮑﯽ ﺑﻪ ﺟﻠﻮ ﺷﯿﻔﺖ ﭘﯿﺪا‬
‫ﻣﯽﮐﻨﻨﺪ‪ .‬ﯾﻌﻨـﯽ ﻓﺎﯾـﻞ ‪ test.log.1‬ﻗﺒﻠـﯽ ﺑـﻪ ‪ test.log.2‬ﺗﻐﯿﯿـﺮ ﻧـﺎم ﭘﯿـﺪا ﻣـﯽﮐﻨـﺪ و ﻓﺎﯾـﻞ‬
‫‪ test.log.4‬ﮐﻪ آﺧﺮﯾﻦ ﻓﺎﯾﻞ ‪ backup‬اﺳﺖ ﺣﺬف ﻣﯽﺷﻮد‪.‬‬

‫در اداﻣﻪ ﺳﻄﺢ ﻻگ ﻫﺮﮐﺪام را ﻣﺸﺨﺺ ﻣﯽﮐﻨﯿﻢ و ﺑﺎ اﺳﺘﻔﺎده از ‪ logging.Formatter‬ﻓﺮﻣﺖ‬


‫ﻣﺸﺨﺼﯽ را ﺑﺮاي ﻫﺮ ﮐﻨﺘﺮلﮐﻨﻨﺪه ﻣﺸﺨﺺ ﻣﯽﮐﻨﯿﻢ و درﻧﻬﺎﯾﺖ ﺑﺎ اﺳﺘﻔﺎده از‪.setFormatter‬‬
‫ﻓﺮﻣﺖﻫﺎ را در ﮐﻨﺘﺮلﮐﻨﻨﺪه ﻗﺮار ﻣﯽدﻫﯿﻢ‪ .‬اﮐﻨﻮن ﻧﻮﺑﺖ ﺑﻪ آن رﺳﯿﺪه اﺳﺖ ﮐﻪ ﮐﻨﺘﺮلﮐﻨﻨﺪهﻫﺎ را ﺑﻪ ﻻﮔﺮ‬
‫ﺑﺎ اﺳﺘﻔﺎده از ‪ .addHandler‬اﺿﺎﻓﻪ ﮐﻨﯿﻢ‪.‬‬

‫ﻧﺘﯿﺠﻪ اﯾﻦ ﮐﺪ ﭘﺲ از ﻣﺪﺗﯽ ‪ 5‬ﻓﺎﯾﻞ اﺳﺖ و در ﺧﺮوﺟﯽ ﭼﯿﺰي ﭼﺎپ ﻧﻤﯽﺷﻮد‪ ،‬زﯾﺮا ﮐﻨﺘﺮلﮐﻨﻨﺪه‬
‫‪ Stream‬ﺳﻄﺢ ﻻگ ‪ logging.ERROR‬را دارد‪ ،‬ﺑﻨﺎﺑﺮاﯾﻦ ﻻگ ﺑﺎ ﺳﻄﺢ ‪ Warning‬را ﭼﺎپ‬
‫ﻧﻤﯽﮐﻨﺪ‪.‬‬
Ahwaz_Hackerz

111 Logging ‫ ﻣﺎژول‬/ 2 ‫ ﻓﺼﻞ‬/ ‫ﺑﺨﺶ ﭼﻬﺎرم‬

Filter ‫اﺳﺘﻔﺎده از‬


‫ ﺑﺎ ﻓﯿﻠﺘﺮ ﻣﯽﺗﻮان ﻻگﻫﺎﯾﯽ ﮐﻪ ﺗﻮﻟﯿﺪ ﻣﯽﺷﻮد را ﻣﺤﺪود‬.‫ﺑﻪ ﮐﻨﺘﺮلﮐﻨﻨﺪهﻫﺎ ﻣﯽﺗﻮاﻧﯿﻢ ﻓﯿﻠﺘﺮ اﺿﺎﻓﻪ ﮐﻨﯿﻢ‬
‫ را‬filter() ‫ ﺑﻪ ارث ﺑﺮد و ﺳﭙﺲ ﺗﺎﺑﻊ‬logging.Filter ‫ ﺑﺮاي اﯾﻦ ﮐﺎر ﺑﺎﯾﺪ ﮐﻼﺳﯽ از‬.‫ﮐﺮد‬
.‫ﭘﯿﺎدهﺳﺎزي ﮐﺮد‬

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())

logger.warning("Warning Log First")


logger.debug("Debug Log Second")

>> WARNING __main__ Warning Log First

‫ ﺗﺎﺑﻊ ﻓﯿﻠﺘﺮي ﮐﻪ ﭘﯿﺎدهﺳﺎزي ﻣﯽﺷﻮد ﺑﺎﯾﺪ‬.‫ اﺿﺎﻓﻪ ﮐﺮدﯾﻢ‬.addFilter ‫در اﯾﻦ ﻣﺜﺎل ﯾﮏ ﻓﯿﻠﺘﺮ ﺑﺎ ﻣﺘﺪ‬
‫ ﺑﺮﮔﺮداﻧﯿﺪ‬True ‫ ﺷﺮاﯾﻂ ﻣﻮرد ﻧﻈﺮ را داﺷﺖ ﻣﻘﺪار‬record ‫ اﮔﺮ‬.‫ را ﺑﺮﮔﺮداﻧﺪ‬False ‫ ﯾﺎ‬True ‫ﻣﻘﺪار‬
record ‫ ﻻزم ﺑﻪ ذﮐﺮ اﺳﺖ ﮐﻪ‬.‫ﺗﺎ ﻻگ ﻧﻤﺎﯾﺶ داده ﺷﻮد در ﻏﯿﺮاﯾﻦﺻﻮرت ﻻگ ﻧﺎدﯾﺪه ﮔﺮﻓﺘﻪ ﻣﯽﺷﻮد‬
‫ ﺗﻤﺎم اﻃﻼﻋﺎت وﻗﻮع ﯾﮏ ﻻگ را ﻫﻤﭽﻮن‬LogRecord .‫ اﺳﺖ‬LogRecord ‫ از ﻧﻮع‬filter ‫در ﺗﺎﺑﻊ‬
"Second" ‫ در اﯾﻦ ﻣﺜﺎل ﻫﻤﺎنﻃﻮريﮐﻪ دﯾﺪﯾﻢ اﮔﺮ ﮐﻠﻤﻪ‬.‫ را دارد‬...‫ و‬name ،asctime ،levelname
.‫در ﭘﯿﺎم ﻻگ ﺑﺎﺷﺪ ﻧﺎدﯾﺪه ﮔﺮﻓﺘﻪ ﻣﯽﺷﻮد‬
‫‪Ahwaz_Hackerz‬‬

‫ﻣﺒﺎﺣﺚ ﭘﯿﺸﺮﻓﺘﻪ در ﭘﺎﯾﺘﻮن ‪ /‬ﻣﻮﺳﺴﻪ ﻓﺮﻫﻨﮕﯽ ﻫﻨﺮي دﯾﺒﺎﮔﺮان ﺗﻬﺮان‬ ‫‪112‬‬

‫اﺳﺘﻔﺎده از ﻓﺎﯾﻞ ﭘﯿﮑﺮﺑﻨﺪي‬


‫در ﺗﻤﺎم ﻣﺜﺎلﻫﺎي ﻗﺒﻠﯽ ﺑﺎ اﺳﺘﻔﺎده از ﭘﺎﯾﺘﻮن و ﮐﺪﻧﻮﯾﺴﯽ‪ ،‬ﻻگﻫﺎ را ﺗﻨﻈﯿﻢ و اﺳﺘﻔﺎده ﮐﺮدﯾﻢ اﻣﺎ در ﺑﯿﺸﺘﺮ‬
‫ﻣﺤﯿﻂﻫﺎي اﺳﺘﻘﺮار ﺑﺮاي ﺗﻨﻈﯿﻤﺎت ﻻگ و ﻣﺸﺨﺺﮐﺮدن ﮐﻨﺘﺮلﮐﻨﻨﺪهﻫﺎ از ﻓﺎﯾﻞ ﭘﯿﮑﺮﺑﻨﺪي اﺳﺘﻔﺎده‬
‫ﻣﯽﺷﻮد؛ زﯾﺮا ﺑﺮاي ﺗﻐﯿﯿﺮات در ﻣﺤﯿﻂﻫﺎي ﮔﻮﻧﺎﮔﻮن ﻧﯿﺎزي ﺑﻪ ﺗﻐﯿﯿﺮ ﮐﺪ ﻧﯿﺴﺖ و ﺗﻨﻬﺎ ﻓﺎﯾﻞ ﭘﯿﮑﺮﺑﻨﺪي‬
‫ﺗﻐﯿﯿﺮ ﭘﯿﺪا ﻣﯽﮐﻨﺪ‪ .‬اﯾﻦ ﻓﺎﯾﻞ ﻣﯽﺗﻮاﻧﺪ ﻓﺮﻣﺖﻫﺎي ﻣﺨﺘﻠﻒ ﻫﻤﭽﻮن ‪ .json ،.ini‬و ‪ .yaml‬ﺑﺎﺷﺪ‪.‬‬
‫در اﯾﻨﺠﺎ ﻣﺜﺎﻟﯽ از اﯾﻦ ﻓﺎﯾﻞ ﺑﺎ ﻓﺮﻣﺖ ‪ yaml‬را ﺑﺮرﺳﯽ ﻣﯽﮐﻨﯿﻢ‪.‬‬

‫‪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‬‬

‫‪with open('config.yml', 'r') as f:‬‬


‫))(‪config = yaml.safe_load(f.read‬‬
Ahwaz_Hackerz

113 Logging ‫ ﻣﺎژول‬/ 2 ‫ ﻓﺼﻞ‬/ ‫ﺑﺨﺶ ﭼﻬﺎرم‬

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')

>> s_logger:WARNING Warning Log

‫ ﭘﺎس ﻣﯽدﻫﯿﻢ و ﺗﻨﻈﯿﻤﺎت‬dictConfig ‫ آن را ﺑﻪ ﺗﺎﺑﻊ‬config.yml ‫ﺑﻌﺪ از ﺧﻮاﻧﺪن ﻓﺎﯾﻞ‬


‫ ﻣﯽﺗﻮاﻧﯿﻢ ﻻﮔﺮﻫﺎﯾﯽ ﮐﻪ در ﻓﺎﯾﻞ‬getLogger ‫ ﺑﺎ اﺳﺘﻔﺎده از‬.‫ﻣﻮﺟﻮد در ﻓﺎﯾﻞ ﭘﯿﮑﺮﺑﻨﺪي اﻋﻤﺎل ﻣﯽﺷﻮد‬
.‫ﭘﯿﮑﺮﺑﻨﺪي ﻧﺎمﮔﺬاري ﮐﺮدهاﯾﻢ را در ﮐﺪ درﯾﺎﻓﺖ ﮐﻨﯿﻢ و از آن اﺳﺘﻔﺎده ﮐﻨﯿﻢ‬

:‫ﺑﺮاي اﻃﻼﻋﺎت ﺑﯿﺸﺘﺮ و ﻣﺸﺎﻫﺪه ﻣﺴﺘﻨﺪات ﺑﯿﺸﺘﺮ ﻣﯽﺗﻮاﻧﯿﺪ ﺑﻪ آدرس زﯾﺮ ﻣﺮاﺟﻌﻪ ﮐﻨﯿﺪ‬

https://docs.python.org/3/library/logging.config.html
‫‪Ahwaz_Hackerz‬‬

‫ﻣﺒﺎﺣﺚ ﭘﯿﺸﺮﻓﺘﻪ در ﭘﺎﯾﺘﻮن ‪ /‬ﻣﻮﺳﺴﻪ ﻓﺮﻫﻨﮕﯽ ﻫﻨﺮي دﯾﺒﺎﮔﺮان ﺗﻬﺮان‬ ‫‪114‬‬

‫ﺑﺨﺶ‪5‬‬

‫ﺗﺴﺖ‬
‫‪Ahwaz_Hackerz‬‬

‫‪115‬‬ ‫ﺑﺨﺶ ﭘﻨﺠﻢ ‪ /‬ﻓﺼﻞ ‪ / 1‬ﻣﻘﺪﻣﻪ‬

‫ﻓﺼﻞ‪1‬‬
‫ﻣﻘﺪﻣﻪ‬
‫‪Ahwaz_Hackerz‬‬

‫ﻣﺒﺎﺣﺚ ﭘﯿﺸﺮﻓﺘﻪ در ﭘﺎﯾﺘﻮن ‪ /‬ﻣﻮﺳﺴﻪ ﻓﺮﻫﻨﮕﯽ ﻫﻨﺮي دﯾﺒﺎﮔﺮان ﺗﻬﺮان‬ ‫‪116‬‬

‫ﻣﻘﺪﻣﻪ‬
‫ﺑﻪ ﻓﺮاﯾﻨﺪ آﻧﺎﻟﯿﺰ اﺟﺰاي ﻧﺮماﻓﺰار ﮐﻪ ﻗﺎدر اﺳﺖ ﺗﻔﺎوت ﻣﯿﺎن ﺷﺮاﯾﻂ ﮐﻨﻮﻧﯽ و ﺷﺮاﯾﻂ ﻣﻮرد اﻧﺘﻈﺎر را ﻣﺸﺨﺺ‬
‫ﮐﻨﺪ‪ ،‬ﺗﺴﺖ ﻧﺮماﻓﺰار ﻣﯽﮔﻮﯾﻨﺪ‪ .1‬ﺑﺮاي ﻣﺜﺎل ﻓﺮض ﮐﻨﯿﺪ ﻣﯽﺧﻮاﻫﯿﻢ ﺑﺮﻧﺎﻣﻪاي ﺳﺎده ﻫﻤﭽﻮن ﻣﺎﺷﯿﻦﺣﺴﺎب‬
‫ﺑﻨﻮﯾﺴﯿﻢ‪ .‬اﻧﺘﻈﺎر ﻣﺎ اﯾﻦ اﺳﺖ ﮐﻪ اﯾﻦ ﻣﺎﺷﯿﻦﺣﺴﺎب ﺟﻤﻊ و ﺗﻔﺮﯾﻖ را ﺑﻪدرﺳﺘﯽ اﻧﺠﺎم دﻫﺪ‪ ،‬ﺣﺎل ﺑﺎ ﺗﺴﺖ‬
‫اﯾﻦ ﻧﺮماﻓﺰار درﻣﯽﯾﺎﺑﯿﻢ ﮐﻪ آﯾﺎ ﻧﺮماﻓﺰار ﻣﻮﺟﻮد ﻗﺎﺑﻠﯿﺖﻫﺎي ﻣﻮرد اﻧﺘﻈﺎر ﻣﺎ ﮐﻪ ﺟﻤﻊ و ﺗﻔﺮﯾﻖ ﺑﻮد را‬
‫ﺑﻪدرﺳﺘﯽ اﻧﺠﺎم ﻣﯽدﻫﺪ و ﯾﺎ ﺧﯿﺮ‪.‬‬

‫اﻫﻤﯿﺖ ﺗﺴﺖ‬
‫ﺑﺎ ﺗﺴﺖ ﻧﺮماﻓﺰار ﻣﯽﺗﻮاﻧﯿﻢ ﺑﺎگﻫﺎ و ﻣﺸﮑﻼت ﻧﺮماﻓﺰاري را ﭘﯿﺶ از ﺗﺤﻮﯾﻞ ﻣﺤﺼﻮل ﺷﻨﺎﺳﺎﯾﯽ‪ ،‬ﮐﺸﻒ و‬
‫درﻧﻬﺎﯾﺖ رﻓﻊ ﮐﻨﯿﻢ‪ .‬ﯾﮏ ﻧﺮماﻓﺰار ﺗﺴﺖﺷﺪه ﻗﺎﺑﻠﯿﺖ اﺗﮑﺎ‪ ،‬اﻣﻨﯿﺖ و ﮐﺎراﯾﯽ ﺑﺎﻻ را ﺗﻀﻤﯿﻦ ﻣﯽﮐﻨﺪ ﮐﻪ‬
‫درﻧﺘﯿﺠﻪ رﺿﺎﯾﺖ ﻣﺸﺘﺮي و ﺻﺮﻓﻪﺟﻮﯾﯽ در زﻣﺎن و ﻫﺰﯾﻨﻪ را ﺑﻪ ﻫﻤﺮاه ﺧﻮاﻫﺪ داﺷﺖ‪ .‬ﺗﺴﺖ از اﻫﻤﯿﺖ‬
‫ﺑﺎﻻﯾﯽ ﺑﺮﺧﻮردار اﺳﺖ زﯾﺮا ﻣﯽﺗﻮاﻧﺪ از ﺧﻄﺮات ﻣﺎﻟﯽ و ﺟﺎﻧﯽ ﺟﻠﻮﮔﯿﺮي ﮐﻨﺪ‪ .‬ﺑﺮاي ﻣﺜﺎل ﺷﺮﮐﺖ‬
‫ﻣﺎﺷﯿﻦﺳﺎزي ﻧﯿﺴﺎن ﻣﺠﺒﻮر ﺑﻪ ﺟﻤﻊآوري ﺑﯿﺶ از ‪ 1‬ﻣﯿﻠﯿﻮن ﺧﻮدروي ﺧﻮد از ﺑﺎزار ﺷﺪ ﮐﻪ دﻟﯿﻞ آن ﻧﻘﺺ‬
‫در ﺳﻨﺴﻮر ﮐﯿﺴﮥ ﻫﻮا و ﺷﮑﺴﺖ در ﻧﺮماﻓﺰار ﺑﻮد ﮐﻪ ﭼﻨﺪﯾﻦ ﺗﺼﺎدف را ﻧﯿﺰ ﺑﻪ ﻫﻤﺮاه داﺷﺖ‪.‬‬

‫ﭼﻪ ﭼﯿﺰي را ﺗﺴﺖ ﮐﻨﯿﻢ؟‬


‫ﻫﻤﺎنﻃﻮرﮐﻪ در ﻓﺼﻞ ﻻگ اﯾﻦ ﺳﺆال را ﻣﻄﺮح ﮐﺮدﯾﻢ‪ ،‬اﯾﻨﺠﺎ ﻫﻢ آن را ﻣﻮرد ﺑﺮرﺳﯽ ﻗﺮار ﻣﯽدﻫﯿﻢ‪ .‬در‬
‫ﺣﺎﻟﺖ ﮐﻠﯽ ﺑﻬﺘﺮ اﺳﺖ ﻫﺮ ﺟﺰ ﺗﮑﺮارﺷﻮﻧﺪه از ﻧﺮماﻓﺰار را ﻣﻮرد ﺗﺴﺖ ﻗﺮار دﻫﯿﻢ‪.‬‬

‫‪ ‬ﭘﺎﯾﺪاري ﺳﯿﺴﺘﻢ‬
‫‪ ‬ﮐﺎراﯾﯽ ﺳﯿﺴﺘﻢ‬
‫‪ ‬ﮐﺎرﮐﺮد ﺳﯿﺴﺘﻢ‬
‫‪ ‬ﻗﺎﺑﻠﯿﺖ ﺗﺮﻣﯿﻢ ﺳﯿﺴﺘﻢ ﭘﺲ از ﺷﮑﺴﺖ‬
‫‪ ‬ﯾﮑﭙﺎرﭼﮕﯽ ﺳﯿﺴﺘﻢ‬
‫‪ ‬اﻣﻨﯿﺖ ﺳﯿﺴﺘﻢ‬
‫‪ ‬ﭘﺸﺘﯿﺒﺎنﮔﯿﺮي ﺳﯿﺴﺘﻢ‬

‫ﻣﻮارد ﺑﺎﻻ از ﺟﻤﻠﻪ ﻣﻮاردي اﺳﺖ ﮐﻪ ﺑﺎﯾﺪ در ﺗﺴﺖ آﻧﻬﺎ را درﻧﻈﺮ ﺑﮕﯿﺮﯾﻢ‪.‬‬

‫‪1‬‬
‫‪ANSI/IEEE 1059‬‬
‫‪Ahwaz_Hackerz‬‬

‫‪117‬‬ ‫ﺑﺨﺶ ﭘﻨﺠﻢ ‪ /‬ﻓﺼﻞ ‪ / 1‬ﻣﻘﺪﻣﻪ‬

‫روﯾﮑﺮدﻫﺎي ﻣﺨﺘﻠﻒ ﺗﺴﺖ‬

‫‪ ‬ﺟﻌﺒﮥ ﺳﻔﯿﺪ‬
‫ﺗﺴﺖ ﺟﻌﺒﮥ ﺳﻔﯿﺪ‪ 1‬ﮐﻪ ﺑﻪ آن ﺗﺴﺖ ﺟﻌﺒﻪ ﺷﯿﺸﻪاي‪ ،2‬ﺗﺴﺖ ﺟﻌﺒﻪ ﺷﻔﺎف‪ 3‬و ﯾﺎ ﺗﺴﺖ ﺳﺎﺧﺘﺎري‪ 4‬ﻧﯿﺰ‬
‫ﻣﯽﮔﻮﯾﻨﺪ ﺑﻪ ﺗﺴﺖ در ﺧﺼﻮص ﺳﺎﺧﺘﺎر ﮐﺪﻫﺎي داﺧﻠﯽ ﻧﺮماﻓﺰار ﻣﯽﭘﺮدازد‪ .‬در ﺗﺴﺖ ﺟﻌﺒﻪ ﺳﻔﯿﺪ ﺑﻪ داﺧﻞ‬
‫ﺳﯿﺴﺘﻢ ﻧﮕﺎه ﻣﯽﮐﻨﯿﻢ و ﺳﻌﯽ دارﯾﻢ آن را ﺗﺴﺖ ﮐﻨﯿﻢ‪.‬‬

‫‪ ‬ﺟﻌﺒﮥ ﺳﯿﺎه‬
‫ﺗﺴﺖ ﺟﻌﺒﮥ ﺳﯿﺎه‪ 5‬ﮐﻪ ﺑﻪ آن ﺗﺴﺖ رﻓﺘﺎري‪ 6‬و ﺗﺴﺖ ورودي‪-‬ﺧﺮوﺟﯽ ﻧﯿﺰ ﻣﯽﮔﻮﯾﻨﺪ ﮐﻪ ﺣﺎﻟﺘﯽ اﺳﺖ ﮐﻪ ﻣﺎ‬
‫ﻧﺮماﻓﺰار را از ﺣﯿﺚ ﮐﺎرﮐﺮد ﻣﻮرد ﺗﺴﺖ ﻗﺮار ﻣﯽدﻫﯿﻢ و از ﺳﺎﺧﺘﺎر ﮐﺪ داﺧﻠﯽ اﻃﻼﻋﯽ ﻧﺪارﯾﻢ‪.‬‬

‫ﺳﻄﻮح ﻣﺨﺘﻠﻒ ﺗﺴﺖ‬


‫ﺳﻄﻮح ﻣﺨﺘﻠﻔﯽ از ﺗﺴﺖ وﺟﻮد دارد ﮐﻪ ﺑﻪ ﺑﺮرﺳﯽ ﻣﻬﻢﺗﺮﯾﻦ آﻧﻬﺎ ﻣﯽﭘﺮدازﯾﻢ‪.‬‬

‫‪ ‬ﺗﺴﺖ واﺣﺪ‬
‫واﺣﺪ درواﻗﻊ ﻣﯽﺗﻮاﻧﺪ ﯾﮏ ﺗﺎﺑﻊ ﮐﻮﭼﮏ ﺗﺎ ﯾﮏ زﯾﺮﺳﯿﺴﺘﻢ ﺑﺎﺷﺪ‪ ،‬اﻣﺎ ﻣﻌﻤﻮﻻً ﻣﻨﻈﻮر ﻣﺎ ﯾﮏ ﺟﺰ از ﺳﯿﺴﺘﻢ‬
‫ﻣﯽﺑﺎﺷﺪ ﮐﻪ ﻣﻌﻤﻮﻻً ﯾﮏ ﺷﯽ و ﯾﺎ ﯾﮏ ﮐﻼس اﺳﺖ‪ .‬ﺑﺎ ﻧﮕﺎه ﺑﻪ اﯾﻦ ﺟﺰ ﻣﯽﺗﻮاﻧﯿﻢ ﻣﻮارد آزﻣﻮﻧﯽ ﻣﺸﺨﺺ‬
‫ﮐﻨﯿﻢ ﮐﻪ ﻋﻤﻠﮑﺮد اﯾﻦ واﺣﺪ را ﺗﺴﺖ ﮐﻨﺪ‪ .‬ﻣﻮارد آزﻣﻮن ﺣﺎوي ﺗﻌﺪادي ورودي ﻣﻌﺘﺒﺮ و ﻏﯿﺮﻣﻌﺘﺒﺮ اﺳﺖ ﮐﻪ‬
‫ﻣﺸﺨﺺ ﻣﯽﮐﻨﺪ ﯾﮏ واﺣﺪ ﺑﻪدرﺳﺘﯽ و ﻫﻤﺎنﻃﻮريﮐﻪ اﻧﺘﻈﺎر ﻣﯽرود‪ ،‬ﻋﻤﻞ ﮐﻨﺪ‪ .‬روﯾﮑﺮد ﻣﺎ در ﺗﺴﺖ‬
‫واﺣﺪ‪ 7‬ﺗﺴﺖ ﺟﻌﺒﮥ ﺳﻔﯿﺪ اﺳﺖ‪ .‬ﺗﺴﺖ واﺣﺪ ﻣﻌﻤﻮﻻً ﻗﺒﻞ از ارﺳﺎل ﮐﺪﻫﺎ ﺑﻪ ﻣﺨﺰن ﮐﺪ اﺟﺮا ﻣﯽﺷﻮد‪ .‬ﯾﮑﯽ‬
‫از اﻫﺪاف ﺗﺴﺖ واﺣﺪ‪ ،‬ﮔﺮﻓﺘﻦ ﭘﻮﺷﺶ ‪ 100‬درﺻﺪي ﮐﺪ اﺳﺖ ﯾﻌﻨﯽ ﭘﺲ از اﺟﺮاي ﻣﻮارد آزﻣﻮن ﺗﻤﺎم‬
‫ﻗﺴﻤﺖﻫﺎي ﮐﺪ اﺟﺮا ﺷﻮد‪ .‬ﺑﺮاي روﺷﻦﺗﺮﺷﺪن ﻣﺒﺤﺚ ﺑﻪ ﮐﺪ ﺳﺎده زﯾﺮ ﺗﻮﺟﻪ ﮐﻨﯿﺪ‪.‬‬

‫‪if x > 10:‬‬


‫‪x -= 10‬‬
‫‪else:‬‬
‫‪x += 10‬‬

‫‪1‬‬
‫‪White box‬‬
‫‪2‬‬
‫‪Glass box‬‬
‫‪3‬‬
‫‪Clear box‬‬
‫‪4‬‬
‫‪Structural‬‬
‫‪5‬‬
‫‪Black box‬‬
‫‪6‬‬
‫‪Behavioral‬‬
‫‪7‬‬
‫‪Unit test‬‬
‫‪Ahwaz_Hackerz‬‬

‫ﻣﺒﺎﺣﺚ ﭘﯿﺸﺮﻓﺘﻪ در ﭘﺎﯾﺘﻮن ‪ /‬ﻣﻮﺳﺴﻪ ﻓﺮﻫﻨﮕﯽ ﻫﻨﺮي دﯾﺒﺎﮔﺮان ﺗﻬﺮان‬ ‫‪118‬‬

‫در ﮐﺪ ﺑﺎﻻ ﺑﺮاي اﯾﻨﮑﻪ ﭘﻮﺷﺶ ﺻﺪدرﺻﺪي ﻓﺮاﻫﻢ ﺷﻮد ﺑﺎﯾﺪ ﯾﮏ ﺑﺎر ﻣﺘﻐﯿﺮ ‪ x‬را ﮐﻤﺘﺮ از ‪ 10‬و ﯾﮏ ﺑﺎر‬
‫ﺑﯿﺸﺘﺮ از ‪ 10‬اﺧﺘﯿﺎر ﮐﻨﯿﻢ ﺗﺎ ﺗﻤﺎم ﻗﺴﻤﺖﻫﺎي ﮐﺪ ﺣﺪاﻗﻞ ﯾﮏﺑﺎر اﺟﺮا ﺷﻮﻧﺪ‪.‬‬

‫‪ ‬ﺗﺴﺖ ﯾﮑﭙﺎرﭼﻪﺳﺎزي‬
‫ﺗﺴﺖ ﯾﮑﭙﺎرﭼﻪﺳﺎزي‪ 1‬ﺑﻌﺪ از ﺗﺴﺖ واﺣﺪ اﻧﺠﺎم ﻣﯽﺷﻮد و زﻣﺎﻧﯽ اﺳﺖ ﮐﻪ ﻣﯽﺧﻮاﻫﯿﻢ ﭼﻨﺪﯾﻦ واﺣﺪ و ﯾﺎ‬
‫ﻣﺎژول را در ﮐﻨﺎر ﻫﻢ ﺗﺴﺖ ﮐﻨﯿﻢ ﺗﺎ از ﻫﻤﺎﻫﻨﮕﯽ ﺑﯿﻦ آﻧﻬﺎ ﻣﻄﻤﺌﻦ ﺷﻮﯾﻢ‪ .‬درواﻗﻊ‪ ،‬ﺗﺴﺖ ﯾﮑﭙﺎرﭼﻪﺳﺎزي‬
‫ﺑﺮاي آن اﺳﺖ ﮐﻪ ﺗﻌﺎﻣﻞ ﺑﯿﻦ واﺣﺪﻫﺎي ﻣﺨﺘﻠﻒ را در ﯾﮏ ﺳﯿﺴﺘﻢ ﺗﺴﺖ ﮐﺮده و از ﺑﺮآوردهﺷﺪن‬
‫ﻧﯿﺎزﻣﻨﺪيﻫﺎي ﺳﯿﺴﺘﻢ اﻃﻤﯿﻨﺎن ﺣﺎﺻﻞ ﮐﻨﯿﻢ‪.‬‬

‫‪ ‬ﺗﺴﺖ ﺳﯿﺴﺘﻢ‬
‫ﻫﺪف ﺗﺴﺖ ﺳﯿﺴﺘﻢ‪ 2‬اﻋﺘﺒﺎر ﺳﻨﺠﯽ ﺑﯿﻦ ﺗﻤﺎم ﻣﺎژولﻫﺎ‪ ،‬واﺣﺪﻫﺎ‪ ،‬دادهﻫﺎ‪ ،‬ﭘﯿﮑﺮﺑﻨﺪيﻫﺎ ﻣﯽﺑﺎﺷﺪ ﮐﻪ‬
‫ﻧﯿﺎزﻣﻨﺪيﻫﺎي ﮐﻞ ﺳﯿﺴﺘﻢ را ﺑﺮآورده ﮐﻨﺪ‪ .‬اﯾﻦ ﺗﺴﺖ درواﻗﻊ ﯾﮏ ﺗﺴﺖ ﺟﻌﺒﮥ ﺳﯿﺎه اﺳﺖ و اﯾﻦ اﻃﻤﯿﻨﺎن‬
‫را ﻣﯽدﻫﺪ ﮐﻪ ﻧﺮماﻓﺰار ﺑﺮ روي ﺳﯿﺴﺘﻢﻫﺎي ﻣﻘﺼﺪ ﺑﻪدرﺳﺘﯽ و ﻫﻤﺎنﮔﻮﻧﻪ ﮐﻪ اﻧﺘﻈﺎر ﻣﯽرود ﮐﺎر ﮐﻨﺪ‪.‬‬

‫‪ ‬ﺗﺴﺖ ﭘﺬﯾﺮش‬
‫ﺗﺴﺖ ﭘﺬﯾﺮش‪ 3‬ﺑﻪ ﻧﯿﺎزﻣﻨﺪيﻫﺎ و ﻓﺮاﯾﻨﺪﻫﺎي ﮐﺴﺐوﮐﺎري ﻣﺮﺑﻮط اﺳﺖ و ﻣﺸﺨﺺ ﻣﯽﮐﻨﺪ ﮐﻪ آﯾﺎ ﺳﯿﺴﺘﻢ‬
‫ﻣﯽﺗﻮاﻧﺪ ﻣﻌﯿﺎرﻫﺎي ﭘﺬﯾﺮش ﺗﻮﺳﻂ ﮐﺎرﺑﺮ ﻧﻬﺎﯾﯽ‪ ،‬ﻣﺸﺘﺮﯾﺎن و ذﯾﻨﻔﻌﺎن را ﺑﺮآورده ﮐﻨﺪ ﯾﺎ ﺧﯿﺮ‪ .‬ﺗﺴﺖﻫﺎﯾﯽ از‬
‫ﻗﺒﯿﻞ ﺗﺴﺖ آﻟﻔﺎ‪ ،‬ﺗﺴﺖ ﺑﺘﺎ و ﺗﺴﺖ ‪ A/B‬از ﺟﻤﻠﻪ ﺗﺴﺖﻫﺎي ﭘﺬﯾﺮش ﻫﺴﺘﻨﺪ‪.‬‬

‫‪1‬‬
‫‪Integration test‬‬
‫‪2‬‬
‫‪System test‬‬
‫‪3‬‬
‫‪Acceptance test‬‬
Ahwaz_Hackerz

119 PyTest ‫ ﻣﺎژول‬/ 2 ‫ ﻓﺼﻞ‬/ ‫ﺑﺨﺶ ﭘﻨﺠﻢ‬

2‫ﻓﺼﻞ‬
PyTest ‫ﻣﺎژول‬
‫‪Ahwaz_Hackerz‬‬

‫ﻣﺒﺎﺣﺚ ﭘﯿﺸﺮﻓﺘﻪ در ﭘﺎﯾﺘﻮن ‪ /‬ﻣﻮﺳﺴﻪ ﻓﺮﻫﻨﮕﯽ ﻫﻨﺮي دﯾﺒﺎﮔﺮان ﺗﻬﺮان‬ ‫‪120‬‬

‫ﻣﻌﺮﻓﯽ‬
‫‪ PyTest‬ﯾﮑﯽ از ﮐﺘﺎﺑﺨﺎﻧﻪﻫﺎي ﻣﻌﺮوف و ﻣﺤﺒﻮب ﭘﺎﯾﺘﻮن اﺳﺖ ﮐﻪ ﻣﯽﺗﻮان ﺳﻄﻮح ﻣﺨﺘﻠﻒ ﺗﺴﺖ را ﺑﺎ آن‬
‫اﻧﺠﺎم داد‪ .‬از اﯾﻦ ﮐﺘﺎﺑﺨﺎﻧﻪ ﺷﺮﮐﺖﻫﺎي ﺑﺰرﮔﯽ ﻫﻤﭽﻮن ‪ Dropbox ،Spotify‬و ‪ Mozilla‬ﺑﻪ ﺟﻬﺖ ﺗﺴﺖ‬
‫اﺳﺘﻔﺎده ﻣﯽﮐﻨﻨﺪ‪ .‬اﯾﻦ ﮐﺘﺎﺑﺨﺎﻧﻪ ﺑﻪراﺣﺘﯽ ﺑﺎ ‪ IDE‬ﻫﺎي ﻣﺤﺒﻮب ﻫﻤﭽﻮن ‪ PyCharm‬ﺑﻪﺧﻮﺑﯽ ﻫﻤﺎﻫﻨﮓ‬
‫ﻣﯽﺷﻮد و ﺑﺎ اﻧﻌﻄﺎفﭘﺬﯾﺮي ﺑﺎﻻ وﯾﮋﮔﯽﻫﺎي ﺑﺴﯿﺎري را ﺑﺮاي ﺗﻮﺳﻌﻪدﻫﻨﺪﮔﺎن ﺑﻪ ارﻣﻐﺎن ﻣﯽآورد‪ .‬ﭘﺎﯾﺘﻮن‬
‫ﻫﻤﺮاه ﺑﺎ ﯾﮏ ﻣﺎژول ﺗﺴﺖ ﺑﺎ ﻧﺎم ‪ unittest‬اراﺋﻪ ﻣﯽﺷﻮد ﮐﻪ ﺗﻨﻬﺎ ﺑﺮاي ﺗﺴﺖ واﺣﺪ ﻣﯽﺗﻮان از آن اﺳﺘﻔﺎده‬
‫ﮐﺮد‪.‬‬

‫ﺑﺮاي اﺳﺘﻔﺎده از ‪ PyTest‬اﺑﺘﺪا ﺑﺎﯾﺪ آن را ﻧﺼﺐ ﮐﺮد‪ .‬ﺑﺎ اﺳﺘﻔﺎده از ﻣﺪﯾﺮﯾﺖ ﮐﻨﻨﺪة ‪ pip‬ﻫﻤﺎﻧﻨﺪ دﺳﺘﻮر زﯾﺮ‬
‫ﻣﯽﺗﻮاﻧﯿﻢ آن را ﻧﺼﺐ ﮐﻨﯿﻢ‪.‬‬

‫‪$> python -m pip install pytest‬‬

‫اﺳﺘﻔﺎده از ‪PyTest‬‬
‫ﺑﺮاي ﺷﺮوع ﯾﺎدﮔﯿﺮي ﻧﺤﻮة اﺳﺘﻔﺎده از اﯾﻦ ﻣﺎژول اﺑﺘﺪا ﮐﻼس ﺳﺒﺪ ﺧﺮﯾﺪ را در ﻓﺎﯾﻠﯽ ﺑﺎ ﻧﺎم ‪cart.py‬‬
‫ﻣﯽﻧﻮﯾﺴﯿﻢ و ﺳﭙﺲ آن را ﺗﺴﺖ ﻣﯽﮐﻨﯿﻢ‪.‬‬

‫‪class Cart:‬‬

‫‪def __init__(self, first_name):‬‬


‫‪self.first_name = first_name‬‬
‫][ = ‪self.items‬‬

‫‪def add_item(self, item_name):‬‬


‫)‪self.items.append(item_name‬‬

‫‪def __len__(self):‬‬
‫)‪return len(self.items‬‬

‫‪def count_item(self, item_name):‬‬


‫)‪return self.items.count(item_name‬‬

‫ﺑﺮاي ﻧﻮﺷﺘﻦ ﻣﻮارد آزﻣﻮن‪ ،‬ﯾﮏ ﻓﺎﯾﻞ ﺟﺪﯾﺪ ﺑﺎ ﻧﺎم ‪ cart_test.py‬ﻣﯽﺳﺎزﯾﻢ و ﮐﻼس ‪ Cart‬را در‬
‫آن ‪ import‬ﻣﯽﮐﻨﯿﻢ‪ .‬ﯾﮑﯽ از روشﻫﺎﯾﯽ ﮐﻪ ‪ PyTest‬ﻓﺎﯾﻞﻫﺎي ﺗﺴﺖ را ﭘﯿﺪا ﻣﯽﮐﻨﺪ ﻫﻤﯿﻦ ﻧﺎمﮔﺬاري‬
‫آﻧﻬﺎ اﺳﺖ‪ .‬اﯾﻦ ﻣﺎژول ﻓﺎﯾﻞﻫﺎﯾﯽ ﮐﻪ ﺑﺎ _‪ test‬ﺷﺮوع و ﯾﺎ ﺑﺎ ‪ _test.py‬ﺧﺎﺗﻤﻪ ﻣﯽﯾﺎﺑﻨﺪ را ﻣﻮرد‬
‫ﺑﺮرﺳﯽ ﻗﺮار ﻣﯽدﻫﺪ‪.‬‬
‫‪Ahwaz_Hackerz‬‬

‫‪121‬‬ ‫ﺑﺨﺶ ﭘﻨﺠﻢ ‪ /‬ﻓﺼﻞ ‪ / 2‬ﻣﺎژول ‪PyTest‬‬

‫ﭘﯿﺶ از ﻧﻮﺷﺘﻦ ﺗﺴﺖﻫﺎ ﻧﯿﺎز اﺳﺖ ﺗﺎ ﺑﺎ دﺳﺘﻮر ‪ assert‬آﺷﻨﺎ ﺷﻮﯾﻢ‪ ،‬اﯾﻦ دﺳﺘﻮر ﯾﮏ ﻋﺒﺎرت ﺑﻮﻟﯿﻦ را‬
‫ﻣﯽﮔﯿﺮد و اﮔﺮ آن ﻋﺒﺎرت ﻣﻘﺪار ‪ False‬داﺷﺘﻪ ﺑﺎﺷﺪ‪ ،‬اﺳﺘﺜﻨﺎي ‪ AssertionError‬را ﺑﺮﻣﯽﮔﺮداﻧﺪ‪.‬‬
‫ﺑﺮاي ﻣﺜﺎل ﻣﯽﺧﻮاﻫﯿﻢ ﺑﺮرﺳﯽ ﮐﻨﯿﻢ ﮐﻪ ﺟﻤﻊ اﻋﺪاد ﯾﮏ ﻟﯿﺴﺖ ﮐﻤﺘﺮ از ‪ 10‬اﺳﺖ و آن را ﺑﺎ ‪assert‬‬
‫ﻣﯽﻧﻮﯾﺴﯿﻢ ﺗﺎ رﻓﺘﺎر اﯾﻦ دﺳﺘﻮر را ﻣﺸﺎﻫﺪه ﮐﻨﯿﻢ‪ .‬در اﺑﺘﺪا ﻟﯿﺴﺘﯽ را ﻣﯽدﻫﯿﻢ ﮐﻪ اﯾﻦ ﺷﺮاﯾﻂ را داﺷﺘﻪ‬
‫ﺑﺎﺷﯿﺪ‪.‬‬

‫]‪numbers = [1, 2, 3‬‬


‫‪assert sum(numbers) < 10‬‬

‫ﭘﺲ از اﺟﺮاي ﮐﺪ ﺑﺎﻻ ﻫﯿﭻ اﺗﻔﺎﻗﯽ ﻧﻤﯽاﻓﺘﺪ و ﺑﺮﻧﺎﻣﻪ ﭘﺲ از اﺟﺮا ﺑﺎ ﻣﻮﻓﻘﯿﺖ ﺑﻪ ﮐﺎر ﺧﻮد ﭘﺎﯾﺎن ﻣﯽدﻫﺪ و‬
‫ﺧﺎرج ﻣﯽﺷﻮد ﺣﺎل ﺑﺎ ﻟﯿﺴﺘﯽ ﮐﻪ ﺟﻤﻊ اﻋﺪاد آن ﺑﯿﺸﺘﺮ از ‪ 10‬اﺳﺖ اﯾﻦ ﻣﺜﺎل را ﻣﺠﺪد ﺑﺮرﺳﯽ ﻣﯽﮐﻨﯿﻢ‪.‬‬

‫]‪numbers = [1, 2, 3, 4‬‬


‫‪assert sum(numbers) < 10‬‬

‫‪!> Traceback (most recent call last):‬‬

‫>‪File "sample.py", line 2, in <module‬‬


‫‪assert sum(numbers) < 10‬‬

‫‪AssertionError‬‬

‫ﺑﺮاي ﻧﻮﺷﺘﻦ ﻣﻮرد آزﻣﻮن ﯾﺎ ‪ Test Case‬ﯾﮏ ﺗﺎﺑﻊ ﺗﻌﺮﯾﻒ ﻣﯽﮐﻨﯿﻢ ﮐﻪ ﻋﻤﻠﯿﺎت اﺿﺎﻓﻪﮐﺮدن دو ﻣﺤﺼﻮل ﺑﻪ‬
‫ﻟﯿﺴﺖ ﺧﺮﯾﺪ را ﺗﺴﺖ ﻣﯽﮐﻨﺪ‪ .‬ﺑﺎﯾﺪ ﺗﻮﺟﻪ داﺷﺘﻪ ﺑﺎﺷﯿﻢ ﮐﻪ ﻧﺎم اﯾﻦ ﺗﻮاﺑﻊ ﺑﺎﯾﺪ ﺑﺎ _‪ test‬ﺷﺮوع ﺷﻮد‪.‬‬

‫‪from cart import Cart‬‬

‫‪def test_add_item():‬‬
‫)(‪cart = Cart‬‬
‫)"‪cart.add_item("Pizza‬‬
‫)"‪cart.add_item("Burger‬‬
‫"‪assert cart.items[0] == "Pizza‬‬
‫"‪assert cart.items[1] == "Burger‬‬

‫در ﺗﺎﺑﻊ ‪ test_add_item‬اﺑﺘﺪا ﯾﮏ ﻧﻤﻮﻧﻪ از ﮐﻼس ‪ Cart‬ﺳﺎﺧﺘﯿﻢ و دو ﻣﺤﺼﻮل ﭘﯿﺘﺰا و ﻫﻤﺒﺮﮔﺮ‬


‫را اﺿﺎﻓﻪ ﮐﺮدﯾﻢ و درﻧﻬﺎﯾﺖ ﺑﺎ دﺳﺘﻮر ‪ assert‬ﭼﮏ ﮐﺮدﯾﻢ ﮐﻪ آﯾﺎ اﯾﻦ ﻣﺤﺼﻮﻻت در ﻟﯿﺴﺖ ﺳﺒﺪ ﺧﺮﯾﺪ‬
‫ﺑﻪ ﺗﺮﺗﯿﺐ درﺳﺖ‪ ،‬ﻗﺮار ﮔﺮﻓﺘﻪ اﺳﺖ ﯾﺎ ﺧﯿﺮ‪.‬‬

‫ﭘﺲ از ﻧﻮﺷﺘﻦ ﮐﺪ ﺑﺎﻻ‪ ،‬دﺳﺘﻮر ‪ pytest‬را در ﺗﺮﻣﯿﻨﺎل وارد ﻣﯽﮐﻨﯿﻢ و در اداﻣﻪ اﯾﻦ ﻣﺎژول ﺑﻪ ﺑﺮرﺳﯽ‬
‫ﺗﻮاﺑﻊ ﺗﺴﺖ در ﻓﺎﯾﻞﻫﺎي ﻣﺮﺑﻮط ﻣﯽﭘﺮدازد و ﻫﺮﮐﺪام را اﺟﺮا ﻣﯽﮐﻨﺪ و ﻧﺘﯿﺠﻪ را ﺑﺮﻣﯽﮔﺮداﻧﺪ‪.‬‬
‫‪Ahwaz_Hackerz‬‬

‫ﻣﺒﺎﺣﺚ ﭘﯿﺸﺮﻓﺘﻪ در ﭘﺎﯾﺘﻮن ‪ /‬ﻣﻮﺳﺴﻪ ﻓﺮﻫﻨﮕﯽ ﻫﻨﺮي دﯾﺒﺎﮔﺮان ﺗﻬﺮان‬ ‫‪122‬‬

‫‪$> pytest‬‬

‫========== ‪========== test session starts‬‬


‫‪collected 1 item‬‬

‫]‪test_cart.py. [100%‬‬

‫========== ‪========== 1 passed in 0.08 seconds‬‬

‫در روﺑﺮوي ﻓﺎﯾﻞ ‪ test_cart.py‬ﯾﮏ ﻋﺪد ﮐﺎراﮐﺘﺮ ﻧﻘﻄﻪ آﻣﺪه اﺳﺖ ﮐﻪ ﺑﺪﯾﻦ ﻣﻌﻨﺎ اﺳﺖ ﮐﻪ‬
‫‪ pytest‬در اﯾﻦ ﻓﺎﯾﻞ ﯾﮏ ﺗﺎﺑﻊ را ﺗﺴﺖ ﮐﺮده و ﺑﺪون ﻫﯿﭻ ﻣﺸﮑﻠﯽ ﺗﺴﺖ اﻧﺠﺎم ﺷﺪه اﺳﺖ‪ .‬ﺑﻪﻃﻮرﮐﻠﯽ‬
‫در ﻣﻘﺎﺑﻞ اﯾﻦ ﻓﺎﯾﻞ ﯾﮏ ﯾﺎ ﭼﻨﺪ ﮐﺎراﮐﺘﺮ ﻣﯽآﯾﺪ ﮐﻪ ﺗﻌﺪاد آﻧﻬﺎ ﻧﺸﺎندﻫﻨﺪة ﺗﻌﺪاد ﺗﻮاﺑﻊ ﺗﺴﺖ در داﺧﻞ آن‬
‫ﻓﺎﯾﻞ اﺳﺖ و ﮐﺎراﮐﺘﺮ آن ﻣﯽﺗﻮاﻧﺪ ﯾﮑﯽ از ﺣﺎﻟﺖﻫﺎي زﯾﺮ ﺑﺎﺷﺪ‪:‬‬

‫‪ ‬ﮐﺎراﮐﺘﺮ ﻧﻘﻄﻪ‪ :‬ﻧﺸﺎندﻫﻨﺪة آن اﺳﺖ ﮐﻪ ﺗﺴﺖ ﺑﺎ ﻣﻮﻓﻘﯿﺖ اﻧﺠﺎم ﺷﺪه اﺳﺖ‪.‬‬


‫‪ ‬ﮐﺎراﮐﺘﺮ ‪ :F‬ﻧﺸﺎندﻫﻨﺪة آن اﺳﺖ ﮐﻪ ﺗﺴﺖ ﺑﺎ ﺷﮑﺴﺖ ﻣﻮاﺟﻪ ﺷﺪه اﺳﺖ‪.‬‬
‫‪ ‬ﮐﺎراﮐﺘﺮ ‪ :E‬ﯾﮏ ﺧﻄﺎي ﻏﯿﺮﻣﻨﺘﻈﺮه رخ داده اﺳﺖ‪.‬‬
‫‪ ‬ﮐﺎراﮐﺘﺮ ‪ :S‬ﺗﺴﺖ ﺑﺪون اﺟﺮا رد ﺷﺪه اﺳﺖ‪.‬‬

‫ﻻزم ﺑﻪ ذﮐﺮ اﺳﺖ ﮐﻪ در اﯾﻨﺠﺎ ﻓﺎﯾﻞ ﺗﺴﺖ و ﮐﻼس اﺻﻠﯽ در ﯾﮏ داﯾﺮﮐﺘﻮري ﻗﺮار دارد اﻣﺎ در ﻋﻤﻞ ﺑﻬﺘﺮ‬
‫اﺳﺖ ﻓﺎﯾﻞﻫﺎي ﺗﺴﺖ را در داﯾﺮﮐﺘﻮري ﺟﺪا ﻧﮕﻪ داﺷﺖ ﺗﺎ در زﻣﺎن اﺳﺘﻘﺮار ﺑﺮﻧﺎﻣﻪ ﺑﻪ اﺷﺘﺒﺎه‪ ،‬اﯾﻦ ﻓﺎﯾﻞﻫﺎ در‬
‫ﻣﺤﯿﻂ اﺳﺘﻘﺮار ﻗﺮار ﻧﮕﯿﺮﻧﺪ‪.‬‬

‫ﻣﺪﯾﺮﯾﺖ اﺳﺘﺜﻨﺎ‬
‫‪1‬‬
‫ﺑﺮﺧﯽ ﻣﻮاﻗﻊ ﻧﯿﺎز دارﯾﻢ ﺗﺎ ﻣﻮاردي را ﺗﺴﺖ ﮐﻨﯿﻢ ﮐﻪ ﻣﻄﻤﺌﻦ ﺷﻮﯾﻢ ﭘﺲ از اﯾﻦ ﻋﻤﻠﯿﺎت ﯾﮏ اﺳﺘﺜﻨﺎ‬
‫درﯾﺎﻓﺖ ﻣﯽﮐﻨﯿﻢ‪ .‬ﺑﺮاي ﻣﺜﺎل ﻣﯽﺧﻮاﻫﯿﻢ ﭘﺲ از اﺿﺎﻓﻪﮐﺮدن ﯾﮏ ﺟﻨﺲ اﻃﻤﯿﻨﺎن ﭘﯿﺪا ﮐﻨﯿﻢ ﮐﻪ ﺟﻨﺲ‬
‫دﯾﮕﺮي در ﺳﺒﺪ ﺧﺮﯾﺪ ﻧﯿﺴﺖ ﭘﺲ وﻗﺘﯽ ﮐﻪ دوﻣﯿﻦ ﺟﻨﺲ را در ﺳﺒﺪ ﺧﺮﯾﺪ درﯾﺎﻓﺖ ﮐﺮدﯾﻢ ﯾﮏ اﺳﺘﺜﻨﺎ‬
‫درﯾﺎﻓﺖ ﺧﻮاﻫﯿﻢ ﮐﺮد ﮐﻪ ﻧﺸﺎن از درﺳﺘﯽ ﺳﯿﺴﺘﻢ دارد‪ .‬ﺑﺮاي ﺑﺮرﺳﯽ اﯾﻦ ﻧﻮع از ﺗﺴﺖﻫﺎ ﺑﺎﯾﺪ از‬
‫‪ pytest.raises‬اﺳﺘﻔﺎده ﮐﺮد‪ .‬ﭘﺲ ﺑﻪ ﻓﺎﯾﻞ ﻗﺒﻠﯽ دو ﺗﺴﺖ دﯾﮕﺮ اﺿﺎﻓﻪ ﻣﯽﮐﻨﯿﻢ‪ ،‬در ﯾﮑﯽ اوﻟﯿﻦ‬
‫ﻣﺤﺼﻮل ﺳﺒﺪ ﺧﺮﯾﺪ ﮐﻪ اﺿﺎﻓﻪ ﺷﺪه را درﯾﺎﻓﺖ و در ﺗﺴﺖ ﺑﻌﺪي ﻣﺤﺼﻮل دوم را از ﺳﺒﺪ ﺧﺮﯾﺪ ﺑﯿﺮون‬
‫ﻣﯽﮐﺸﯿﻢ‪ .‬در ﺗﺴﺖ اول ﮐﺪ ﻣﺎ ﺑﺎﯾﺪ ﺑﺪون ﻣﺸﮑﻞ اﺟﺮا ﺷﻮد وﻟﯽ ﭼﻮن از ‪ pytest.raises‬اﺳﺘﻔﺎده‬
‫ﮐﺮدﯾﻢ‪ ،‬ﺑﺮاي ﻗﺒﻮﻟﯽ از ﺗﺴﺖ ﺑﺎﯾﺪ ﺣﺘﻤﺎً ﯾﮏ اﺳﺘﺜﻨﺎ ﯾﺎ ‪ Exception‬رخ دﻫﺪ و ﭼﻮن اﯾﻦ اﺗﻔﺎق ﻧﻤﯽاﻓﺘﺪ‬
‫ﭘﺲ ﺗﺴﺖ ﻣﺎ ﺑﺎ ﺷﮑﺴﺖ ﻣﻮاﺟﻪ ﻣﯽﺷﻮد‪ ،‬اﻣﺎ ﺗﺴﺖ ﺑﻌﺪي ﺑﺎ ﻣﻮﻓﻘﯿﺖ ﭘﺬﯾﺮﻓﺘﻪ ﻣﯽﺷﻮد‪.‬‬

‫‪1‬‬
‫‪Exception‬‬
Ahwaz_Hackerz

123 PyTest ‫ ﻣﺎژول‬/ 2 ‫ ﻓﺼﻞ‬/ ‫ﺑﺨﺶ ﭘﻨﺠﻢ‬

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_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 ‫ﻧﺘﯿﺠﮥ اﺟﺮاي دﺳﺘﻮر‬

$> pytest

========== test session starts ==========


collected 3 items

test_cart.py.F. [100%]

========== FAILURES ==========


_________ test_index_error_fail _________

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

‫ ﺳﻪ ﮐﺎراﮐﺘﺮ وﺟﻮد دارد ﮐﻪ ﻧﺸﺎندﻫﻨﺪه‬test_cart.py ‫ﻫﻤﺎنﻃﻮريﮐﻪ ﻣﯽﺑﯿﻨﯿﻢ در ﻣﻘﺎﺑﻞ ﻧﺎم ﻓﺎﯾﻞ‬


‫ اﺳﺖ ﮐﻪ ﻧﺸﺎندﻫﻨﺪة ﺷﮑﺴﺖ آن ﺗﺴﺖ ﺑﻮده‬F ‫ دوﻣﯿﻦ ﮐﺎراﮐﺘﺮ اﯾﻦ ﺗﺴﺖ‬.‫ﺳﻪ ﺗﺴﺖ داﺧﻞ اﯾﻦ ﻓﺎﯾﻞ اﺳﺖ‬
‫ در اﯾﻦ ﻣﺜﺎل ﻣﺎ ﺑﻪ دﻧﺒﺎل آن ﻫﺴﺘﯿﻢ ﮐﻪ ﮐﺪ ﻣﻮرد ﻧﻈﺮ ﺧﻄﺎ ﺗﻮﻟﯿﺪ ﮐﻨﺪ و ﭼﻮن ﮐﺪ ﺑﺪون ﻣﺸﮑﻞ اﺟﺮا‬.‫اﺳﺖ‬
‫‪Ahwaz_Hackerz‬‬

‫ﻣﺒﺎﺣﺚ ﭘﯿﺸﺮﻓﺘﻪ در ﭘﺎﯾﺘﻮن ‪ /‬ﻣﻮﺳﺴﻪ ﻓﺮﻫﻨﮕﯽ ﻫﻨﺮي دﯾﺒﺎﮔﺮان ﺗﻬﺮان‬ ‫‪124‬‬

‫ﻣﯽﺷﻮد‪ ،‬ﺗﺴﺖ ﻣﺎ ﺑﺎ ﺷﮑﺴﺖ ﻣﻮاﺟﻪ ﻣﯽﺷﻮد‪ .‬ﺑﻪ زﺑﺎن دﯾﮕﺮ‪ ،‬ﺑﺮاي ﺗﺴﺖ ﺑﺮﺧﯽ از ﻋﻤﻠﮑﺮدﻫﺎي ﺳﯿﺴﺘﻢ ﻧﯿﺎز‬
‫اﺳﺖ ﺗﺎ ﻣﻄﻤﺌﻦ ﺷﻮﯾﻢ ﮐﻪ رﻓﺘﺎر ﻏﯿﺮﻃﺒﯿﻌﯽ ﻧﺪاﺷﺘﻪ ﺑﺎﺷﯿﻢ‪ .‬ﺑﺮاي ﻣﺜﺎل ﺑﺎ اﯾﻦ روش ﭼﮏ ﻣﯽﮐﻨﯿﻢ ﮐﻪ در‬
‫ﺻﻮرت اﺿﺎﻓﻪﮐﺮدن ﯾﮏ ﻣﺤﺼﻮل‪ ،‬ﻣﺤﺼﻮل دﯾﮕﺮي در ﺳﺒﺪ ﺧﺮﯾﺪ اﺿﺎﻓﻪ ﻧﺸﻮد‪.‬‬

‫اﺳﺘﻔﺎده از ‪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‬‬

‫‪125‬‬ ‫ﺑﺨﺶ ﭘﻨﺠﻢ ‪ /‬ﻓﺼﻞ ‪ / 2‬ﻣﺎژول ‪PyTest‬‬

‫‪from cart import Cart‬‬


‫‪import pytest‬‬

‫‪@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‬ﻫﺎي‬
‫ﻣﻮﺟﻮد را ﻣﺸﺎﻫﺪه ﮐﻨﯿﺪ‪.‬‬

‫‪$> pytest ––fixtures‬‬

‫‪--------- fixtures defined from test_cart ---------‬‬


‫‪cart‬‬
‫‪Creating an object from Cart class‬‬

‫ﻣﯽﺗﻮاﻧﯿﻢ ﺑﺮاي ‪ Fixture‬ﻫﺎ ﺑﻪوﺳﯿﻠﮥ ﭘﺎراﻣﺘﺮ ‪ scope‬ﻣﺤﺪوده ﺗﻌﺮﯾﻒ ﮐﻨﯿﻢ‪ .‬ﻣﺤﺪوده ﻣﺸﺨﺺ ﻣﯽﮐﻨﺪ ﮐﻪ‬
‫ﻫﺮ ‪ fixture‬ﭼﻪ زﻣﺎﻧﯽ اﺟﺮا ﺷﻮد‪ .‬ﻣﻘﺪارﻫﺎي ﻗﺎﺑﻞ اﺳﺘﻔﺎده ﺑﺮاي ﻣﺤﺪوده‪ ،‬ﻋﺒﺎرﺗﻨﺪ از‪:‬‬

‫‪) :"function" ‬ﭘﯿﺶﻓﺮض( ‪ Fixture‬ﺑﻌﺪ از اﺟﺮاي ﻫﺮ ﺗﺎﺑﻊ ﺗﺴﺖ ﻧﺎﺑﻮد ﻣﯽﺷﻮد‪.‬‬


‫‪ Fixture :"class" ‬ﺑﻌﺪ از اﺟﺮاي ﺗﻤﺎم ﺗﻮاﺑﻊ ﺗﺴﺖ در ﮐﻼس ﺟﺎري ﻧﺎﺑﻮد ﻣﯽﺷﻮد‪.‬‬
‫‪ Fixture :"module" ‬ﺑﻌﺪ از اﺟﺮاي ﺗﻤﺎم ﺗﻮاﺑﻊ ﺗﺴﺖ در ﻣﺎژول ﺟﺎري ﻧﺎﺑﻮد ﻣﯽﺷﻮد‪.‬‬
‫‪Ahwaz_Hackerz‬‬

‫ﻣﺒﺎﺣﺚ ﭘﯿﺸﺮﻓﺘﻪ در ﭘﺎﯾﺘﻮن ‪ /‬ﻣﻮﺳﺴﻪ ﻓﺮﻫﻨﮕﯽ ﻫﻨﺮي دﯾﺒﺎﮔﺮان ﺗﻬﺮان‬ ‫‪126‬‬

‫‪ Fixture :"package" ‬ﺑﻌﺪ از اﺟﺮاي ﺗﻤﺎم ﺗﻮاﺑﻊ ﺗﺴﺖ در ﭘﮑﯿﺞ ﺟﺎري ﻧﺎﺑﻮد ﻣﯽﺷﻮد‪.‬‬
‫‪ Fixture :"session" ‬ﺑﻌﺪ از اﺟﺮاي ﺗﻤﺎم ﺗﻮاﺑﻊ ﺗﺴﺖ در ﻧﺸﺴﺖ ﺟﺎري ﻧﺎﺑﻮد ﻣﯽﺷﻮد‪.‬‬

‫ﺑﻪﻣﻨﻈﻮر آﻧﮑﻪ ﺗﺴﺘﯽ اﻧﺠﺎم دﻫﯿﻢ ﻓﺎﯾﻠﯽ ﺑﺎ ﻧﺎم ‪ conftest.py‬ﻣﯽﺳﺎزﯾﻢ و ‪ Fixture‬ﺧﻮد را ﮐﻪ‬
‫ﻣﺤﺪوده آن از ﻧﻮع ﮐﻼس اﺳﺖ در آن ﺗﻌﺮﯾﻒ ﻣﯽﮐﻨﯿﻢ‪.‬‬

‫‪import pytest‬‬

‫‪from cart import Cart‬‬

‫)"‪@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

127 PyTest ‫ ﻣﺎژول‬/ 2 ‫ ﻓﺼﻞ‬/ ‫ﺑﺨﺶ ﭘﻨﺠﻢ‬

$> pytest

=========== test session starts ===========


collected 3 items

test_cart.py ... [100%]

=========== 3 passed in 0.09 seconds ===========

‫ﭘﺎراﻣﺘﺮيﮐﺮدن ﺗﺴﺖﻫﺎ‬
‫ ﺑﺮاي ﻣﺜﺎل ﻓﺮض ﮐﻨﯿﺪ ﯾﮏ ﺗﺎﺑﻊ‬.‫در ﺑﺴﯿﺎري ﻣﻮاﻗﻊ ﺗﺴﺖﮐﺮدن ﺑﺎﯾﺪ ﺑﺎ ﭘﺎراﻣﺘﺮﻫﺎي ﻣﺨﺘﻠﻒ اﻧﺠﺎم ﺷﻮد‬
.‫دارﯾﻢ ﮐﻪ واﺣﺪ ﻓﻮت را ﺑﻪ ﻣﺘﺮ ﺗﺎ ﺳﻪ رﻗﻢ اﻋﺸﺎر ﺗﺒﺪﯾﻞ ﻣﯽﮐﻨﺪ‬

def foot_to_metre(length):
return round(length / 3.281, 3)

.‫ ﺑﺎ داﻧﺶ ﻓﻌﻠﯽ ﻣﯽﺗﻮاﻧﯿﻢ ﺑﺪﯾﻦﮔﻮﻧﻪ ﻋﻤﻞ ﮐﻨﯿﻢ‬.‫ﻣﯽﺧﻮاﻫﯿﻢ اﯾﻦ ﺗﺎﺑﻊ را ﺑﺎ اﻋﺪاد ﻣﺘﻔﺎوت ﺗﺴﺖ ﮐﻨﯿﻢ‬

from convertor import foot_to_metre

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

from convertor import foot_to_metre


import pytest

@pytest.mark.parametrize('length,expected_value',
[(1, 0.305),
(2, 0.610),
(3, 0.914),
(5, 1.524)])
‫‪Ahwaz_Hackerz‬‬

‫ﻣﺒﺎﺣﺚ ﭘﯿﺸﺮﻓﺘﻪ در ﭘﺎﯾﺘﻮن ‪ /‬ﻣﻮﺳﺴﻪ ﻓﺮﻫﻨﮕﯽ ﻫﻨﺮي دﯾﺒﺎﮔﺮان ﺗﻬﺮان‬ ‫‪128‬‬

‫‪def test_feet_to_metre(length,expected_value):‬‬
‫‪assert foot_to_metre(length) == expected_value‬‬

‫ﺗﻤﺎم ﺗﺴﺖﻫﺎي ﻗﺒﻠﯽ را در ﯾﮏ ﺗﺴﺖ ﭘﺎراﻣﺘﺮيﺷﺪه ﮔﺮدآوري ﮐﺮدﯾﻢ‪ .‬اﯾﻦ ‪ Decorator‬ﺑﻪ اﯾﻦﺻﻮرت‬
‫ﻋﻤﻞ ﻣﯽﮐﻨﺪ ﮐﻪ دو ورودي ﻣﯽﮔﯿﺮد‪ ،‬ورودي اول ﯾﮏ رﺷﺘﻪ اﺳﺖ ﮐﻪ ﺑﺎ ﮐﺎﻣﺎ‪ ،‬ﻧﺎم ﭘﺎراﻣﺘﺮﻫﺎ از ﻫﻢ ﺟﺪا‬
‫ﺷﺪه اﺳﺖ و ﭘﺎراﻣﺘﺮ دوم ﻟﯿﺴﺘﯽ از ﺗﺎﭘﻞﻫﺎ اﺳﺖ ﮐﻪ ﺑﻪﺗﺮﺗﯿﺐ ﻣﺘﻨﺎﻇﺮ ﺑﺎ ﭘﺎراﻣﺘﺮﻫﺎي ورودي اول اﺳﺖ‪.‬‬
‫در ﺗﺎﺑﻊ ﺗﺴﺖ ﻧﯿﺰ ﺑﺎﯾﺪ ﻧﺎم آرﮔﻮﻣﺎنﻫﺎ را ﺑﺎ ﻧﺎم ﭘﺎراﻣﺘﺮﻫﺎ ﯾﮑﺴﺎن درﻧﻈﺮ ﺑﮕﯿﺮﯾﻢ و در ﺗﺎﺑﻊ از آﻧﻬﺎ اﺳﺘﻔﺎده‬
‫ﮐﻨﯿﻢ‪ PyTest .‬ﺑﺮاي ﺗﺴﺖ ﺑﺮاي ﻫﺮﮐﺪام از ﺗﺎﭘﻞﻫﺎي داﺧﻞ ﻟﯿﺴﺖ ﯾﮑﺒﺎر ﺗﺎﺑﻊ ﺗﺴﺖ را اﺟﺮا ﻣﯽﮐﻨﺪ‪ .‬ﭘﺲ‬
‫در ﻣﺜﺎل ﻣﻮﺟﻮد ‪ 4‬ﺗﺴﺖ اﻧﺠﺎم ﻣﯽﺷﻮد‪.‬‬

‫‪$> pytest‬‬

‫========== ‪========== test session starts‬‬


‫‪collected 4 items‬‬

‫]‪test_convertor.py .... [100%‬‬

‫========== ‪========== 4 passed in 0.05 seconds‬‬

‫ﻧﺎدﯾﺪهﮔﺮﻓﺘﻦ ﺗﺴﺖﻫﺎ‬
‫در ﺑﺴﯿﺎري ﻣﻮارد ﺗﺎﺑﻊ ﻣﻮرد ﻧﻈﺮ را ﮐﺎﻣﻞ ﭘﯿﺎدهﺳﺎزي ﻧﮑﺮدﯾﻢ ﯾﺎ ﭘﺎراﻣﺘﺮﻫﺎي ﺗﺴﺖ را ﻧﺪارﯾﻢ و ﯾﺎ ﺗﺎﺑﻊ ﺗﺴﺖ‬
‫ﻣﺎ ﻧﯿﺎز ﺑﻪ ﺑﺎزﻧﮕﺮي دارد‪ ،‬اﻣﺎ ﺑﺮاي ﺟﻠﻮﮔﯿﺮي از ﻓﺮاﻣﻮﺷﯽ ﺑﺮاي ﺗﺴﺖ‪ ،‬ﻣﯽﺧﻮاﻫﯿﻢ ﺗﺎﺑﻊ ﺗﺴﺖ وﺟﻮد داﺷﺘﻪ‬
‫ﺑﺎﺷﺪ‪ .‬ﺑﻪ اﯾﻦ ﻣﻨﻈﻮر ﻣﯽﺗﻮاﻧﯿﻢ از ‪ Decorator‬ﺑﺎ ﺷﻨﺎﺳﻪ ‪ pytest.mark.skip‬اﺳﺘﻔﺎده ﮐﻨﯿﻢ‪.‬‬
‫ﭘﺎراﻣﺘﺮ ‪ msg‬ﻧﯿﺰ ﺑﺮاي ﺑﯿﺎن دﻟﯿﻞ ﻧﺎدﯾﺪهﮔﺮﻓﺘﻦ ﺗﺴﺖ اﺳﺘﻔﺎده ﻣﯽﺷﻮد‪.‬‬

‫‪import pytest‬‬

‫)"‪@pytest.mark.skip(msg="I will test it later‬‬


‫‪def test_feet_to_metre():‬‬
‫‪assert something(1,2) == 3‬‬

‫ﭘﺲ از اﺟﺮاي دﺳﺘﻮر ‪ PyTest‬در ﺗﺮﻣﯿﻨﺎل ﻧﺘﯿﺠﻪ ﺑﻪ ﺷﮑﻞ زﯾﺮ اﺳﺖ ﮐﻪ ﮐﺎراﮐﺘﺮ ‪ s‬ﻧﺸﺎندﻫﻨﺪة ﻧﺎدﯾﺪه‪-‬‬
‫ﮔﺮﻓﺘﻦ ﯾﮏ ﺗﺴﺖ در اﯾﻦ ﻓﺎﯾﻞ اﺳﺖ‪.‬‬

‫‪$> pytest‬‬

‫========== ‪========== test session starts‬‬


‫‪collected 1 item‬‬
‫]‪test_convertor.py s [100%‬‬

‫========== ‪========== 1 skipped in 0.08 seconds‬‬


‫‪Ahwaz_Hackerz‬‬

‫‪129‬‬ ‫ﺑﺨﺶ ﭘﻨﺠﻢ ‪ /‬ﻓﺼﻞ ‪ / 2‬ﻣﺎژول ‪PyTest‬‬

‫اﻓﺰوﻧﻪﻫﺎي ‪PyTest‬‬
‫ﯾﮑﯽ از ﻗﺎﺑﻠﯿﺖﻫﺎي ﺗﺤﺴﯿﻦﺑﺮاﻧﮕﯿﺰ ﮐﺘﺎﺑﺨﺎﻧﻪ ‪ PyTest‬اﻧﻌﻄﺎفﭘﺬﯾﺮي ﺑﺎﻻي آن اﺳﺖ و ﺑﻪ ﻫﻤﯿﻦ دﻟﯿﻞ ﺑﺮاي‬
‫آن اﻓﺰوﻧﻪﻫﺎي ﺑﺴﯿﺎري ﺗﻮﺳﻌﻪ داده ﺷﺪه اﺳﺖ‪ .‬در اداﻣﻪ ﺑﻪ ﺑﺮرﺳﯽ ﺑﺮﺧﯽ از اﯾﻦ اﻓﺰوﻧﻪﻫﺎي ﮐﺎرﺑﺮدي‬
‫ﻣﯽﭘﺮدازﯾﻢ‪.‬‬

‫‪ ‬ﺑﺮرﺳﯽ ﺗﺴﺖ ﭘﻮﺷﺶ ﺑﺎ ‪pytest-cov‬‬


‫ﻫﻤﺎنﻃﻮريﮐﻪ ﻗﺒﻼً ﮔﻔﺘﯿﻢ ﺗﺴﺖ ﺗﻤﺎم ﻗﺴﻤﺖﻫﺎي ﮐﺪ ﻣﻤﮑﻦ اﺳﺖ ﺑﻪ ﮐﺸﻒ ﻣﺸﮑﻼت ﮐﻤﮏ ﮐﻨﺪ و ﺑﺎﯾﺪ‬
‫ﺗﺴﺖﻫﺎ ﺑﻪ ﺻﻮرﺗﯽ ﺑﺎﺷﺪ ﮐﻪ ﺗﻤﺎم ﻗﺴﻤﺖﻫﺎي ﮐﺪ را ﭘﻮﺷﺶ دﻫﺪ‪ .‬ﯾﮏ ﺗﺎﺑﻊ ﻣﯽﻧﻮﯾﺴﯿﻢ ﮐﻪ ﻣﺜﺒﺖ‪ ،‬ﻣﻨﻔﯽ و‬
‫ﯾﺎ ﺻﻔﺮﺑﻮدن ﯾﮏ ﻋﺪد را ﻣﺸﺨﺺ ﻣﯽﮐﻨﺪ‪.‬‬

‫‪def check_number(number):‬‬
‫‪if number > 0:‬‬
‫"‪return "Positive‬‬
‫‪elif number < 0:‬‬
‫"‪return "Negative‬‬
‫‪else:‬‬
‫‪return Zero‬‬

‫و ﺑﺮاي ﺗﺴﺖ‪ ،‬ﺗﺎﺑﻊ زﯾﺮ را ﻣﯽﻧﻮﯾﺴﯿﻢ‪.‬‬

‫‪from check_number import check_number‬‬

‫‪def test_positive():‬‬
‫"‪assert check_number(10) == "Positive‬‬

‫ﺗﺴﺖ ﻣﺎ ﺑﺎ ﻣﻮﻓﻘﯿﺖ اﻧﺠﺎم ﻣﯽﺷﻮد اﻣﺎ ﺗﻤﺎم ﻗﺴﻤﺖﻫﺎي ﮐﺪ ﺣﺪاﻗﻞ ﯾﮑﺒﺎر اﺟﺮا ﻧﻤﯽﺷﻮد‪ .‬ﺑﺮاي ﺑﺮرﺳﯽ‬
‫درﺻﺪ ﭘﻮﺷﺶ از اﻓﺰوﻧﻪ ‪ pytest-cov‬اﺳﺘﻔﺎده ﻣﯽﮐﻨﯿﻢ‪ .‬ﺑﺮاي ﺷﺮوع ﺑﻪوﺳﯿﻠﮥ دﺳﺘﻮر زﯾﺮ اﯾﻦ اﻓﺰوﻧﻪ را‬
‫ﻧﺼﺐ ﻣﯽﮐﻨﯿﻢ‪.‬‬

‫‪$> python -m pip install pytest-cov‬‬

‫ﭘﺲ از ﻧﺼﺐ ﺑﺎ دﺳﺘﻮر ‪ pytest --cov‬ﺗﺴﺖﻫﺎ اﺟﺮا ﻣﯽﺷﻮد و درﺻﺪ ﭘﻮﺷﺶ ﻧﯿﺰ ﮔﺰارش ﻣﯽﺷﻮد‪.‬‬

‫‪>> pytest --cov‬‬

‫========== ‪========== test session starts‬‬


‫‪plugins: cov-2.10.1‬‬
‫‪collected 1 item‬‬

‫]‪test_number.py. [100%‬‬
Ahwaz_Hackerz

‫ ﻣﻮﺳﺴﻪ ﻓﺮﻫﻨﮕﯽ ﻫﻨﺮي دﯾﺒﺎﮔﺮان ﺗﻬﺮان‬/ ‫ﻣﺒﺎﺣﺚ ﭘﯿﺸﺮﻓﺘﻪ در ﭘﺎﯾﺘﻮن‬ 130

---------- coverage -----------


Name Stmts Miss Cover
-------------------------------------
check_number.py 6 3 50%
test_number.py 3 0 100%
-------------------------------------
TOTAL 9 3 67%

========== 1 passed in 0.15 seconds ==========

‫ درﺻﺪ اﺳﺖ؛ زﯾﺮا‬50 ‫ ﺑﺮاﺑﺮ‬check_number.py ‫ﻫﻤﺎنﻃﻮرﮐﻪ ﻣﺸﺎﻫﺪه ﻣﯽﮐﻨﯿﻢ درﺻﺪ ﭘﻮﺷﺶ در ﻓﺎﯾﻞ‬
‫ ﺑﻪ ﻣﻨﻈﻮر‬.‫در ﺗﺎﺑﻊ ﻣﺎ دو ﺷﺎﺧﻪ ﺻﻔﺮ و ﻋﺪد ﻣﻨﻔﯽ ﭼﮏ ﻧﺸﺪه اﺳﺖ و ﺗﻤﺎم ﺧﻄﻮط ﮐﺪ اﺟﺮا ﻧﺸﺪه اﺳﺖ‬
‫اﯾﻨﮑﻪ ﺗﺴﺖ ﻣﺎ ﺗﻤﺎم ﺣﺎﻻت را ﺑﺮرﺳﯽ ﮐﻨﺪ و ﺗﻤﺎم ﻗﺴﻤﺖﻫﺎي ﮐﺪ را ﭘﻮﺷﺶ دﻫﺪ دو ﺗﺴﺖ دﯾﮕﺮ ﺑﻪﺻﻮرت‬
.‫ﭘﺎراﻣﺘﺮيﺷﺪه ﺑﻪ ﮐﺪ اﺿﺎﻓﻪ ﻣﯽﮐﻨﯿﻢ ﺗﺎ ﺗﻤﺎم ﺷﺎﺧﻪﻫﺎي دﺳﺘﻮر ﺷﺮﻃﯽ را اﺟﺮا ﮐﻨﺪ‬

import pytest

from check_number import check_number

@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 session starts ==========


collected 3 items

test_number.py..F [100%]

========== FAILURES ==========


__________ test_number[0-Zero] __________

number = 0, expected_value = 'Zero'

@pytest.mark.parametrize('number,expected_value', [
(10, "Positive"),
(-10, "Negative"),
(0, "Zero")
‫‪Ahwaz_Hackerz‬‬

‫‪131‬‬ ‫ﺑﺨﺶ ﭘﻨﺠﻢ ‪ /‬ﻓﺼﻞ ‪ / 2‬ﻣﺎژول ‪PyTest‬‬

‫)]‬
‫‪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‬‬

‫‪---------- coverage -----------‬‬


‫‪Name‬‬ ‫‪Stmts Miss Cover‬‬
‫‪-------------------------------------‬‬
‫‪check_number.py 6‬‬ ‫‪0‬‬ ‫‪100%‬‬
‫‪test_number.py‬‬ ‫‪4‬‬ ‫‪0‬‬ ‫‪100%‬‬
‫‪-------------------------------------‬‬
‫‪TOTAL‬‬ ‫‪10‬‬ ‫‪0‬‬ ‫‪100%‬‬

‫========== ‪========== 1 failed, 2 passed in 0.27 seconds‬‬

‫در اﺑﺘﺪا ﻣﯽﺑﯿﻨﯿﻢ ﮐﻪ ﯾﮑﯽ از ﺗﺴﺖﻫﺎي ﻣﺎ ﺑﺎ ﺷﮑﺴﺖ ﻣﻮاﺟﻪ ﺷﺪه و دﻟﯿﻞ آن‪ ،‬اﺷﺘﺒﺎه ﺑﺮﮔﺮداﻧﺪن ﻣﻘﺪار‬
‫‪ Zero‬اﺳﺖ ﮐﻪ ﺑﻪ ﺟﺎي آﻧﮑﻪ ﺑﻪ ﺻﻮرت رﺷﺘﻪاي ﺑﺮﮔﺮداﻧﺪه ﺷﻮد‪ ،‬ﺑﻪ ﺻﻮرت ﯾﮏ ﻣﺘﻐﯿﺮ ﺑﺮﮔﺮداﻧﺪه ﻣﯽﺷﻮد‬
‫و ﭼﻮن اﯾﻦ ﻣﺘﻐﯿﺮ ﺑﺎ اﯾﻦ ﻧﺎم ﺗﻌﺮﯾﻒ ﻧﺸﺪه اﺳﺖ اﺳﺘﺜﻨﺎ ‪ NameError‬رخ ﻣﯽدﻫﺪ‪ .‬در ﻋﯿﻦ ﺣﺎل ﻣﻘﺪار‬
‫ﭘﻮﺷﺶ دﯾﮕﺮ ‪ 100‬درﺻﺪ ﺷﺪه و ﺗﻤﺎم ﻗﺴﻤﺖﻫﺎي ﮐﺪ ﺣﺪاﻗﻞ ﯾﮑﺒﺎر اﺟﺮا ﺷﺪه اﺳﺖ‪.‬‬

‫ﭘﺲ دﯾﺪم ﮐﻪ اﮔﺮ ﭘﻮﺷﺶ ﮐﺎﻣﻞ را رﻋﺎﯾﺖ ﻧﻤﯽﮐﺮدﯾﻢ ﻣﻤﮑﻦ ﺑﻮد ﻫﯿﭻﮔﺎه ﺑﻪ اﯾﻦ ﺧﻄﺎ ﭘﯽ ﻧﺒﺮﯾﻢ و ﺑﻌﺪاً‬
‫ﺑﺮاﯾﻤﺎن ﻣﺸﮑﻞﺳﺎز ﺷﻮد‪.‬‬

‫‪ ‬ﻧﻤﺎﯾﺶ آﻧﯽ و ﺑﻬﺘﺮ ﺗﺴﺖﻫﺎ ﺑﺎ ‪pytest-sugar‬‬


‫‪ pytest-sugar‬ﻧﺎم اﻓﺰوﻧﻪاي اﺳﺖ ﮐﻪ ﺑﺎ اﺳﺘﻔﺎده از آن ﻣﯽﺗﻮان ﺷﮑﺴﺖﻫﺎ را ﺑﻪ ﺻﻮرت آﻧﯽ ﻧﺸﺎن داد‪ .‬در‬
‫ﺣﺎﻟﺖ ﻣﻌﻤﻮل ‪ PyTest‬ﺗﻤﺎم ﺗﺴﺖﻫﺎ را اﺟﺮا ﻣﯽﮐﻨﺪ و درﻧﻬﺎﯾﺖ ﻧﺘﯿﺠﻪ را در ﺧﺮوﺟﯽ ﻧﻤﺎﯾﺶ ﻣﯽدﻫﺪ ﭘﺲ‬
‫اﮔﺮ در ﭘﺮوژة ﺧﻮد از ﺗﻌﺪاد زﯾﺎدي ﺗﺴﺖ اﺳﺘﻔﺎده ﻣﯽﮐﻨﯿﺪ و ﻫﺮ دﻓﻌﻪ زﻣﺎن زﯾﺎدي را ﺑﺮاي ﻣﺸﺎﻫﺪه ﻧﺘﯿﺠﻪ‬
‫ﺗﺴﺖﻫﺎ ﻫﺪر ﻣﯽدﻫﯿﺪ‪ ،‬ﻣﯽﺗﻮاﻧﯿﺪ ﺑﺎ اﯾﻦ اﻓﺰوﻧﻪ ﺷﮑﺴﺖﻫﺎ را ﺳﺮﯾﻌﺎً ﻣﺘﻮﺟﻪ ﺷﺪه و ﻧﺴﺒﺖ ﺑﻪ رﻓﻊ آﻧﻬﺎ اﻗﺪام‬
‫‪Ahwaz_Hackerz‬‬

‫ﻣﺒﺎﺣﺚ ﭘﯿﺸﺮﻓﺘﻪ در ﭘﺎﯾﺘﻮن ‪ /‬ﻣﻮﺳﺴﻪ ﻓﺮﻫﻨﮕﯽ ﻫﻨﺮي دﯾﺒﺎﮔﺮان ﺗﻬﺮان‬ ‫‪132‬‬

‫ﮐﻨﯿﺪ‪ .‬ﻫﻤﭽﻨﯿﻦ اﯾﻦ اﻓﺰوﻧﻪ داراي ‪ progressbar‬اﺳﺖ ﮐﻪ ﺑﺴﯿﺎر روﻧﺪ ﺗﺴﺖ را ﮐﺎرﺑﺮﭘﺴﻨﺪ ﻣﯽﮐﻨﺪ‪ .‬ﺑﺮاي‬
‫اﺳﺘﻔﺎده از اﯾﻦ اﻓﺰوﻧﻪ ﮐﺎر ﺧﺎﺻﯽ ﻧﯿﺎز ﻧﯿﺴﺖ و ﻓﻘﻂ ﺑﺎﯾﺪ آن را ﺑﺎ اﺳﺘﻔﺎده از دﺳﺘﻮر زﯾﺮ ﻧﺼﺐ ﮐﻨﯿﺪ‪.‬‬

‫‪$> python -m pip install pytest-sugar‬‬

‫ﭘﺲ از اﺟﺮاي ﺗﺴﺖ ﻧﺘﯿﺠﻪ ﺑﻪﺻﻮرت زﯾﺮ ﺗﻐﯿﯿﺮ ﭘﯿﺪا ﻣﯽﮐﻨﺪ‪.‬‬

‫‪$> pytest‬‬

‫‪plugins: sugar-0.9.4‬‬
‫‪collecting ...‬‬
‫✓ ‪test_number.py‬‬ ‫███ ‪33%‬‬
‫✓✓ ‪test_number.py‬‬ ‫██████ ‪67%‬‬
‫✓✓✓ ‪test_number.py‬‬ ‫██████████ ‪100%‬‬

‫‪Results (0.10s): 3 passed‬‬

‫‪ ‬ﮔﺰارش ﺗﺴﺖ ﺑﺎ ‪pytest-html‬‬


‫ﺑﺎ ﻧﺼﺐ اﯾﻦ اﻓﺰوﻧﻪ ﮔﺰارشﻫﺎي ﺗﺴﺖ را در ﻗﺎﻟﺐ ‪ html‬درﯾﺎﻓﺖ ﻣﯽﮐﻨﯿﺪ و ﻣﯽﺗﻮاﻧﯿﺪ اﯾﻦ ﻓﺎﯾﻞ را ﺑﺎ‬
‫ﻣﺮورﮔﺮ ﺧﻮد ﺑﺎز ﮐﻨﯿﺪ‪ .‬ﮐﺎﻓﯿﺴﺖ اﺑﺘﺪا ﺑﺎ دﺳﺘﻮر زﯾﺮ اﯾﻦ اﻓﺰوﻧﻪ را ﻧﺼﺐ ﮐﻨﯿﺪ‪.‬‬

‫‪$> python -m pip install pytest-html‬‬

‫ﺳﭙﺲ ﺑﺎ دﺳﺘﻮر زﯾﺮ ﺧﺮوﺟﯽ در ﻓﺎﯾﻠﯽ ﺑﺎ ﻧﺎم ‪ report.html‬ذﺧﯿﺮه ﻣﯽﺷﻮد و ﻗﺎﺑﻞ ﻣﺸﺎﻫﺪه اﺳﺖ‪ .‬در اﯾﻦ‬
‫ﮔﺰارش ﺷﻤﺎ روﻧﺪ ﮐﻠﯽ ﻫﺮ ﺗﺴﺖ را ﻣﺸﺎﻫﺪه ﮐﺮده و ﻣﯽﺗﻮاﻧﯿﺪ ﺑﺮاﺳﺎس ﻧﺘﺎﯾﺞ‪ ،‬ﺗﺴﺖﻫﺎ را ﻓﯿﻠﺘﺮ ﮐﻨﯿﺪ‪.‬‬
‫ﺧﺮوﺟﯽ ﺗﻮﻟﯿﺪﺷﺪه ﻫﻤﺎﻧﻨﺪ ﺗﺼﻮﯾﺮ ‪ 12‬اﺳﺖ‪.‬‬

‫‪pytest --html=report.html‬‬

‫‪ ‬ﻧﻤﺎﯾﺶ ﺑﻬﺘﺮ ﺷﮑﺴﺖﻫﺎ ﺑﺎ ‪pytest-clarity‬‬


‫ﺑﺎ اﺳﺘﻔﺎده از اﯾﻦ اﻓﺰوﻧﻪ ﻣﯽﺗﻮاﻧﯿﺪ ﺗﻔﺎوت ﻣﯿﺎن ﻣﻘﺎدﯾﺮ ﻣﻮرد اﻧﺘﻈﺎر و ﻣﻘﺎدﯾﺮ ﻓﻌﻠﯽ را ﻫﻤﺎﻧﻨﺪ ‪ diff‬در ‪git‬‬
‫ﺑﻪ ﺻﻮرت رﻧﮕﯽ و ﺑﺴﯿﺎر ﻣﺸﺨﺺ و ﺷﻔﺎف ﻣﺸﺎﻫﺪه ﮐﻨﯿﺪ‪ .‬اﯾﻦ اﻓﺰوﻧﻪ ﺑﻪ ﺗﻔﺴﯿﺮ و ﻓﻬﻢ راﺣﺖﺗﺮ ﺷﮑﺴﺖﻫﺎ‬
‫ﮐﻤﮏ ﻣﯽﮐﻨﺪ‪ .‬ﺑﺮاي اﺳﺘﻔﺎده ﺑﺎﯾﺪ ﻫﻤﺎﻧﻨﺪ اﻓﺰوﻧﻪﻫﺎي ﻗﺒﻠﯽ آن را ﻧﺼﺐ ﮐﻨﯿﻢ‪.‬‬

‫‪$> python -m pip install pytest-clarity‬‬

‫و ﺳﭙﺲ ﺑﺮاي ﻓﻌﺎلﺳﺎزي اﯾﻦ اﻓﺰوﻧﻪ از دﺳﺘﻮر ‪ pytest‬ﺑﻪ ﻫﻤﺮاه آرﮔﻮﻣﺎن ‪ -vv‬اﺳﺘﻔﺎده ﮐﻨﯿﺪ‪.‬‬
‫‪Ahwaz_Hackerz‬‬

‫‪133‬‬ ‫ﺑﺨﺶ ﭘﻨﺠﻢ ‪ /‬ﻓﺼﻞ ‪ / 2‬ﻣﺎژول ‪PyTest‬‬

‫‪$> pytest --vv‬‬

‫ﺗﺼﻮﯾﺮ ‪ - 12‬ﮔﺰارش ﺗﺴﺖ ﺑﺎ ‪pytest-html‬‬

‫ﺟﻤﻊﺑﻨﺪي‬
‫در اﯾﻦ ﻓﺼﻞ ﺑﻪ ﺑﺮرﺳﯽ ﻣﺎژول ‪ pytest‬ﭘﺮداﺧﺘﯿﻢ و ﮐﻠﯿﺎﺗﯽ در ﻣﻮرد آن ﻣﻄﺮح ﮐﺮدﯾﻢ‪ .‬اﯾﻦ ﻣﺎژول ﺑﺴﯿﺎر‬
‫ﻣﺎژول ﮔﺴﺘﺮده و ﭘﺮﮐﺎرﺑﺮدي ﺑﺮاي اﻧﻮاع ﺳﻄﻮح ﺗﺴﺖ اﺳﺖ و ﺑﻪدﻟﯿﻞ اﻧﻌﻄﺎفﭘﺬﯾﺮي ﺑﺎﻻ‪ ،‬ﺑﺮاي آن‬
‫اﻓﺰوﻧﻪﻫﺎي ﺑﺴﯿﺎري ﺗﻮﺳﻌﻪ داده ﺷﺪه اﺳﺖ‪ .‬ﺳﻌﯽ ﮐﻨﯿﺪ در ﺷﺮوع ﯾﮏ ﭘﺮوژه‪ ،‬ﻫﻤﺎﻧﻨﺪ ﻻگ‪ ،‬ﺗﺴﺖﻫﺎي ﺧﻮد‬
‫را ﺑﺮاي ﻗﺴﻤﺖﻫﺎي ﻣﺨﺘﻠﻒ ﮐﺪ از ﺟﻤﻠﻪ ﺗﻮاﺑﻊ‪ ،‬ﮐﻼسﻫﺎ‪ ،‬ﻣﺎژولﻫﺎ و ‪ API‬ﻫﺎ ﺑﻨﻮﯾﺴﯿﺪ‪.‬‬

‫ﭘﻮﺷﺶ ‪ 100‬درﺻﺪي را ﺗﺎ ﺣﺪ ﻣﻤﮑﻦ اﺟﺮا ﮐﻨﯿﺪ و ﻫﻤﯿﺸﻪ ﻗﺒﻞ از ﻗﺮاردادن ﮐﺪﻫﺎ در ﻣﺤﯿﻂ اﺳﺘﻘﺮار‪ ،‬آﻧﻬﺎ‬
‫را ﺗﺴﺖ ﮐﻨﯿﺪ‪ .‬ﺑﺮاي ﻣﻄﺎﻟﻌﮥ ﮔﺴﺘﺮدهﺗﺮ ﻣﯽﺗﻮاﻧﯿﺪ ﻣﺴﺘﻨﺪات ‪ pytest‬را در آدرس زﯾﺮ ﻣﻄﺎﻟﻌﻪ ﻓﺮﻣﺎﯾﯿﺪ‪.‬‬

‫‪https://docs.pytest.org/en/stable‬‬
‫‪Ahwaz_Hackerz‬‬

‫ﻣﺒﺎﺣﺚ ﭘﯿﺸﺮﻓﺘﻪ در ﭘﺎﯾﺘﻮن ‪ /‬ﻣﻮﺳﺴﻪ ﻓﺮﻫﻨﮕﯽ ﻫﻨﺮي دﯾﺒﺎﮔﺮان ﺗﻬﺮان‬ ‫‪134‬‬

‫ﺑﺨﺶ‪6‬‬

‫ﭘﮑﯿﺞ‬
‫‪Ahwaz_Hackerz‬‬

‫‪135‬‬ ‫ﺑﺨﺶ ﺷﺸﻢ ‪ /‬ﻓﺼﻞ ‪ / 1‬ﺳﺎﺧﺖ ﯾﮏ ﭘﮑﯿﺞ ﺳﺎده‬

‫ﻓﺼﻞ‪1‬‬
‫ﺳﺎﺧﺖ ﯾﮏ ﭘﮑﯿﺞ ﺳﺎده‬
‫‪Ahwaz_Hackerz‬‬

‫ﻣﺒﺎﺣﺚ ﭘﯿﺸﺮﻓﺘﻪ در ﭘﺎﯾﺘﻮن ‪ /‬ﻣﻮﺳﺴﻪ ﻓﺮﻫﻨﮕﯽ ﻫﻨﺮي دﯾﺒﺎﮔﺮان ﺗﻬﺮان‬ ‫‪136‬‬

‫‪ ‬ﭘﮑﯿﺞ ﭼﯿﺴﺖ؟‬
‫ﻣﯽداﻧﯿﻢ ﮐﻪ ﻣﺎژول ﭼﯿﺰي ﺑﻪ ﺟﺰ ﯾﮏ ﻓﺎﯾﻞ ﭘﺎﯾﺘﻮن ﻧﯿﺴﺖ‪ .‬ﭘﮑﯿﺞ داﯾﺮﮐﺘﻮري اﺳﺖ ﮐﻪ ﺣﺎوي ﭼﻨﺪﯾﻦ‬
‫ﻣﺎژول اﺳﺖ‪ .‬درواﻗﻊ ﯾﮏ ﭘﮑﯿﺞ ﻣﯽﺗﻮاﻧﺪ داﺧﻞ ﭘﮑﯿﺞ دﯾﮕﺮي ﻧﯿﺰ ﺑﺎﺷﺪ‪ ،‬ﭘﺲ ﺑﻬﺘﺮ اﺳﺖ ﺑﮕﻮﯾﯿﻢ ﭘﮑﯿﺞ ﺷﺎﻣﻞ‬
‫ﭼﻨﺪﯾﻦ ﻣﺎژول و ﭘﮑﯿﺞﻫﺎي دﯾﮕﺮ ﻣﯽﺗﻮاﻧﺪ ﺑﺎﺷﺪ‪.‬‬

‫ﭘﺎﯾﺘﻮن ﺑﺎ ﺑﺴﯿﺎري از ﭘﮑﯿﺞﻫﺎي ﮐﺎرﺑﺮدي و ﻣﻔﯿﺪ اراﺋﻪ ﻣﯽﺷﻮد‪ .‬ﻋﻼوهﺑﺮ ﭘﮑﯿﺞﻫﺎي ﺧﻮدﺳﺎﺧﺘﻪ‪ ،‬ﺑﺴﯿﺎري از‬
‫ﺗﻮﺳﻌﻪدﻫﻨﺪﮔﺎن ﭘﮑﯿﺞﻫﺎي ﮐﺎرﺑﺮدي را در ‪ PyPI‬ﻣﻨﺘﺸﺮ ﻣﯽﮐﻨﻨﺪ و ﻫﻤﮥ اﯾﻨﻬﺎ ﺑﺎﻋﺚ ﺷﺪه ﺗﺎ ﭘﺎﯾﺘﻮن ﺑﻪ‬
‫زﺑﺎﻧﯽ ﻗﺪرﺗﻤﻨﺪ و آﺳﺎن ﺗﺒﺪﯾﻞ ﮔﺮدد‪ .‬در اﯾﻦ ﻓﺼﻞ ﻣﯽﺧﻮاﻫﯿﻢ ﺑﻪ ﺑﺮرﺳﯽ ﭼﮕﻮﻧﮕﯽ ﺳﺎﺧﺖ ﯾﮏ ﭘﮑﯿﺞ و‬
‫اﻧﺘﺸﺎر آن در ‪ PyPI‬ﺑﭙﺮدازﯾﻢ‪.‬‬

‫ﺳﺎﺧﺖ ﯾﮏ ﭘﮑﯿﺞ ﺳﺎده‬


‫ﭘﮑﯿﺠﯽ ﮐﻪ در اﯾﻨﺠﺎ ﻣﯽﺧﻮاﻫﯿﻢ ﺑﺴﺎزﯾﻢ ﭘﮑﯿﺞ ﺑﺴﯿﺎر ﺳﺎدهاي اﺳﺖ ﮐﻪ ﮐﻤﮏ ﻣﯽﮐﻨﺪ ﺗﺎ ﻣﺘﻐﯿﺮﻫﺎ را ﻫﻤﺮاه‬
‫ﺑﺎ ﻣﻘﺎدﯾﺮ آن ﺑﻪ ﺻﻮرت رﻧﮕﯽ در ﺗﺮﻣﯿﻨﺎل ﭼﺎپ ﮐﻨﺪ‪ .‬در اﺑﺘﺪا ﺑﻪ ﻣﺤﺘﻮﯾﺎت داﯾﺮﮐﺘﻮري ﭘﺮوژه ﻧﮕﺎه ﮐﻨﯿﺪ‪.‬‬
‫ﯾﮏ داﯾﺮﮐﺘﻮري ﻫﻢﻧﺎم ﺑﺎ ﻧﺎم ﭘﺮوژه ﯾﻌﻨﯽ ‪ printvarcolor‬وﺟﻮد دارد‪ .‬ﯾﮏ داﯾﺮﮐﺘﻮري ‪ test‬ﮐﻪ‬
‫ﻣﺮﺑﻮط ﺑﻪ ﺗﺴﺖﻫﺎﯾﯽ اﺳﺖ ﮐﻪ ﻧﻮﺷﺘﻪاﯾﻢ ﺗﺎ از ﻋﻤﻠﮑﺮد ﮐﺪ اﻃﻤﯿﻨﺎن ﺣﺎﺻﻞ ﮐﻨﯿﻢ‪.‬‬

‫‪printvarcolor‬‬

‫│‬
‫‪├─── printvarcolor‬‬
‫‪│ │ helper.py‬‬
‫‪│ │ __init__.py‬‬
‫│‬
‫│‬
‫‪├─── test‬‬
‫‪│ │ test_cprint.py‬‬
‫│ │‬
‫│‬
‫‪└─── setup.py‬‬

‫ﻓﺎﯾﻞ ‪helper.py‬‬
‫اﯾﻦ ﻓﺎﯾﻞ درﺑﺮدارﻧﺪة ﻣﻨﻄﻖ ﺑﺮﻧﺎﻣﻪ ﻣﺎﺳﺖ‪ ،‬اﮔﺮﭼﻪ ﮐﻪ ﻣﯽﺗﻮاﻧﯿﻢ از ﭼﻨﺪﯾﻦ ﻓﺎﯾﻞ ﺑﺎ ﻧﺎمﻫﺎي دﯾﮕﺮ اﺳﺘﻔﺎده‬
‫ﮐﻨﯿﻢ وﻟﯽ ﭼﻮن ﻣﺜﺎل ﻣﺎ ﻣﺜﺎل ﺳﺎدهاي اﺳﺖ‪ ،‬ﺳﺎﺧﺖ ﯾﮏ ﻓﺎﯾﻞ ﮐﻔﺎﯾﺖ ﻣﯽﮐﻨﺪ‪.‬‬
‫‪Ahwaz_Hackerz‬‬

‫‪137‬‬ ‫ﺑﺨﺶ ﺷﺸﻢ ‪ /‬ﻓﺼﻞ ‪ / 1‬ﺳﺎﺧﺖ ﯾﮏ ﭘﮑﯿﺞ ﺳﺎده‬

‫‪from termcolor import colored‬‬

‫‪def make_colorful_text(text: str, color: str = 'white') -> str:‬‬


‫)‪return colored(text, color‬‬

‫‪def cprint(**kwargs) -> None:‬‬


‫‪for k, v in kwargs.items():‬‬
‫)'‪key = make_colorful_text(k, 'red‬‬
‫)'‪value = make_colorful_text(v, 'green‬‬
‫)"}‪print(f"{key}: {value‬‬

‫در اﯾﻦ ﺑﺮﻧﺎﻣﻪ ﻣﺎ از ﮐﺘﺎﺑﺨﺎﻧﮥ دﯾﮕﺮي ﺑﺎ ﻧﺎم ‪ termcolor‬اﺳﺘﻔﺎده ﮐﺮدﯾﻢ‪ .‬اﯾﻦ ﮐﺘﺎﺑﺨﺎﻧﻪ اﯾﻦ اﻣﮑﺎن را ﺑﻪ‬
‫ﻣﺎ ﻣﯽدﻫﺪ ﺗﺎ ﺧﺮوﺟﯽﻫﺎي ﭼﺎپﺷﺪه در ﺗﺮﻣﯿﻨﺎل را رﻧﮕﯽ ﮐﻨﯿﻢ‪ .‬ﺗﺎﺑﻊ ‪ make_colorful_text‬ﯾﮏ‬
‫ﻣﺘﻦ و ﯾﮏ رﻧﮓ درﯾﺎﻓﺖ ﮐﺮده و ﻣﺘﻦ رﻧﮕﯽ را ﺑﺮﻣﯽﮔﺮداﻧﺪ‪ .‬درواﻗﻊ‪ ،‬ﺑﺮاي رﻧﮕﯽﮐﺮدن ﻣﺘﻦ‪ ،‬اﯾﻦ ﮐﺘﺎﺑﺨﺎﻧﻪ‬
‫ﺗﻌﺪادي ﮐﺎراﮐﺘﺮ ﺧﺎص را ﺑﻪ اول و آﺧﺮ ﻣﺘﻦ اﺿﺎﻓﻪ ﻣﯽﮐﻨﺪ و آﻧﻬﺎ را رﻧﮕﯽ ﻣﯽﮐﻨﺪ‪ .‬ﺗﺎﺑﻊ ‪cprint‬‬
‫ﺗﻌﺪادي آرﮔﻮﻣﺎن ﺑﺎ ﮐﻠﯿﺪواژه درﯾﺎﻓﺖ ﻣﯽﮐﻨﺪ و آنﻫﺎ را ﺑﺎ ﻧﺎﻣﺸﺎن ﭼﺎپ ﻣﯽﮐﻨﺪ ﮐﻪ ﻧﺎم آرﮔﻮﻣﺎن ﺑﻪ رﻧﮓ‬
‫ﻗﺮﻣﺰ و ﻣﻘﺪار آن ﺑﻪ رﻧﮓ ﺳﺒﺰ در ﻣﯽآﯾﺪ‪.‬‬

‫ﻓﺎﯾﻞ ‪__init__.py‬‬
‫اﯾﻦ ﻓﺎﯾﻞ ﻫﻤﺎنﻃﻮريﮐﻪ از ﻧﺎﻣﺶ ﭘﯿﺪاﺳﺖ ﻣﻌﻨﯽ ﺧﺎﺻﯽ ﺑﺮاي ﭘﺎﯾﺘﻮن دارد و ﻧﺸﺎندﻫﻨﺪة‬
‫‪ root directory‬ﭘﺮوژه ﻣﺎ اﺳﺖ و ﻣﺤﻞ ﺧﻮﺑﯽ ﺑﺮاي ﻗﺮاردادن ورژن ﭘﮑﯿﺞ و ﻣﺴﺘﻨﺪات و ﺛﺎﺑﺖﻫﺎ اﺳﺖ‪.‬‬
‫ﺑﺮاي ﻣﺜﺎل در اﯾﻦ ﻓﺎﯾﻞ ورژن را ﻟﺤﺎظ ﻣﯽﮐﻨﯿﻢ‪.‬‬

‫‪from printvarcolor.helper import cprint‬‬

‫"‪__version__ = "1.0.0‬‬

‫زﻣﺎﻧﯽﮐﻪ ﭘﮑﯿﺞ ﻣﺎ ‪ import‬ﻣﯽﺷﻮد ﻣﯽﺗﻮاﻧﯿﻢ اﯾﻦ ﺛﺎﺑﺖﻫﺎ را ﻣﺸﺎﻫﺪه ﮐﻨﯿﻢ‪.‬‬

‫‪import printvarcolor as pvc‬‬

‫)__‪print(pvc.__version‬‬

‫‪>> 1.0.0‬‬

‫زﻣﺎﻧﯽﮐﻪ ‪ import‬را ﺑﺮاي ﺗﺎﺑﻊ ﯾﺎ ﮐﻼﺳﯽ در ‪ __init__.py‬ﻣﯽﻧﻮﯾﺴﯿﻢ ﺑﻪ اﯾﻦ ﻣﻌﻨﺎﺳﺖ ﮐﻪ آن ﺗﺎﺑﻊ‪،‬‬


‫ﮐﻼس ﯾﺎ ﻣﺘﻐﯿﺮ ﻣﯽﺗﻮاﻧﺪ ‪ import‬ﺷﻮد‪ .‬در اﯾﻦ ﻣﺜﺎل ﻣﺎ ﺗﺎﺑﻊ ‪ cprint‬را در اﯾﻦ ﻓﺎﯾﻞ ‪ import‬ﮐﺮدﯾﻢ‬
‫ﭘﺲ اﮔﺮ ﭘﮑﯿﺞ ﻣﺎ ‪ import‬ﺷﻮد اﯾﻦ ﺗﺎﺑﻊ ﻗﺎﺑﻞ اﺳﺘﻔﺎده اﺳﺖ وﻟﯽ ﺗﺎﺑﻊ ‪make_colorful_text‬‬
‫ﻗﺎﺑﻞ اﺳﺘﻔﺎده ﻧﯿﺴﺖ‪.‬‬
Ahwaz_Hackerz

‫ ﻣﻮﺳﺴﻪ ﻓﺮﻫﻨﮕﯽ ﻫﻨﺮي دﯾﺒﺎﮔﺮان ﺗﻬﺮان‬/ ‫ﻣﺒﺎﺣﺚ ﭘﯿﺸﺮﻓﺘﻪ در ﭘﺎﯾﺘﻮن‬ 138

from printvarcolor import cprint

cprint(var="value")

>> var: value

from printvarcolor import make_colorful_text

!> ImportError: cannot import name 'make_colorful_text' from


'printvarcolor'

‫ ﮐﻼس و‬،‫ ﺑﻨﺎﺑﺮاﯾﻦ ﺗﻨﻬﺎ ﺗﻮاﺑﻊ‬.‫ ﮐﺮد‬import ‫ آن را‬helper.py ‫ﺑﺮاي اﺳﺘﻔﺎده از اﯾﻦ ﺗﺎﺑﻊ ﺑﺎﯾﺪ از‬
‫__ وارد ﺷﺪه ﺑﺎﺷﻨﺪ ﺑﻪﺻﻮرت ﻣﺴﺘﻘﯿﻢ از ﻃﺮﯾﻖ ﭘﮑﯿﺞ ﻗﺎﺑﻞ اﺳﺘﻔﺎده ﻫﺴﺘﻨﺪ‬init__.py ‫ﻣﺘﻐﯿﺮﻫﺎﯾﯽ ﮐﻪ در‬
.‫ ﺷﻮﻧﺪ‬import ‫و ﺑﺎﻗﯽ ﻣﻮارد ﺑﺎﯾﺪ ﺑﻪﺻﻮرت ﮐﺎﻣﻞ‬

from printvarcolor.helper import make_colorful_text

blue_text = make_colorful_text("Something", "blue")

print(blue_text)

>> Something

test_cprint.py ‫ﻓﺎﯾﻞ‬
‫ﻫﻤﺎنﻃﻮريﮐﻪ در ﻓﺼﻞ ﻗﺒﻞ ﺑﻪ آن اﺷﺎره ﮐﺮدﯾﻢ ﺑﻬﺘﺮ اﺳﺖ ﻓﺎﯾﻞﻫﺎي ﺗﺴﺖ را از ﺑﺮﻧﺎﻣﮥ اﺻﻠﯽ ﺟﺪا ﮐﻨﯿﻢ؛‬
.‫ ﻗﺮار دادﯾﻢ‬test ‫ﺑﻨﺎﺑﺮاﯾﻦ اﯾﻦ ﻓﺎﯾﻞ را در داﯾﺮﮐﺘﻮري‬

from printvarcolor import cprint

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

‫‪139‬‬ ‫ﺑﺨﺶ ﺷﺸﻢ ‪ /‬ﻓﺼﻞ ‪ / 1‬ﺳﺎﺧﺖ ﯾﮏ ﭘﮑﯿﺞ ﺳﺎده‬

‫اﯾﻦ ﺗﺎﺑﻊ ﺑﺎ ﺗﺎﺑﻊﻫﺎي ﻗﺒﻠﯽ ﺗﻔﺎوت دارد و ﻣﻘﺪاري را ﺑﺮﻧﻤﯽﮔﺮداﻧﺪ و ﻣﺘﻨﯽ را در ﺧﺮوﺟﯽ ﭼﺎپ ﻣﯽﮐﻨﺪ‪.‬‬
‫ﺑﺮاي درﯾﺎﻓﺖ رﺷﺘﻪﻫﺎي ﭼﺎپﺷﺪه در ﺧﺮوﺟﯽ )‪ (stdout‬ﻣﯽﺗﻮاﻧﯿﻢ از ‪ Fixture‬ﺧﺎﺻﯽ ﺑﺎ ﻧﺎم ‪capsys‬‬
‫اﺳﺘﻔﺎده ﮐﻨﯿﻢ‪ .‬ﺑﺎ ﻓﺮاﺧﻮاﻧﯽ ﻣﺘﺪ )(‪ readouterr‬ﻣﯽﺗﻮاﻧﯿﻢ ﺧﺮوﺟﯽ و ﺧﻄﺎﻫﺎﯾﯽ ﮐﻪ ﻗﺒﻞ از آن ﭼﺎپ‪-‬‬
‫ﺷﺪه را درﯾﺎﻓﺖ ﮐﻨﯿﻢ و ﺳﭙﺲ ﺑﻪ ﺑﺮرﺳﯽ درﺳﺘﯽ آن ﺑﭙﺮدازﯾﻢ‪ .‬ﺑﺮاي درﯾﺎﻓﺖ ﺧﺮوﺟﯽ و ﺧﻄﺎ ﻣﯽﺗﻮاﻧﯿﻢ‬
‫ﻫﻤﺎﻧﻨﺪ ﻣﺜﺎل زﯾﺮ ﻋﻤﻞ ﮐﻨﯿﻢ‪.‬‬

‫)(‪captured = capsys.readouterr‬‬
‫"" == ‪assert captured.out‬‬
‫"" == ‪assert captured.err‬‬

‫ﻧﺘﯿﺠﮥ ﺗﺴﺖ را ﺑﺎ اﺟﺮاي دﺳﺘﻮر ‪ pytest‬ﻣﯽﺗﻮاﻧﯿﻢ ﻣﺸﺎﻫﺪه ﮐﻨﯿﻢ‪.‬‬

‫‪$> pytest‬‬

‫============ ‪============ test session starts‬‬


‫‪collected 2 items‬‬

‫]‪tests\test_cprint.py.. [100%‬‬

‫============ ‪============ 2 passed in 0.05s‬‬

‫ﻓﺎﯾﻞ ‪setup.py‬‬
‫ﻓﺎﯾﻞ ﻧﺼﺐ ﺑﺎﯾﺪ در ﺑﺎﻻﺗﺮﯾﻦ ﺳﻄﺢ داﯾﺮﮐﺘﻮري ﺷﻤﺎ ﺑﺎﺷﺪ‪ .‬اﯾﻦ ﻓﺎﯾﻞ اﻃﻼﻋﺎﺗﯽ ﺑﺮاي ﻧﺼﺐ و ﻫﻤﭽﻨﯿﻦ ﺑﺮاي‬
‫اﻧﺘﺸﺎر در ‪ PyPI‬را در ﺧﻮد دارد‪ .‬در اداﻣﻪ ﻣﺤﺘﻮﯾﺎت اﯾﻦ ﻓﺎﯾﻞ را ﻣﺸﺎﻫﺪه ﻣﯽﮐﻨﯿﺪ‪.‬‬

‫‪from setuptools import setup, find_packages‬‬

‫(‪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‬‬

‫ﻣﺒﺎﺣﺚ ﭘﯿﺸﺮﻓﺘﻪ در ﭘﺎﯾﺘﻮن ‪ /‬ﻣﻮﺳﺴﻪ ﻓﺮﻫﻨﮕﯽ ﻫﻨﺮي دﯾﺒﺎﮔﺮان ﺗﻬﺮان‬ ‫‪140‬‬

‫‪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‬‬

‫‪141‬‬ ‫ﺑﺨﺶ ﺷﺸﻢ ‪ /‬ﻓﺼﻞ ‪ / 2‬اﻧﺘﺸﺎر ﭘﮑﯿﺞ در ‪PyPI‬‬

‫ﻓﺼﻞ‪2‬‬
‫اﻧﺘﺸﺎر ﭘﮑﯿﺞ در ‪PyPI‬‬
‫‪Ahwaz_Hackerz‬‬

‫ﻣﺒﺎﺣﺚ ﭘﯿﺸﺮﻓﺘﻪ در ﭘﺎﯾﺘﻮن ‪ /‬ﻣﻮﺳﺴﻪ ﻓﺮﻫﻨﮕﯽ ﻫﻨﺮي دﯾﺒﺎﮔﺮان ﺗﻬﺮان‬ ‫‪142‬‬

‫ﺳﺎﺧﺖ ﺣﺴﺎب ﮐﺎرﺑﺮي در ‪PyPI‬‬


‫ﺑﻪﻣﻨﻈﻮر آﻧﮑﻪ ﭘﮑﯿﺠﯽ را ﺑﻪﺻﻮرت ﻋﻤﻮﻣﯽ در ‪ PyPI‬اﻧﺘﺸﺎر دﻫﯿﺪ ﻧﯿﺎز اﺳﺖ ﺗﺎ ﯾﮏ ﺣﺴﺎب ﮐﺎرﺑﺮي در آن‬
‫اﯾﺠﺎد ﮐﻨﯿﺪ‪ .‬ﺑﺮاي اﯾﻦ ﮐﺎر ﺑﻪ آدرس زﯾﺮ ﻣﺮاﺟﻌﻪ ﮐﺮده و ﻣﺸﺨﺼﺎت ﺧﻮد را وارد ﮐﻨﯿﺪ‪.‬‬

‫‪https://pypi.org/account/register‬‬

‫ﺗﺼﻮﯾﺮ ‪ - 13‬ﻧﻤﺎﯾﯽ از ﺻﻔﺤﻪ ﺛﺒﺖﻧﺎم در ‪PyPI‬‬

‫ﺳﺎﺧﺖ ﭘﮑﯿﺞ‬
‫در ‪ PyPI‬ﮐﺪﻫﺎ ﺑﻪ ﺻﻮرت ﻣﺘﻦ ﺑﺎز و ﻓﺎﯾﻞ ﺑﻪ ﻓﺎﯾﻞ ﻗﺮار ﻧﺪارد و ﺑﻪﺻﻮرت ﯾﮏ ﻓﺎﯾﻞ ﺑﺴﺘﻪﺑﻨﺪيﺷﺪه ﺗﻮزﯾﻊ‬
‫ﻣﯽﺷﻮد‪ .‬ﻓﺮﻣﺖ اﯾﻦ ﻓﺎﯾﻞ ‪ .whl‬اﺳﺖ و ﺑﺎ داﺷﺘﻦ اﯾﻦ ﻓﺎﯾﻞ ﻣﯽﺗﻮاﻧﯿﻢ ﭘﮑﯿﺞ را ﺑﺎ دﺳﺘﻮر زﯾﺮ ﻧﺼﺐ ﮐﻨﯿﻢ‪.‬‬

‫‪$> python -m pip install something.whl‬‬


‫‪Ahwaz_Hackerz‬‬

‫‪143‬‬ ‫ﺑﺨﺶ ﺷﺸﻢ ‪ /‬ﻓﺼﻞ ‪ / 2‬اﻧﺘﺸﺎر ﭘﮑﯿﺞ در ‪PyPI‬‬

‫ﺑﺮاي ﻧﺼﺐ اﺑﺰار ‪ wheel‬ﺑﺮاي ﺑﺴﺘﻪﺑﻨﺪي ﻓﺎﯾﻞﻫﺎي ﭘﮑﯿﺞ‪ ،‬اﺑﺘﺪا ﺑﺎ دﺳﺘﻮر زﯾﺮ آن را ﻧﺼﺐ ﻣﯽﮐﻨﯿﻢ‪.‬‬

‫‪$> python -m pip install wheel‬‬

‫ﺑﺮاي ﺳﺎﺧﺖ ﻓﺎﯾﻞ ﺑﺴﺘﻪﺑﻨﺪيﺷﺪه ﭘﮑﯿﺞ ﻓﻌﻠﯽ‪ ،‬از دﺳﺘﻮر زﯾﺮ اﺳﺘﻔﺎده ﻣﯽﮐﻨﯿﻢ و آن را داﺧﻞ ﺗﺮﻣﯿﻨﺎل وارد‬
‫ﻣﯽﮐﻨﯿﻢ‪.‬‬

‫‪$> python setup.py bdist_wheel‬‬

‫ﭘﺲ از اﺟﺮاي اﯾﻦ دﺳﺘﻮر ﺳﻪ داﯾﺮﮐﺘﻮري ﺑﻪ ﭘﺮوژه اﺿﺎﻓﻪ ﻣﯽﺷﻮد‪.‬‬

‫‪build ‬‬
‫ﺣﺎوي اﻃﻼﻋﺎت ﭘﮑﯿﺞ ﺳﺎﺧﺘﻪ ﺷﺪه اﺳﺖ‪.‬‬
‫‪dist ‬‬
‫ﺣﺎوي ﻓﺎﯾﻞ ‪ .whl‬اﺳﺖ ﮐﻪ ﺑﺮاي اﻧﺘﺸﺎر ﭘﮑﯿﺞ ﺑﻪﮐﺎر ﻣﯽرود‪.‬‬
‫‪printvarcolor.egg-info ‬‬
‫ﺣﺎوي اﻃﻼﻋﺎﺗﯽ ﻫﻤﭽﻮن ﺑﺎﯾﺖﮐﺪﻫﺎ‪ ،‬اﻃﻼﻋﺎت ﭘﮑﯿﺞ و واﺑﺴﺘﮕﯽﻫﺎي ﭘﮑﯿﺞ اﺳﺖ‪.‬‬

‫ﻧﺼﺐ ‪Twine‬‬
‫ﺑﺮاي ارﺳﺎل ﭘﮑﯿﺞﻫﺎ ﺑﻪ ‪ PyPI‬از اﺑﺰاري ﺑﻪ ﻧﺎم ‪ twine‬اﺳﺘﻔﺎده ﻣﯽﮐﻨﯿﻢ‪ ،‬اﯾﻦ اﺑﺰار را ﺑﺎ دﺳﺘﻮر زﯾﺮ و‬
‫ﺑﻪوﺳﯿﻠﮥ ‪ pip‬ﻣﯽﺗﻮاﻧﯿﺪ ﺑﺮ روي ﺳﯿﺴﺘﻢ ﺧﻮد ﻧﺼﺐ ﮐﻨﯿﺪ‪.‬‬

‫‪$> python -m pip install twine‬‬

‫ﭘﺲ از ﻧﺼﺐ ‪ twine‬ﻓﺎﯾﻞ ‪ wheel‬ﺳﺎﺧﺘﻪ ﺷﺪه را ﭼﮏ ﻣﯽﮐﻨﯿﻢ ﺗﺎ اﮔﺮ ﺧﻄﺎﯾﯽ در آن وﺟﻮد دارد ﻗﺒﻞ از‬
‫ارﺳﺎل ﺑﺮرﺳﯽ و رﻓﻊ ﮔﺮدد‪ .‬ﺑﺮاي اﯾﻦ ﮐﺎر از دﺳﺘﻮر زﯾﺮ اﺳﺘﻔﺎده ﻣﯽﮐﻨﯿﻢ‪.‬‬

‫*‪$> twine check dist/‬‬

‫‪Checking dist\printvarcolor-1.0.0-py3-none-any.whl: PASSED‬‬

‫زﻣﺎﻧﯽﮐﻪ ﺑﺮرﺳﯽ ﻓﺎﯾﻞ ‪ wheel‬ﺗﻤﺎم ﺷﺪ و ﻧﺘﯿﺠﻪ ‪ PASSED‬ﺷﺪ ﻧﻮﺑﺖ ﺑﻪ ارﺳﺎل ﻧﻬﺎﯾﯽ ﻣﯽرﺳﺪ‪ .‬ﺑﺮاي‬
‫ارﺳﺎل ﺑﻪ ‪ PyPI‬از دﺳﺘﻮر ‪ upload‬اﺳﺘﻔﺎده ﻣﯽﮐﻨﯿﻢ‪ .‬ﭘﺲ از اﺟﺮاي اﯾﻦ دﺳﺘﻮر از ﺷﻤﺎ ﻧﺎم ﮐﺎرﺑﺮي و رﻣﺰ‬
‫ﻋﺒﻮري ﮐﻪ ﺑﺎ آن ﺣﺴﺎب ﮐﺎرﺑﺮي اﯾﺠﺎد ﮐﺮدهاﯾﺪ‪ ،‬ﭘﺮﺳﯿﺪه ﻣﯽﺷﻮد ﺳﭙﺲ ﻋﻤﻠﯿﺎت ﺑﺎرﮔﺬاري ﺑﺮ روي ‪PyPI‬‬
‫اﻧﺠﺎم ﻣﯽﺷﻮد و درﻧﻬﺎﯾﺖ آدرس ﭘﮑﯿﺞ ﺷﻤﺎ ﺑﺮ روي ﺳﺎﯾﺖ ‪ PyPI‬ﻧﺸﺎن داده ﻣﯽﺷﻮد‪.‬‬

‫*‪$> twine upload dist/‬‬

‫‪Uploading distributions to https://upload.pypi.org/legacy/‬‬


Ahwaz_Hackerz

‫ ﻣﻮﺳﺴﻪ ﻓﺮﻫﻨﮕﯽ ﻫﻨﺮي دﯾﺒﺎﮔﺮان ﺗﻬﺮان‬/ ‫ﻣﺒﺎﺣﺚ ﭘﯿﺸﺮﻓﺘﻪ در ﭘﺎﯾﺘﻮن‬ 144

Enter your username: siyanew


Enter your password:
Uploading printvarcolor-1.0.0-py3-none-any.whl
100%|█████████████████████████████|

View at:
https://pypi.org/project/printvarcolor/1.0.0

.‫ ﻣﯽﺗﻮاﻧﻨﺪ ﭘﮑﯿﺞ ﺷﻤﺎ را درﯾﺎﻓﺖ ﮐﻨﻨﺪ‬pip ‫ ﺗﻮﺳﻌﻪدﻫﻨﺪﮔﺎن دﯾﮕﺮ ﺑﺎ اﺳﺘﻔﺎده از دﺳﺘﻮر‬،‫ﭘﺲ از اﻧﺘﺸﺎر‬

$> python -m pip install printvarcolor


‫‪Ahwaz_Hackerz‬‬

‫‪145‬‬ ‫ﺑﺨﺶ ﺷﺸﻢ ‪ /‬ﻓﺼﻞ ‪ / 3‬ﻣﺤﯿﻂ ﻣﺠﺎزي‬

‫ﻓﺼﻞ‪3‬‬
‫ﻣﺤﯿﻂ ﻣﺠﺎزي‬
‫‪Ahwaz_Hackerz‬‬

‫ﻣﺒﺎﺣﺚ ﭘﯿﺸﺮﻓﺘﻪ در ﭘﺎﯾﺘﻮن ‪ /‬ﻣﻮﺳﺴﻪ ﻓﺮﻫﻨﮕﯽ ﻫﻨﺮي دﯾﺒﺎﮔﺮان ﺗﻬﺮان‬ ‫‪146‬‬

‫ﻣﺤﯿﻂ ﻣﺠﺎزي ﭼﯿﺴﺖ؟‬


‫ﻣﺤﯿﻂ ﻣﺠﺎزي‪ 1‬ﯾﮏ ﻣﺤﯿﻂ ﮐﺎﻣﻼً اﯾﺰوﻟﻪ اﺳﺖ ﮐﻪ داﯾﺮﮐﺘﻮري ‪ site‬و ﻣﻔﺴﺮ ﭘﺎﯾﺘﻮن )ورژن آن ﻫﻤﺎن‬
‫ورژﻧﯽ اﺳﺖ ﮐﻪ ﺑﺎ آن ﻣﺤﯿﻂ ﻣﺠﺎزي ﺳﺎﺧﺘﻪ ﺷﺪه اﺳﺖ( را ﺑﻪﺻﻮرت ﮐﺎﻣﻼً ﺟﺪاﮔﺎﻧﻪ دارد‪ .‬ﻫﺮ ﻣﺤﯿ ﻂ‬
‫ﻣﺠﺎزي ﻣﯽﺗﻮاﻧﺪ ﭘﮑﯿﺞﻫﺎي ﻣﺘﻔﺎوت ﺧﻮد را داﺷﺘﻪ ﺑﺎﺷﺪ و ﺑﻪ ﻣﺤﯿﻂﻫﺎي دﯾﮕﺮ دﺳﺘﺮﺳﯽ ﻧﺪارد‪ .‬ﺑﺮاي ﻣﺜﺎل‬
‫دو ﭘﺮوژه را درﻧﻈﺮ ﺑﮕﯿﺮﯾﺪ‪ ،‬در ﭘﺮوژة اول ﻣﺎ از ﭘﮑﯿﺞﻫﺎي ‪ requests‬و ‪ pytest‬اﺳﺘﻔﺎده ﻣﯽﮐﻨﯿﻢ و در‬
‫ﭘﺮوژة دوم از ﭘﮑﯿﺞﻫﺎي ‪ numpy ،pandas‬و ‪ tensorflow‬اﺳﺘﻔﺎده ﻣﯽﮐﻨﯿﻢ‪ .‬اﯾﻦ ﭘﺮوژهﻫﺎ ﺑﻪ ﭘﮑﯿﺞﻫﺎي‬
‫ﯾﮑﺪﯾﮕﺮ دﺳﺘﺮﺳﯽ ﻧﺪارﻧﺪ‪ .‬ﻫﻤﺎنﻃﻮريﮐﻪ ﻣﯽﺑﯿﻨﯿﺪ اﯾﻦ دو ﭘﺮوژه واﺑﺴﺘﮕﯽﻫﺎي ﯾﮑﺴﺎﻧﯽ ﻧﺪارﻧﺪ ﭘﺲ ﻧﯿﺎزي‬
‫ﺑﻪ ﻧﺼﺐ ﭘﮑﯿﺞﻫﺎ ﺑﻪﺻﻮرت ﻋﻤﻮﻣﯽ ﺑﺮاي اﯾﻦ ﭘﺮوژهﻫﺎ ﻧﯿﺴﺖ و ﺑﻪ اﯾﻦ ﺗﺮﺗﯿﺐ ﻣﺤﯿﻂﻫﺎﯾﯽ ﻣﯿﻨﯿﻤﺎل و‬
‫ﺧﺼﻮﺻﯽ ﺑﺮاي ﭘﺮوژهﻫﺎي ﺧﻮد ﺗﻌﺮﯾﻒ ﻣﯽﮐﻨﯿﻢ‪.‬‬

‫ﻧﺼﺐ ‪virtualenv‬‬
‫اﮔﺮ از ﭘﺎﯾﺘﻮن ‪ 3.3‬ﯾﺎ ﺟﺪﯾﺪﺗﺮ اﺳﺘﻔﺎده ﻣﯽﮐﻨﯿﺪ‪ ،‬ﻧﯿﺎزي ﺑﻪ ﻧﺼﺐ ‪ virtualenv‬ﻧﯿﺴﺖ و اﯾﻦ اﺑﺰار ﻫﻤﺮاه ﺑﺎ‬
‫ﭘﺎﯾﺘﻮن اراﺋﻪ ﻣﯽﺷﻮد‪ .‬ﭘﺲ اﮔﺮ ﭘﺎﯾﺘﻮن ﺷﻤﺎ اﯾﻦ ﻣﺎژول را دارد ﻣﯽﺗﻮاﻧﯿﺪ اﯾﻦ ﻗﺴﻤﺖ را ﻧﺎدﯾﺪه ﺑﮕﯿﺮﯾﺪ‪.‬‬

‫ﺑﺮاي ﻧﺼﺐ ﻫﻤﻮاره از ‪ pip‬ﮐﻤﮏ ﻣﯽﮔﯿﺮﯾﻢ‪.‬‬

‫‪$> python -m pip install virtualenv‬‬

‫ﺳﺎﺧﺖ ﯾﮏ ﻣﺤﯿﻂ ﻣﺠﺎزي‬


‫‪ venv‬ﺑﻪ ﺷﻤﺎ اﯾﻦ اﻣﮑﺎن را ﻣﯽدﻫﺪ ﺗﺎ ﭘﮑﯿﺞﻫﺎي ﻧﺼﺒﯽ ﺧﻮد را در ﭘﺮوژهﻫﺎي ﻣﺨﺘﻠﻒ ﺟﺪا ﮐﻨﯿﺪ‪ .‬اﯾﻦ‬
‫ﻣﺎژول ﺑﻪ ﺷﻤﺎ ﮐﻤﮏ ﻣﯽﮐﻨﺪ ﺗﺎ ﻣﻔﺴﺮ ﭘﺎﯾﺘﻮن و ﭘﮑﯿﺞﻫﺎي ﻧﺼﺒﯽ را اﯾﺰوﻟﻪﺷﺪه داﺷﺘﻪ ﺑﺎﺷﯿﺪ‪ .‬زﻣﺎﻧﯽﮐﻪ ﺷﻤﺎ‬
‫از ﭘﺮوژهاي ﺑﻪ ﭘﺮوژه دﯾﮕﺮ ﻣﯽروﯾﺪ دﯾﮕﺮ ﻧﮕﺮان ﺗﺪاﺧﻞ و ﻣﺸﮑﻞ در ﭘﮑﯿﺞﻫﺎي ﻧﺼﺐﺷﺪه ﻧﯿﺴﺘﯿﺪ‪ .‬ﺗﻮﺻﯿﮥ‬
‫اﮐﯿﺪ ﻣﯽﺷﻮد ﮐﻪ ﻫﻨﮕﺎم ﺗﻮﺳﻌﮥ ﺑﺮﻧﺎﻣﻪﻫﺎي ﭘﺎﯾﺘﻮن از ﻣﺤﯿﻂ ﻣﺠﺎزي اﺳﺘﻔﺎده ﮐﻨﯿﺪ‪.‬‬

‫ﺑﺮاي ﺳﺎﺧﺖ ﯾﮏ ﻣﺤﯿﻂ ﻣﺠﺎزي ﺑﻪ داﯾﺮﮐﺘﻮري ﭘﺮوژه ﺧﻮد رﻓﺘﻪ و دﺳﺘﻮر زﯾﺮ را ﺑﺮاي ﺳﺎﺧﺖ ﯾﮏ ﻣﺤﯿﻂ‬
‫ﻣﺠﺎزي ﺑﺎ ﻧﺎم ‪ env‬اﺟﺮا ﮐﻨﯿﺪ‪.‬‬

‫‪python -m venv env‬‬


‫ﭘﺲ از اﺟﺮاي اﯾﻦ دﺳﺘﻮر ﭘﺎﯾﺘﻮن ﺑﻪﺻﻮرت ﻣﺠﺎزي در داﯾﺮﮐﺘﻮري ‪ env‬ﻧﺼﺐ ﻣﯽﺷﻮد‪ .‬ﺣﺘﻤﺎً ﺗﻮﺟﻪ ﮐﻨﯿﺪ‬
‫ﮐﻪ اﮔﺮ از ﺳﺎﻣﺎﻧﮥ ﮐﻨﺘﺮل ﻧﺴﺨﻪ ﻫﻤﭽﻮن ‪ git‬اﺳﺘﻔﺎده ﻣﯽﮐﻨﯿﺪ اﯾﻦ داﯾﺮﮐﺘﻮري را در ﻟﯿﺴﺖ ‪.gitignore‬‬
‫ﻗﺮار دﻫﯿﺪ‪.‬‬

‫‪1‬‬
‫‪Virtual Environment‬‬
‫‪Ahwaz_Hackerz‬‬

‫‪147‬‬ ‫ﺑﺨﺶ ﺷﺸﻢ ‪ /‬ﻓﺼﻞ ‪ / 3‬ﻣﺤﯿﻂ ﻣﺠﺎزي‬

‫ﻓﻌﺎلﺳﺎزي ﯾﮏ ﻣﺤﯿﻂ ﻣﺠﺎزي‬


‫ﭘﺲ از آﻧﮑﻪ ﻣﺤﯿﻂ ﻣﺠﺎزي ﺳﺎﺧﺘﻪ ﺷﺪ‪ ،‬ﻧﯿﺎز اﺳﺖ ﮐﻪ آن را ﻓﻌﺎل ﮐﻨﯿﻢ‪ .‬ﻓﻌﺎلﺳﺎزي ﻣﺤﯿﻂ ﻣﺠﺎزي درواﻗﻊ‬
‫آدرسﻫﺎي ‪ PATH‬را دﺳﺖﮐﺎري ﮐﺮده و آدرس ﭘﺎﯾﺘﻮن و ﭘﮑﯿﺞﻫﺎي ﻧﺼﺒﯽ در داﯾﺮﮐﺘﻮري ‪ env‬را‬
‫ﺟﺎﯾﮕﺰﯾﻦ آدرس ﻗﺒﻠﯽ ﻣﯽﮐﻨﺪ‪.‬‬

‫ﺑﺮاي ﻓﻌﺎلﺳﺎزي در ﺳﯿﺴﺘﻢﻋﺎﻣﻞ ﻟﯿﻨﻮﮐﺲ ﯾﺎ ﻣﮏ از دﺳﺘﻮر زﯾﺮ اﺳﺘﻔﺎده ﻣﯽﮐﻨﯿﻢ‪.‬‬

‫‪$> source env/bin/activate‬‬

‫و ﻫﻤﭽﻨﯿﻦ ﺑﺎ زدن دﺳﺘﻮر زﯾﺮ ﻣﺤﯿﻂ ﻣﺠﺎزي در ﺳﯿﺴﺘﻢﻋﺎﻣﻞ وﯾﻨﺪوز ﻓﻌﺎل ﻣﯽﺷﻮد‪.‬‬

‫‪$> .\env\Scripts\activate‬‬

‫ﭘﺲ از ﻓﻌﺎلﺳﺎزي در اﺑﺘﺪاي ﺗﺮﻣﯿﻨﺎل ﻧﺎم ﻣﺤﯿﻂ ﻣﺠﺎزي در ﭘﺮاﻧﺘﺰ ﻧﻮﺷﺘﻪ ﻣﯽﺷﻮد‪.‬‬

‫ﻏﯿﺮﻓﻌﺎلﮐﺮدن ﻣﺤﯿﻂ ﻣﺠﺎزي‬


‫زﻣﺎﻧﯽﮐﻪ ﺑﺨﻮاﻫﯿﺪ ﻣﺤﯿﻂ ﻣﺠﺎزي ﺧﻮد را ﺗﻐﯿﯿﺮ دﻫﯿﺪ و روي ﭘﺮوژهاي دﯾﮕﺮ ﮐﺎر ﮐﻨﯿﺪ و ﯾﺎ ﻧﯿﺎز دارﯾﺪ‬
‫ﻣﺤﯿﻂ ﻣﺠﺎزي را ﻏﯿﺮﻓﻌﺎل ﮐﻨﯿﺪ‪ ،‬ﺑﻪراﺣﺘﯽ ﺑﺎ اﺟﺮاي دﺳﺘﻮر زﯾﺮ در ﺗﺮﻣﯿﻨﺎل اﯾﻦ ﮐﺎر را اﻧﺠﺎم دﻫﯿﺪ‪.‬‬

‫‪deactivate‬‬

‫ﺗﻮﺟﻪ ﮐﻨﯿﺪ ﮐﻪ ﺑﺮاي ﻓﻌﺎلﺳﺎزي دوﺑﺎره ﻧﯿﺎز ﺑﻪ ﺳﺎﺧﺖ ﻣﺠﺪد ﻣﺤﯿﻂ ﻣﺠﺎزي ﻧﯿﺴﺖ و ﺗﻨﻬﺎ اﺳﺘﻔﺎده از‬
‫دﺳﺘﻮرات ﻗﺴﻤﺖ ﻓﻌﺎلﺳﺎزي ﮐﻔﺎﯾﺖ ﻣﯽﮐﻨﺪ‪.‬‬

‫ﻧﺼﺐ ﭘﮑﯿﺞ‬
‫ﭘﺲ از ﺳﺎﺧﺖ ﻣﺤﯿﻂ ﻣﺠﺎزي‪ ،‬ﭘﮑﯿﺞﻫﺎ را ﺑﺎ اﺳﺘﻔﺎده ﻣﺴﺘﻘﯿﻢ دﺳﺘﻮر ‪ pip‬و ﺑﺪون ذﮐﺮ ﻧﺴﺨﻪ ﻣﯽﺗﻮاﻧﯿﻢ‬
‫اﺟﺮا ﮐﻨﯿﻢ؛ زﯾﺮا ‪ pip‬ﻣﻮﺟﻮد ﻫﻤﺎن ﻧﺴﺨﻪاي اﺳﺖ ﮐﻪ ﺑﺎ آن ﻣﺤﯿﻂ ﻣﺠﺎزي را ﺳﺎﺧﺘﯿﻢ‪.‬‬

‫ﺑﻪﻋﻨﻮان ﻣﺜﺎل ﭘﮑﯿﺞ ‪ requests‬ﮐﻪ ﺑﺮاي اﻧﺠﺎم درﺧﻮاﺳﺖﻫﺎي ‪ HTTP‬اﺳﺖ را ﻧﺼﺐ ﮐﻨﯿﻢ‪.‬‬

‫‪(env) $> pip install requests‬‬

‫اﯾﻦ ﭘﮑﯿﺞ ﺗﻨﻬﺎ در اﯾﻦ ﻣﺤﯿﻂ ﻣﺠﺎزي ﻧﺼﺐﺷﺪه و در ﭘﺮوژهﻫﺎي دﯾﮕﺮ ﻗﺎﺑﻞ اﺳﺘﻔﺎده ﻧﯿﺴﺖ‪.‬‬
‫‪Ahwaz_Hackerz‬‬

‫ﻣﺒﺎﺣﺚ ﭘﯿﺸﺮﻓﺘﻪ در ﭘﺎﯾﺘﻮن ‪ /‬ﻣﻮﺳﺴﻪ ﻓﺮﻫﻨﮕﯽ ﻫﻨﺮي دﯾﺒﺎﮔﺮان ﺗﻬﺮان‬ ‫‪148‬‬

‫ﻟﯿﺴﺖ ﭘﮑﯿﺞﻫﺎي ﻧﺼﺒﯽ‬


‫در ﻫﺮ ﻣﺤﯿﻂ ﻣﺠﺎزي ﺑﺎ ﭘﺎراﻣﺘﺮ ‪ freeze‬در دﺳﺘﻮر ‪ pip‬ﻣﯽﺗﻮاﻧﯿﺪ ﻟﯿﺴﺖ ﭘﮑﯿﺞﻫﺎي ﻧﺼﺐﺷﺪه در آن‬
‫ﻣﺤﯿﻂ را ﺑﻪ ﻫﻤﺮاه ورژن ﻧﺼﺐﺷﺪه آﻧﻬﺎ درﯾﺎﻓﺖ ﮐﻨﯿﺪ‪.‬‬

‫‪(env) $> pip freeze‬‬

‫‪chardet==3.0.4‬‬
‫‪idna==2.10‬‬
‫‪requests==2.24.0‬‬
‫‪urllib3==1.25.10‬‬

‫ﻣﯽﺗﻮاﻧﯿﺪ اﻃﻼﻋﺎت ﭼﺎپﺷﺪه را در ﻓﺎﯾﻠﯽ ﺑﺎ ﻧﺎم ‪ requirements.txt‬ﯾﺎ ﻫﺮ ﻧﺎﻣﯽ ذﺧﯿﺮه ﮐﻨﯿﺪ و‬


‫ﺳﭙﺲ ﺑﺮاي ﻣﺤﯿﻂﻫﺎي دﯾﮕﺮ از آن اﺳﺘﻔﺎده ﮐﻨﯿﺪ‪ .‬ﺳﺎﺧﺖ ﻓﺎﯾﻞ ﻧﯿﺎزﻣﻨﺪيﻫﺎ‪ ،‬ﭘﮑﯿﺞﻫﺎي ﻧﺼﺐﺷﺪه را ﺑﺎ‬
‫ورژن آنﻫﺎ ﻧﮕﻬﺪاري ﻣﯽﮐﻨﺪ‪ .‬اﯾﻦ ﮐﺎر ﺑﻪ اﻧﺘﻘﺎل و ﻧﺼﺐ ﻣﺠﺪد ﭘﮑﯿﺞﻫﺎ در ﯾﮏ ﻣﺤﯿﻂ ﻣﺠﺎزي دﯾﮕﺮ ﮐﻤﮏ‬
‫ﻣﯽﮐﻨﺪ و ﻧﯿﺎزي ﺑﻪ اﻧﺘﻘﺎل ﺣﺠﻢ زﯾﺎد داﯾﺮﮐﺘﻮري ‪ env‬ﻧﯿﺴﺖ‪.‬‬

‫ﺑﻪﻣﻨﻈﻮر ﺳﺎﺧﺖ ﯾﮏ ﻣﺤﯿﻂ ﺟﺪﯾﺪ ﺑﺎ ﭘﮑﯿﺞﻫﺎي ﻧﺼﺒﯽ ﻣﺸﺨﺺ‪ ،‬اﺑﺘﺪا ﯾﮏ ﻣﺤﯿﻂ ﻣﺠﺎزي ﻣﯽﺳﺎزﯾﻢ و‬
‫ﺳﭙﺲ آن را ﻓﻌﺎل ﻣﯽﮐﻨﯿﻢ و ﺑﺎ اﺳﺘﻔﺎده از دﺳﺘﻮر زﯾﺮ و ﻓﺎﯾﻞ ‪ requirements.txt‬ﭘﮑﯿﺞﻫﺎي ﻗﺒﻠﯽ‬
‫را ﻧﺼﺐ ﻣﯽﮐﻨﯿﻢ‪ .‬درواﻗﻊ ‪ pip‬ﺑﺎ ﺧﻮاﻧﺪن ﺧﻂ ﺑﻪ ﺧﻂ اﯾﻦ ﻓﺎﯾﻞ‪ ،‬ﭘﮑﯿﺞﻫﺎ را ﻧﺼﺐ ﻣﯽﮐﻨﺪ و ﻣﺤﯿﻂ ﻣﺠﺎزي‬
‫ﺟﺪﯾﺪ ﻫﻤﭽﻮن ﻣﺤﯿﻂ ﻣﺠﺎزي ﻗﺪﯾﻤﯽ ﺣﺎوي ﭘﮑﯿﺞﻫﺎي ﭘﯿﺸﯿﻦ اﺳﺖ‪.‬‬

‫‪(new_env) $> pip install -r requirements.txt‬‬

You might also like