ประเภทไม่เปลี่ยนรูป vs ไม่แน่นอน


186

ฉันสับสนในสิ่งที่ไม่เปลี่ยนรูปแบบ ฉันรู้ว่าfloatวัตถุนั้นถือว่าไม่เปลี่ยนรูปพร้อมกับตัวอย่างประเภทนี้จากหนังสือของฉัน:

class RoundFloat(float):
    def __new__(cls, val):
        return float.__new__(cls, round(val, 2))

สิ่งนี้ถือว่าไม่เปลี่ยนรูปเนื่องจากโครงสร้างคลาส / ลำดับชั้นหรือไม่ความหมายfloatอยู่ที่ด้านบนสุดของคลาสและเป็นการเรียกเมธอดของตัวเอง คล้ายกับตัวอย่างประเภทนี้ (แม้ว่าหนังสือของฉันบอกว่าdictไม่แน่นอน):

class SortedKeyDict(dict):
    def __new__(cls, val):
        return dict.__new__(cls, val.clear())

ในขณะที่บางสิ่งบางอย่างไม่แน่นอนมีวิธีการภายในชั้นเรียนด้วยตัวอย่างประเภทนี้:

class SortedKeyDict_a(dict):
    def example(self):
        return self.keys()

และสำหรับสุดท้ายclass(SortedKeyDict_a)หากฉันผ่านการตั้งค่าประเภทนี้:

d = (('zheng-cai', 67), ('hui-jun', 68),('xin-yi', 2))

โดยไม่ต้องเรียกexampleเมธอดมันจะส่งคืนพจนานุกรม SortedKeyDictกับ__new__ธงว่ามันเป็นข้อผิดพลาด ฉันลองส่งจำนวนเต็มไปที่RoundFloatชั้นเรียนด้วย__new__และมันก็ไม่มีข้อผิดพลาด


คุณยังสามารถตรวจสอบการมอบหมายรายชื่อด้วย [:]และpython เมื่อใช้ copy.copyซึ่งฉันได้รับคำตอบสำหรับข้อมูลเพิ่มเติมเกี่ยวกับความไม่แน่นอน
agf

คำตอบ:


232

อะไร? ลอยไม่เปลี่ยนรูป? แต่ฉันทำไม่ได้

x = 5.0
x += 7.0
print x # 12.0

นั่นไม่ใช่ "mut" x ใช่ไหม

คุณเห็นด้วยว่าสตริงต่าง ๆ ไม่เปลี่ยนรูปใช่มั้ย แต่คุณสามารถทำสิ่งเดียวกัน

s = 'foo'
s += 'bar'
print s # foobar

ค่าของการเปลี่ยนแปลงตัวแปร แต่จะเปลี่ยนโดยการเปลี่ยนแปลงสิ่งที่ตัวแปรหมายถึง ชนิดที่ไม่แน่นอนสามารถเปลี่ยนวิธีการนั้นและก็สามารถยังเปลี่ยน "ในสถานที่"

นี่คือความแตกต่าง

x = something # immutable type
print x
func(x)
print x # prints the same thing

x = something # mutable type
print x
func(x)
print x # might print something different

x = something # immutable type
y = x
print x
# some statement that operates on y
print x # prints the same thing

x = something # mutable type
y = x
print x
# some statement that operates on y
print x # might print something different

ตัวอย่างคอนกรีต

x = 'foo'
y = x
print x # foo
y += 'bar'
print x # foo

x = [1, 2, 3]
y = x
print x # [1, 2, 3]
y += [3, 2, 1]
print x # [1, 2, 3, 3, 2, 1]

def func(val):
    val += 'bar'

x = 'foo'
print x # foo
func(x)
print x # foo

def func(val):
    val += [3, 2, 1]

x = [1, 2, 3]
print x # [1, 2, 3]
func(x)
print x # [1, 2, 3, 3, 2, 1]

5
สิ่งที่คุณอธิบายหมายถึงฉัน: ตัวแปรที่เปลี่ยนแปลงได้ถูกส่งผ่านโดยการอ้างอิงตัวแปรที่ไม่เปลี่ยนรูปจะถูกส่งผ่านตามค่า ถูกต้องหรือไม่
Lorenz Meyer

17
เกือบแล้ว แต่ไม่ใช่อย่างแน่นอน เทคนิคตัวแปรทั้งหมดจะถูกส่งผ่านโดยการอ้างอิงในหลาม แต่มีความหมายมากขึ้นเช่นผ่านค่าในซี counterexample def f(my_list): my_list = [1, 2, 3]การเปรียบเทียบของคุณคือถ้าคุณทำ ด้วย pass-by-reference ใน C ค่าของอาร์กิวเมนต์สามารถเปลี่ยนแปลงได้โดยการเรียกใช้ฟังก์ชันนั้น ใน Python ฟังก์ชันนั้นไม่ได้ทำอะไรเลย def f(my_list): my_list[:] = [1, 2, 3]จะทำอะไรสักอย่าง
Morningstar

6
สามารถเปลี่ยนประเภทที่ไม่แน่นอนได้ ประเภทที่ไม่เปลี่ยนรูปไม่สามารถเปลี่ยนแปลงได้ นั่นคือวิธีที่หลามมองเห็นโลก โดยไม่คำนึงถึงว่าตัวแปรถูกส่งผ่านไปยังฟังก์ชันอย่างไร
ychaouche

13
ความแตกต่างที่สำคัญระหว่างซีแมนทิกส์ของไพ ธ อนและซีเทนติกแบบพาส - ที - อาร์อ้างอิงคือการกำหนดไม่ใช่การกลายพันธุ์ใน Python และเป็น C ++ ( แต่แน่นอนที่ซับซ้อนโดยความจริงที่ว่ายิ่งที่ได้รับมอบหมายเช่นa += bบางครั้งคือการกลายพันธุ์. และความจริงที่ว่ามอบหมายให้ส่วนหนึ่งของวัตถุขนาดใหญ่บางครั้งหมายถึงการกลายพันธุ์ของว่าวัตถุที่มีขนาดใหญ่ก็ไม่เคยเกิดการกลายพันธุ์ของส่วนเช่นa[0] = bไม่กลายพันธุ์a[0]แต่มันอาจจะกลายพันธุ์a... ซึ่งเป็นเหตุผลว่าทำไมมันอาจจะดีกว่าที่จะไม่ใส่สิ่งของในรูปของ C ++ และแทนที่จะอธิบายว่า Python ทำอะไรในเงื่อนไขของตัวเอง…)
abarnert

2
ฉันพบคำตอบนี้ทำให้เข้าใจผิดเพราะไม่ได้ใช้ id () ซึ่งจำเป็นสำหรับการทำความเข้าใจว่าหมายถึงไม่เปลี่ยนรูป
pawel_winzig

185

คุณต้องเข้าใจว่า Python แสดงข้อมูลทั้งหมดเป็นวัตถุ วัตถุเหล่านี้บางอย่างเช่นรายการและพจนานุกรมไม่แน่นอนซึ่งหมายความว่าคุณสามารถเปลี่ยนเนื้อหาได้โดยไม่ต้องเปลี่ยนข้อมูลประจำตัว วัตถุอื่น ๆ เช่นจำนวนเต็มลอยสตริงและสิ่งอันดับเป็นวัตถุที่ไม่สามารถเปลี่ยนแปลงได้ วิธีที่ง่ายต่อการเข้าใจนั่นคือถ้าคุณได้ดู ID ของวัตถุ

ด้านล่างคุณจะเห็นสตริงที่ไม่เปลี่ยนรูป คุณไม่สามารถเปลี่ยนเนื้อหา มันจะเพิ่มTypeErrorถ้าคุณพยายามที่จะเปลี่ยน นอกจากนี้หากเรากำหนดเนื้อหาใหม่วัตถุใหม่จะถูกสร้างขึ้นแทนเนื้อหาที่กำลังแก้ไข

>>> s = "abc"
>>>id(s)
4702124
>>> s[0] 
'a'
>>> s[0] = "o"
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'str' object does not support item assignment
>>> s = "xyz"
>>>id(s)
4800100
>>> s += "uvw"
>>>id(s)
4800500

คุณสามารถทำได้ด้วยรายการและจะไม่เปลี่ยนข้อมูลประจำตัวของวัตถุ

>>> i = [1,2,3]
>>>id(i)
2146718700
>>> i[0] 
1
>>> i[0] = 7
>>> id(i)
2146718700

หากต้องการอ่านเพิ่มเติมเกี่ยวกับตัวแบบข้อมูลของ Python คุณสามารถดูการอ้างอิงภาษา Python:


4
+1 สำหรับลิงก์ไปยังเอกสาร Python อย่างไรก็ตามมันใช้เวลาพอสมควรจนกระทั่งฉันรู้ว่าวันนี้คุณต้องแยกความแตกต่างของ Python 2 และ 3 ออก - ฉันได้อัปเดตคำตอบเพื่อเน้นว่า
benjamin

107

สามัญไม่เปลี่ยนรูปแบบ:

  1. หมายเลข: int(), float(),complex()
  2. ลำดับที่ไม่เปลี่ยนรูป: str(), tuple(), frozenset(),bytes()

ประเภทที่ไม่แน่นอนทั่วไป (เกือบทุกอย่าง):

  1. ลำดับที่ไม่แน่นอน: list(),bytearray()
  2. ชุดประเภท: set()
  3. ประเภทการทำแผนที่: dict()
  4. คลาส, อินสแตนซ์ของคลาส
  5. เป็นต้น

เคล็ดลับหนึ่งในการทดสอบอย่างรวดเร็วว่าประเภทไม่แน่นอนหรือไม่คือการใช้id()ฟังก์ชั่นในตัว

ตัวอย่างการใช้จำนวนเต็ม

>>> i = 1
>>> id(i)
***704
>>> i += 1
>>> i
2
>>> id(i)
***736 (different from ***704)

ใช้ในรายการ

>>> a = [1]
>>> id(a)
***416
>>> a.append(2)
>>> a
[1, 2]
>>> id(a)
***416 (same with the above id)

11
อธิบายได้ดี id()ชอบแนวคิดของการตรวจสอบโดย +1
Parag Tyagi

4
จริงๆแล้วการใช้งานid()ทำให้เข้าใจผิดที่นี่ วัตถุที่กำหนดจะมี ID เดียวกันตลอดช่วงอายุการใช้งาน แต่วัตถุต่าง ๆ ที่มีอยู่ในเวลาต่างกันอาจมี ID เดียวกันเนื่องจากการรวบรวมขยะ
สิงหาคม

37

ก่อนอื่นไม่ว่าคลาสจะมีวิธีการหรือโครงสร้างของคลาสนั้นไม่เกี่ยวข้องกับความผันแปร

intและfloats มีไม่เปลี่ยนรูป ถ้าฉันทำ

a = 1
a += 5

มันชี้ชื่อaไป1ที่ไหนสักแห่งในหน่วยความจำในบรรทัดแรก ในบรรทัดที่สองก็เงยหน้าขึ้นมองว่า1เพิ่ม5ได้รับ6แล้วจุดaที่ว่า6ในความทรงจำ - มันไม่ได้เปลี่ยน1ไป6ในทางใดทางหนึ่ง ตรรกะเดียวกันนี้ใช้กับตัวอย่างต่อไปนี้โดยใช้ชนิดไม่เปลี่ยนรูปอื่น ๆ:

b = 'some string'
b += 'some other string'
c = ('some', 'tuple')
c += ('some', 'other', 'tuple')

สำหรับประเภทที่ไม่แน่นอนฉันสามารถทำสิ่งที่เปลี่ยนค่าที่เก็บไว้ในหน่วยความจำได้ ด้วย:

d = [1, 2, 3]

ฉันได้สร้างรายการสถานที่ของที่1, 2และ3ในหน่วยความจำ ถ้าฉันทำแล้ว

e = d

ฉันแค่ชี้eไปที่จุดเดียวกันlist dที่ ฉันสามารถทำได้:

e += [4, 5]

และรายการที่ทั้งสองeและdจุดที่จะได้รับการปรับปรุงให้มีตำแหน่งของ4และ5ในหน่วยความจำ

ถ้าฉันกลับไปที่ประเภทที่ไม่เปลี่ยนรูปและทำสิ่งนั้นด้วยtuple:

f = (1, 2, 3)
g = f
g += (4, 5)

จากนั้นfยังคงเป็นจุดเท่านั้นที่เดิมtuple - คุณได้ชี้gไปที่ใหม่ทั้งหมดtuple

ตอนนี้ด้วยตัวอย่างของคุณ

class SortedKeyDict(dict):
    def __new__(cls, val):
        return dict.__new__(cls, val.clear())

ที่ที่คุณผ่าน

d = (('zheng-cai', 67), ('hui-jun', 68),('xin-yi', 2))

(ซึ่งเป็นtupleของtuples) ในขณะที่valคุณกำลังได้รับข้อผิดพลาดเนื่องจากtuples ไม่ได้มี.clear()วิธีการ - คุณจะต้องผ่านการdict(d)เป็นvalให้มันทำงานซึ่งในกรณีนี้คุณจะได้รับที่ว่างเปล่าSortedKeyDictเป็นผล


2
นี่เป็นคำอธิบายที่ดีมาก รักคำถามนี้และมุมมองใหม่ที่น่าสนใจเพื่ออธิบาย
นักวิทยาศาสตร์ที่ล้มเหลว

25

หากคุณมาถึง Python จากภาษาอื่น (ยกเว้นภาษาที่มีชื่อว่า Python เช่น Ruby) และยืนยันในการทำความเข้าใจในแง่ของภาษาอื่น ๆ นี่คือที่ซึ่งผู้คนมักจะสับสน:

>>> a = 1
>>> a = 2 # I thought int was immutable, but I just changed it?!

ใน Python การมอบหมายไม่ใช่การกลายพันธุ์ใน Python

ใน C ++ ถ้าคุณเขียนa = 2, คุณโทรซึ่งจะกลายพันธุ์วัตถุที่เก็บไว้ในa.operator=(2) a(และถ้ามีก็ไม่มีวัตถุที่เก็บไว้ในaที่เกิดข้อผิดพลาด.)

ใน Python a = 2ไม่ทำอะไรกับสิ่งที่ถูกเก็บไว้ในa; มันหมายถึงว่า2ตอนนี้ถูกเก็บไว้ในaแทน (และถ้ามีก็ไม่มีวัตถุที่เก็บไว้ในaที่ดี.)


ในที่สุดนี่เป็นส่วนหนึ่งของความแตกต่างที่ลึกซึ้งยิ่งขึ้น

ตัวแปรในภาษาเช่น C ++ เป็นตำแหน่งที่พิมพ์ในหน่วยความจำ ถ้าaเป็นintนั่นหมายความว่ามันเป็น 4 intไบต์บางที่คอมไพเลอร์รู้ว่าควรจะตีความว่าเป็น ดังนั้นเมื่อคุณทำa = 2มันจะเปลี่ยนสิ่งที่เก็บไว้ในหน่วยความจำ 4 ไบต์จาก0, 0, 0, 1เป็น0, 0, 0, 2ไปหากมีตัวแปร int ตัวอื่นที่อื่นจะมี 4 ไบต์ของตัวเอง

ตัวแปรในภาษาเช่น Python เป็นชื่อของวัตถุที่มีอายุการใช้งานของมันเอง มีวัตถุสำหรับหมายเลขที่เป็นและวัตถุสำหรับหมายเลขอื่น1 2และaไม่ใช่หน่วยความจำ 4 ไบต์ที่แสดงเป็นintมันเป็นเพียงชื่อที่ชี้ไปที่1วัตถุ มันไม่สมเหตุสมผลเลยที่a = 2จะเปลี่ยนหมายเลข 1 เป็นหมายเลข 2 (นั่นจะทำให้โปรแกรมเมอร์ Python มีอำนาจมากเกินไปในการเปลี่ยนการทำงานพื้นฐานของจักรวาล) สิ่งที่มันทำแทนเพียงแค่aลืม1วัตถุและชี้ไปที่2วัตถุนั้นแทน


ดังนั้นหากการมอบหมายไม่ใช่การกลายพันธุ์การกลายพันธุ์คืออะไร

  • a.append(b)เรียกวิธีการที่เอกสารการกลายพันธุ์เช่น (โปรดทราบว่าวิธีการเหล่านี้มักจะกลับมาNone) ประเภทที่ไม่เปลี่ยนรูปไม่ได้มีวิธีการใด ๆ ประเภทที่ไม่แน่นอนมักจะทำ
  • กำหนดเพื่อเป็นส่วนหนึ่งของวัตถุที่ต้องการหรือa.spam = b a[0] = bประเภทที่ไม่เปลี่ยนรูปไม่อนุญาตให้มีการกำหนดให้กับคุณลักษณะหรือองค์ประกอบประเภทที่ไม่แน่นอนมักจะอนุญาตให้หนึ่งหรืออื่น ๆ
  • บางครั้งใช้การมอบหมายเพิ่มเติมเช่นa += bบางครั้งไม่ ประเภทที่ไม่แน่นอนมักจะกลายพันธุ์ค่า; ประเภทที่ไม่เปลี่ยนรูปไม่เคยทำและให้สำเนาแก่คุณแทน (พวกเขาคำนวณa + bแล้วกำหนดผลลัพธ์ให้a)

แต่ถ้าการมอบหมายไม่ใช่การกลายพันธุ์การกำหนดให้ส่วนหนึ่งของการกลายพันธุ์ของวัตถุเป็นอย่างไร นั่นคือสิ่งที่มันยุ่งยาก a[0] = bไม่ได้กลายพันธุ์a[0](อีกครั้งซึ่งแตกต่างจาก C ++) แต่มันจะกลายพันธุ์a(ไม่เหมือน C ++ ยกเว้นทางอ้อม)

ทั้งหมดนี้เป็นสาเหตุที่น่าจะดีกว่าที่จะไม่ลองใส่ความหมายของภาษาไพ ธ อนในแง่ของภาษาที่คุณคุ้นเคย


2
พูด a = 'สวัสดี' a [0] = 'f' จะมี 'พิมพ์' print out 'fi' (ฉันจนถึงตอนนี้) ดังนั้นเมื่อคุณพูดว่ามันไม่ได้กลายพันธุ์ [0] แทนที่จะเป็น [0] ? [n] มีสถานที่เป็นของตัวเองในขณะนี้หรือไม่และการเปลี่ยนค่าให้ชี้ไปที่ค่าอื่นหรือไม่
Daniel Springer

19

ไม่ว่าจะเป็นวัตถุที่ไม่แน่นอนหรือไม่ขึ้นอยู่กับชนิดของวัตถุ สิ่งนี้ไม่ได้ขึ้นอยู่กับว่ามีวิธีการบางอย่างหรือไม่หรืออยู่ในโครงสร้างของลำดับชั้นของชั้นเรียน

ประเภทที่ผู้ใช้กำหนด (เช่นคลาส) นั้นมักจะไม่แน่นอน มีข้อยกเว้นบางอย่างเช่นคลาสย่อยง่าย ๆ ของประเภทไม่เปลี่ยนรูป ประเภทไม่เปลี่ยนรูปแบบอื่น ๆ รวมถึงบางส่วนในตัวชนิดเช่นint, float, tupleและstrเช่นเดียวกับบางชั้นเรียนหลามดำเนินการใน C.

คำอธิบายทั่วไปจากบท "Data Model" ใน Python Language Reference " :

มูลค่าของวัตถุบางอย่างสามารถเปลี่ยนแปลงได้ วัตถุที่มีค่าสามารถเปลี่ยนแปลงได้กล่าวจะไม่แน่นอน; วัตถุที่มีค่าไม่เปลี่ยนแปลงเมื่อมีการสร้างจะเรียกว่าไม่เปลี่ยนรูป

(ค่าของวัตถุคอนเทนเนอร์ที่ไม่เปลี่ยนรูปที่มีการอ้างอิงถึงวัตถุที่เปลี่ยนแปลงไม่ได้สามารถเปลี่ยนแปลงได้เมื่อค่าของหลังถูกเปลี่ยนอย่างไรก็ตามคอนเทนเนอร์ยังถือว่าไม่เปลี่ยนรูปเนื่องจากการรวบรวมวัตถุที่มีอยู่นั้นไม่สามารถเปลี่ยนแปลงได้ดังนั้นการเปลี่ยนแปลงไม่ได้อย่างเคร่งครัด เช่นเดียวกับการมีค่าที่ไม่สามารถเปลี่ยนแปลงได้มันจะบอบบางกว่า)

ความไม่แน่นอนของวัตถุถูกกำหนดโดยชนิดของวัตถุ เช่นตัวเลขสตริงและทูเปิลนั้นไม่สามารถเปลี่ยนแปลงได้ในขณะที่พจนานุกรมและรายการต่าง ๆ ไม่แน่นอน


+1 โปรดทราบว่ามีเพียงส่วนขยายบางประเภทเท่านั้น (คุณอาจต้องการตรวจสอบคำจำกัดความของคุณ แต่ประเภทบิวอินใน Python ทั้งหมดจะใช้งานใน C) นั้นไม่เปลี่ยนรูป อื่น ๆ (ส่วนใหญ่ฉันกล้าพูด) มีความเปลี่ยนแปลงได้อย่างสมบูรณ์แบบ

@delnan คุณเรียกว่า"ประเภทส่วนขยาย"อะไร
eyquem

@ eyquem: ฉันใช้คำว่า "ประเภทส่วนขยาย" ไม่ถูกต้องในคำตอบของฉันและ delnan ก็อ้างถึง หลังจากความคิดเห็นของเขาฉันแก้ไขคำตอบของฉันและหลีกเลี่ยงการใช้คำนี้
taleinat

19

ความแตกต่างระหว่างวัตถุที่เปลี่ยนแปลงได้และไม่เปลี่ยนรูป

คำนิยาม

วัตถุที่ไม่แน่นอน : วัตถุที่สามารถเปลี่ยนแปลงได้หลังจากสร้างมัน
วัตถุที่ไม่เปลี่ยนรูป : วัตถุที่ไม่สามารถเปลี่ยนแปลงได้หลังจากสร้างแล้ว

ในหลามหากคุณเปลี่ยนค่าของวัตถุที่ไม่เปลี่ยนรูปมันจะสร้างวัตถุใหม่

วัตถุที่ไม่แน่นอน

นี่คือวัตถุใน Python ที่เป็นประเภทที่ไม่แน่นอน:

  1. list
  2. Dictionary
  3. Set
  4. bytearray
  5. user defined classes

วัตถุไม่เปลี่ยนรูป

นี่คือวัตถุใน Python ที่เป็นชนิดไม่เปลี่ยนรูป:

  1. int
  2. float
  3. decimal
  4. complex
  5. bool
  6. string
  7. tuple
  8. range
  9. frozenset
  10. bytes

คำถามที่ไม่มีคำตอบ

คำถาม : สตริงเป็นประเภทที่ไม่เปลี่ยนรูปหรือไม่
คำตอบ : ใช่ มันเป็น แต่คุณสามารถอธิบายสิ่งนี้ได้: บท พิสูจน์ที่ 1 :

a = "Hello"
a +=" World"
print a

เอาท์พุต

"Hello World"

ในตัวอย่างข้างต้นสตริงที่ถูกสร้างขึ้นครั้งเดียวเป็น "Hello" แล้วเปลี่ยนเป็น "Hello World" นี่ก็หมายความว่าสตริงเป็นประเภทที่ไม่แน่นอน แต่ไม่ใช่เมื่อเราตรวจสอบตัวตนเพื่อดูว่าเป็นชนิดที่ไม่แน่นอนหรือไม่

a = "Hello"
identity_a = id(a)
a += " World"
new_identity_a = id(a)
if identity_a != new_identity_a:
    print "String is Immutable"

เอาท์พุต

String is Immutable

พิสูจน์ 2 :

a = "Hello World"
a[0] = "M"

เอาท์พุต

TypeError 'str' object does not support item assignment

คำถาม : Tuple เป็นรูปแบบที่ไม่เปลี่ยนรูปแบบหรือไม่
คำตอบ : ใช่มันเป็น พิสูจน์ 1 :

tuple_a = (1,)
tuple_a[0] = (2,)
print a

เอาท์พุต

'tuple' object does not support item assignment

ใน [46]: a = "Hello" ใน [47]: id (a) Out [47]: 140071263880128 ใน [48]: a = a.replace ("H", "g") ใน [49]: a ออก [49]: 'gello' ใน [50]: id (a) ออก [50]: 140071263881040
Argus Malware

คุณสนใจที่จะพิสูจน์ปัญหาการมอบหมายรายการของคุณกับตัวอย่างที่ระบุด้านบนของฉันหรือไม่
Argus Malware

การกำหนดรายการไม่ได้อยู่ในประเภทที่ไม่เปลี่ยนรูป ในกรณีของคุณคุณกำลังเปลี่ยนสตริง a แต่ในหน่วยความจำการกำหนดให้กับตัวแปรใหม่ การกำหนดรายการในกรณีของฉันจะไม่เปลี่ยนหน่วยความจำของตัวแปรเช่นในกรณีของรายการหรือพจนานุกรม หากคุณกำลังทำหน้าที่แทนที่คุณกำลังสร้างตัวแปรใหม่ที่ไม่ได้แก้ไขตัวแปรที่มีอยู่
anand tripathi

@ArgusMalware ในกรณีของคุณ id สองตัวมีค่าเท่ากันเนื่องจาก GC ตัวแรกที่รีไซเคิลโดย GC ดังนั้นอันที่สองใช้หน่วยความจำอีกครั้ง
Cologler

11

วัตถุที่ไม่แน่นอนต้องมีอย่างน้อยวิธีที่สามารถกลายพันธุ์วัตถุได้ ตัวอย่างเช่นlistวัตถุมีappendวิธีการซึ่งจะกลายพันธุ์วัตถุจริง:

>>> a = [1,2,3]
>>> a.append('hello') # `a` has mutated but is still the same object
>>> a
[1, 2, 3, 'hello']

แต่คลาสfloatไม่มีเมธอดในการกลายพันธุ์วัตถุลอย คุณทำได้:

>>> b = 5.0 
>>> b = b + 0.1
>>> b
5.1

แต่=ตัวถูกดำเนินการไม่ใช่วิธีการ มันแค่ผูกระหว่างตัวแปรกับอะไรก็ตามที่อยู่ทางขวาของมันไม่มีอะไรอื่นอีกแล้ว มันไม่เคยเปลี่ยนแปลงหรือสร้างวัตถุ มันเป็นการประกาศว่าตัวแปรจะชี้ไปที่ตั้งแต่นี้เป็นต้นไป

เมื่อคุณทำผูกตัวถูกดำเนินการตัวแปรที่จะลอยใหม่ชถูกสร้างขึ้นด้วยผลของเต้ b = b + 0.1=5 + 0.1

เมื่อคุณกำหนดตัวแปรให้กับวัตถุที่มีอยู่ไม่แน่นอนหรือไม่=ถูกดำเนินการผูกตัวแปรกับวัตถุนั้น และไม่มีอะไรเกิดขึ้นอีกแล้ว

ในทั้งสองกรณี=เพียงแค่ทำการผูก มันไม่เปลี่ยนหรือสร้างวัตถุ

เมื่อคุณดำเนินa = 1.0การ=ตัวถูกดำเนินการจะไม่สร้าง float แต่เป็น1.0ส่วนหนึ่งของบรรทัด ที่จริงเมื่อคุณเขียน1.0มันเป็นชวเลขสำหรับfloat(1.0)การโทรคอนสตรัคที่ส่งกลับวัตถุลอย (นั่นคือเหตุผลที่ถ้าคุณพิมพ์1.0และกด Enter คุณจะได้รับ "echo" 1.0พิมพ์ด้านล่างนั่นคือค่าตอบแทนของฟังก์ชั่นคอนสตรัคเตอร์ที่คุณเรียกว่า)

ตอนนี้ถ้าbเป็นแบบลอยตัวและคุณกำหนดa = bตัวแปรทั้งสองจะชี้ไปที่วัตถุเดียวกัน แต่จริงๆแล้วตัวแปรไม่สามารถเชื่อมต่อระหว่างตัวเองได้เพราะวัตถุนั้นไม่สามารถเปลี่ยนแปลงได้และถ้าคุณทำb += 1ตอนนี้bชี้ไปที่วัตถุใหม่และaเป็น ยังคงชี้ไปที่ oldone และไม่ทราบว่าbมีอะไรชี้ไปที่

แต่ถ้าcเป็นเช่นนั้น a listและคุณกำหนดa = cตอนนี้aและcสามารถ "comunicate" เพราะlistไม่แน่นอนและถ้าคุณทำc.append('msg')แล้วเพียงตรวจสอบaว่าคุณได้รับข้อความ

(โดยวิธีการที่ทุกวัตถุมีหมายเลขประจำตัวที่ไม่ซ้ำกันซึ่งคุณจะได้รับด้วยid(x)ดังนั้นคุณสามารถตรวจสอบว่าวัตถุเดียวกันหรือไม่ตรวจสอบว่ามีการเปลี่ยนแปลงรหัสที่ไม่ซ้ำกัน)


6

ชั้นคือไม่เปลี่ยนรูปถ้าวัตถุของคลาสที่แต่ละคนมีค่าคงที่เมื่อ instantiation ที่ไม่สามารถต่อจากนั้นจะมีการเปลี่ยนแปลง

ในคำอื่นเปลี่ยนค่าทั้งหมดของตัวแปรนั้น(name)หรือทิ้งไว้คนเดียว

ตัวอย่าง:

my_string = "Hello world" 
my_string[0] = "h"
print my_string 

คุณคาดว่าสิ่งนี้จะทำงานและพิมพ์Hello worldแต่จะทำให้เกิดข้อผิดพลาดต่อไปนี้:

Traceback (most recent call last):
File "test.py", line 4, in <module>
my_string[0] = "h"
TypeError: 'str' object does not support item assignment

ตัวแปลกำลังพูดว่า: ฉันไม่สามารถเปลี่ยนอักขระตัวแรกของข้อความนี้ได้

คุณจะต้องเปลี่ยนทั้งหมดstringเพื่อให้มันใช้งานได้:

my_string = "Hello World" 
my_string = "hello world"
print my_string #hello world

ตรวจสอบตารางนี้:

ป้อนคำอธิบายรูปภาพที่นี่

แหล่ง


หนึ่งจะสามารถแก้ไขส่วนประกอบของสตริงไพ ธ อนได้อย่างไรในแบบที่กระชับกว่าที่คุณแสดงไว้ด้านบน?
ลุคเดวิส

@LukeDavis my_string = 'h' + my_string[1:]คุณสามารถทำ สิ่งนี้จะสร้างสตริงใหม่ที่ชื่อว่า my_string และ my_string ดั้งเดิมจะหายไป (พิมพ์id(my_string)เพื่อดูสิ่งนี้) แน่นอนว่าไม่ยืดหยุ่นมากสำหรับกรณีทั่วไปมากขึ้นคุณสามารถแปลงเป็นลิสต์และย้อนกลับได้:l = list(my_string) l[0] = 'h' my_string = ''.join(l)
danio

5

สำหรับผมแล้วดูเหมือนว่าคุณกำลังต่อสู้กับคำถามที่ความไม่แน่นอน / ไม่เปลี่ยนรูปหมายถึงอะไร ดังนั้นนี่คือคำอธิบายง่ายๆ:

ก่อนอื่นเราต้องมีพื้นฐานในการอธิบาย

ดังนั้นให้คิดถึงสิ่งที่คุณตั้งโปรแกรมเป็นวัตถุเสมือนสิ่งที่บันทึกไว้ในหน่วยความจำคอมพิวเตอร์เป็นลำดับเลขฐานสอง (อย่าพยายามจินตนาการสิ่งนี้ยากเกินไป ^^) ในภาษาคอมพิวเตอร์ส่วนใหญ่คุณจะไม่สามารถทำงานกับเลขฐานสองเหล่านี้ได้โดยตรง แต่คุณจะใช้การตีความเลขฐานสองแทน

เช่นคุณไม่ได้คิดถึงตัวเลขเช่น 0x110, 0xaf0278297319 หรือคล้ายกัน แต่คุณคิดว่าตัวเลขเช่น 6 หรือ Strings เช่น "Hello, world" ตัวเลขหรือสตริงที่น้อยกว่านี้เป็นความหมายของเลขฐานสองในหน่วยความจำคอมพิวเตอร์ เช่นเดียวกับค่าตัวแปรใด ๆ

กล่าวโดยย่อ: เราไม่ได้เขียนโปรแกรมด้วยค่าจริง แต่ด้วยการตีความค่าไบนารีจริง

ตอนนี้เรามีการตีความที่จะต้องไม่เปลี่ยนแปลงเพื่อเหตุผลและสิ่งอื่น ๆ ที่ "เรียบร้อย" ในขณะที่มีการตีความที่อาจมีการเปลี่ยนแปลง ตัวอย่างเช่นลองนึกถึงการจำลองของเมืองหรืออีกนัยหนึ่งโปรแกรมที่มีวัตถุเสมือนจริงจำนวนมากและบางส่วนเป็นที่อยู่อาศัย ตอนนี้อาจมีการเปลี่ยนแปลงวัตถุเสมือน (บ้าน) และพวกเขายังคงสามารถพิจารณาว่าเป็นบ้านเดียวกันได้หรือไม่ แน่นอนพวกเขาสามารถ ดังนั้นพวกเขาจะไม่แน่นอน: พวกเขาสามารถเปลี่ยนแปลงได้โดยไม่ต้องกลายเป็นวัตถุที่แตกต่าง "สมบูรณ์"

ทีนี้ลองคิดถึงจำนวนเต็ม: นี่เป็นวัตถุเสมือน (ลำดับของเลขฐานสองในหน่วยความจำคอมพิวเตอร์) ดังนั้นถ้าเราเปลี่ยนหนึ่งในนั้นเช่นการเพิ่มค่าหกต่อหนึ่งมันจะยังเป็นหกหรือไม่? ไม่แน่นอน ดังนั้นจำนวนเต็มใด ๆ ไม่เปลี่ยนรูป

ดังนั้น: หากการเปลี่ยนแปลงใด ๆ ในวัตถุเสมือนหมายความว่าจริง ๆ แล้วมันกลายเป็นวัตถุเสมือนจริงอีกอย่างหนึ่งมันก็จะเรียกว่าไม่เปลี่ยนรูป

หมายเหตุสุดท้าย:

(1) อย่าผสมผสานประสบการณ์ในโลกแห่งความเป็นจริงที่ไม่แน่นอนและไม่เปลี่ยนแปลงกับการเขียนโปรแกรมในบางภาษา:

ภาษาการเขียนโปรแกรมทุกภาษามีคำจำกัดความของตัวเองซึ่งวัตถุอาจถูกปิดเสียงและสิ่งที่อาจไม่

ดังนั้นในขณะที่คุณอาจเข้าใจความแตกต่างในความหมายคุณยังต้องเรียนรู้การใช้งานจริงสำหรับแต่ละภาษาโปรแกรม ... แน่นอนอาจมีจุดประสงค์ของภาษาที่ 6 อาจถูกทำให้กลายเป็น 7 จากนั้นอีกครั้งสิ่งนี้จะเป็นสิ่งที่บ้าหรือน่าสนใจเช่นการจำลองของจักรวาลคู่ขนาน ^^

(2) คำอธิบายนี้ไม่แน่นอนทางวิทยาศาสตร์มันมีไว้เพื่อช่วยให้คุณเข้าใจความแตกต่างระหว่างไม่แน่นอนและไม่เปลี่ยนรูป


5

เป้าหมายของคำตอบนี้คือการสร้างสถานที่เดียวเพื่อค้นหาความคิดที่ดีทั้งหมดเกี่ยวกับวิธีการบอกว่าคุณกำลังจัดการกับการกลายพันธุ์ / การไม่กลายพันธุ์ (ไม่เปลี่ยนรูป / ไม่แน่นอน / ไม่แน่นอน) และหากเป็นไปได้จะทำอย่างไรกับมัน มีบางครั้งที่การกลายพันธุ์ไม่เป็นที่พึงปรารถนาและพฤติกรรมของงูเหลือมในเรื่องนี้สามารถรู้สึกตอบโต้กับผู้เขียนโค้ดที่มาจากภาษาอื่น

ตามโพสต์ที่มีประโยชน์โดย @ mina-gabriel:

การวิเคราะห์ข้างต้นและรวม w / a โพสต์โดย @ arrakëën:

สิ่งที่ไม่สามารถเปลี่ยนแปลงได้โดยไม่คาดหมาย?

  • เซนต์คิตส์และเนวิส (ประเภทตัวแปรที่เก็บค่าเดียว) จะไม่เปลี่ยนแปลงอย่างกะทันหัน
    • ตัวอย่างที่เป็นตัวเลข: int (), float (), complex ()
  • มีบางส่วน "ลำดับที่ไม่แน่นอน":
    • str (), tuple (), frozenset (), ไบต์ ()

อะไรสามารถ?

  • รายการเช่นวัตถุ (รายการ, พจนานุกรม, ชุด, bytearray ())
  • โพสต์ที่นี่ยังบอกคลาสและอินสแตนซ์ของคลาส แต่สิ่งนี้อาจขึ้นอยู่กับสิ่งที่คลาสสืบทอดมาจากและ / หรือวิธีการสร้าง

โดย "ไม่คาดคิด" ฉันหมายความว่าโปรแกรมเมอร์จากภาษาอื่นอาจไม่คาดหวังพฤติกรรมนี้ (ยกเว้นหรือ Ruby และอาจเป็นภาษาอื่นที่ "Python like" บางภาษา)

เพิ่มในการสนทนานี้:

พฤติกรรมนี้เป็นข้อได้เปรียบเมื่อป้องกันไม่ให้คุณใส่รหัสของคุณโดยไม่ตั้งใจด้วยสำเนาข้อมูลโครงสร้างหน่วยความจำขนาดใหญ่ที่กินหน่วยความจำจำนวนมาก แต่เมื่อสิ่งนี้ไม่เป็นที่พึงปรารถนาเราจะไปรอบ ๆ มันได้อย่างไร

ด้วยรายการวิธีแก้ปัญหาง่ายๆคือการสร้างรายการใหม่ดังนี้:

list2 = รายการ (list1)

กับโครงสร้างอื่น ๆ ... วิธีการแก้ปัญหาอาจมีเล่ห์เหลี่ยม วิธีหนึ่งคือการวนลูปผ่านอิลิเมนต์และเพิ่มลงในโครงสร้างข้อมูลว่างเปล่าใหม่ (ชนิดเดียวกัน)

ฟังก์ชั่นสามารถกลายพันธุ์ของต้นฉบับเมื่อคุณผ่านในโครงสร้างที่ไม่แน่นอน จะบอกได้อย่างไร

  • มีการทดสอบบางอย่างเกี่ยวกับความคิดเห็นอื่น ๆ ในชุดข้อความนี้ แต่จากนั้นมีความคิดเห็นที่แสดงว่าการทดสอบเหล่านี้ไม่ใช่ข้อพิสูจน์แบบเต็ม
  • object.function () เป็นวิธีการของวัตถุต้นฉบับ แต่มีเพียงบางส่วนของการกลายพันธุ์เหล่านี้ หากพวกเขาไม่คืนสิ่งใดพวกเขาก็อาจทำ หนึ่งคาดว่าจะใช้. ผนวก () เพื่อทำการกลายพันธุ์โดยไม่ทำการทดสอบโดยให้ชื่อ .union () ส่งคืนการรวมของ set1.union (set2) และจะไม่กลายพันธุ์ เมื่อมีข้อสงสัยฟังก์ชั่นสามารถตรวจสอบค่าตอบแทน ถ้า return = None มันจะไม่กลายพันธุ์
  • เรียงลำดับ () อาจเป็นวิธีแก้ปัญหาในบางกรณี เนื่องจากมันส่งคืนรุ่นดั้งเดิมที่เรียงลำดับแล้วจึงอนุญาตให้คุณเก็บสำเนาที่ไม่ได้กลายพันธุ์ก่อนที่คุณจะเริ่มทำงานกับต้นฉบับด้วยวิธีอื่น อย่างไรก็ตามตัวเลือกนี้ถือว่าคุณไม่สนใจลำดับขององค์ประกอบดั้งเดิม (ถ้าคุณต้องการคุณต้องหาวิธีอื่น) ในทางตรงกันข้าม. sort () ทำให้กลายพันธุ์เดิม (ตามที่คาดไว้)

วิธีการที่ไม่ได้มาตรฐาน (ในกรณีที่เป็นประโยชน์): พบสิ่งนี้ใน github เผยแพร่ภายใต้ใบอนุญาต MIT:

  • ที่เก็บ github ภายใต้: tobgu ชื่อ: pyrsistent
  • มันคืออะไร: Python รหัสโครงสร้างข้อมูลถาวรที่เขียนขึ้นเพื่อใช้แทนโครงสร้างข้อมูลหลักเมื่อการกลายพันธุ์ไม่พึงประสงค์

สำหรับคลาสที่กำหนดเอง @semicolon แนะนำให้ตรวจสอบว่ามี__hash__ฟังก์ชั่นหรือไม่เพราะโดยทั่วไปแล้ววัตถุที่ไม่แน่นอนควรไม่มี__hash__()ฟังก์ชั่น

นี่คือทั้งหมดที่ฉันได้รวบรวมไว้ในหัวข้อนี้ในตอนนี้ ความคิดอื่น ๆ การแก้ไข ฯลฯ ยินดีต้อนรับ ขอบคุณ


3

วิธีคิดที่แตกต่าง:

การกำหนดให้กับวัตถุที่ไม่เปลี่ยนรูปแบบในหลามนั้นสามารถคิดได้ว่าเป็นสำเนาลึกในขณะที่การมอบหมายให้วัตถุที่ไม่แน่นอนนั้นเป็นสิ่งที่ตื้น


1
สิ่งนี้ไม่ถูกต้อง การมอบหมายทั้งหมดใน Python นั้นเป็นการอ้างอิง ไม่มีการคัดลอกที่เกี่ยวข้อง
สิงหาคม

3

คำตอบที่ง่ายที่สุด:

ตัวแปรที่ไม่แน่นอนคือตัวแปรที่ค่าอาจเปลี่ยนแปลงในขณะที่การเปลี่ยนแปลงค่าตัวแปรที่ไม่เปลี่ยนรูปจะไม่เกิดขึ้น การแก้ไขตัวแปรที่ไม่เปลี่ยนรูปจะสร้างตัวแปรเดียวกันใหม่

ตัวอย่าง:

>>>x = 5

จะสร้างค่า 5 อ้างอิงโดย x

x -> 5

>>>y = x

คำสั่งนี้จะทำให้ y หมายถึง 5 จาก x

x -------------> 5 <----------- y

>>>x = x + y

เนื่องจาก x เป็นจำนวนเต็ม (ชนิดไม่เปลี่ยนรูป) ได้ถูกสร้างใหม่

ในคำสั่งนิพจน์บน RHS จะส่งผลเป็นค่า 10 และเมื่อสิ่งนี้ถูกกำหนดให้กับ LHS (x), x จะสร้างใหม่เป็น 10 ดังนั้นตอนนี้

x ---------> 10

Y ---------> 5


-1

ฉันไม่ได้อ่านคำตอบทั้งหมด แต่คำตอบที่เลือกไม่ถูกต้องและฉันคิดว่าผู้เขียนมีความคิดที่สามารถกำหนดตัวแปรใหม่ได้หมายความว่าประเภทข้อมูลใดไม่แน่นอน ไม่เป็นเช่นนั้น ความไม่แน่นอนนั้นเกี่ยวข้องกับการส่งต่อโดยการอ้างอิงมากกว่าผ่านค่า

ให้บอกว่าคุณสร้างรายการ

a = [1,2]

ถ้าคุณจะพูดว่า:

b = a
b[1] = 3

แม้ว่าคุณจะกำหนดค่าใหม่ให้กับ B แต่ก็จะกำหนดค่าให้กับ a มันเป็นเพราะเมื่อคุณกำหนด "b = a" คุณกำลังส่ง "อ้างอิง" ไปยังวัตถุแทนที่จะคัดลอกค่า นี่ไม่ใช่กรณีที่มีสตริงลอย ฯลฯ สิ่งนี้ทำให้รายการพจนานุกรมและไลค์ที่ไม่แน่นอน แต่ booleans ลอย ฯลฯ ไม่เปลี่ยนรูป


-1

สำหรับออบเจ็กต์ที่ไม่เปลี่ยนรูปแบบการกำหนดจะสร้างสำเนาของค่าใหม่ตัวอย่างเช่น

x=7
y=x
print(x,y)
x=10 # so for immutable objects this creates a new copy so that it doesnot 
#effect the value of y
print(x,y)

สำหรับวัตถุที่ไม่แน่นอนการมอบหมายนั้นไม่ได้สร้างค่าอีกชุด ตัวอย่างเช่น,

x=[1,2,3,4]
print(x)
y=x #for immutable objects assignment doesn't create new copy 
x[2]=5
print(x,y) # both x&y holds the same list

1
ไม่ถูกต้องอย่างแน่นอน ที่ได้รับมอบหมายไม่เคยสร้างสำเนา โปรดอ่านnedbatchelder.com/text/names.html ในกรณีแรกx=10เป็นเพียงการมอบหมายอีกอย่างหนึ่งขณะx[2] = 5เรียกใช้วิธีการเปลี่ยนแปลง intวัตถุก็ขาดวิธีการเปลี่ยนแปลงแต่ความหมายของการมอบหมายหลามไม่ขึ้นอยู่กับประเภท
juanpa.arrivillaga

-2

ใน Python มีวิธีง่าย ๆ ในการรู้:

เปลี่ยนรูป:

    >>> s='asd'
    >>> s is 'asd'
    True
    >>> s=None
    >>> s is None
    True
    >>> s=123
    >>> s is 123
    True

ไม่แน่นอน:

>>> s={}
>>> s is {}
False
>>> {} is {}
Flase
>>> s=[1,2]
>>> s is [1,2]
False
>>> s=(1,2)
>>> s is (1,2)
False

และ:

>>> s=abs
>>> s is abs
True

ดังนั้นฉันคิดว่าฟังก์ชั่นในตัวก็ไม่เปลี่ยนรูปแบบใน Python

แต่ฉันไม่เข้าใจวิธีการลอยทำงาน:

>>> s=12.3
>>> s is 12.3
False
>>> 12.3 is 12.3
True
>>> s == 12.3
True
>>> id(12.3)
140241478380112
>>> id(s)
140241478380256
>>> s=12.3
>>> id(s)
140241478380112
>>> id(12.3)
140241478380256
>>> id(12.3)
140241478380256

มันแปลกมาก


แต่นั่นไม่ชัดเจน เพราะทูเปิลนั้นไม่เปลี่ยนรูป พิมพ์x = (1, 2)แล้วลองและกลายพันธุ์xเป็นไปไม่ได้ วิธีหนึ่งที่ฉันได้พบว่าตรวจสอบความไม่แน่นอนคือhashมันใช้งานได้กับวัตถุในตัวอย่างน้อย hash(1) hash('a') hash((1, 2)) hash(True)ทำงานทั้งหมดและhash([]) hash({}) hash({1, 2})ทั้งหมดไม่ทำงาน
อัฒภาค

@semicolon สำหรับคลาสที่ผู้ใช้กำหนดเองhash()จะทำงานหากอ็อบเจ็กต์กำหนด__hash__()วิธีแม้ว่าคลาสที่ผู้ใช้กำหนดเองจะไม่แน่นอน
สิงหาคม

1
@augurar ฉันหมายถึงใช่ แต่ไม่มีอะไรใน Python ที่จะรับประกันทุกอย่างเพราะ Python ไม่มีการพิมพ์แบบคงที่หรือการรับรองอย่างเป็นทางการ แต่hashวิธีการนี้ยังคงเป็นวิธีที่ดีเพราะวัตถุที่ไม่แน่นอนโดยทั่วไปไม่ควรมี__hash__()วิธีการเนื่องจากการทำให้ปุ่มเหล่านั้นในพจนานุกรมนั้นเป็นอันตราย
อัฒภาค

1
@augurar และเซมิโคลอน (หรืออื่น ๆ หากพวกเขารู้): __hash __ () วิธีแก้ปัญหา ... ผู้สร้างคลาสที่กำหนดเองต้องเพิ่มมันเพื่อให้มีหรือไม่ ถ้าเป็นเช่นนั้นกฎคือถ้ามีอยู่วัตถุควรจะไม่เปลี่ยนรูป; หากไม่มีอยู่เราไม่สามารถบอกได้เนื่องจากผู้สร้างอาจเหลือเพียงแค่ปิด
TMWP
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.