הבלוג של ינון פרק

טיפים קצרים וחדשות למתכנתים

מי כתב את זה בכלל!?

17/01/2020

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

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

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

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

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

איך קוראים קובץ CSV מתוך פייתון

16/01/2020

המודול csv של פייתון נותן לנו פיתרון ממש פשוט כשאנחנו רוצים לקרוא קובץ CSV ולגשת לשדות שלו מתוך תוכנית פייתון. שימו לב לתוכנית הדוגמא הבאה שקוראת קובץ בשם my.csv:

import csv
with open('my.csv', encoding='utf8', newline='') as csvfile:
    reader = csv.reader(csvfile)
    for row in reader:
        print(row[-1])

נקרא את התוכנית שורה אחר שורה:

  1. הפקודה import מייבאת את המודול csv לתוכנית. מודול זה מגיע עם פייתון ולא דורש שום התקנה מיוחדת. המודול מספק לנו ממשק לעבודה עם מידע בפורמט CSV.

  2. הפקודה השניה פותחת את הקובץ my.csv ושומרת את הקישור אליו במשתנה בשם csvfile. בגלל שהקובץ נפתח בתוך בלוק with, בסיום הבלוק באופן אוטומטי הקובץ ייסגר בסיום הבלוק. מילת המפתח encoding תעזור לפייתון להתמודד עם קובץ בכל שפה ומילת המפתח newline גורמת לקובץ לא לטפל בשורות חדשות (כי המודול CSV יהיה זה שיטפל בהן).

  3. הפקודה השלישית יוצרת אוביקט מסוג CSV Reader. אוביקט זה מקבל חיבור לקובץ (או מידע דמוי-קובץ) ויאפשר לנו לקרוא את המידע שורה אחר שורה. האוביקט בעצם הופך את המידע מ"שורת טקסט" ל"רשימת ערכים".

  4. בפקודה הרביעית אנחנו רצים בלולאה על כל השורת ב Reader. כל שורה מתקבלת בתור רשימת ערכים למשתנה row.

  5. בפקודה החמישית בשביל הדוגמא אני מדפיס את התא האחרון בשורה.

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

למידע נוסף על CSV Reader שווה להעיף מבט בתיעוד בקישור: https://docs.python.org/3.8/library/csv.html#id3

המחשה פשוטה כדי להבין למה צריך להעביר פונקציה ל setState ב React

15/01/2020

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

להרבה אנשים שמתחילים עם ריאקט יצא לעשות את הטעות הבאה ב Class Component:

class Counter {
  constructor() {
    this.state = { count: 0 };
  }

  inc = () => {
    this.setState({ count: this.state.count + 1 });
  }

  render() {
    return (
      <button onClick={this.inc}>{this.state.count}</button>
    );
  }
}

שזהה לטעות הבאה ב Functional Component:

function Counter() {
  const [count, setCount] = useState(0);

  function inc() {
    setCount(count + 1);
  }

  return (
    <button onClick={inc}>{count}</button>
  );
}

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

function Counter() {
  const [count, setCount] = useState(0);

  function inc() {
    const nextValue = count + 1;
    setCount(nextValue);
    setCount(nextValue);
  }

  return (
    <button onClick={inc}>{count}</button>
  );
}

או בגירסת הקלאסים:

class Counter {
  constructor() {
    this.state = { count: 0 };
  }

  inc = () => {
    const nextValue = this.state.count + 1;
    this.setState({ count: nextValue });
    this.setState({ count: nextValue });
  }

  render() {
    return (
      <button onClick={this.inc}>{this.state.count}</button>
    );
  }
}

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

  inc = () => {
    this.setState({ count: this.state.count + 1 });
    this.setState({ count: this.state.count + 1 });
  }

אני חושב שמה שהיה קצת מוזר ב Class Component והפך למאוד ברור במעבר ל Hooks הוא שהפונקציה setState לא משנה את הסטייט באופן מיידי אלא רק בפעם הבאה שיקרא render אז נקבל את הסטייט העדכני.

אני בחיים לא אבין את זה

14/01/2020

״זה מסובך מדי״

״כל האנשים שיודעים את זה התחילו בגיל הרבה יותר צעיר״

״זה ייקח יותר מדי זמן״

״זה בעצם לא שווה את ההשקעה״

״אולי בשנה הבאה אהיה יותר זמין לנסות ללמוד שוב״

״אני ממילא לא הולך להשתמש בזה״

״עד עכשיו הסתדרתי טוב בלי לדעת את זה״

״אני בחיים לא אבין את זה״

״גם אם אצליח להבין בחיים לא אהיה טוב בזה״

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

בואו נבנה את React.lazy כדי להבין איך הוא עובד

13/01/2020

הפונקציה React.lazy משמשת לטעינה עצלה של פקדי ריאקט, כלומר היא תאפשר לנו בצורה מאוד קלה להגדיר שפקד ריאקט מסוים לא ייכלל בקובץ ה JS הגדול של העמוד וייטען רק אם מישהו ינסה לעשות לו render. דוגמא פשוטה לשימוש בה אפשר למצוא בתיעוד והיא נראית כך:

const OtherComponent = React.lazy(() => import('./OtherComponent'));

function MyComponent() {
  return (
    <div>
      <Suspense fallback={<div>Loading...</div>}>
        <OtherComponent />
      </Suspense>
    </div>
  );
}

כתיבת קוד React כזה בתוך פרויקט תגרום לוובפאק באופן אוטומטי לפצל את הקוד של הפרויקט ולשמור את הקוד של OtherComponent בקובץ js נפרד, שייטען בצורה אסינכרונית ב Ajax אחרי טעינת העמוד.

קסם? לא ממש. רוב מה ש React.lazy עושה אנחנו יכולים לעשות נפלא גם בלעדיו - בואו נראה איך.

המשך קריאה

טיפ Python: שימוש ב ExitStack כדי לצמצם with-ים מקוננים

12/01/2020

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

def v1(fin_name, fout_name):
    with open(fin_name, encoding='utf8') as fin:
        with open(fout_name, 'w') as fout:
            for line in fin:
                fout.write(line)

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

פיתרון קל למצב הזה אפשר למצוא במודול contextlib שמגיע כחלק מהספריה הסטנדרטית של פייתון. הפונקציה ExitStack מתוך אותו מודול היא Context Manager שמחבר יחד Context Managers אחרים, או בשפה פשוטה היא מאפשרת להשתמש בבלוק with אחד כדי לתפוס מספר דברים שיכולים להישבר.

הנה הקוד אחרי שהוספתי את ExitStack:

from contextlib import ExitStack
def v2(fin_name, fout_name):
    with ExitStack() as stack:
        fin  = stack.enter_context(open(fin_name, encoding='utf8'))
        fout = stack.enter_context(open(fout_name, 'w'))
        for line in fin:
            fout.write(line)

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

ל ExitStack יש עוד כמה טריקים עליהם לא סיפרתי ואם אתם סקרנים תוכלו למצוא את כל הפרטים בדף התיעוד. קריאה מהנה.

פשוט כי זה עבד

11/01/2020

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

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

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

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

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

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

חדש באתר: קורס ריאקט לייב - לומדים חודשיים עם מרצה ויוצאים עם פרויקט ביד

08/01/2020

הי חברים,

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

אני יכול לראות את השיעור כמה פעמים שאני צריך עד שאני מבין.

אני יכול לדלג על קטעים שלא מעניינים אותי.

אני יכול להתאמן בלי שאף אחד מסתכל.

ואני יכול לשאול שאלות בלי להרגיש שרק אני לא הבנתי.

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

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

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

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

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

לכל הפרטים על קורס ריאקט הקרוב מוזמנים לקפוץ לדף הנחיתה החדש שבניתי בקישור https://www.tocode.co.il/bundles/react

נתראה בקורס ינון