ru
Feedback
ToCode

ToCode

Открыть в Telegram

טיפים קצרים למתכנתים מאת ינון פרק

Больше
1 419
Подписчики
Нет данных24 часа
Нет данных7 дней
Нет данных30 день
Архив постов
ToCode
1 419
# בואו נמצא מספרים שמחים עם Elixir מספר שמח הוא מספר שסכום ריבועי הספרות שלו בסוף יוצא 1. מספר עצוב הוא מספר שסכום ריבועי הספרות שלו לא יוצא 1, לא משנה כמה פעמים מעלים בריבוע ומחברים. ככה יוצא ש 7 הוא מספר שמח, כי כשמעלים אותו בריבוע מקבלים 49, וכשמעלים בריבוע את שתי הספרות של 49 מקבלים 16 ו 81 שסכומם יוצא 97, ממשיכים עוד סיבוב ומקבלים את 49 ו 81 שסכומם הוא 130, ועכשיו אנחנו מתקרבים למשהו כי 3 בריבוע זה 9 ו 1 בריבוע נשאר 1, יחד הם נותנים 10, שסכום ריבועי הספרות שלו הוא פשוט 1 בריבוע ועוד אפס בריבוע כלומר המספר 1. לעומתו 4 הוא מספר עצוב כי בריבוע הוא נותן 16, וכשמעלים בריבוע את 1 ו 6 מקבלים 1 ו 36, שזה יחד 37, הסכום הבא הוא 58, אחרי זה 89, ואז 145, 42, 20 ושוב 4. בויקיפדיה יש כל מיני סיפורים מעניינים על מספרים שמחים וגם דוגמת קוד בפייתון שמוצאת אותם, אבל אני חשבתי שזו הזדמנות טובה להוריד חלודה מאליקסיר וליהנות מכל הפינוקים של הרקורסיות בשפה פונקציונאלית. ## איך סוכמים את ריבועי הספרות כבר בצעד הראשון בהתמודדות עם תרגיל כזה ברור שנצטרך לקחת מספר ולחשב את סכום ריבועי הספרות שלו. בשפה פונקציונאלית זה יהיה שלוש פעולות: 1. פיצול מספר לספרות 2. העלאה של כל סיפרה בריבוע 3. חישוב הסכום באליקסיר כשיש לי ערך יחיד (מספר) ואני רוצה להפוך אותו לרשימה של ספרות אני יכול להשתמש בפונקציה Stream.unfold. כן, אני יכול להשתמש בעוד הרבה שיטות ופונקציות, אבל אני אוהב את הגמישות של unfold. בגדול היא לוקחת ערך ראשוני ופונקציה, ואז מפעילה את הפונקציה על הערך. היא מצפה לקבל שתי תוצאות, הראשונה היא "הערך" הבא בסידרה, והשניה היא הקלט ל unfold הבא. כשצריכים לפרק מספר לספרות אפשר לקחת את שארית החלוקה בעשר בתור הערך הבא לסידרה, ואת חלוקת השלמים בעשר בתור הקלט ל unfold הבא. במילים אחרות זאת דרך אחת לפרק מספר לספרות שלו באליקסיר:
Stream.unfold(n, fn 
  0 -> nil
  n -> { rem(n, 10), div(n, 10) }
end)
אנחנו לא עוצרים בחלוקה לספרות ורוצים גם להעלות בריבוע כל סיפרה (map) ואחרי זה לסכום את התוצאות (sum). סך הכל אני יכול לכתוב פונקציה ראשונה כזאת:
def sum_squared_digits(n) do
  Stream.unfold(n, fn 
    0 -> nil
    n -> { rem(n, 10), div(n, 10) }
  end)
  |> Enum.map(&(&1 ** 2))
  |> Enum.sum
end
## איך בודקים אם מספר הוא שמח עכשיו יש לנו את הכלים להתקדם לצעד השני והוא הבדיקה אם מספר הוא מספר שמח. הפונקציה הרקורסיבית מקבלת מספר ואת רשימת כל המספרים שכבר ראינו וצריכה להתנהג ככה: 1. המספר 1 הוא שמח. 2. מספר שאינו 1 ושנמצא ברשימת המספרים שראינו הוא עצוב. 3. מספר שאינו 1 ושאינו נמצא ברשימת המספרים שראינו אולי יהיה שמח ואולי יהיה עצוב. צריך להפעיל מחדש את הפונקציה עם סכום ריבועי הספרות שלו, ולהוסיף אותו לרשימת המספרים שראינו. בתרגום לאליקסיר זה נראה כך:
def is_happy(n, seen \\ %{}) do
  cond do
    n == 1 ->
      true

    Map.has_key?(seen, n) ->
      false

    true -> 
      is_happy(
        sum_squared_digits(n),
        Map.put(seen, n, true)
      )
  end
end
## איך מוצאים את כל המספרים השמחים עד 100 בשביל להתחיל למצוא מספרים שמחים אני יכול לרוץ בלולאה על רשימה של מספרים ולהפעיל את הפונקציה filter, שמסננת מהרשימה רק את המספרים שמתאימים לתנאי. הדפסת המספרים השמחים עד 100 היא:
def main() do
  1..100
  |> Enum.filter(&is_happy/1)
  |> IO.inspect
end
והקוד המלא באליקסיר הוא:
defmodule HappyNumbers do
  def main() do
    1..100
    |> Enum.filter(&is_happy/1)
    |> IO.inspect
  end

  def is_happy(n, seen \\ %{}) do
    cond do
      n == 1 ->
        true

      Map.has_key?(seen, n) ->
        false

      true -> 
        is_happy(
          sum_squared_digits(n),
          Map.put(seen, n, true)
        )
    end
  end

  def sum_squared_digits(n) do
    Stream.unfold(n, fn 
      0 -> nil
      n -> { rem(n, 10), div(n, 10) }
    end)
    |> Enum.map(&(&1 ** 2))
    |> Enum.sum
  end
end

HappyNumbers.main()
ובגירסה אינטרקטיבית אם אתם קוראים את הפוסט בדפדפן: <iframe frameborder="0" width="100%" height="500px" src="https://replit.com/@ynonp/ShrillClientsideUsernames?lite=true"></iframe>

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

ToCode
1 419
# שטח מת אני כותב הרבה ריאקט בתקופה האחרונה, אולי אפילו יותר מדי. כך יצא שכשישבתי לכתוב HTML רגיל הקלדתי בלי לשים לב:
<div />
<div />
<div />
<div />
פתחתי אותו בדפדפן, ה CSS כמובן נשבר לגמרי ואני מסתכל ולא רואה את הבעיה. כאילו שטח מת. ברכב אפשר לכוון את המראות או לקנות מובילאיי כדי להילחם בשטחים המתים. בקוד, לכל שפת תכנות וסביבה יש את הדרכים שלה ועם הזמן אנחנו לומדים לשים לב ולבנות עוד דרכים כדי לראות טוב יותר. ב HTML שווה להשתמש ב HTML Hint, שאומנם לא זיהה את הבעיה הפעם אבל עוזר לזהות אינסוף בעיות אחרות. דרך שניה היא להתרגל לפתוח את הקובץ בדפדפן עם Inspect Element כדי לראות איך הדפדפן רואה את העמוד. כפתור זה מיד היה מראה את הבעיה כי בחלון ה Inspect אני רואה מה באמת קורה שם:
<div>
    <div>
        <div>
            <div></div>
        </div>
    </div>
</div>

ToCode
1 419
# טיפ טייפסקריפט: פיצול ממשקים בכתיבת TypeScript הרבה פעמים אני אתחיל בהגדרת סוגי המשתנים בגוף הפונקציה בתור אוביקט. בריאקט זה יהיה משהו כזה:
function Counter(props: { initialValue: number, step: number }) {
}
עם הגדילה של הקומפוננטה טבעי להוסיף עוד משתנים ב props. לפעמים נוח לנו גם להוציא את כל ההגדרה לקובץ אחר כדי שאפשר יהיה להשתמש באותה רשימת props בעוד מקומות. לכן אחרי כמה שבועות או חודשים של עבודה על הקוד השורה יכולה להפוך ל:
interface ICounterProps {
  initialValue: number;
  step: number;
  buttonText?: string;
}

function Counter(props: ICounterProps) {
}

ואז ככל שהקומפוננטה ממשיכה לגדול אנחנו עשויים לרצות להוסיף מאפיינים שיש ביניהם קשר. לדוגמה אולי נרצה להוסיף כפתור turbo שיגדיל את ה Counter במספר גדול יותר מה step הרגיל. במצב כזה אני יכול להגיע ל:
interface ICounterProps {
  initialValue: number;
  step: number;
  buttonText?: string;
  turboButtonText?: string;
  turboStep?: number;
}

function Counter(props: ICounterProps) {
}
מבנה כזה לא רצוי כיוון שהוא לא נותן לקוד שמשתמש בקומפוננטה דרך לדעת שיש קשר בין turboStep ל turboButtonText ושאם מציינים אחד צריך גם לציין את השני. במקום זה עדיף לפצל את ה Interface לשני ממשקים שונים:
interface ICounterProps {
  initialValue: number;
  step: number;
  buttonText?: string;
}

interface ICounterWithTurboProps extends ICounterProps {
  turboButtonText: string;
  turboStep: number;
}
בקוד הקומפוננטה נאפשר לקבל פרמטרים משני הממשקים באמצעות איחוד טיפוסים, ונשתמש ב Casting כדי להפוך את ה props לממשק הכללי ביותר:
function Counter(props: ICounterProps|ICounterWithTurboProps) {
  const {
    initialValue,
    step,
    buttonText = 'Increase',
    turboButtonText,
    turboStep,
  } = props as ICounterWithTurboProps;
}
בתוך גוף הקומפוננטה אם turboButtonText קיבל ערך אנחנו יודעים שגם turboStep קיבל ערך. בקוד חיצוני אנחנו נותנים לטייפסקריפט להבין לבד את הטיפוסים וטייפסקריפט מספיק חכם כדי לוודא שהעברנו ערכים לשני המאפיינים של הטורבו או לאף אחד מהם. נ.ב. הסיפור הזה עובד כשיש לי שני ממשקים או גג שלושה. ככל שנוסיף יותר משתנים ותלויות זה כבר מסתבך. בעיקרון טייפסקריפט מאפשרת להרחיב כמה interface-ים אבל נשמע שאם הגעתם לשם עדיף לפצל את הקומפוננטה.

ToCode
1 419
# זמן לימוד מתי עוצרים? מתי למדתם "מספיק להיום"? אולי... - בסיום השיעור (כשהשעון מראה 12, בעוד 20 דקות) - בסוף הפרק - אחרי שאסיים את כל התרגילים - אחרי שאפתור את הבאג, או כשהקוד יעבוד - עד שהאוכל יגיע - עד שתיכנס שיחת טלפון מעניינת - עד שתגיע הודעה חשובה בווטסאפ - עד שאירדם הרבה פעמים אנחנו מסתכלים על תהליך לימודי בתור משהו לינארי - אני לומד פייתון עכשיו ויש לי את התרגיל הבא לעשות, אז זה מה שאני צריך כדי להתקדם בפייתון. ואם בדיוק הזמן הפנוי שיש לי עכשיו הוא "עד שהאוכל יגיע" אז אני לא יכול לפתור את התרגיל הזה בפייתון כי הוא לוקח יותר זמן, ולכן אני לא יכול להתקדם בקורס פייתון, ובמקום אני מעביר רבע שעה ב ynet או טוויטר. מאוד עוזר כשאתם יושבים ללמוד נושא לסדר ולכתוב לכם משימות לימודיות לפי קבועי הזמן (ה"מתי מסיימים") האפשריים. כלומר יהיו משימות שדורשות שעתיים עבודה, משימות שדורשות יום שלם ומשימות שאפשר לעשות גם בעשר או 15 דקות. כשיש לכם רשימה כזאת, לא משנה מה משבצת הזמן הפנוי שקיבלתם תמיד אפשר לנצל אותה כדי להתקדם עוד צעד. גם צעדים קטנים עוזרים להרגשה, עוזרים למומנטום ולפעמים מחביאים התקדמות גדולה.

ToCode
1 419
# זמן לימוד מתי עוצרים? מתי למדתם "מספיק להיום"? אולי... - בסיום השיעור (כשהשעון מראה 12, בעוד 20 דקות) - בסוף הפרק - אחרי שאסיים את כל התרגילים - אחרי שאפתור את הבאג, או כשהקוד יעבוד - עד שהאוכל יגיע - עד שתיכנס שיחת טלפון מעניינת - עד שתגיע הודעה חשובה בווטסאפ - עד שאירדם הרבה פעמים אנחנו מסתכלים על תהליך לימודי בתור משהו לינארי - אני לומד פייתון עכשיו ויש לי את התרגיל הבא לעשות, אז זה מה שאני צריך כדי להתקדם בפייתון. ואם בדיוק הזמן הפנוי שיש לי עכשיו הוא "עד שהאוכל יגיע" אז אני לא יכול לפתור את התרגיל הזה בפייתון כי הוא לוקח יותר זמן, ולכן אני לא יכול להתקדם בקורס פייתון, ובמקום אני מעביר רבע שעה ב ynet או טוויטר. מאוד עוזר כשאתם יושבים ללמוד נושא לסדר ולכתוב לכם משימות לימודיות לפי קבועי הזמן (ה"מתי מסיימים") האפשריים. כלומר יהיו משימות שדורשות שעתיים עבודה, משימות שדורשות יום שלם ומשימות שאפשר לעשות גם בעשר או 15 דקות. כשיש לכם רשימה כזאת, לא משנה מה משבצת הזמן הפנוי שקיבלתם תמיד אפשר לנצל אותה כדי להתקדם עוד צעד. גם צעדים קטנים עוזרים להרגשה, עוזרים למומנטום ולפעמים מחביאים התקדמות גדולה.

ToCode
1 419
# הזמנה לוובינר: תקשורת בין סרביסים עם RabbitMQ הכלל הראשון בבניית ארכיטקטורת Micro Services הוא שהסרביסים צריכים להיות עצמאיים. הכלל השני בבניית ארכיטקטורת Micro Services הוא שהם צריכים לעבוד יחד. ובשביל ששני חלקים עצמאיים במערכת יעבדו יחד הם צריכים לתקשר. בעולם הישן של המונוליט כששני חלקים במערכת צריכים לדבר מספיק לקרוא לפונקציה, אבל בעולם ה Micro Services, כל חלק במערכת רץ בתור תהליך נפרד. מה שיותר גרוע, הוא שאפשר לשדרג חלק אחד במערכת בלי לגעת בחלק השני (Deployment בלתי תלוי). הפונקציה שרצית לקרוא לה בעולם של המונוליט אולי כבר לא תהיה קיימת בגירסה הבאה של הסרביס. ובדיוק בגלל זה אחד החלקים הכי חשובים בארכיטקטורת Micro Services הוא התקשורת בין הסרביסים. תקשורת בין סרביסים מחולקת לשני סוגים: יש תקשורת סינכרונית, שזה אומר שסרביס אחד שולח הודעה לסרביס אחר ומחכה לתשובה כדי להמשיך; ותקשורת אסינכרונית שזה אומר שסרביס אחד שולח הודעה ומקווה לטוב. מבין השתיים תקשורת אסינכרונית תהיה המועדפת עלינו, כיוון שהיא יוצרת תלות יותר קטנה בין הסרביסים. בתקשורת אסינכרונית אני שולח הודעה ואולי הסרביס השני שם עכשיו ויכול לטפל בה, אבל גם אולי הוא עמוס או שבדיוק מעלים גירסה חדשה שלו, ולכן יוכל לטפל בה רק עוד כמה דקות. ותור הודעות הוא המפתח לבניית מערכת תקשורת אסינכרונית בין סרביסים שונים בארכיטקטורת Micro Services. בתיאור מאוד כללי, אפשר להגיד שתור הודעות הוא רכיב במערכת שמתפקד כמו מערכת דואר בין סרביסים. סרביס יודע לשלוח הודעה לתור והתור אחראי לנתב את ההודעה למי שצריך לקבל אותה ובזמן שמתאים למקבל. שני תורי ההודעות הפופולריים היום הם RabbitMQ ו Kafka, כאשר לכל אחד מהכלים יכולות ייחודיות משלו בנוסף למנגנון הבסיסי של העברת הודעות. ביום חמישי הקרוב אני אקח שעה כדי לדבר עם אלה מכם שירצו להצטרף על מערכות מבוזרות, ארכיטקטורת Micro Services ובמיוחד על רכיב התקשורת RabbitMQ. הבחירה ב RabbitMQ מקרית לגמרי ויום אחד בטח נעשה את אותו וובינר עם Kafka. בתחילת הוובינר אראה איך להקים שרת RabbitMQ בתוך דוקר, איך לקנפג אותו ואיך לגשת אליו מקוד Node.JS שרץ בסרביסים אחרים. לאחר מכן אציג מספר תוכניות דוגמה שימחישו בניה של שלוש תבניות מבוזרות בפיתוח: 1. מערכת של שרת מרכזי שמוציא משימות ל Workers ומקבל מהם סטטוסים על המשימות. לדוגמה מערכת ווב שמאפשרת ללקוחות להעלות תמונות ומשתמשת בסרביס חיצוני כדי להריץ פילטרים על אותן תמונות. 2. מערכת של טיפול מקבילי באירוע. לדוגמה מערכת ווב בה לקוח קונה מוצר ויש מספר תהליכים שצריכים להגיב לקניה - צריך להוסיף את הלקוח לרשימת דיוור, להוציא חשבונית, לשלוח את המוצר וכן הלאה. 3. מערכת של טיפול סדרתי באירוע, שקורית כשיש תלות בין הסרביסים. בדוגמה של קניית המוצר יכול להיות סרביס שאחראי על הגביה ורק אם הוא מצליח אז סרביס אחר שולח את המוצר ללקוח. בין אם אתם בעסקי ה Micro Services ורוצים לשמוע יותר על התפקיד של תור ההודעות ולקבל טיפים לעבודה נכונה איתו, או אם אתם רוצים להיכנס לעסקי ה Micro Services ורוצים לראות איך מתחילים אני מקווה שהשעה הזאת תהיה רלוונטית עבורכם. לפרטים נוספים והרשמה שווה לבקר בדף הוובינר בקישור: https://www.tocode.co.il/workshops/115

ToCode
1 419
הכנסה יותר מורכבת תהיה לטבלת הספרים: שם אני צריך להשתמש בתת-שאילתה כדי לדעת מה ה id של הסופר שכתב את הספר. אני לא יודע את ה id כי בסיס הנתונים יצר אותו אוטומטית בעת ההכנסה ולכן צריך לתשאל. אני יוצר אוביקט שאילתה, שגם הוא פקודה, עם הפקודה:
    author_subquery = (
            select(authors_table.c.id).
            where(authors_table.c.name == bindparam('author_name')).
            scalar_subquery())
ומשלב אותו בפקודת ההכנסה באופן הבא:
    conn.execute(insert(books_table).values(author_id=author_subquery), [
        { "ISBN": "9781982134488", "name": "Think Like a Monk", "author_name": "Jay Shetty" },
        { "ISBN": "9780316066525", "name": "Infinite Jest", "author_name": "David Foster Wallace" },
        { "ISBN": "9781515208563", "name": "Meditations", "author_name": "Marcus Aurelius" },
        ])
## שליפת מידע ו Join ואם כבר ראינו את select בואו נרחיב עליו ונכתוב שאילתה שמושכת את כל הספרים והמחברים שלהם. הפקודה select מקבלת רשימה של עמודות ומחזירה "פקודת" שליפה. הפעלת פקודות כמו where על פקודת השליפה מוסיפה מידע לשליפה עד שיש לנו פקודה שלמה ואותה מעבירים ל execute. כדי לקבל את כל הספרים של כל המחברים אני רוצה להפעיל פקודת שליפה עם join והקוד נראה כך:
with engine.begin() as conn:
## Part 3 - Join Select
    books = conn.execute(select(books_table.c.name, books_table.c.ISBN, authors_table.c.name).join_from(books_table, authors_table))
    for book in books:
        book_name, isbn, author = book
        print(f"Book: {book_name}; ISBN: {isbn}; author: {author}")
הפקודה select לוקחת רשימה של עמודות, עליה אני מפעיל את הפקודה join_from כדי להגיד לה מה סדר ה Join-ים והיא כבר מחשבת את פקודת ה SQL אוטומטית. ## הקוד המלא ולאן ממשיכים סך הכל הקוד שראינו בכל הפוסט הוא:
from sqlalchemy import create_engine, MetaData, Table, Column, Integer, String, ForeignKey, insert, select, bindparam

## Part 1 - Create the tables
engine = create_engine("sqlite+pysqlite:///db.sql", echo=True, future=True)
metadata_obj = MetaData()

authors_table = Table(
        "authors",
        metadata_obj,
        Column('id', Integer, primary_key=True),
        Column('name', String()))

books_table = Table(
        "books",
        metadata_obj,
        Column('ISBN', String(13), primary_key=True),
        Column('name', String()),
        Column('author_id', ForeignKey('authors.id'), nullable=False))


metadata_obj.create_all(engine)

## Part 2 - Insert some data
with engine.begin() as conn:
    conn.execute(insert(authors_table), [
        { "name": "Jay Shetty" },
        { "name": "David Foster Wallace" },
        { "name": "Marcus Aurelius" },
        ])

    author_subquery = (
            select(authors_table.c.id).
            where(authors_table.c.name == bindparam('author_name')).
            scalar_subquery())

    conn.execute(insert(books_table).values(author_id=author_subquery), [
        { "ISBN": "9781982134488", "name": "Think Like a Monk", "author_name": "Jay Shetty" },
        { "ISBN": "9780316066525", "name": "Infinite Jest", "author_name": "David Foster Wallace" },
        { "ISBN": "9781515208563", "name": "Meditations", "author_name": "Marcus Aurelius" },
        ])


with engine.begin() as conn:
## Part 3 - Join Select
    books = conn.execute(select(books_table.c.name, books_table.c.ISBN, authors_table.c.name).join_from(books_table, authors_table))
    for book in books:
        book_name, isbn, author = book
        print(f"Book: {book_name}; ISBN: {isbn}; author: {author}")

אני מקווה שהוא עזר לכם לקבל תמונה מגבוה על SQL Alchemy. אם אהבתם את הממשק ותרצו ללמוד עוד על הספריה אני ממליץ על התיעוד שלהם ובמיוחד על ה Tutorial, שהרבה מהדוגמאות בפוסט מבוססות עליו. זה הקישור: https://docs.sqlalchemy.org/en/14/tutorial/engine.html

ToCode
1 419
# צעדים ראשונים עם SQL Alchemy ספריית SQL Alchemy היא אחת הספריות הפופולריות בפייתון לעבודה עם בסיסי נתונים. היא אמינה ובעלת תיעוד מעולה ואפילו תומכת בעבודה עם asyncio. בפוסט זה ניקח שלושה צעדים ראשונים כדי שיהיה לכם קל להתחיל לעבוד עם הספריה ואשתדל לכסות כמה שיותר מושגים בסיסיים שלה. ## התקנה וחיבור לבסיס הנתונים צעד ראשון בעבודה עם SQL Alchemy הוא להתקין את הספריה. בפייתון זה פשוט אומר להפעיל:
$ pip install sqlalchemy
שווה לציין שלי זה לא הצליח מהפעם הראשונה בגלל שידרוגי גירסאות. אם אתם מקבלים הודעות שגיאה מוזרות שווה להפעיל שידרוג לגירסה החדשה ביותר של pip ושל setuptools עם:
pip install -U pip setuptools
ואחרי זה הכל יתחיל לעבוד. כדי לוודא שאתם מותקנים אפשר להיכנס למסוף פייתון ושם לכתוב:
Python 3.10.0 (default, Nov  3 2021, 23:29:09) [Clang 13.0.0 (clang-1300.0.29.3)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import sqlalchemy
>>> print(sqlalchemy.__version__)
1.4.36
>>>
כל העבודה מול בסיס הנתונים ב SQL Alchemy מבוצעת דרך אוביקט שנקרא Engine. הדבר המרכזי שנעשה איתו זה ליצור חיבור, ולכן כשיוצרים את ה Engine צריך להגיד לו לאיזה בסיס נתונים אנחנו רוצים להתחבר. אני מפעיל את הדוגמה עם SQLite שנשמר בקובץ ולכן מפעיל:
from sqlalchemy import create_engine, MetaData, Table, Column, Integer, String, ForeignKey, insert, select, bindparam

engine = create_engine("sqlite+pysqlite:///db.sql", echo=True, future=True)
שם הקובץ יהיה db.sql וזה נוח לשמור בסיס נתונים שלם בקובץ כשמשחקים עם ספריה חדשה כי לא צריך להתקין כלום וקל להסתכל בקובץ (באמצעות SQLite CLI או כל כלי גרפי אחר) ולראות שדברים נוצרו כמו שחשבנו שהם צריכים להיות. ## יצירת טבלאות פעולה ראשונה שנרצה לעשות בעבודה עם בסיס נתונים היא לאתחל אותו, כלומר ליצור את הטבלאות אם הן לא קיימות. ברוב המקרים לא באמת נצטרך ליצור טבלאות כי בסיס הנתונים כבר יהיה קיים, אבל אנחנו רק משחקים עם הספריה. בשביל ליצור את הטבלאות אנחנו צריכים קודם להגדיר מה צריך להיות בהן, כלומר מה העמודות בכל טבלה ומה היחסים בין הטבלאות. ספריית SQL Alchemy מספקת את המחלקות Table ו Column כדי להגדיר טבלאות ועוד כמה מחלקות לסוגי נתונים. בואו ניקח דוגמה ונגדיר שתי טבלאות אחת עבור ספרים והשניה עבור סופרים, ונקבל:
metadata_obj = MetaData()

authors_table = Table(
        "authors",
        metadata_obj,
        Column('id', Integer, primary_key=True),
        Column('name', String()))

books_table = Table(
        "books",
        metadata_obj,
        Column('ISBN', String(13), primary_key=True),
        Column('name', String()),
        Column('author_id', ForeignKey('authors.id'), nullable=False))
האוביקט metadata מרכז את כל נתוני הטבלאות, אוביקט Table מייצג טבלה ו Column מייצג עמודה. ביצירה של עמודות אפשר להעביר את סוגי הנתונים ומאפיינים של העמודה כמו primary_key או nullable. אחר כך מגיעה השורה האופציונאלית:
metadata_obj.create_all(engine)
שיוצרת את כל הטבלאות. עליה נוכל לוותר כשנעבוד מול בסיס נתונים קיים. ## הכנסת מידע בעבודה עם SQL Alchemy יש לי ביד מנוע (engine) ובעזרתו אני מתחבר לבסיס הנתונים. החיבור תמיד כולל טרנזאקציה, מבוצע בתוך בלוק with ובסוף הבלוק, אם הכל עבד כמו שצריך, יהיה commit. אני משתמש בפקודה engine.begin כדי להתחיל את החיבור ולבצע אוטומטית את ה commit בסוף הבלוק. בתוך הבלוק שיטת העבודה היא לפי תבנית שנקראת Command Pattern. זה אומר שיש פונקציות של SQL Alchemy שמייצגות פקודות שיכולות להישלח לבסיס הנתונים, ואוביקט החיבור יודע להפעיל אותן עם הפונקציה execute שלו. אז בשביל להפעיל הכנסה לטבלת המחברים אני אפעיל:
## Part 2 - Insert some data
with engine.begin() as conn:
    conn.execute(insert(authors_table), [
        { "name": "Jay Shetty" },
        { "name": "David Foster Wallace" },
        { "name": "Marcus Aurelius" },
        ])
הפונקציה insert יוצרת פקודת הכנסה, והפונקציה conn.execute מקבלת את פקודת ההכנסה ואוסף של פרמטרים לפקודה ומפעילה אותה כדי להכניס את שלושת השורות.

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