ตัวสร้าง Python และ __init__


107

เหตุใดผู้ก่อสร้างจึงเรียกว่า "คอนสตรัคเตอร์"? จุดประสงค์ของพวกเขาคืออะไรและแตกต่างจากวิธีการในชั้นเรียนอย่างไร?

นอกจากนี้__init__ในชั้นเรียนยังมีอีกไหม ฉันลองทำสิ่งต่อไปนี้ใครช่วยอธิบายผลลัพธ์ได้ไหม

>>> class test:
    def __init__(self):
        print "init 1"
    def __init__(self):
        print "init 2"

>>> s=test()
init 2

สุดท้าย__init__ตัวดำเนินการคือโอเวอร์โหลดหรือไม่?


41
ในทางเทคนิค__init__เป็นinitialiser ตัวสร้าง python คือ__new__. Python ใช้การเริ่มต้นสองเฟสอัตโนมัติ - __new__ส่งคืนอ็อบเจ็กต์ที่ถูกต้อง แต่ (โดยปกติ) ไม่มีประชากร (ดูboolตัวอย่างตัวนับ) ซึ่งจะ__init__เรียกใช้โดยอัตโนมัติ สิ่งนี้จะหลีกเลี่ยงปัญหาภาษาเช่น C ++ ที่มีกับอ็อบเจ็กต์ที่สร้างขึ้นบางส่วน - คุณไม่เคยมีใน Python (แม้ว่ามันอาจจะเริ่มต้นบางส่วนก็ตาม) คุณแทบไม่จำเป็นต้องลบล้างทั้งสองอย่าง__new__และ__init__ในชั้นเรียน
Tim Delaney

2
@TimDelaney: ฉันไม่แน่ใจว่าคุณหมายถึงอะไรกับวัตถุที่สร้างขึ้นบางส่วนใน C ++
Sebastian Mach

11
@phresnel ใน C ++ ประเภทของอ็อบเจ็กต์คือคลาสพื้นฐาน (ไม่ใช่คลาสย่อย) ในขณะที่อยู่ในตัวสร้างคลาสพื้นฐาน คุณไม่สามารถเรียกใช้เมธอดเสมือนในตัวสร้างคลาสพื้นฐานและให้คลาสย่อยจัดเตรียมการนำไปใช้ ใน Java คุณสามารถเรียกเมธอดคลาสย่อยในตัวสร้างคลาสพื้นฐานได้ แต่ตัวแปรสมาชิกคลาสย่อยจะเริ่มต้นโดยอัตโนมัติหลังจากตัวสร้างคลาสฐาน (และการเรียกเมธอด) ในภาษาที่มีการเริ่มต้นสองเฟสเช่น Python คุณสามารถเรียกใช้เมธอดในตัวเริ่มต้นคลาสพื้นฐานได้อย่างมีความสุขและให้คลาสย่อยจัดเตรียม (หรือแทนที่) พฤติกรรม
Tim Delaney

@TimDelaney: อ่าขอบคุณที่ชี้แจง
Sebastian Mach

4
@TimDelaney. ฉันคิดว่าความคิดเห็นของคุณควรแทนที่คำตอบที่ยอมรับ
ฟลาเมงโก

คำตอบ:


114

ไม่มีฟังก์ชันโอเวอร์โหลดใน Python ซึ่งหมายความว่าคุณไม่สามารถมีหลายฟังก์ชันที่มีชื่อเดียวกัน แต่มีอาร์กิวเมนต์ต่างกัน

ในตัวอย่างรหัสของคุณคุณไม่ได้บรรทุกเกินพิกัด __init__()สิ่งที่เกิดขึ้นคือความหมายที่สองrebindsชื่อ__init__กับวิธีการใหม่, การแสดงวิธีการที่ไม่สามารถเข้าถึงได้เป็นครั้งแรก

สำหรับคำถามทั่วไปของคุณเกี่ยวกับตัวสร้างWikipediaเป็นจุดเริ่มต้นที่ดี สำหรับสิ่งหลามเฉพาะผมขอแนะนำเอกสารหลาม


นอกจากนี้ยังหมายความว่าไฟล์ต้นฉบับถูกแยกวิเคราะห์ (ตีความ?) ตามลำดับ? ฉันแน่ใจได้หรือไม่ว่าฟังก์ชันใดที่ฉันกำหนดไว้ในภายหลังจะเขียนทับฟังก์ชันที่กำหนดด้วยชื่อเดียวกันก่อนหน้านี้? :( Q ของฉันฟังดูงี่เง่า .. น่าจะรู้นะ
0xc0de

4
@ 0xc0de: ใน Python คำจำกัดความของฟังก์ชันเป็นคำสั่งที่เรียกใช้งานได้จริงและทำงานจากบนลงล่างใช่
NPE

1
@ 0xc0de สิ่งที่เกิดขึ้นจริงคือเนื้อหาของคลาสถูกเรียกใช้งานในเนมสเปซของตัวเองจากนั้นเนมสเปซจะถูกส่งไปยังเมตาคลาส (ระหว่างชื่อและฐาน) นิยามฟังก์ชันเป็นเพียงการสร้างฟังก์ชันด้วยเนื้อความที่ระบุและกำหนดให้เป็นชื่อ งานสุดท้าย__init__จะเป็นสิ่งที่จบลงในชั้นเรียน
skyking

64

เหตุใดผู้ก่อสร้างจึงเรียกว่า "คอนสตรัคเตอร์"?

ตัวสร้าง (ชื่อ__new__) สร้างและส่งคืนอินสแตนซ์ใหม่ของคลาส ดังนั้นC.__new__เมธอดคลาสจึงเป็นตัวสร้างสำหรับคลาส C

C.__init__วิธีการเช่นนี้เรียกว่าอินสแตนซ์เฉพาะหลังจากที่มันถูกสร้างขึ้นเพื่อ initialise มันก่อนที่จะถูกส่งกลับไปยังผู้โทร ดังนั้นวิธีนี้จึงเป็นตัวเริ่มต้นสำหรับอินสแตนซ์ใหม่ของ C.

พวกเขาแตกต่างจากวิธีการในชั้นเรียนอย่างไร?

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

จุดประสงค์ของพวกเขาคืออะไร?

วัตถุประสงค์ของตัวสร้างC.__new__คือการกำหนดลักษณะการทำงานที่กำหนดเองในระหว่างการสร้างCอินสแตนซ์ใหม่

จุดประสงค์ของ initialiser C.__init__คือการกำหนดการเริ่มต้นที่กำหนดเองของแต่ละอินสแตนซ์ของCafter it ถูกสร้างขึ้น

ตัวอย่างเช่น Python ให้คุณทำ:

class Test(object):
    pass

t = Test()

t.x = 10   # here you're building your object t
print t.x

แต่ถ้าคุณต้องการให้ทุกอินสแตนซ์Testมีแอตทริบิวต์xเท่ากับ 10 คุณสามารถใส่รหัสนั้นไว้ด้านใน__init__:

class Test(object):
    def __init__(self):
        self.x = 10

t = Test()
print t.x

ทุกวิธีการอินสแตนซ์ (วิธีที่เรียกบนอินสแตนซ์เฉพาะของคลาส) จะได้รับอินสแตนซ์เป็นอาร์กิวเมนต์แรก selfอาร์กิวเมนต์ที่มีชื่อตามอัตภาพ

เมธอดคลาสเช่นคอนสตรัค__new__เตอร์รับคลาสเป็นอาร์กิวเมนต์แรกแทน

ตอนนี้ถ้าคุณต้องการค่าที่กำหนดเองสำหรับxแอตทริบิวต์สิ่งที่คุณต้องทำคือส่งค่านั้นเป็นอาร์กิวเมนต์ไปที่__init__:

class Test(object):
    def __init__(self, x):
        self.x = x

t = Test(10)
print t.x
z = Test(20)
print t.x

ฉันหวังว่านี่จะช่วยให้คุณคลายข้อสงสัยและเนื่องจากคุณได้รับคำตอบที่ดีสำหรับคำถามอื่น ๆ แล้วฉันจะหยุดที่นี่ :)


9

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

สิ่งที่คุณมักจะใช้คือสร้างตัวแปรสำหรับออบเจ็กต์นั้นดังนี้:

>>> class testing:
...     def __init__(self, init_value):
...         self.some_value = init_value

ดังนั้นสิ่งที่คุณทำได้คือสร้างวัตถุจากคลาสนี้ดังนี้:

>>> testobject = testing(5)

จากนั้นวัตถุทดสอบจะมีวัตถุที่เรียกsome_valueว่าในตัวอย่างนี้จะเป็น 5

>>> testobject.some_value
5

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

>>> class testing:
...     def __init__(self):
...         self.some_value = 5

จากนั้นค่าของ some_value จะเป็น 5 และคุณไม่จำเป็นต้องตั้งค่าเมื่อคุณสร้างวัตถุ

>>> testobject = testing()
>>> testobject.some_value
5

>>> และ ... ในตัวอย่างของฉันไม่ใช่สิ่งที่คุณเขียน มันจะมีลักษณะอย่างไรใน pyshell ...


ไม่มีปัญหาดีใจที่ช่วยคุณ :)
Niclas Nilsson

1

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

def func(): #now func refers to an empty funcion
    pass
...
func=5      #now func refers to the number 5
def func():
    print "something"    #now func refers to a different function

ในคำจำกัดความชั้นเรียนของคุณจะเก็บไว้ในภายหลัง


0

ไม่มีความคิดเกี่ยวกับวิธีการโอเวอร์โหลดใน Python แต่คุณสามารถบรรลุผลที่คล้ายกันได้โดยระบุอาร์กิวเมนต์ที่ไม่บังคับและคำหลัก

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.