פייתון/פייתון גרסה 3/העתקים
העתקים היא פעולה שמייצרת העתקה של טיפוסים. קיימים שני סוגים של העתקים:
- העתק עמוק (deep copy) - למשתנה המועתק יש מיקום זיכרון משל עצמו. שינויים שבוצעו על המשתנה לא ישפיעו על המקור.
- העתק רדוד (shallow copy)- המשתנה החדש "מצביע" (alias) אל אותו תא זיכרון של המשתנה המקורי ועל כן שינוים במשתנה החדש ישפיעו על המשתנה המקורי. לדוגמה השמה מייצרת העתק רדוד.
- בכדי לדעת האם מדובר בהעתק עמוק או רדוד נעזר בפונקציית id שמחזירה את "מקום הזיכרון".
פונקצית id
[עריכה]פקודת השמה היא דוגמה להעתק רדוד. העותק החדש של המשתנה מצביע בדיוק על אותו מקום בזיכרון :
a=[1,2,3]
x=a
print(id(x))
print(id(a))
x.append(4)
print(a)
>>>2414993751560
>>>2414993751560
>>>[1, 2, 3, 4]
לעומת זאת הצהרה מייצרת בדיוק את אותו רשימה אך היא נשמרת במקום חדש:
x=[1,2,3]
y=x[:]
print(id(x))
print(id(y))
y.append(1)
print(x)
print(y)
>>>2123513615304
>>>2123477079176
>>>[1, 2, 3]
>>>[1, 2, 3, 1]
חשוב לזכור כי טיפוסים בעצמם אינם מחזקים תווים או ערכים אלא הם מצביעים אל כתובת זיכרון עם ערך.
רשימה
[עריכה]העתקים מייצרים העתק רדוד לרשימות כלומר מייצר מצביע לאותה. נראה את ההשפעה של העתקים על רשימות : נשם לב לקריאות הבאות:
>>> num = 2
>>> L = [num] * 3
>>> L
[2, 2, 2]
>>> L[2] = 3
>>> L
[2, 2, 3]
במקרה הראשון יצרנו העתק למספר, איבר שלא ניתן לשנות אותו, בתוך רשימה. כאשר שינינו את המספר ברשימה, התבצעה החלפה של המספר.
>>> num = 2
>>> L2 = [[num] ] * 3
>>> L2
[[2], [2], [2]]
>>> L2[1][0] = 10
>>> L2
[[10], [10], [10]]
# same to...
>>> nest_lst = [num]
>>> L2 = [nest_lst]*3
>>> L2
[[2], [2], [2]]
>>> L2[1][0] = 9
>>> L2
[[9], [9], [9]]
במקרה השני יצרנו העתק רדוד לרשימה מקוננת ולכן כאשר שיננו את הרשימה המקוננת (בדוגמה השנייה nest_lst) כל הרשימות שהצביעו אליה השתנו.
>>> num = 2
>>> L3 = [[num]*4]*2
>>> L3
[[2, 2, 2, 2], [2, 2, 2, 2]]
>>> L3[1][0] = 5
>>> L3
[[5, 2, 2, 2], [5, 2, 2, 2]]
במקרה השלישי יצרנו תערובת של דוגמא ראשונה ודוגמה שנייה. מצד אחד יצרנו העתקים למספרים ומצד שני מצביעים לרשימות.
רשומה
[עריכה]אם נחדד את החשיבות של העתקים: אנו יכולים לבצע שרשור לדוגמה של רשומה, טיפוס אותו לא ניתן לשנות, מפני שאנו יוצרים העתק רדוד
tpl=(1,2,3)
tpl=tpl+(123,)
print(tpl)
>>>(1, 2, 3, 123)
ההבדל בין אופרטור ==
לעומת is
[עריכה]#Declaration - creates a new variables
>>> a=400
>>> b=400
>>> id(a)
89043264
>>> id(b)
89043920
# == vs is
>>> a==b
True
>>> a is b
False
# Assigning
>>> x=23423
>>> y=x
>>> id(x)
103332672
>>> id(y)
103332672
# == vs is
>>> x is y
True
>>> x ==y
True
#comparing list:
x=[1,3,2]
y=x[:] #deep copy
z=x # assigning
print(x is y) # Flase - "is" cheacks if 'x' and 'y' has the same location in the memory
print(x is z) #true
print(x==y) #true - cheacks if the values of x,y are the same!
כלומר, אופרטור is בודק האם שני משתנים רשומים באותו מקום בזיכרון, ו-== בודק האם הערך שווה
הערה
[עריכה]שמו לב שפייתון מאחסנת מספרים נמוכים באותה כתובת ולכן עלולים להתבלבל בין סוגי העתקים:
>>> a=1
>>> b=1
>>> id(a)
1573705520
>>> id(b)
1573705520
>>> a==b
True
>>> a is b
True