คลาส Python สืบทอดวัตถุ


1243

มีเหตุผลใดสำหรับการประกาศคลาสเพื่อสืบทอดจากobject?

ฉันเพิ่งพบรหัสบางอย่างที่ทำสิ่งนี้และฉันไม่สามารถหาเหตุผลที่ดีได้

class MyClass(object):
    # class code follows...

2
สิ่งนี้สร้างคลาสสไตล์ใหม่
Slaks

116
คำตอบสำหรับคำถามนี้ (ในขณะที่เรียบง่าย) ค่อนข้างหายาก สิ่งที่ Googling เช่น "ระดับฐานวัตถุหลาม" หรือคล้ายกันเกิดขึ้นกับหน้าและหน้าของบทเรียนเกี่ยวกับการเขียนโปรแกรมเชิงวัตถุ Upvoting เพราะนี่คือลิงค์แรกที่นำฉันไปสู่คำค้นหา "วัตถุหลามแบบเก่ากับรูปแบบใหม่"
vastlysuperiorman

คำตอบ:


763

มีเหตุผลใดสำหรับการประกาศคลาสเพื่อสืบทอดจากobject?

ในหลาม 3 นอกเหนือจากการทำงานร่วมกันระหว่างงูหลาม 2 และ 3 ไม่มีเหตุผล ในหลาม 2 ด้วยเหตุผลหลายประการ


Python 2.x เรื่องราว:

ใน Python 2.x (จาก 2.2 เป็นต้นไป) มีสองรูปแบบของคลาสขึ้นอยู่กับว่ามีหรือไม่มีobjectคลาสฐาน:

  1. คลาสสไตล์ "คลาสสิค" : ไม่มีobjectคลาสพื้นฐาน:

    >>> class ClassicSpam:      # no base class
    ...     pass
    >>> ClassicSpam.__bases__
    ()
  2. คลาสสไตล์ "ใหม่" : มีทั้งทางตรงหรือทางอ้อม (เช่นสืบทอดจากประเภทบิวด์อิน ) objectเป็นคลาสพื้นฐาน:

    >>> class NewSpam(object):           # directly inherit from object
    ...    pass
    >>> NewSpam.__bases__
    (<type 'object'>,)
    >>> class IntSpam(int):              # indirectly inherit from object...
    ...    pass
    >>> IntSpam.__bases__
    (<type 'int'>,) 
    >>> IntSpam.__bases__[0].__bases__   # ... because int inherits from object  
    (<type 'object'>,)

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

  • สนับสนุน descriptorsการสนับสนุนสำหรับการอธิบายโดยเฉพาะโครงสร้างต่อไปนี้จะเกิดขึ้นกับ descriptors:

    1. classmethod: เมธอดที่รับคลาสเป็นอาร์กิวเมนต์โดยนัยแทนอินสแตนซ์
    2. staticmethod: วิธีที่ไม่ได้รับอาร์กิวเมนต์โดยนัย selfเป็นอาร์กิวเมนต์แรก
    3. คุณสมบัติด้วยproperty: สร้างฟังก์ชั่นสำหรับจัดการการรับการตั้งค่าและการลบแอททริบิว
    4. __slots__: บันทึกการใช้หน่วยความจำของคลาสและยังส่งผลให้เข้าถึงแอตทริบิวต์ได้เร็วขึ้น แน่นอนมันไม่กำหนดข้อ จำกัด
  • __new__วิธีสถิต: ช่วยให้คุณสามารถกำหนดวิธีการใหม่อินสแตนซ์ชั้นที่ถูกสร้างขึ้น

  • ลำดับการแก้ไขเมธอด (MRO) : ในลำดับใดคลาสฐานของคลาสจะถูกค้นหาเมื่อพยายามแก้ไขวิธีการโทร

  • ที่เกี่ยวข้องกับการซ่อมบำรุง, การโทรsuper ยังเห็นsuper()ว่าถือว่าสุดยอด

หากคุณไม่ได้รับมรดกobjectให้ลืมสิ่งเหล่านี้ คำอธิบายที่ละเอียดมากขึ้นของสัญลักษณ์แสดงหัวข้อย่อยก่อนหน้าพร้อมกับ perks อื่น ๆ ของการเรียนสไตล์ "ใหม่" สามารถพบได้ที่นี่

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


Python 3.x เรื่องราว:

ใน Python 3 สิ่งต่างๆจะง่ายขึ้น มีคลาสสไตล์ใหม่เท่านั้น (เรียกอย่างชัดเจนว่าคลาส) ดังนั้นความแตกต่างเพียงอย่างเดียวในการเพิ่มobjectคือต้องการให้คุณพิมพ์อักขระอีก 8 ตัว นี้:

class ClassicSpam:
    pass

เทียบเท่าโดยสิ้นเชิง (นอกเหนือจากชื่อ :-) ถึงสิ่งนี้:

class NewSpam(object):
     pass

และสิ่งนี้:

class Spam():
    pass

ทั้งหมดมีอยู่ในพวกเขาobject__bases__

>>> [object in cls.__bases__ for cls in {Spam, NewSpam, ClassicSpam}]
[True, True, True]

ดังนั้นคุณควรทำอย่างไร

ใน Python 2: สืบทอดจากเสมอobjectอย่างชัดเจนอย่างชัดเจนรับสิทธิพิเศษ

ใน Python 3:สืบทอดมาจากobjectถ้าคุณกำลังเขียนโค้ดที่พยายามจะเป็น Python ผู้ไม่เชื่อเรื่องพระเจ้านั่นคือมันต้องทำงานทั้งใน Python 2 และ Python 3 ไม่เช่นนั้นมันจะไม่สร้างความแตกต่างเลยเพราะ Python แทรกมันไว้ให้คุณ เบื้องหลัง.


"ขึ้นอยู่กับการมีหรือไม่มีประเภท builtin เป็นฐานระดับ" => จริงๆแล้วมันไม่ได้เกี่ยวกับ "การขาดหรือการปรากฏตัวของชนิดในตัวเป็นชั้นฐาน" แต่อากาศสืบทอดคลาส - โดยตรงหรือโดยอ้อม - objectจาก IIRC มีช่วงเวลาที่ยังไม่มีตัวบิวท์อินทั้งหมดที่ย้ายไปยังคลาสสไตล์ใหม่
bruno desthuilliers

@brunodesthuilliers objectความประทับใจของฉันคือการที่ทุกคนในตัวชนิดไม่รับมรดกจาก ฉันมี Python 2.2.3 รอบตัวและหลังจากการตรวจสอบอย่างรวดเร็วฉันไม่พบผู้กระทำความผิด แต่ฉันจะป้อนคำตอบในภายหลังเพื่อให้ชัดเจนยิ่งขึ้น จะสนใจถ้าคุณสามารถหาตัวอย่างแม้ว่าความอยากรู้ของฉันป่องๆ
Dimitris Fasarakis Hilliard

ในความซื่อสัตย์ทั้งหมด (cf the "IIRC" ในความคิดเห็นก่อนหน้าของฉัน) ฉันไม่แน่ใจ 101% เกี่ยวกับประเด็นนี้ (ไม่ว่าทุกประเภท builtin จะถูกแปลงเป็นคลาสสไตล์ใหม่เมื่อมีการแนะนำคลาสสไตล์ใหม่) - ฉันอาจเป็นธรรมดา ผิดหรือสิ่งนี้อาจเกี่ยวข้องเฉพาะบางประเภทของ lib มาตรฐาน (แต่ไม่ได้สร้างขึ้นภายใน) แต่ถึงกระนั้นฉันคิดว่ามันควรจะดีกว่าที่จะชี้แจงว่าสิ่งที่ทำให้ชั้นเรียนรูปแบบใหม่มีobjectอยู่ในฐานของมัน
bruno desthuilliers

7
stackoverflow ที่แย่มาก ๆ เพียง upvote = upvote +1 สำหรับคำตอบแบบนี้หวังว่าฉันจะทำ upvote + = N
PirateApp

1
staticmethodและclassmethodทำงานได้ดีแม้ในชั้นเรียนแบบเก่า propertysorta ใช้สำหรับการอ่านในคลาสแบบเก่ามันไม่สามารถดักการเขียนได้ (ดังนั้นหากคุณกำหนดให้กับชื่ออินสแตนซ์จะได้รับแอตทริบิวต์ของชื่อที่กำหนดซึ่งทำให้คุณสมบัติเป็นเงา) โปรดทราบว่า__slots__การปรับปรุงความเร็วในการเข้าถึงแอตทริบิวต์นั้นส่วนใหญ่เกี่ยวกับการยกเลิกการสูญเสียที่เกิดขึ้นกับการเข้าถึงแอตทริบิวต์ของคลาสสไตล์ใหม่ดังนั้นจึงไม่ใช่จุดขายของคลาสสไตล์ใหม่ (ประหยัดหน่วยความจำเป็นจุดขาย)
ShadowRanger

540

Python 3

  • class MyClass(object): = คลาสสไตล์ใหม่
  • class MyClass:= คลาสสไตล์ใหม่ (สืบทอดโดยปริยายobject)

Python 2

  • class MyClass(object): = คลาสสไตล์ใหม่
  • class MyClass:= คลาสเก่า

คำอธิบาย :

เมื่อกำหนดคลาสพื้นฐานใน Python 3.x คุณได้รับอนุญาตให้ดรอปobjectจากนิยาม อย่างไรก็ตามนี่สามารถเปิดประตูให้กับปัญหาที่ติดตามยาก ...

Python แนะนำคลาสแบบใหม่ใน Python 2.2 และตอนนี้คลาสแบบเก่านั้นค่อนข้างเก่า การอภิปรายของชั้นเรียนแบบเก่าจะถูกฝังไว้ในเอกสาร 2.xและไม่มีอยู่ในเอกสาร 3.x

ปัญหาคือไวยากรณ์สำหรับการเรียนแบบเก่าในหลาม 2.x เป็นเช่นเดียวกับรูปแบบทางเลือกสำหรับการเรียนแบบใหม่ในหลาม 3.x Python 2.x ยังคงใช้กันอย่างแพร่หลายมาก (เช่น GAE, Web2Py) และโค้ดใด ๆ (หรือ coder) นำคำจำกัดความสไตล์คลาส 3.x ไปสู่โค้ด 2.x โดยไม่ได้ตั้งใจจะจบลงด้วยวัตถุฐานที่ล้าสมัยอย่างจริงจัง และเนื่องจากชั้นเรียนแบบเก่าไม่ได้อยู่ในเรดาห์ของใครเลยพวกเขาจึงไม่รู้ว่าสิ่งใดกระทบ

ดังนั้นลองสะกดมันให้ไกลและช่วยนักพัฒนา 2.x ให้น้ำตา


13
"เมื่อกำหนดคลาสพื้นฐานใน Python 3.x คุณได้รับอนุญาตให้วางวัตถุจากคำจำกัดความอย่างไรก็ตามสิ่งนี้สามารถเปิดประตูสำหรับปัญหาที่ยากต่อการติดตามอย่างจริงจัง ... " คุณกำลังอ้างถึงปัญหาอะไร
Aidis

6
@Aidis: ฉันคิดว่าพวกเขาหมายความว่ารหัสที่ทำงานบนทั้ง Py2 และ Py3 จะดีใน Py3 แต่จะเสียใน Py2 ถ้ามันขึ้นอยู่กับคุณสมบัติชั้นสไตล์ใหม่ โดยส่วนตัวถ้าฉันกำลังเขียนโค้ดแบบนั้นฉันจะไม่ได้รับมรดกที่ชัดเจนและเพียงแค่ใส่__metaclass__ = typeที่ด้านบนของโมดูล (หลังจากfrom __future__ import absolute_import, division, print_functionบรรทัด :-)); มันเป็นแฮ็คที่ใช้งานร่วมกันได้ใน Py2 ซึ่งจะทำให้คลาสที่กำหนดในภายหลังทั้งหมดในโมดูลรูปแบบใหม่โดยค่าเริ่มต้นและใน Py3 จะถูกละเว้นโดยสิ้นเชิง
ShadowRanger

400

ใช่นี่เป็นวัตถุ 'รูปแบบใหม่' มันเป็นคุณสมบัติที่แนะนำใน python2.2

วัตถุรูปแบบใหม่ที่มีรูปแบบที่แตกต่างกันของวัตถุวัตถุคลาสสิกและบางสิ่งบางอย่างจะไม่ทำงานอย่างถูกต้องกับวัตถุแบบเก่าเช่นsuper(), @propertyและอธิบาย ดูบทความนี้สำหรับคำอธิบายที่ดีเกี่ยวกับสไตล์คลาสใหม่

ลิงก์สำหรับคำอธิบายของความแตกต่าง SO: ความแตกต่างระหว่างสไตล์เก่ากับคลาสสไตล์ใหม่ใน Python คืออะไร


110
+1 นี่ โปรดทราบว่าคลาสแบบเก่าจะหายไปใน Python 3 ดังนั้นคุณจะต้องสืบทอดจากobjectใน Python 2 เท่านั้น

9
นี่ไม่ใช่คำตอบที่แท้จริง ฉันแค่ให้การอ้างอิงถึงบทความอื่น ๆ ฉันคิดว่าคำตอบของ Yarin ควรเป็นคำตอบสำหรับคำถามนี้
alwbtc

2
@alwbtc: คำตอบนี้มีอะไรใหม่เช่นกัน ตัวอย่างเช่นการกล่าวถึง "super ()" นำฉันไปสู่อีกหนึ่งสิ่งสำคัญ [ที่นี่] ( stackoverflow.com/questions/576169/… )
ViFI

34

ประวัติความเป็นมาจากLearn Python the Hard Way :

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

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

นอกจากนี้เพื่อแจ้งให้คุณทราบว่าความแตกต่างระหว่างคลาสสไตล์ใหม่กับคลาสเก่าคือคลาสสไตล์ใหม่นั้นมักสืบทอดจาก objectคลาสหรือคลาสอื่นที่สืบทอดมาจากobject:

class NewStyle(object):
    pass

ตัวอย่างอื่นคือ:

class AnotherExampleOfNewStyle(NewStyle):
    pass

ในขณะที่คลาสพื้นฐานแบบเก่ามีลักษณะดังนี้:

class OldStyle():
    pass

และคลาสเด็กแบบเก่าจะมีลักษณะดังนี้:

class OldStyleSubclass(OldStyle):
    pass

คุณจะเห็นได้ว่าคลาสพื้นฐานของ Old Style นั้นไม่ได้สืบทอดจากคลาสอื่น ๆ อย่างไรก็ตามคลาสของ Old Style นั้นสามารถสืบทอดจากคลาสอื่นได้ การรับค่าจากวัตถุรับประกันได้ว่าการใช้งานบางอย่างนั้นมีให้ใน Python ทุกคลาส มีการนำสไตล์คลาสใหม่มาใช้ใน Python 2.2


8
การเรียกคลาสรูทobjectนั้นไม่ใช่เรื่องที่น่าสับสน แต่ที่จริงแล้วมันเป็นมาตรฐานที่ค่อนข้างดี Smalltalk มีชื่อคลาสรูObjectทและมีชื่อเมตาคลาสรูClassต ทำไม? เพราะเช่นเดียวDogกับชั้นเรียนสำหรับสุนัขObjectเป็นชั้นเรียนสำหรับวัตถุและClassเป็นชั้นเรียนสำหรับชั้นเรียน Java, C #, ObjC, Ruby และภาษา OO ตามคลาสอื่น ๆ ส่วนใหญ่ที่ผู้คนใช้ในปัจจุบันที่มีคลาสรูทใช้รูปแบบของObjectชื่อไม่ใช่แค่ Python
abarnert

30

ใช่มันเป็นประวัติศาสตร์ ก็ไม่มีมันจะสร้างชั้นเรียนแบบเก่า

หากคุณใช้type()กับวัตถุแบบเก่าคุณจะได้รับ "อินสแตนซ์" บนออบเจ็กต์สไตล์ใหม่คุณจะได้คลาส


นอกจากนี้หากคุณใช้type()กับคลาสแบบเก่าคุณจะได้รับ "classobj" แทนที่จะเป็น "type"
Joel Sjögren

7

ไวยากรณ์ของคำสั่งการสร้างชั้นเรียน:

class <ClassName>(superclass):
    #code follows

ในกรณีที่ไม่มีซูเปอร์คลาสอื่น ๆ ที่คุณต้องการสืบทอดโดยเฉพาะsuperclassควรเป็นobjectซึ่งเป็นรากของคลาสทั้งหมดใน Python

objectในทางเทคนิคแล้วรูทของคลาส "สไตล์ใหม่" ใน Python แต่วันนี้การเรียนสไตล์ใหม่นั้นดีพอ ๆ กับการเป็นสไตล์การเรียนเท่านั้น

แต่ถ้าคุณไม่ได้ใช้คำอย่างชัดเจนobjectเมื่อสร้างคลาสดังนั้นอย่างที่คนอื่น ๆ พูดถึง Python 3.x ได้รับสืบทอดมาจากobjectซูเปอร์คลาสโดยปริยาย แต่ฉันคิดว่าชัดเจนดีกว่า implicit (นรก) เสมอ

การอ้างอิง

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