סדנאת פייתון לפיזיקאים

סקריפטים, מודולים, קבצים ושגיאות

עבודה עם קבצי קוד שונים

הפקולטה לפיזיקה, הטכניון. חורף 2013

מרצה: רונן אברבנאל

עד כה, עבדנו ישירות מול המפרש: הקלדנו פקודות והם בוצעו מיידית.

זה נחמד לפעולות קטנות ופעולות ביניים, אבל זה קצת מטופש לעבוד ככה כל היום .

מומלץ לעבוד עם עורך טקסט ש"מכיר" את פייתון: בפרט, כזה שצובע מילים שמורות בפייתון, כמו למשל כאן:

 if 2==3:
     pass

רצוי גם כזה שיודע לייצר 4 רווחים כשלוחצים על טאב, ויותר טוב: כזה שעושה טאב לבד כאשר מסיימים שורת קוד בנקודותיים.

בכל מקרה, יש להשתמש בתוכנה שיוצרת קובץ טקסט פשוט. פנקס רשימות (notepad) יעשה את העבודה, גם אם לא טוב במיוחד. Word אפילו לא ינסה.

אפשר להשתמש ב-scite שמגיע יחד עם ההתקנה של winPython, ב-notepad++ או באחד מעשרות עורכים טקסט אחרים.

סקריפטים

סקריפט הוא קובץ המכיל רצף של פקודות בזו אחר זו, שמבוצעות על ידי המפרש. סקריפטי פייתון הם קובצי טקסט פשוטים, עם סיימות .py

In [4]:
%load ../example_code/example1.py
In [8]:
message = "Some string"
for word in message.split():
    print word
Some
string

In []:
message = "Hello how are you?"
for word in message.split():
    print word

קובץ זה ניתן להריץ מתוך ipython באמצעות

In [9]:
%run  ../example_code/example1.py
Hello
how
are
you?

In [10]:
message
Out[10]:
'Hello how are you?'

לאחר שהקובץ הורץ, אפשר לגשת למשתנים (הגלובאליים, אלו שלא נמצאים בשום פונקציה) מתוך הסביבה.

ניתן להריץ סקריפט כ"תוכנה עצמאית" ישירות באמצעות המפרש,

 $ python example1.py
 Hello
 how
 are
 you?

יבוא ושימוש במודולים

רק חלק מה"פקודות" והפונקציות של פייתון זמינות מרגע שפחנו את המפרש (או, מרגע שהתחלנו לכתוב קוד). חלק מהפקודות והפונקציות נמצאות בתוך "מודולים" אותם יש לייבא במפורש.

In [7]:
import antigravity
xkcd

אחד המודולים השימושיים והנפוצים בפייתון הוא המודל os המכיל פקודות שקשורות לקבצים, למחשב ולמערכת ההפעלה

In [11]:
import os
os
Out[11]:
<module 'os' from '/usr/lib/python2.7/os.pyc'>
In [13]:
os.listdir("/lib64")
Out[13]:
['ld-linux-x86-64.so.2']

למשל, הפונקציה listdir שמקבלת שם של ספריה ומחזירה רשימה של קבצים הנמצאים בספריה

אפשר לייבא גם פקודות בודדות מתוך מודול:

In [8]:
 from os import listdir
listdir(".")
Out[8]:
['.ipynb_checkpoints',
 'lecture1.ipynb',
 'lecture2.ipynb',
 'lecture4.ipynb',
 'Untitled0.ipynb',
 'lecture3.ipynb']

וכן לשנות את השם של מודולים מיובאים לשם קיצור, למשל,

In [15]:
import numpy as np
my_new_zeros = np.zeros
my_new_zeros(5)
Out[15]:
array([ 0.,  0.,  0.,  0.,  0.])

הערה אפשר ליבא את כל הפונקציות מתוך מודול מסויים באמצעות *, למשל,

In [11]:
from os import *

זה נוח, אבל עלול גם ליצור בעיות, בפרט, אם יובאו שני מודולים ובהם פונקציות עם אותו השם. לכן, יש להמנע מזה.

עבודה עם קבצי טקסט

כתיבת וקריאת קבצי טקסט "פשוטים" בפייתון היא עסק פשוט למדי, אבל נדון בו רש בשטחיות. בהמשך, נראה איך עובדים עם קבצים "מסובכים" יותר.

In [9]:
f = open("somefile.txt","w")
In [13]:
f
Out[13]:
<open file 'somefile.txt', mode 'w' at 0x2e774b0>

כאן, הפרמטר הראשון הוא שם הקובץ, והפרמטר השני הוא אחד (או יותר) מהתווים r,w,a, על מנת לפתוח את הקובץ לקריאה בלבד, לכתיבה או להוספת טקסט בסופו.

In [14]:
f.write("This is content of a file\n with two lines")
f.close()

חשוב מאוד לסגור קובץ בגמר השימוש!

כדי לקרוא מקובץ,

In [15]:
f = open("somefile.txt","r")
content = f.read()
print content
f.close()
This is content of a file
 with two lines

טיפול בשגיאות

בדוגמאות השונות ראינו מדי פעם שהמפרש זרק עלינו שגיאה. כשכותבים קוד ישירות זה נחמד - יודעים שהיתה בעיה ופועלים בהתאם, אבל אם כותבים סקריפטים ארוכים, שגיאה כזו גורמת להפסקת הריצה.

שגיאות לדוגמה

In [8]:
1/0
---------------------------------------------------------------------------
ZeroDivisionError                         Traceback (most recent call last)
<ipython-input-8-05c9758a9c21> in <module>()
----> 1 1/0

ZeroDivisionError: integer division or modulo by zero
In [9]:
d = {1:1, 2:2}
d[3]
---------------------------------------------------------------------------
KeyError                                  Traceback (most recent call last)
<ipython-input-9-38f25624df81> in <module>()
      1 d = {1:1, 2:2}
----> 2 d[3]

KeyError: 3
In [10]:
l = [1,2,3]
l[3]
---------------------------------------------------------------------------
IndexError                                Traceback (most recent call last)
<ipython-input-10-0e767cd8fb1f> in <module>()
      1 l = [1,2,3]
----> 2 l[3]

IndexError: list index out of range
In [11]:
open("there_is_no_file","r")
---------------------------------------------------------------------------
IOError                                   Traceback (most recent call last)
<ipython-input-11-b30199f33520> in <module>()
----> 1 open("there_is_no_file","r")

IOError: [Errno 2] No such file or directory: 'there_is_no_file'

התמודדות עם שגיאות

כדי לתפוס שגיאות, אפשר להשתמש ב-try, except

In [16]:
try:
    open("somefile.txt","r")
except:
    print "some error happend!"

אפשר לתפוס שגיאה ולא לעשות כלום באמצעות pass

In [13]:
try:
    a = 1/0
except:
    pass #Do nothing

הערה פייתון לא אוהב בלוקים ריקים. אם איפשהו אתם כותבים בלוק ואין לכם מה לכתוב בו, שימו בו pass.

אפשר גם לתפוס שגיאות מסוגים ספציפיים:

In [17]:
try:
    l = [1,2,3]
    l[4]
except KeyError:
    print "Key Error catched"
---------------------------------------------------------------------------
IndexError                                Traceback (most recent call last)
<ipython-input-17-3bf3e93f1555> in <module>()
      1 try:
      2     l = [1,2,3]
----> 3     l[4]
      4 except KeyError:
      5     print "Key Error catched"

IndexError: list index out of range

כאן, הקוד זרק IndexError אבל ניסינו לתפוס רק KeyError, אז הוא לא נתפס. לו היינו כותבים,

In [18]:
try:
    l = [1,2,3]
    l[4]
except IndexError:
    print "Index Error catched"
Index Error catched

אבל למה לתפוס רק סוגים מסויימים של שגיאות?

לפעמים סוגים מסויימים של שגיאות הם "חוקיים" - אם הקובץ לא נמצא אפשר לעבור הלאה, אבל בטעות חילקנו באפס, זו קטסטרופה, ואנחנו רוצים להודיע למשתמש.

לא חייבים לתפוס שגיאות מיד. אפשר גם לתת להן לצוף "למעלה"

In [19]:
 def open_end_divide(filename, number):
    f = open(filename,"r")
    print "file opened"
    f.close()
    return 1./number
In [20]:
 open_end_divide("somefile.txt", 3)
file opened

Out[20]:
0.3333333333333333
In [20]:
try:
    open_end_divide("nofile.tמxt", 3)

except DefaultError, m:
    print m
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
<ipython-input-20-5fe6e823b53c> in <module>()
      2     open_end_divide("nofile.tמxt", 3)
      3 
----> 4 except Error, m:
      5     print m

NameError: name 'Error' is not defined
In [22]:
try:
    open_end_divide("somefile.txt", 0)
except Exception, m:
    print m.args, m.message
("name 'open_end_divide' is not defined",) name 'open_end_divide' is not defined

תרגילון

  1. כתבו תוכנית (בקובץ הנפרד) המבצעת:
  • מקבלת שני פרמטרים משורת הפקודה.

השתמשו ברשימה argv שנמצאת במודול sys * הפרמטר הראשון הוא שם ספריה והפרמטר השני הוא מילה לחיפוש. * התוכנית מדפיסה את שמות כל קבצי ה-.py בספריה המופיעה בפרמטר הראשון, המכילים את המילה בפרמטר השני.

  1. מה קורה כאשר התוכנית שכתבתם לעיל מקבלת שם ספריה שאינו קיים?

נסו לפתוח ספריה לא קיימת וקבלו הודעת שגיאה. שנו את התוכנית כך שאם הספריה לא קיימת, התוכנית לא תבצע דבר, ורק תדפיס הודעת שגיאה.