פייתון/פייתון גרסה 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