ToCode
Open in Telegram
1 419
Subscribers
No data24 hours
No data7 days
No data30 days
Posts Archive
1 419
# חידת פנדס: לאן נעלמו הערכים?
לפני כמה ימים במסגרת כתיבת פרק פנדס חדש לקורס פייתון שבאתר (יעלה עוד כמה ימים אל דאגה, עכשיו בעריכות), רציתי לצייר גרף שמתאר כמה שילמו כל נוסעי הטיטאניק על הכרטיסים שלהם. כן, מסתבר שגם את פרטי נוסעי הטיטאניק אפשר למצוא היום בקלות ברשת, אבל זה כבר נושא לסיפור אחר.
בגלל שכל אחד שילם מחיר קצת שונה היה נוח לחלק את המחירים לקבוצות עם pd.cut וכך הגעתי לקוד הבא:
import pandas as pd
url = 'https://raw.githubusercontent.com/guipsamora/pandas_exercises/master/07_Visualization/Titanic_Desaster/train.csv'
titanic = pd.read_csv(url)
s=pd.cut(titanic['Fare'], bins=[0, 10, 20, 50, 100, 1000], right=True)
data = s.value_counts()
print(data)
אבל להפתעתי התוצאה לא כללה את כל המחירים. הנה מה שקיבלתי מההדפסה:
(0, 10] 321
(20, 50] 216
(10, 20] 179
(50, 100] 107
(100, 1000] 53
Name: Fare, dtype: int64
והנה מאפייני הטיטאניק:
In: titanic['Fare'].describe()
Out:
count 891.000000
mean 32.204208
std 49.693429
min 0.000000
25% 7.910400
50% 14.454200
75% 31.000000
max 512.329200
Name: Fare, dtype: float64
קל לראות שבחלוקה לסלים בשביל לצייר את הגרף איבדנו 15 נתוני כרטיסים.
והשאלות: לאן נעלמו 15 נתוני כרטיסים? באיזו שורה הבעיה? ומה השינוי הקטן שאפשר לבצע כדי לתקן אותה?1 419
# תרגיל פייתון פשוט
לפעמים דווקא תרגילים פשוטים עוזרים לנו להתמקד ולקבל רעיונות לשיפור, שאחרי זה נוכל ליישם אותם גם במקרים יותר מורכבים. כך קרה לי עם דוגמת הריפקטורינג הבאה שמתחילה פשוט אבל מהר מאוד מסתבכת.
## מה אנחנו בונים
נכתוב תוכנית פייתון המקבלת 10 מספרים מהמשתמש ומדפיסה את סכומם.
## ניסיון 1
סכום של 10 מספרים? קלי קלות, אני יודע לכתוב לולאות for:
nums = []
for i in range(10):
num = input("Enter a number: ")
nums.append(int(num))
print(f"Maximum number is: {max(nums)}")
עובד? כן. אבל:
1. הקוד שומר רשימה של כל הערכים שקיבלנו מהמשתמש בזיכרון.
2. אין טיפול בערך לא תקני.
3. הקוד מרגיש מסורבל, למרות שבמבט ראשון לא לגמרי ברור איך לשפר את זה.
בואו נתחיל עם הדברים שאנחנו יודעים איך לפתור.
## ניסיון 2 - טיפול בקלט לא תקין ושמירת רק מספר אחד בזיכרון
שתי הבעיות הקלות הן קלט לא תקין וצמצום המקום שהתוכנית לוקחת בזיכרון. הנה גירסה שניה שמתקנת את שתיהן:
def numeric_input(prompt):
while True:
try:
return int(input(prompt))
except ValueError:
print("Numeric input required")
continue
max_num = float('-inf')
for i in range(10):
next_value = numeric_input(f"Enter a number #{i+1}: ")
max_num = max(max_num, next_value)
print(f"Maximum number is: {max_num}")
הקוד אולי יותר ארוך אבל נראה הרבה יותר טוב. את פונקציית העזר numeric_input כדאי להוציא לקובץ חיצוני ואז גם נוכל לקצר את התוכנית.
עכשיו אפשר לחזור לבעיית המבנה: לולאת for היא מנגנון כללי מדי למה שאנחנו עושים כאן. הרי, כל מה שיש לנו זה חישוב ערך מקסימלי מסידרת ערכים. ומה שיפה שהפונקציה max עצמה כבר כוללת בתוכה לולאה בדיוק כזו.
## ניסיון 3 - נארגן אחרת את הקוד כך שישתמש בפונקציה max בצורה טובה יותר
מנגנון Generators של פייתון מאפשר לי להפוך את הלולאה ועדיין לשמור בזיכרון רק משתנה אחד, את הערך המקסימלי. בשילוב עם max הקוד יראה כך:
def numeric_input(prompt):
while True:
try:
return int(input(prompt))
except ValueError:
print("Numeric input required")
continue
max_num = max(numeric_input(f"Enter a number #{i+1}: ") for i in range(10))
print(f"Maximum number is: {max_num}")
וכמובן אחרי שנוציא את numeric_input לקובץ חיצוני נישאר עם קוד קצר יותר, מדויק יותר וגם נכון יותר.1 419
# לפעמים זה משעמם
בתחומים רבים ובמיוחד אצלנו בהייטק, בסיס תיאורטי חזק הוא תנאי הכרחי להתקדמות. אם אתה לא יודע מה זה סיבוכיות זמן ריצה יהיה לך קשה מאוד לראות את ההבדל בין אלגוריתמים שונים בהיבט הזה. וגם מצד שני - אם את מבינה טוב את התיאוריה של תכנות מונחה עצמים ו Design Patterns יהיה לך קל מאוד להיכנס לשפת תכנות חדשה שעובדת בפרדיגמה זו.
מה שאנשים לא תמיד מספרים זה שהם עבדו מאוד קשה בשביל לקבל את הבסיס התיאורטי החזק שלהם, וכן חלק גדול מהזמן זה היה משעמם. הרבה יותר כיף לרוץ לפתור בעיות בתכנות או לכתוב איזה בוט חמוד לטלגרם מאשר ללמוד קורס באלגברה לינארית או הסתברות.
בקישור הזה:
https://github.com/ossu/computer-science
יש רשימה של קורסים חינמיים לגמרי שילמדו אתכם את כל התיאוריה המשעממת של מדעי המחשב. זה ייקח שנתיים וממה שהצלחתי להתרשם לא ילמד אתכם לכתוב בוט לטלגרם. ובכל זאת זה מסלול הרבה יותר חכם מכניסה לבוטקמפ שילמד אתכם תוך שלושה חודשים את כל הטכנולוגיות שאתם צריכים (מאפס לעבודה או משהו כזה).
לפני הטכנולוגיות המדליקות; לפני מיומנויות למידה; לפני עבודת צוות; לפני גיט וריאקט ופייתון ו Node ו Full Stack ו Machine Learning יש בסיס תיאורטי חזק שאתם צריכים לקבל כדי שתוכלו להסתדר בעולם הזה ולתקשר בצורה מקצועית עם האנשים שסביבכם. המחשבה שמספיק פרויקט אחד טוב כדי למצוא עבודה ולהסתדר היא נאיבית. גם אם לפעמים זה עובד, בטווח הרחוק כשהטכנולוגיה משתנה זה לא מספיק להיות מסוגל "ללמוד לבד". צריך גם בסיס חזק שאפשר ללמוד ממנו.
1 419
# שינוי שם ענף ברירת המחדל ב git
החל מ 1.10 גיטהאב משנים את השם של ענף ברירת המחדל מ master ל main כדי להתאים לרוח התקופה. זה הולך לשנות הרבה Tutorials ברשת ואולי אחרי שהשינוי ישקע אני אוכל לנצל את ההזדמנות להקליט גירסה חדשה של קורס גיט כאן באתר.
בינתיים בכל מקרה וכהכנה לאירוע שני טיפים קשורים-
הראשון הוא שאתם יכולים (ותמיד יכולתם) לבחור מה יהיה שם הענף הראשון במקום master, פשוט באמצעות יצירת ענף ראשון חדש לפני הקומיט הראשון:
$ git init
$ git checkout -b main
והשני הוא שאתם יכולים (מיולי האחרון) לבחור מה יהיה שם ענף ברירת המחדל במאגרים מקומיים חדשים שאתם יוצרים באמצעות שינוי קונפיגורציה פשוט. הרצת הפקודה הבאה מ cmd (כרגיל בלי הדולר שבהתחלה) תשנה את שם ענף ברירת המחדל ל main בכל מאגר מקומי חדש שלכם:
$ git config --global init.defaultBranch main
בהנחה שיש לכם גיט 2.28 או עדכני יותר מותקן על המכונה.1 419
# במורד מחילת הארנב
לפני כמה ימים חשבתי לשנות את תמונת הרקע בלינוקס. עכשיו אני ב i3 אז קפצתי ל arch wiki כדי למצוא איך משנים ב i3 תמונת רקע ולמדתי שם על תוכנה בשם feh שמגדירה wallpaper. ההוראות עבדו והצלחתי ליהנות מתמונת הרקע החדשה ... לשתי דקות בערך.
כי כשהמחשב חזר ממצב שינה תמונת הרקע נעלמה. וכאן התחיל המסע כדי למצוא למה היא נעלמת ואולי יש תוכנות אחרות איתן אפשר לצייר רקע (יש והרבה). וככל שהמסלול הזה נמשך מצאתי את עצמי מוצא רעיונות יותר ויותר הזויים בגוגל: לא בגלל שהם היו גרועים, אלא פשוט כי הם לא נכתבו עבור הבעיה שלי.
הפיתרון בסוף? כמו שאולי ניחשתם היתה קונפיגורציה אצלי על המכונה שאיפסה את תמונת הרקע בכל מיני מצבים בגלל תוכנה אחרת שהיתה מותקנת. ברגע שמצאתי את הקונפליקט הבנתי גם איך לסדר את הכל, ומשם גם הצלחתי לחפש את התשובות הנכונות בגוגל בקלות.
כי אחת הבעיות בריצה בתוך מחילת הארנב היא שככל שאנחנו יורדים יותר עמוק ככה קשה יותר להסתכל לצדדים ולבחון את הסיבות האמיתיות לבעיה. והאמת שהרבה פעמים בתכנות כשמשהו מתחיל להסתבך זה בגלל שאנחנו מנסים לעקוף מגבלה או מנגנון של תוכנה או ספריה אחרת. אם נצליח להבין מה המנגנון שפועל נגדנו יהיה לנו הרבה יותר קל להשתמש בו לטובתנו.
וכן CSS - אני מסתכל עליך...
<img src="https://www.tocode.co.il/system/css.gif class="img-responsive" />
1 419
# איך להוסיף תמיכה ב react-testing-library לכל פרויקט וובפאק
לא חייבים להשתמש ב create-react-app רק בשביל לקבל תמיכה בבדיקות אוטומטיות ב React, ולמעשה זה די פשוט להתקין את jest ואיתו את התמיכה ב react-testing-library כמעט לכל פרויקט וובפאק. נכון יש כמה מגבלות אבל ברוב המקרים הצעדים בפוסט כאן מספיקים. בואו נצא לדרך.
## התקנת הכלים
הכלי שמריץ אוטומטית את הבדיקות בריאקט נקרא jest. אולי הדבר המעצבן בסיפור שלנו היום הוא ש jest ו webpack הם לא חברים ולכן ג'סט חייב לקמפל מחדש את כל המודולים. ככל שהגדרות הבניה של הפרויקט שלכם יותר מורכבות כך יש סיכוי שתצטרכו לעבוד יותר קשה בשביל להוסיף תמיכה בג'סט.
אבל בואו לא נקדים את המאוחר ונתחיל בהתקנת ג'סט והחברים שלו:
npm install --save-dev @testing-library/react @testing-library/jest-dom babel-jest jest @babel/preset-env
התוסף babel-jest הולך לחבר בין jest ל babel וכך jest בעצם יריץ את בייבל במקום וובפאק.
## קבצי ההגדרות
קובץ ההגדרות המרכזי של jest נקרא jest.config.js ואנחנו רוצים לשמור אותו בתיקיה הראשית של הפרויקט. זה התוכן שעבד אצלי:
module.exports = {
"verbose": true,
"globals": {
"NODE_ENV": "test"
},
"moduleFileExtensions": [
"js",
"jsx"
],
setupFilesAfterEnv: ['./setUpTests.js'],
"moduleDirectories": [
"node_modules",
"src",
]
};
ככל שתרצו לטעון מודולים מיותר תיקיות אולי תרצו לשנות את moduleDirectories.
הקובץ setUpTests.js שמופיע במפתח setupFilesAfterEnv הוא קובץ ש jest יטען אוטומטית לפני הרצת הבדיקות. אני משתמש בו כדי לטעון את המודול extend-expect שמוסיף ל expect את הפונקציות של react-testing-library. זה תוכן הקובץ אצלי על המכונה, שוב בתיקיית הפרויקט הראשית:
import '@testing-library/jest-dom/extend-expect';
אם יהיו לכם ספריות נוספות שתרצו לטעון לפני כל בדיקה תוכלו להוסיף עוד שורות import לכאן.
והנודניק האחרון ברשימת הקונפיגורציות הוא babel.config.js. בגלל ש jest מפעיל את babel ישירות ולא עובר דרך וובפאק אנחנו צריכים לוודא שהקונפיגורציה של בייבל זמינה בקובץ נפרד. זה התוכן אצלי על המכונה:
module.exports = {
presets: ['@babel/preset-env', '@babel/preset-react'],
};
והוא לקוח ישירות מהמפתח options של babel-loader ב webpack-config.js (וכן אם הקונפיגורציה שלכם יותר מתוחכמת תצטרכו לקחת את כולה לקובץ ה babel.config.js).
## קובץ הבדיקות עצמו
קובץ הבדיקות הוא בפורמט רגיל של react-testing-library וכתבתי כבר פוסט היכרות עם הספריה בעבר. בפרויקט הדוגמה השתמשתי בקובץ בדיקות פשוט שנקרא person.test.js ונמצא בתיקיית src של הפרויקט:
import React from 'react';
import { screen, render, cleanup, fireEvent } from '@testing-library/react'
import Person from './person.js'
describe('Person component', () => {
it('should have the right message in the dom', () => {
const { container, getByText } = render(<Person />);
expect(getByText('Hi!')).toBeInTheDocument()
})
})
## הרצת הבדיקות
עכשיו שהכל במקום אנחנו יכולים להריץ את הבדיקות ישירות משורת הפקודה ובלי להפעיל דפדפן.
הפקודה:
npx jest
מפעילה את הבדיקות פעם אחת ומסיימת עם דוח תוצאות. הפלט שלה בפרויקט הדוגמה אצלי נראה כך:
PASS src/person.test.js
Person component
✓ should have the right message in the dom (51 ms)
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 2.797 s
Ran all test suites.
וכמובן הכיף האמיתי הוא להשאיר חלון עם בדיקות שרצות בכל פעם שאני משנה קובץ במערכת ואת זה אפשר לעשות עם המתג watch של ג'סט באופן הבא:
npx jest --watch
אבל האמת שהרבה יותר מומלץ להוסיף ל package.json את הסקריפטים המתאימים כדי כך ש npm test יפעיל את הבדיקה עם watch.
מוזמנים להוריד את פרויקט הדוגמה ולשחק עם הקוד בעצמכם בגיטהאב בקישור https://github.com/ynonp/react-testing-library-webpack-demo.1 419
# שינויים קטנים שעושים את ההבדל
שתי התוכניות הבאות עושות בדיוק את אותו דבר ונכתבו כדי לפתור את Advent Of Code 2019 Day 2. הראשונה:
mem = [1,9,10,3,2,3,11,0,99,30,40,50]
for i in range(0, len(mem), 4):
if mem[i] == 1:
mem[mem[i+3]] = mem[mem[i+1]] + mem[mem[i+2]]
elif mem[i] == 2:
mem[mem[i+3]] = mem[mem[i+1]] * mem[mem[i+2]]
elif mem[i] == 99:
break
else:
raise Exception(f"Invalid cmd #{mem[i]}")
print(mem[0])
וחברתה הארוכה יותר:
from more_itertools import sliced
memory = [1,9,10,3,2,3,11,0,99,30,40,50]
for [opcode,
input_index_1,
input_index_2,
output_index
] in sliced(memory, 4):
if opcode == 1:
memory[output_index] = memory[input_index_1] + memory[input_index_2]
elif opcode == 2:
memory[output_index] = memory[input_index_1] * memory[input_index_2]
elif opcode == 99:
break
else:
raise Exception(f"Invalid cmd #{memory[i]}")
print(memory[0])
קל לראות שמבחינת מימוש אין הבדל בין שתי התוכניות. גם מבחינת מוכנות לשינויים בעתיד, קלות הבדיקה, יכולת הרחבה, בקיצור בכל הפרמטרים המעניינים שתי התוכניות זהות. מלבד כמובן שמות המשתנים הארוכים יותר בתוכנית השניה.
ולמרות שאני מכיר את התרגיל ואת הפיתרון ועם כל האהבה לקוד קצר - התוכנית הראשונה מצליחה לבלבל אותי כל פעם.
שם נותן היגיון, הוא מסדר את המחשבות ועוזר לנו לקרוא את הקוד כשנצטרך. והיום עם השלמה אוטומטית בעורכי הטקסט שלכם, לכתוב שם ארוך אפילו לא לוקח יותר מדי מאמץ.1 419
# הפיתרון לא יעזור לך
ב 90% מהאתגרים התכנותיים שתתמודדו איתם (אפילו יותר בניקוי ראיונות עבודה) זאת לא התשובה שחשובה אלא הדרך. ו 90% מהאנשים יודעים את זה ועדיין מחפשים את התשובה.
בגלל זה כולם מחפשים להבין אם צריך לבחור ב Vue או React לפרויקט הבא, במקום להבין טוב יותר איך להגיע להחלטה הזאת.
אנחנו לא במשחק הזה בשביל להדביק פיתרונות מדף וללכת לים. העבודה אף פעם לא תסתיים וממילא בחיים האמיתיים אין פיתרונות מדף. הכיף הוא החיפוש (וכן זה אומר שלפעמים נשתמש בפיתרון לא הכי טוב; זאת הדרך שאני מכיר ללמוד ולהשתפר).
1 419
# בגלל השם
לקח לי הרבה זמן לעבור מ RabbitMQ ל Kafka, בעיקר בגלל הארנב. ל ZeroMQ לא היה סיכוי.
בבחירה בין Dancer ל Mojolicious תמיד העדפתי את השני כי השם שלו התגלגל טוב יותר על הלשון.
ספק ה VPN האהוב עליי הוא TunnelBear (וכן זה בגלל הדוב) ובעבודה עם בסיסי נתונים אין תחרות ל DBeaver (בגלל הבונה) ובדיקות אוטומטיות אני כותב ב Capybara (נו, בגלל הקפיברה)
ויש לי גם חשד שלמרות כל הטיעונים המלומדים, הסיבה ש lodash היה יותר פופולרי מ underscore היא אך ורק השם המדליק יותר שלו.
זה בסדר להעדיף תוכנה בגלל שם טוב, זה קורה לכולם, וכמו תמיד בתוכנה זה מועיל לשמור על ראש פתוח ותמיד לבחור גם את האלטרנטיבה עם השם הפחות מדליק.
