בפייתון אין משתנים

From Python
Jump to: navigation, search

Contents

בפייתון אין משתנים

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

מהו משתנה?

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

int a;
a=3;
a=5;

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

ומה בפייתון?

a=3
a=5

המספרים '3' ו- '5' שמופיעים באגף ימין הם אובייקטים. אוביקטים בפייתון חיים בשטחי זכרון משלהם כל עוד מישהו צריך אותם (אפשר לזהות אותם ע"י הפונקציה id -הפונקציה נתפסת כמחזירה מזהה של האוביקט, לכתובת המספרית עצמה אין משמעות בשפה). לאובייקטים יש טיפוס קבוע ואינו ניתן לשינוי במשך חיי האובייקט (מה שמוביל לעוד אמרה מעוררת מחלוקת שאני נוהג לומר: פייתון היא strictly typed. שפות כמו C או פורטרן הן, באופן יחסי, weakly typed).

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

בצורה דומה del a מוחקת את השם a, לא את האובייקט ששם זה מתייחס אליו.

כאשר כותבים a=5 בשורה השנייה, אין לכך שום קשר לאובייקט 3. קיים איזשהו שטח בזכרון שמכיל 5, ומעכשיו השם a יתייחס לשטח זה. האובייקט "3" שנקרא קודם לכן a יכול להמשיך להתקיים בזכרון במקום אחר, או שלא. הדבר תלוי בשיקולים חיצוניים (במקרה זה הוא יישאר כי פייתון מחזיק cache של מספרים קטנים). למשל - אם אין לאובייקט זה שמות נוספים, והוא לא מוכל בתוך אובייקט אחר, אז אין כל דרך להתייחס אליו מתוך השפה. על כן הוא מיותר וברוב המקרים יימחק.

דוגמאות

דוגמאות לבאגים ואי הבנות שנגרמים בעקבות זאת


1.

>>> x=[1]
>>> y=x
>>> x is y
True
>>> x.append(1)
>>> print y
[1,1]
או קי, אם כך המשתנים בפייתון הם בעצם reference. האמנם?
>>> a=5
>>> b=a
>>> a is b
True
>>> a=3
>>> print b
5
מה? אבל שיניתי את המשתנה! שמתי שם 3!
למי שקרא מה שכתבתי קודם התשובה צריכה להיות ברורה: לא שינינו שום "משתנה". פשוט קשרנו את השם a לאובייקט אחר. b ממשיך להתייחס לאותו אובייקט.


2.

def f(a,b,c):
    a = 5
    b = b+[2]
    c.append(2)

x,y,z = [1],[2],[3]
f(x,y,z)
print x,y,z

[1],[2],[3,2]

אנשים שואלים האם העברת משתנים בקריאה לפונקציה היא by reference או by value. אם תגיד להם by value, הם יצפו שהמשתנה z לא ישנה את ערכו אחרי הקריאה לפונקציה. אם תגיד להם by reference, הם יצפו ש- x ו- y ישנו את ערכם לאחר הקריאה לפונקציה. מה שקורה בפועל הוא שקריאה לפונקציה פשוט מגדירה שמות לוקאליים (ב namespace פרטי של הפונקציה, על ה- stack). לאובייקטים שכבר קיימים. אח"כ בפונקציה מגדירים שהשמות a ו- b יתייחסו לאוביקטים אחרים. את השם c לא מגדירים מחדש, אלא פשוט משרשרים 2 לאוביקט שנקרא בשם זה.

Personal tools