ToCode
Kanalga Telegram’da o‘tish
טיפים קצרים למתכנתים מאת ינון פרק
Ko'proq ko'rsatish1 419
Obunachilar
-124 soatlar
-17 kunlar
-130 kunlar
Postlar arxiv
1 419
# שתי שיטות ערבוב ב Python
המודול random של Python מציע שתי דרכים לערבב אלמנטים ברשימה: הראשונה היא הפונקציה sample והשניה הפונקציה shuffle. בואו נראה את ההבדל ומתי נשתמש בכל שיטה.
בפייתון ברוב הפעמים שנרצה לשנות רשימה נשתמש במתודות של אוביקט הרשימה עצמו - לדוגמא
arr.append, arr.reverse ו arr.clear. אחת הפקודות המעניינות של רשימות בהקשר זה היא [].sort שממיינת את הרשימה בסדר עולה:
arr = [10, 20, 7, 30]
arr.sort()
arr
Out[23]: [7, 10, 20, 30]
והחברה שלה היא הפונקציה sorted שמקבלת רשימה ומחזירה רשימה חדשה ממוימת:
arr = [10, 20, 7, 30]
sorted(arr)
Out[30]: [7, 10, 20, 30]
הפקודה arr.sort שינתה את הרשימה, ולעומתה sorted(arr) לא שינתה את הרשימה המקורית ורק החזירה רשימה חדשה.
באותו האופן אבל בתחביר קצת שונה יש לנו שתי דרכים לערבב את האלמנטים ברשימה. כאן במקום להכניס את shuffle כפונקציה של מערך ואת shuffled כפונקציה גלובלית נפרדת בחרו ב Python לייצר שתי פונקציות שונות בתוך המודול random.
הפונקציה הראשונה random.shuffle מקבלת רשימה ומשנה אותה במקום, כלומר:
In [31]: arr = [10, 20, 7, 30]
In [32]: random.shuffle(arr)
In [33]: arr
Out[33]: [20, 30, 10, 7]
זה עובד בדיוק כמו המתודה sort של מערך, אפילו שהמערך עובר כפרמטר לפונקציה.
הפונקציה random.sample לעומתה יודעת להחזיר רשימה של "מדגם" בכל אורך מתוך מערך. הפרמטר k קובע את גודל המדגם. הנה כמה דוגמאות:
In [34]: random.sample(arr, k=2)
Out[34]: [10, 7]
In [35]: random.sample(arr, k=3)
Out[35]: [30, 20, 10]
In [36]: random.sample(arr, k=2)
Out[36]: [10, 30]
In [37]: random.sample(arr, k=2)
Out[37]: [7, 30]
זאת הסיבה שבתיעוד של random ממליצים לנו להשתמש בפונקציה זו אם אנחנו רוצים לקבל את איברי הרשימה בסדר אקראי בלי לפגוע ברשימה המקורית. כל מה שצריך זה להעביר בתור ערך של k את אורך הרשימה:
In [38]: random.sample(arr, k=len(arr))
Out[38]: [30, 7, 10, 20]
ומה לגבי זמן ריצה? לא מפתיע לגלות שהגירסא שמשנה את הרשימה In Place היא קצת יותר מהירה בעבודה על רשימות גדולות:
In [43]: a = [random.randint(0, 100) for _ in range(1_000_000)]
In [44]: %timeit random.sample(a, k=len(a))
629 ms ± 24.5 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
In [45]: %timeit random.shuffle(a)
538 ms ± 20.9 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
אז במי נבחר? כמו הרבה פעמים בתכנות התשובה הנכונה היא "זה תלוי" - אם יש לכם רשימה שערבוב האיברים בה הוא חלק מסדר של כמה פעולות שצריך לעשות ואחרי זה אף אחד לא יצטרך עוד את המידע אפשר להשתמש ב shuffle. לעומת זאת אם אתם צריכים לערבב חלק מרשימה שקיבלתם אבל יש עוד מקומות בקוד שעובדים עם אותו מידע סיכוי טוב שתעדיפו לא לשנות את המידע המקורי ולספוג את הפגיעה הקלה מאוד בזמן הריצה (ממילא כנראה לא תרגישו את זה).1 419
# הטעות הכי נפוצה של פרילאנסרים (ואיך להימנע ממנה)
יותר מדי פרילאנסרים שאני מכיר נופלים במלכודת שמונעת מהם להרוויח כסף, ביטחון תעסוקתי ושלוות נפש. נכון, צריך לעבוד קשה בשביל לקבל כסף, ביטחון תעסוקתי ושלוות נפש בתור פרילאנס, אבל גם העבודה הכי קשה שבעולם לא תעזור אם אתם טועים בנקודה הזאת.
והנקודה היא ההבדל בין פרילאנס ליזם.
בואו ניתן לזה לשקוע רגע עם שתי דוגמאות:
1. חבר פרילאנסר מייעץ לארגונים איך לשפר תהליכי עבודה. הוא מגיע לארגון, לומד מה כולם עושים, עושה את הקסם שלו ושלושה חודשים אחר כך כולם עובדים יותר יעיל. יום אחד, כך הוא מספר, יהיו לי מספיק לקוחות, מספיק פרויקטים, תהליכים מספיק מסודרים - ואז אביא עובדים שיחליפו אותי ותהיה לי חברת ייעוץ ענקית.
2. חברה פרילאנסרית היא אלופה בנגישות אתרים. רק ממבט חטוף ב HTML היא תדע להגיד לכם מה שבור באתר שלכם ואיך לתקן. היא מעסיקה סטאז'ר וחולמת יום אחד לתת לו לעשות את רוב העבודה השחורה, והיא תעשה רק את המכירה.
שניהם לא בכיוון.
כשהחבר היועץ יתחיל להביא עובדים הוא יגלה שהלקוחות מוכנים לשלם הרבה פחות בשביל יועץ עובד מאשר שעת ייעוץ שלו, והרווח שנשאר לו בסוף יהיה הרבה יותר קטן ממה שהרוויח כשהיה לבד.
וככל שהחברה מנגישת האתרים תתן יותר עבודה לסטאז'ר שלה, הוא יגלה בסוף שעדיף לו למכור לבד את שירותיו כמנגיש אתרים ואולי אפילו הוא יוכל להתחיל לעבוד ישירות עם חלק מהלקוחות שלה (שבעצם עבדו כל הזמן איתו).
פרילאנס מרוויח על הזמן שהוא עובד. לחלום על היום בו לא תעבדו יותר לא יקדם אתכם במסלול להצלחה, שלוות נפש ואושר כפרילאנסרים, להיפך. זה רק יגרום לכם לעשות טעויות שיפגעו לכם בקריירה.
(נ.ב. אני לא מציע לכם לעבוד לבד. העבודה עם עוד אנשים מועילה ומפרה, והרבה פעמים כשאנחנו מוציאים עבודה רלוונטית לפרילאנסרים אחרים כולם מרוויחים. אני כן מציע לכם לבנות את העסק כך שתרוויחו יותר על הזמן בו אתם עובדים, במקום להתאמץ להרוויח קצת בלי לעבוד).
1 419
# מה שטוב לפרויקט
הדרך היחידה להתפרנס לאורך זמן כפרילאנסר היא לעשות עבודה טובה, עבודה שאנשים אוהבים באותו רגע אבל גם כזו שממשיכה להשאיר חותם הרבה אחרי שהלכת.
ובתור פרילאנסרים לא פעם ניתקל בלקוחות שבגלל משבר בטווח הקצר מאוד מתקשים לתת לך את המשאבים לעשות את העבודה שאתה יודע שצריך לעשות.
קחו לדוגמא לקוח שממשיך להתלבט לגבי העיצוב חודשים אחרי שהמערכת עובדת ובגלל זה דוחה את פרסום המערכת. הפרילאנסר שבנה את המערכת קיבל את הכסף, אבל כל עוד המערכת לא עובדת והלקוח לא משתמש בה, אנחנו מפסידים את הרווח לטווח הרחוק שהיה יכול לעזור לנו לקבל פרויקטים טובים יותר.
אני לא מכיר פיתרון קסם שמרכך את כל הלקוחות הקשים, אבל כמה דברים שעבדו לי לאורך השנים כולללים:
1. להוסיף לוחות זמנים בחוזה עבודה, עם קנסות על חריגה.
2. לבחור לקוחות שכבר יש להם מערכת או שיודעים להרים מערכות.
3. לייצר תהליך עבודה מול הלקוח שכולל שיקוף לאורך כל הדרך של הקשיים והדאגות של הלקוח. אני אוהב למשל לייצר רשימת טרלו משותפת של "דברים שצריך לסגור לפני השקה".
4. להיכנס לפרויקט אחרי שרוב הנכסים מוכנים, כלומר אחרי שיש איפיון ברור, מטרה ברורה למערכת ורוב הגרפיקה כבר נוצרה.
5. לשים את הדברים בצורה ברורה על השולחן, כלומר לדבר עם הלקוח כבר משיחת המכירה ולהסביר שהמטרה שלכם היא שתהיה מערכת באוויר עד תאריך עליו תסכמו.
6. והכי חשוב להשאיר לכם דרך מילוט כשאתם מזהים שהלקוח לא בראש שלכם ושאתם טוחנים מים. לאורך זמן גם אם תפסידו כסף בפרויקט אחד או שניים, הבחירה שלכם להתעסק רק בפרויקטים טובים שישפרו את המוניטין שלכם היא הדרך הכי טובה להצליח.
1 419
# מה ההבדל בין git rm cached ל git reset
הגיעה אליי שאלה מעניינת הבוקר במייל וחשבתי לשתף את התשובה כאן לטובת כולם. והשאלה: מה ההבדל בין הפקודה
git rm --cached לבין הפקודה git reset, מתי אשתמש בכל אחת?
הדבר הראשון שצריך לזכור הוא שקומיט כולל Snapshot של הפרויקט, כלומר אפשר לחשוב עליו בתור עותק של כל הקבצים והתיקיות שבפרויקט. עד שנוצר קומיט יש לנו תיקיה זמנית שנקראת Staging Area אליה אפשר להוסיף וממנה אפשר למחוק קבצים. ברגע שנרגיש מוכנים נפעיל git commit ואז ה Staging Area יהפוך ל Commit Object ויישמר במאגר הגיט לעולמי עולמים. ה Staging Area אגב לא יימחק וימשיך לשמור את כל הקבצים והתיקיות כמו שהם היו בקומיט האחרון.
הפקודה git add לוקחת קובץ מתיקיית העבודה ומעתיקה אותו לאותה תיקיה זמנית של "הכנה לקומיט" שקראתי לה Staging Area. הפקודה git reset לוקחת קובץ מהריפוזיטורי ומעתיקה אותו לאותה תיקיה זמנית. לכן אפשר לחשוב על שתי פקודות אלה בתור הפכים. נכין רגע מאגר לדוגמא:
$ mkdir demo
$ cd demo
$ git init
$ echo one > demo.txt
$ git add demo.txt
$ git commit -m 'commit 1'
אז כשאני יוצר קובץ חדש בתיקיית העבודה אני יכול להשתמש ב git add כדי להוסיף אותו לתיקיה הזמנית:
$ echo two > two.txt
$ git add two.txt
$ git ls-files --stage
100644 5626abf0f72e58d7a153368ba57db4c673c0e171 0 demo.txt
100644 f719efd430d52bcfc8566a43b2eb655688d38871 0 two.txt
או בפקודה git reset כדי להעתיק את הגירסא מהריפוזיטורי לתיקיה הזמנית מה שיגרום למחיקת הקובץ משם:
$ git reset two.txt
$ git ls-files --stage
100644 5626abf0f72e58d7a153368ba57db4c673c0e171 0 demo.txt
בכל מקרה מהלך זה לא השפיע על הקבצים בתיקיה הנוכחית שלי או במאגר. כל מה שאני עושה זה בוחר איזה קבצים ייכנסו לקומיט הבא.
לעומתה הפקודה git rm הרבה יותר דומה לפקודה git add, רק שבעוד ש git add מעתיקה קובץ מתיקיית העבודה ל Staging Area, תפקיד הפקודה git rm הוא להעתיק את ה"אין קובץ", כלומר למחוק קובץ מה Staging Area.
נזכור שהדרך הרגילה שלנו להוציא קובץ מה Staging Area היא לדרוס את הקובץ ולהחליף אותו בגירסא ששמורה בריפוזיטורי, וזה מה שעושה git reset. אבל אם אנחנו רוצים למחוק קובץ לחלוטין זה לא באמת יעזור להעתיק את הגירסא מהריפוזיטורי. במילים אחרות בדוגמא שלנו הקובץ demo.txt נמצא ב Staging Area וגם ב Repository. אם אפעיל git reset עליו לא יקרה כלום:
$ git ls-files --stage
100644 5626abf0f72e58d7a153368ba57db4c673c0e171 0 demo.txt
$ git reset demo.txt
$ git ls-files --stage
100644 5626abf0f72e58d7a153368ba57db4c673c0e171 0 demo.txt
בכל מקרה הוא נשאר ב Staging Area וייכנס לקומיט הבא. הפקודה git rm היא הדרך שלנו להעתיק את העובדה שהקובץ לא קיים אל ה Staging Area. שימו לב להשפעה שלה על המאגר שלנו:
$ git rm demo.txt
$ git ls-files --stage
$ ls
two.txt
עכשיו ה Staging Area ריק. מאחר והקומיט הבא שיווצר הוא בסך הכל עותק של ה Staging Area ברגע יצירת הקומיט, הקובץ demo.txt לא יהיה חלק מהקומיט הבא. הפעם לא לקחנו את הגירסא מהריפוזיטורי והעתקנו אותה ל Staging Area, אלא בדיוק כמו עם add לקחנו את הגירסא מתיקיית העבודה והעתקנו אותה ל Staging Area, אבל עשינו את זה אחרי שמחקנו את הקובץ מתיקיית העבודה.
ומה לגבי git rm --cached ? אם הגעתם עד לפה הסיפור שלו הוא הכי פשוט מכולם. לפעמים אנחנו תקועים עם קובץ או תיקייה שאנחנו רוצים להוציא מהריפוזיטורי אבל להשאיר אצלנו בתיקיית העבודה. דוגמא קלאסית היא תיקיית node_modules: בטעות הוספנו תיקיה זו למאגר ועכשיו אנחנו רוצים להוציא אותה, אבל התיקיה כן עוזרת לי בעבודה השוטפת ולכן אני לא רוצה למחוק אותה מהדיסק. הפעלת git rm -r node_modules לא באה בחשבון כיוון שהיא תביא למחיקת התיקיה מהדיסק. אבל, אם נוסיף את --cached נקבל גירסא של git rm שלא מוחקת את הקובץ או התיקיה מהדיסק אלא רק מה Staging Area.
בחזרה למאגר הדוגמא נחזיר את הקובץ demo.txt מהריפוזיטורי עם:
$ git restore -s HEAD demo.txt
$ git ls-files --stage
100644 5626abf0f72e58d7a153368ba57db4c673c0e171 0 demo.txt
ועכשיו נמחק אותו רק מה Staging Area בלי למחוק אותו מתיקיית העבודה:
$ git rm --cached demo.txt
$ ls
demo.txt two.txt
$ git ls-files --stage1 419
# למה (ואיך) להמשיך ללמוד טכנולוגיה לאורך שנים
פייתון, ביג דאטה, פול סטאק, ריאקט, נוד, גיט - אנשים באים לפה ללמוד כל אחת מהטכנולוגיות האלה ומתכנתים רבים שאני מכיר מאוד מתלהבים כשמגלים אותן לראשונה. אבל גם למתכנתים המוכשרים ביותר קשה להמשיך להינות וללמוד את אותה טכנולוגיה לאורך שנים, וזאת הסיבה שמתכנתים רבים כל כך מחפשים כל הזמן ללמוד את הדבר הבא.
אין רע בלהישאר פתוחים לרעיונות חדשים, בלהמשיך לחקור, בלנסות כל הזמן לגלות עולמות חדשים. כל עוד הנטיה לגלות את הדבר הגדול הבא לא באה על חשבון הדבר הגדול הקודם. או במילים אחרות, כל עוד אתם לא חוזרים כל פעם לנקודת ההתחלה.
הסיבה הכי טובה להמשיך ללמוד יותר לעומק את הדבר הגדול הקודם היא שלימוד קורה בשכבות. יש דברים בריאקט שתצליחי לראות רק אחרי שנתיים של עבודה עם הפריימוורק. לכן אם אחרי שנה את מרגישה שאת כבר יודעת הכל והגיע הזמן להמשיך הלאה לפריימוורק הגדול הבא, את תפסידי את הקומה השניה, השלישית והחמישית. זה כמו לבנות בניין אבל כל פעם להתחיל מחדש אחרי שמסיימים את הקומה הראשונה.
אני חושב שסיבה מרכזית לקפיצה היא התחושה אחרי שסיימנו את הקומה הראשונה שאנחנו כבר יודעים הכל. שהקומה השניה, השלישית והעשירית הן בסך הכל עוד מאותו דבר, ולכן אם כבר ללמוד עדיף ללמוד משהו חדש לגמרי. לאורך זמן תחושה זו מחזקת את עצמה כיוון ששנה קדימה עם הטכנולוגיה החדשה ואנחנו שוב מרגישים בדיוק את אותה תחושה שכבר ראינו הכל, ולכן ממשיכים את המעגל ושוב קופצים לדבר הגדול הבא.
האלטרנטיבה היא קשה ושווה את המאמץ: במקום להתחיל בניין חדש בטכנולוגיה חדשה לגמרי, להתאמץ ולבנות את הקומה השניה בטכנולוגיה הנוכחית. אחריה את השלישית, החמישית והעשירית - כשכל קומה פותחת לנו את הדלת לידע חדש ונקודת מבט חדשה על אותם אתגרים.
אבל- איך עושים את זה? מהי אותה קומה שניה? ואיך אני יודע שאני לא עושה פשוט "עוד מאותו דבר?". הנה שלושה טיפים שעוזרים לי להתמיד:
## חפשו גישות חדשות
בכל טכנולוגיה שאתם לומדים יש המון דרכים לגשת לפיתרון בעיות. בקומה הראשונה אנחנו לוקחים את הדרך ההגיונית, הפופולרית, המקובלת ביותר לפיתרון. בשביל להגיע לקומה הבאה צריך לחפש דרכים נוספות, גם אם בהתחלה לא לגמרי ברור מתי הן טובות יותר. אם ניקח לדוגמא את Redux, אז יכול להיות שבהתחלה למדתם להוציא בקשות Ajax עם Redux Thunk וזה עבד אחלה. בשלב הבא אתם יכולים לנסות ללמוד איך עובד Saga ואחרי זה לכתוב Middleware בעצמכם.
## חפשו מה אתם עדיין לא יודעים
לכל טכנולוגיה יש פינות אפלות שרוב המתכנתים מצליחים להסתדר מעולה בלי לגעת בהן. אחת הדרכים להגיע להיכרות טובה יותר עם הטכנולוגיה היא בדיוק לחפש ולהיכנס לאותן פינות אפלות ולראות מי צריך אותן ולמה.
בחזרה ל React ו Redux - אני מוכן להמר שהסתדרתם עד עכשיו די טוב בלי לדעת מה זה Store Enhancer, ולכן כתיבה של אחד היא דרך להיכנס לשביל שעדיין לא צעדתם בו. יכול להיות שאתם מרוצים ממש מריאקט עם JavaScript, ובדיוק בגלל זה שווה לנסות לראות איך ריאקט עובד עם TypeScript. ואם כבר יש לכם בדיקות אוטומטיות לפרויקט הכתובות ב Selenium אולי הגיע הזמן להעיף מבט על Jest ולהוסיף גם בדיקות יחידה.
המטרה במשחקים האלה היא פשוט לגלות עוד ועוד חלקים בטכנולוגיה שעדיין לא נגעתם בהם. תמיד יש עוד, וככל שנתאמץ באופן פעיל למצוא אותם כך נוכל להמשיך ללמוד ולהתרגש מהתוצאות לאורך זמן.
## חפשו את המקורות או נקודות מבט נוספות
כל טכנולוגיה שנבחר מושפעת ומשפיעה על עשרות טכנולוגיות אחרות. במקרה של React רעיונות של תכנות פונקציונאלי משחקים תפקיד מאוד חשוב בפריימוורק, ולכן דרך טובה לטפס לקומה הבאה בריאקט תהיה ללמוד כלים של תכנות פונקציונאלי.
הספריה ReasonReact (ואיתה סביבת Reason באופן כללי) היא מקום טוב להמשיך בו את לימודי הריאקט אחרי שאתם מרגישים שכבר יודעים הכל. זו שפת תכנות חדשה לגמרי בתחביר חדש לגמרי, שמשתמש באותה פריימוורק ונותן לכם נקודת מבט חדשה על הדברים שאתם כביכול יודעים.
הבחירה להעמיק במשהו מעבר ליכולת לבנות פרויקט שעובד היא לא מובנת מאליה ולא הרבה מתכנתים לוקחים אותה. בדיוק בגלל זה היא שווה את המאמץ ותתן לכם את היתרון שאתם צריכים כדי להצליח בשוק העבודה ובבניית מוצרים טובים יותר.
1 419
# מנגנוני ניהול זיכרון ב Python
שפות תכנות רבות דורשות מהמתכנתים לנהל את הזיכרון כלומר להפעיל פקודה כדי "לקבל" זיכרון מהמערכת ואז פקודה נוספת כדי "להחזיר" את הזיכרון למערכת. בשפות כאלה מתכנתים ששכחו להחזיר זיכרון למערכת יצרו קוד שככל שפעל יותר זמן רק דרש יותר ויותר זיכרון מהמערכת, עד שהזיכרון של המכונה נגמר. במצבים כאלה היינו אומרים שיש בתוכנית זליגת זיכרון או Memory Leak באנגלית.
המעבר לשפות שמנהלות לעצמן את הזיכרון תיקן חלקית את הבעיה אבל גם שפות שמנהלות לעצמן את הזיכרון עשויות לטעות ולייצר זליגות זיכרון. שני המודלים המרכזיים בהם שפות מנהלות לעצמן את הזיכרון נקראים Reference Count ו Garbage Collection.
מנגנון Reference Count אומר שהשפה שומרת ליד כל אוביקט מספר שמציין כמה משתנים מתיחסים לאוביקט זה. כשהמספר יורד לאפס באופן אוטומטי הזיכרון של האוביקט חוזר למערכת ההפעלה.
מנגנון Garbage Collection אומר שהשפה מפעילה כל כמה דקות תהליך שמחפש אוביקטים שאי אפשר להגיע אליהם ומחזיר אותם למערכת ההפעלה.
שפת פייתון משתמשת בשילוב של שני המנגנונים כדי לנהל את הזיכרון במקום המתכנת. מנגנון Reference Count פועל בצורה מיידית ומנקה את הזיכרון כשמספר השמות של אוביקט מסוים יורד לאפס, ומנגנון Garbage Collection משמש לגיבוי כדי לזהות מעגלים.
מהו מעגל? שמח ששאלתם. מעגל בהקשר של משתנים וזיכרון אומר שיש לנו מספר משתנים שכל אחד מצביע על אחד אחר. דוגמא קלה היא הקוד הבא ב Python:
a = []
a.append(a)
יצרנו רשימה של איבר אחד שהאיבר הזה הוא מצביע לאותה רשימה ממש. זה נחמד כי עכשיו אפשר לגשת לתא הראשון ברשימה כמה פעמים שרק נרצה:
>>> len(a)
1
>>> len(a[0])
1
>>> len(a[0][0])
1
>>> len(a[0][0][0])
1
>>> len(a[0][0][0][0])
1
>>> len(a[0][0][0][0][0])
1
אבל החיסרון הוא שמנגנון Reference Count כבר לא יכול לעבוד. נלך לראות מה פייתון חושב על הקוד שלנו.
המודול sys מספק פונקציה בשם getrefcount שמחזירה את מספר השמות של אוביקט נתון. הנה כמה דוגמאות:
>>> a = []
>>> sys.getrefcount(a)
2
>>> b = {}
>>> sys.getrefcount(b)
2
>>> a = 10
>>> sys.getrefcount(a)
12
שני הראשונים די מסבירים את עצמם: השם a הוא השם הראשון של הרשימה, ואז בתוך הפונקציה getrefcount יש לרשימה זו שם נוסף (זה שם הפרמטר שהופיע בסוגריים בהגדרת הפונקציה).
הקטע האחרון קצת מבלבל - מאיפה הגיעו 12 שמות למספר 10? הסיבה שבכל מקום שאנחנו כותבים 10 יצרנו עוד שם לדבר הזה בזיכרון שאנחנו קוראים לו המספר 10, וגם פייתון עצמו משתמש בקבוע עשר מדי פעם. ככל שנהיה יותר יצירתיים מספר השמות יורד:
>>> a = 19821
>>> sys.getrefcount(a)
2
עכשיו אפשר לחזור למעגל שלנו:
>>> a = []
>>> a.append(a)
>>> sys.getrefcount(a)
3
לאוביקט יש הפעם 3 שמות: השניים הראשונים מוכרים לנו מהדוגמאות הקודמות והשם השלישי הוא ההצבעה הפנימית. מנגנון Reference Count לעולם לא יוכל למחוק משתנה זה מהזיכרון כי מספר השמות שלו לא ירד אף פעם ל-0. ההצבעה הפנימית תימחק רק כשנאפס אותה ידנית.
לכן פייתון משתמש במנגנון משלים שנקרא Garbage Collection. במנגנון זה פייתון יחפש בצורה פעילה משתנים שאי אפשר להגיע אליהם וימחק אותם מהזיכרון.
נתחיל בחיסול האוביקט שלנו וננסה לראות אם פייתון מחק אותו לבד מהזיכרון:
import sys, gc
a = []
a.append(a)
ida = id(a)
a = 10
# is there still an object with id == i ?
>>> ida in [id(x) for x in gc.get_objects()]
True
הפקודה האחרונה משתמשת במנגנון ה Garbage Collector כדי לזהות האם עדיין יש בזיכרון אוביקט שה id שלו הוא מה שהיה ה id של הרשימה שלנו שהצביעה לעצמה. התשובה היא כמובן True.
זאת בדיוק הסיבה שפייתון מחזיקה גם מנגנון של Garbage Collection לגיבוי. מנגנון זה מכיר את כל האוביקטים במערכת וימחק אוביקטים כאשר אי אפשר להגיע אליהם, גם אם הם כוללים הצבעה מעגלית. מנגנון ה gc מתעורר אוטומטית אחת לכמה דקות או כשאנחנו מבקשים להפעיל אותו עם הפקודה gc.collect. ננסה את זה:
>>> gc.collect()
1
>>> ida in [id(x) for x in gc.get_objects()]
False
ועכשיו הרשימה עם המעגל נמחקה מהזיכרון.
בזכות השילוב בין שני המנגנונים הרבה יותר קשה לייצר זליגות זיכרון בתוכניות פייתון. כדאי לשים לב עם זאת שמנגנון ה Garbage Collector מתעורר בזמנים לא צפויים ועלול לפגוע בביצועים של התוכנית אם אנחנו רוצים לכתוב תוכנית שתגיב בזמן אמת לאירועים חיצוניים. לכן קיימת בפייתון הפקודה gc.disable שמבטלת את מנגנון ה Garbage Collection ומשאירה אותנו רק עם Reference Count. אם אתם בטוחים שאין לכם הצבעות מעגליות באפליקציה וממש צריכים ביצועי Real Time אולי זה משהו ששווה לבדוק אותו.1 419
# לא רוצה, לא יכול ולא יודע איך
בכל פעם שאני מנסה לעשות משהו ולא מצליח האינסטינקט הראשון שלי הוא להאשים את "לא רוצה": אני שם שעון לחמש בבוקר כדי לצאת לריצה וכשהוא מצלצל אני שוב נשאר בתוך הפוך במקום ללבוש את הנעלי ספורט. שעתיים אחר כך הדבר הכי קל להגיד לעצמי זה "כנראה שאני לא באמת רוצה להיכנס לכושר".
בן דוד רחוק של "לא רוצה" נקרא "לא יכול" או "אני לא הטיפוס": אני יושב לנסות ללמוד איך Machine Learning עובד ומסתבך עם המתמטיקה. שלושה ימים אחרי הדבר הכי קל להגיד לעצמי יהיה משהו כמו "כנראה שזה לא לאנשים כמוני", או "אין מה לעשות אין לי ראש לדברים כאלה".
ובדיוק בגלל שאני לא רוצה ואני לא יכול הם חברים כל כך טובים שלנו כדאי לשאול עד כמה הם באמת עוזרים לנו להתמודד עם קשיים. הרי אם אני לא באמת רוצה לעשות ספורט אז בשביל מה להתאמץ פעם הבאה? אולי כשאני ארצה באמת זה יצליח. ואם אני לא יכול ללמוד תכנות אז בשביל מה להמשיך לנסות? עדיף למצוא משהו שאני טוב בו.
סיפור קצת יותר יעיל במצבים שדברים לא מצליחים נקרא "לא יודע איך". הוא בן דוד ממש רחוק של לא רוצה ולא יכול, כל כך רחוק שהם נפגשים רק בראש השנה וגם אז בקושי מחליפים שתי מילים. כי כשלא יודע איך נכנס לתמונה הדלת לרעיונות חדשים נפתחת:
כנראה שאני עדיין לא יודע איך לקום מהמיטה בחמש בבוקר לעשות ספורט. לא סוף העולם מחר אנסה לשים את נעלי הספורט יותר קרוב למיטה ואכין לי פרק של פודקסט שאני רוצה לשמוע. אולי זה יעבוד.
או - כנראה שאני עדיין לא יודע איך ללמוד Machine Learning כמו שצריך. אולי שווה לנסות קורס אחר או לשבת ללמוד עם חבר. או אולי שווה לעשות ריענון על תכנים חשובים במתמטיקה לפני שלוקחים את הקורס אונליין שנרשמתי אליו. אנסה מחר.
הרבה פעמים כשלא רוצה ולא יכול מבלבלים לי את המוח, אני מעדיף להזמין את "לא יודע איך" לארוחה. ממילא אחרי שהוא יגיע שני הנודניקים האלה ימצאו סיבה טובה להיעלם, ואני אוכל להמשיך לעבוד במקום להיתקע על תירוצים.
1 419
# שגיאות זה לא אוכל
בליעת שגיאות היא אחת התופעות הנפוצות והכואבות בכתיבת קוד. לבלוע שגיאה זה אומר שהקוד מזהה שמשהו לא בסדר אבל ממשיך לעבוד כרגיל בלי להציף את הבעיה למשתמש. דוגמא לפונקציה שבולעת שגיאה מצאתי השבוע בקוד רובי של חבר:
def self.track_event(user_token)
return if user_token.nil?
session = UserSession.active.find_by(token: user_token)
session&.update_attribute('last_seen', Time.now)
end
הפונקציה מצפה לקבל פרמטר שאינו nil ואת זה היא מוודאת באמצעות if בהתחלה. אבל, אם בטעות היא קיבלה nil השגיאה תיבלע ואף אחד לא ידע שהיה נתיב בעייתי בקוד.
בהמשך הפונקציה מנסה למצוא אוביקט UserSession לפי אותו טוקן. וגם כאן אם היא הצליחה למצוא היא תעדכן את ה session, אבל אם לא הצליחה למצוא היא תבלע את השגיאה ולא תדווח על הבעיה לאף אחד. משתמשים פשוט לא יראו את המידע המעודכן שלהם.
אגב האופרטור &. אומר שאם האוביקט אינו nil אז צריך להפעיל את הפונקציה, אבל אם הוא כן nil אז לא עושים כלום. האופרטור נקרא Optional chaining וגירסא שלו תגיע בקרוב גם ל JavaScript.
בחזרה לרובי - אני מבין מאיפה בא הרצון לבלוע שגיאות. אני מבין את הפחד מלוגים ארוכים מדי ומזה שאף אחד לא מוצא זמן לחפש שגיאות או לטפל בהן וממילא יש לנו מספיק באגים גם בלי לקבל עוד דיווחי שגיאות מהמערכת. באמת שאני מבין. אבל האמת שבליעת שגיאות לא חוסכת לכם זמן אלא מבזבזת לכם יותר ממנו, כי כשמשתמש מדווח על התנהגות מוזרה, הדבר שאתם הכי רוצים בעולם זה לפתוח את הקובץ לוג ולראות הודעה שכתבתם לעצמכם מהקוד ומסבירה בדיוק מה קרה.1 419
# קוד סטארטר לפיתוח Dashboard ב React
השילוב של React ו Redux עושה עבודה מצוינת בפיתוח יישומים אינטרקטיביים ובמיוחד כאלה שצריכים להציג או לערוך מידע משותף. במקרה של Dashboard המידע יכול להיות עומס על השרת, מספר משימות שבוצעו, שורות קוד שנכתבו היום או אפילו איזה תאי שירותים פנויים בכל רגע נתון. בעקבות שאלה של חבר כתבתי קוד סטארטר ליישומים מסוג זה ואני משתף אותו כאן גם אתכם.
## אמלק
הקוד נמצא בקודסנדבוקס בקישור כאן: https://codesandbox.io/s/ecstatic-chandrasekhar-1cl0d
ואפשר לראות אותו לייב בהטמעה כאן:
<iframe src="https://codesandbox.io/embed/ecstatic-chandrasekhar-1cl0d?fontsize=14&hidenavigation=1&theme=dark" style="width:100%; height:500px; border:0; border-radius: 4px; overflow:hidden;"
title="ecstatic-chandrasekhar-1cl0d"
allow="geolocation; microphone; camera; midi; vr; accelerometer; gyroscope; payment; ambient-light-sensor; encrypted-media; usb"
sandbox="allow-modals allow-forms allow-popups allow-scripts allow-same-origin"
></iframe>
## איך זה עובד
הדשבורד כולל Redux Store ששומר את המידע ושני Widgets שיודעים להציג את המידע: אחד בצורת טבלא ואחד בצורת גרף.
המידע נשמר בתור מערך דו-מימדי כדי שיהיה קל להציג אותו. השורה הראשונה היא הכותרות ואחרי זה המידע עצמו. בשביל הדוגמא הלכתי על מדידת rpm (בקשות לדקה) לאורך זמן, כלומר אוביקט מידע שנראה כך:
data: [
['time', 'rpm'],
['10:00', 50],
['10:15', 60],
['10:30', 50],
['10:45', 80],
['11:00', 30],
],
בנוסף דשבורדים נותנים למשתמשים אפשרות לבחור איזה Widgets יוצגו, וגם שלנו לא שונה. בשדה נוסף ב store אני שומר את רשימת ה Widgets שמוצגים על המסך (כרגע נתמכים רק טבלא וגרף):
widgets: new Set(['table', 'graph']),
בזכות החיבור ל Redux שני ה Widgets מקבלים את המידע בזמן אמת וכל עדכון של המידע יופיע מיידית בשניהם. כרגע בקוד הסטארטר אין אפשרות עריכה אז הדרך היחידה לראות את זה היא לשלוח הודעת שינוי ערך מתוך ה Developer Tools.
את הגרף ציירתי עם recharts שהיא ספריית גרפים מאוד נוחה לריאקט. יש גם תיעוד טוב והמון דוגמאות באתר שלהם, ובנוסף ל Line Graph שבדוגמא הם יודעים לצייר גם גרפים של עמודים, שטחים, עוגות, רדאר, נקודות, משפך ואפילו מפה.
## הרחבות
יש המון דרכים להפוך את הסטארטר הזה לאפליקציה מעניינת. הנה כמה רעיונות:
1. אפשר להוסיף קוד צד שרת ו Middleware ששולף כל שניה את המידע המעודכן מהשרת. המידע החדש ייכנס ל Redux Store ובאופן מיידי יוצג בכל ה Widgets שעל המסך מה שיתן תחושה של עדכון חי.
2. אפשר (ורצוי) להוסיף קוד CSS כדי שה Widgets יהיו מסודרים יפה יותר על המסך. ההרפתקנים שביניכם יכולים לזרוק פנימה את react-dnd וכך לאפשר למשתמשים לסדר לעצמם את ה Widgets באמצעות גרירה על המסך. ולשמור את הסידור ב local storage כדי שיישאר לנו גם לפעם הבאה.
3. אפשר להוסיף מסך פתיחה שיציג רשימה של Dashboards שמורים במערכת. כל Dashboard יכול להקשיב לאוביקט מידע שונה ולהציג אותו באמצעות Widgets שונים. משתמשים יוכלו ליצור Dashboards חדשים או למחוק כאלה שהם כבר לא אוהבים.
רעיונות נוספים? דרישות שאתם לא בטוחים איך לממש? מוזמנים לספר ולשתף בתגובות.