קוד הפיתון ה"רציני" הראשון שלי

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

בגרסה הראשונה שלי, כתבתי את הקוד הבא:

1 #!/usr/bin/env python
2 # -*- coding: utf-8 -*-
3 #
4 # The aim of this script is to raise the network again if there is no response from the gateway/router.
5 # The script must run as root, preferred in a crontab
6
7 import os
8 import sys
9 import re
10
11 lifeline = re.compile(r"(\d) received")
12
13 def isAlive(address):
14 """
15 This function check using ping to see if an address is responsive.
16 The address parameter is the domain/ip to check the connectivity.
17
18 If the address is not responsive, then it will return 0.
19 If the address is partly responsive, then it will return 1.
20 If the address is fully responsive, then it will return 2.
21 """
22   ping = os.popen("ping -q -c2 " + address, "r")
23   while 1:
24     line = ping.readline()
25     if not line:
26       break
27     answer = re.findall(lifeline,line)
28     if answer:
29       return int(answer[0])
30
31 def raiseNetwork():
32 """
33 This function execute the /etc/init.d/networking daemon again.
34 """
35   network = os.popen("/etc/init.d/networking restart", "r")
36   while 1:
37     line = network.readline()
38     if not line:
39       break
40
41 if __name__ == '__main__':
42   if int(isAlive("1.2.3.4")) == 0:
43     raiseNetwork()

ונתתי למאיר להעיר לי (וגם לשכתב) את הקוד שלי.

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

לאחר כל ההערות וההארות שלו כלפי וכלפי הקוד שלי, הוא גם שלח לי תיקון:

1 #!/usr/bin/env python
2 #
3 # raise the network again if there is no response from the gateway/router.
4 # The script must run as root, preferred in a crontab
5
6 from subprocess import Popen, PIPE
7
8 def is_alive(address):
9     """Ping a host for responsiveness. `address` is domain or ip.
10
11     Return:
12         True: fully or partially responsive
13         False: not responsive
14     """
15
16     ping = Popen(['ping', '-c 2', address], stdout=PIPE, stderr=PIPE)
17     ping.communicate()
18     return not ping.returncode
19
20 def raise_network():
21     """Restart /etc/init.d/networking service"""
22
23     network = Popen(['/etc/init.d/networking','restart'], shell=True,
24             stdout=PIPE, stderr=PIPE)
25     network.communicate()
26
27 if __name__ == '__main__':
28     if not is_alive('5.6.7.8'):
29         raise_network()

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

ורק התוצאה הבאה סוף כל סוף עושה את העבודה:

1 #!/usr/bin/env python
2 #
3 # raise the network again if there is no response from the gateway/router.
4 # The script must run as root, preferred in a crontab
5
6 from subprocess import Popen, PIPE
7 from os import system
8
9 def is_alive(address):
10     """Ping a host for responsiveness. `address` is domain or ip.
11
12     Return:
13         True: fully or partially responsive
14         False: not responsive
15     """
16
17     ping = Popen(['ping', '-c 2', address], stdout=PIPE, stderr=PIPE)
18     ping.communicate()
19     return ping.returncode == 0
20
21 def raise_network():
22     """Restart /etc/init.d/networking service"""
23
24 #    network = Popen(['/etc/init.d/networking', 'restart'], shell=True,
25 #            stdout=PIPE, stderr=PIPE)
26 #    network.communicate()
27     system("/etc/init.d/networking restart")
28
29 if __name__ == '__main__':
30     if not is_alive('1.2.3.4'):
31         raise_network()

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

אז מאיר, תודה רבה על העזרה !

* המספרים שאתם רואים נכתבו (ע"י vim והועתקו על ידי) בשביל לשמור על הזחות

12 מחשבות על “קוד הפיתון ה"רציני" הראשון שלי

  1. עידו

    אגב, כדי להשאר בעולם של subprocess אתה יכול להחליף את שורה 27 בקוד האחרון שלך בשורה הבאה:
    subprocess.call(["/etc/init.d/networking", "restart"])

    ובאותה מידה, אתה יכול להחליף את שורות 17-19 בשורה:
    return subprocess.call(['ping', '-c 2', address]) == 0

    ואם אתה לא מעוניין בפלט על המסך, אתה יכול להוסיף הפניה של STDOUT ל PIPE או ל dev/null/ (בפלטים גדולים הפנייה ל PIPE בלי קריאה ממנו עלולה לפגוע בביצועים)

  2. צפריר כהן

    לקח לי זמן להבין מה זה עושה.

    לכן, לטובת הקהל, תרגום ל־shell מדוברת:

    #!/bin/sh
    ADDRESS=1.2.3.4
    # check if we can contact ADDRESS. otherwise restart networking
    if ping -c2 $ADDRESS >/dev/null 2>&1; then
    # all's well
    return
    fi
    /etc/init.d/networking restart

  3. מאיר

    אכן, זה מה ששאלתי "מדוע לא shellscript". למעט מבנה ושימוש ב-docstring, אין כאן שימוש בתכונות של השפה.

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

    העדפה שלי היא לשימוש ב-subprocess ולא ב-system מאחר והאחרון משתמש ב-subshell, מה שיכול להכשיל בהמשך הדרך (לדוגמא מעבירים מידע משתמש, כגון פרמטרים בשורת פקודה, אשר יכול לשמש לתקיפה אם מועברים כמות שהוא). הפקודות של subprocess לא משתמשות ב-shell בברירת מחדל, אלא אם מעבירים להם פרמטר ספציפי, לדעתי עדיף להזהר מראש מאשר לחטוף בהמשך הפיתוח.

  4. עירא

    מודה שלא כתבתי מספיק בפייתון כדי להסכים או לבקר, אבל מה משגע אותך בהזחות? VIM יודע להפעיל הזחות אוטומטיות כשאתה עורך, זה אמור להיות קל…

  5. ik_5 מאת

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

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

  6. אילן שביט

    עידו
    פייתון בעיני היא שפה קלה ואלגנטית. ממש לא ברור לי למה אתה מסתבך עם ההזחות. תיקח IDE סביר (ERIC, SPE או במקרה הגרוע IDLE) ותעבור איתו. תקבל על הדרך גם Debugger סביר, Code completion ועוד… (בקיצור אל תעבוד עם VIM כ- IDE)

    בעניין ההזחה: מה זה משנה אם יש לך טוקן כזה '}', כזה 'Begin' או כזה שמיושם באמצעות ארבעה רווחים (להיזהר לא להשתמש ב- Tab!).
    SPE למשל "מבין" את מה שאתה כותב ואם תרשום while, if ועוד הוא לבד ימקם את הסמן המיקום המתאים בשורה החדשה.
    הנושא של Begin ו- End, למשל, זה אחד הדברים שהכי מעצבנים בפסקל: צריך לכתוב הרבה על כלום… זה נותן לך הרגשה של תלמיד כיתה ט' שכותב תוכנה בפעם הראשונה שלו…

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

  7. ik_5 מאת

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

    הסיבה שאני מוצא את begin ו end עדיפים על הזחה ועל סוגריים מסולסלים היא, שהם קראים מאוד. הרבה פעמים אני מתקשה כשאני עייף להבין איפה מתחילים ונגמרים בלוקים. עם begin ו end הרבה יותר קל לי להבין מה שייך למה. אני חושב שהמאמץ הזה (במידה ואתה משתמש בעורכי טקסט, ולא בלזרוס/דלפי, ששם אפשר לחסוך את הכתיבה הזו, רק אני לא עושה את זה אישית) משתלם בטווח הרחוק, היות וסימנים זה לא תמיד הדבר הכי קריא, ואנחנו כן רגילים לקרוא טקסט.

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

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

  8. אילן שביט

    כן, אני יודע שיש הרחבה ל- vim שמאפשרת "להבין" את השפה:
    http://www.vim.org/scripts/script.php?script_id=790
    בסביבת gnome היא כבר מגיעה build in אבל כשעובדים מול שרתים (ללא סביבה גרפית) היא יכולה לעזור. בנוסף יש הרחבות רבות שעוזרות לעבוד עם פייתון ב- vim (כמו סופרטאב), אבל עדיין מדובר בעורך טקסט (משוכלל) ולא IDE.

    בכל מקרה: הייתי שמח אם היו מאפשרים, כמו בפרל, להגדיר
    ;use strict
    בקשר להגדרות של בלוקים: לדעתי זה עניין של הרגל. אם היית יודע רק פייתון וביום בהיר עובר לפסקל היית עובר גם כן הלם תרבותי …🙂

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

  9. ik_5 מאת

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

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

  10. אילן שביט

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

    לגבי ; – זה בזבוז של עוד תו שמוקלד🙂 (בדומה לזה שלא משתמשים ב- } אלא בהזחה). נקודותיים שדיברתי עליו מפריד בין בדיקת התנאי לשאר המבנה (וזה חשוב במיוחד בבטויי למבדא).
    אם אתה רוצה להמשיך לכתוב בשורה חדשה אתה משתמש ב- \\

כתיבת תגובה

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

הלוגו של WordPress.com

אתה מגיב באמצעות חשבון WordPress.com שלך. לצאת מהמערכת / לשנות )

תמונת Twitter

אתה מגיב באמצעות חשבון Twitter שלך. לצאת מהמערכת / לשנות )

תמונת Facebook

אתה מגיב באמצעות חשבון Facebook שלך. לצאת מהמערכת / לשנות )

תמונת גוגל פלוס

אתה מגיב באמצעות חשבון Google+ שלך. לצאת מהמערכת / לשנות )

מתחבר ל-%s