เรียกวิธีการเรียนของผู้ปกครองจากชั้นเรียนเด็ก?


607

เมื่อสร้างลำดับชั้นวัตถุแบบง่าย ๆ ใน Python ฉันต้องการที่จะสามารถเรียกใช้เมธอดของคลาสพาเรนต์จากคลาสที่ได้รับ ใน Perl และ Java มีคำสำคัญสำหรับสิ่งนี้ ( super) ใน Perl ฉันอาจทำสิ่งนี้:

package Foo;

sub frotz {
    return "Bamf";
}

package Bar;
@ISA = qw(Foo);

sub frotz {
   my $str = SUPER::frotz();
   return uc($str);
}

ใน Python ดูเหมือนว่าฉันต้องตั้งชื่อคลาส parent อย่างชัดเจนจาก child Foo::frotz()ในตัวอย่างข้างต้นที่ผมจะต้องทำสิ่งที่ชอบ

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

นี่เป็นข้อ จำกัด ที่แท้จริงในหลามช่องว่างในความเข้าใจของฉันหรือทั้งสองอย่าง?


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

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

จะตระหนักถึงการเปลี่ยนแปลงในการจัดการซุปเปอร์ระหว่างงูหลาม 2 และ Python 3 https://www.python.org/dev/peps/pep-3135/ การตั้งชื่อชั้นไม่จำเป็นต้องใช้อีกต่อไป (แม้ว่าอาจจะยังเป็นความคิดของพระเจ้า
jwpfox

คำตอบ:


736

ใช่ แต่มีเฉพาะคลาสที่มีสไตล์ใหม่ ใช้super()ฟังก์ชั่น:

class Foo(Bar):
    def baz(self, arg):
        return super().baz(arg)

สำหรับ python <3 ให้ใช้:

class Foo(Bar):
    def baz(self, arg):
        return super(Foo, self).baz(arg)

53
คุณไม่สามารถ "ส่งคืน Bar.baz (self, arg)" ได้หรือไม่? ถ้าเช่นนั้นจะดีกว่ากัน?

15
ใช่คุณทำได้ แต่ OP ได้ขอวิธีการทำโดยไม่ตั้งชื่อ superclass อย่างชัดเจนที่ไซต์การโทร (แถบในกรณีนี้)
Adam Rosenfield

7
super () แปลกพิสดารหลายมรดก มันเรียกคลาส "ถัดไป" นั่นอาจเป็นคลาสพาเรนต์หรืออาจเป็นคลาสที่ไม่เกี่ยวข้องอย่างสมบูรณ์ซึ่งปรากฏที่อื่นในลำดับชั้น
Steve Jessop

6
ฉันใช้ Python 2.x และฉันได้รับ "ข้อผิดพลาด: ต้องเป็นประเภทไม่ใช่ classobj" เมื่อฉันทำเช่นนี้
Chris F

28
ไวยากรณ์super(Foo, self)สำหรับ Python 2.x ถูกต้องหรือไม่ ใน Python 3.x super()เป็นที่ต้องการหรือไม่
Trevor Boyd Smith

144

Python ก็มีสุดยอดเช่นกัน:

super(type[, object-or-type])

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

ตัวอย่าง:

class A(object):     # deriving from 'object' declares A as a 'new-style-class'
    def foo(self):
        print "foo"

class B(A):
    def foo(self):
        super(B, self).foo()   # calls 'A.foo()'

myB = B()
myB.foo()

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

83
ImmediateParentClass.frotz(self)

จะไม่เป็นไรไม่ว่าผู้ปกครองจะกำหนดระดับfrotzเองหรือสืบทอด superจำเป็นสำหรับการรองรับการสืบทอดหลายครั้งอย่างถูกต้องเท่านั้น (และจะใช้งานได้ก็ต่อเมื่อทุกคลาสใช้อย่างถูกต้อง) โดยทั่วไปแล้วAnyClass.whateverจะค้นหาwhateverในAnyClassบรรพบุรุษของถ้าAnyClassไม่ได้กำหนด / แทนที่มันและสิ่งนี้ถือเป็นจริงสำหรับ "ชั้นเรียนเด็กที่เรียกวิธีการของผู้ปกครอง" เป็นเหตุการณ์อื่น ๆ !


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

เกิดอะไรขึ้นถ้ามันเป็นวิธีการเรียน?
Shiplu Mokaddim

@TusharVazirani ถ้าระดับผู้ปกครองเป็นแบบทันทีparentมันอยู่ในขอบเขตของทั้งชั้นใช่มั้ย
Yaroslav Nikitenko

1
@YaroslavNikitenko ฉันกำลังพูดถึงการนำเข้าอินสแตนซ์ไม่ใช่คลาส
Tushar Vazirani

1
@TusharVazirani ตอนนี้ฉันเข้าใจแล้วขอบคุณ ฉันกำลังคิดเกี่ยวกับการโทรจากวิธีการเรียนปัจจุบัน (ตามที่ระบุไว้โดย OP)
Yaroslav Nikitenko

75

Python 3มีไวยากรณ์ที่แตกต่างและง่ายกว่าสำหรับการเรียกเมธอด parent

หากFooคลาสสืบทอดมาจากBarนั้นBar.__init__สามารถเรียกใช้จากFooผ่านทางsuper().__init__():

class Foo(Bar):

    def __init__(self, *args, **kwargs):
        # invoke Bar.__init__
        super().__init__(*args, **kwargs)

5
ดีใจที่ได้ยิน python 3 มีทางออกที่ดีกว่า คำถามมาจาก python 2.X ขอบคุณสำหรับคำแนะนำว่า
jjohn

46

คำตอบมากมายได้อธิบายวิธีเรียกวิธีการจากผู้ปกครองที่ถูกแทนที่ในเด็ก

อย่างไรก็ตาม

"คุณจะเรียกวิธีการเรียนของผู้ปกครองจากชั้นเรียนเด็กได้อย่างไร"

อาจหมายถึง:

"คุณจะเรียกวิธีการที่สืบทอดมาได้อย่างไร"

คุณสามารถเรียกเมธอดที่สืบทอดจากคลาสพาเรนต์ราวกับว่าเป็นเมธอดของคลาสย่อยตราบใดที่ยังไม่ถูกเขียนทับ

เช่นในหลาม 3:

class A():
  def bar(self, string):
    print("Hi, I'm bar, inherited from A"+string)

class B(A):
  def baz(self):
    self.bar(" - called by baz in B")

B().baz() # prints out "Hi, I'm bar, inherited from A - called by baz in B"

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

ฉันพบ: https://docs.python.org/3/tutorial/classes.html#inheritance เพื่อเป็นประโยชน์ในการทำความเข้าใจวิธีการเข้าถึงวิธีการสืบทอด


@gizzmole ในสถานการณ์สิ่งนี้ไม่ทำงาน? ฉันยินดีที่จะเพิ่มคำเตือนหรือคำเตือนใด ๆ ที่เกี่ยวข้อง แต่ฉันไม่สามารถทราบได้ว่าลิงก์ที่คุณโพสต์เกี่ยวข้องกับคำตอบนี้อย่างไร
เบ็น

@Ben ขออภัยฉันอ่านคำตอบของคุณไม่ดีพอ คำตอบนี้ไม่ได้ตอบคำถามเกี่ยวกับวิธีการโอเวอร์โหลดฟังก์ชั่น แต่ตอบคำถามที่แตกต่างกัน ฉันลบความคิดเห็นเริ่มต้นแล้ว
gizzmole

@gizzmole อ่าไม่ต้องกังวล ฉันรู้ว่าคำตอบนี้ใช้ไม่ได้กับฟังก์ชั่นการโหลดมากเกินไป แต่คำถามไม่ได้กล่าวถึงการโอเวอร์โหลดอย่างชัดเจน (แม้ว่าตัวอย่าง perl ที่ให้ไว้จะแสดงกรณีของการโอเวอร์โหลด) คำตอบอื่น ๆ ส่วนใหญ่ครอบคลุมการใช้งานมากเกินไป - อันนี้อยู่ที่นี่สำหรับคนที่ไม่ต้องการ
เบ็น

@ ถ้าฉันต้องการโทร B (). baz () จากคลาสอื่นที่ชื่อว่า 'C' โดยใช้ vars ที่กำหนดไว้ใน C? ฉันพยายามทำ B (). baz () ใน class C โดยที่ self.string เป็น val อื่น มันทำให้ฉันไม่มีเลยเมื่อฉันดำเนินการ
joey

@joey ฉันไม่แน่ใจว่าสิ่งที่คุณพยายามจะทำหรือถ้ามันอยู่ในขอบเขตของคำถามนี้ สตริงเป็นตัวแปรท้องถิ่นการตั้งค่าself.stringจะไม่ทำอะไรเลย ในคลาส C คุณยังสามารถโทรติดต่อได้B().bar(string)
เบ็น

27

นี่คือตัวอย่างของการใช้super () :

#New-style classes inherit from object, or from another new-style class
class Dog(object):

    name = ''
    moves = []

    def __init__(self, name):
        self.name = name

    def moves_setup(self):
        self.moves.append('walk')
        self.moves.append('run')

    def get_moves(self):
        return self.moves

class Superdog(Dog):

    #Let's try to append new fly ability to our Superdog
    def moves_setup(self):
        #Set default moves by calling method of parent class
        super(Superdog, self).moves_setup()
        self.moves.append('fly')

dog = Superdog('Freddy')
print dog.name # Freddy
dog.moves_setup()
print dog.get_moves() # ['walk', 'run', 'fly']. 
#As you can see our Superdog has all moves defined in the base Dog class

นี่เป็นตัวอย่างที่ไม่ดีใช่ไหม ฉันเชื่อว่ามันไม่ปลอดภัยที่จะใช้ Super เว้นแต่ว่าลำดับชั้นทั้งหมดของคุณคือคลาส "สไตล์ใหม่" และเรียก Super () อย่างถูกต้อง init ()
Jaykul

14

Python มี super () เหมือนกัน มันค่อนข้างจะแปลกไปเพราะคลาสเก่าและใหม่ของไพ ธ อน แต่ก็มีการใช้กันอย่างแพร่หลายเช่นในคอนสตรัคเตอร์:

class Foo(Bar):
    def __init__(self):
        super(Foo, self).__init__()
        self.baz = 5

10

ฉันอยากจะแนะนำให้ใช้CLASS.__bases__ สิ่งนี้

class A:
   def __init__(self):
        print "I am Class %s"%self.__class__.__name__
        for parentClass in self.__class__.__bases__:
              print "   I am inherited from:",parentClass.__name__
              #parentClass.foo(self) <- call parents function with self as first param
class B(A):pass
class C(B):pass
a,b,c = A(),B(),C()

1
โปรดจำไว้ว่าในขณะที่คุณสามารถบอกได้จากปริมาณของการอ้างอิงถึงซุปเปอร์ที่นี่คนที่ไม่เต็มใจที่จะใช้ฐานเว้นแต่คุณจะทำอะไรที่ลึกจริงๆ ถ้าคุณไม่ชอบซุปเปอร์ดูที่MRO การบันทึกรายการแรกในmroนั้นปลอดภัยกว่าการติดตามว่าจะใช้รายการใดในฐาน การสืบทอดหลายครั้งใน Python จะสแกนสิ่งต่าง ๆ ไปข้างหลังและสิ่งอื่น ๆ ที่อยู่ข้างหน้าดังนั้นการปล่อยให้ภาษาผ่อนคลายลงกระบวนการนี้ปลอดภัยที่สุด
Jon Jay Obermark

8

หากคุณไม่ทราบว่าคุณอาจได้รับข้อโต้แย้งจำนวนเท่าใดและต้องการส่งต่อให้เด็กด้วย:

class Foo(bar)
    def baz(self, arg, *args, **kwargs):
        # ... Do your thing
        return super(Foo, self).baz(arg, *args, **kwargs)

(จาก: Python - วิธีที่สะอาดที่สุดในการแทนที่ __init__ โดยที่ต้องใช้ kwarg ซึ่งเป็นตัวเลือกหลังจากการเรียก super ()? )


5

มี super () ในหลามด้วย

ตัวอย่างการเรียกใช้วิธีการเรียนพิเศษจากวิธีการเรียนย่อย

class Dog(object):
    name = ''
    moves = []

    def __init__(self, name):
        self.name = name

    def moves_setup(self,x):
        self.moves.append('walk')
        self.moves.append('run')
        self.moves.append(x)
    def get_moves(self):
        return self.moves

class Superdog(Dog):

    #Let's try to append new fly ability to our Superdog
    def moves_setup(self):
        #Set default moves by calling method of parent class
        super().moves_setup("hello world")
        self.moves.append('fly')
dog = Superdog('Freddy')
print (dog.name)
dog.moves_setup()
print (dog.get_moves()) 

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


3

ในตัวอย่างcafec_paramนี้เป็นคลาสพื้นฐาน (คลาสพาเรนต์) และabcเป็นคลาสย่อย abcเรียกAWCวิธีการในชั้นฐาน

class cafec_param:

    def __init__(self,precip,pe,awc,nmonths):

        self.precip = precip
        self.pe = pe
        self.awc = awc
        self.nmonths = nmonths

    def AWC(self):

        if self.awc<254:
            Ss = self.awc
            Su = 0
            self.Ss=Ss
        else:
            Ss = 254; Su = self.awc-254
            self.Ss=Ss + Su   
        AWC = Ss + Su
        return self.Ss


    def test(self):
        return self.Ss
        #return self.Ss*4

class abc(cafec_param):
    def rr(self):
        return self.AWC()


ee=cafec_param('re',34,56,2)
dd=abc('re',34,56,2)
print(dd.rr())
print(ee.AWC())
print(ee.test())

เอาท์พุต

56

56

56

2

ใน Python 2 ฉันไม่มีโชคมากมายกับ super () ฉันใช้คำตอบจาก jimifiki ในหัวข้อ SO นี้วิธีการอ้างอิงถึงวิธีหลักในหลาม? . จากนั้นฉันเพิ่มการบิดตัวเองเล็กน้อยซึ่งฉันคิดว่าเป็นการปรับปรุงการใช้งาน (โดยเฉพาะถ้าคุณมีชื่อชั้นยาว)

กำหนดคลาสพื้นฐานในหนึ่งโมดูล:

 # myA.py

class A():     
    def foo( self ):
        print "foo"

จากนั้นอิมพอร์ตคลาสไปยังโมดูลอื่นas parent:

# myB.py

from myA import A as parent

class B( parent ):
    def foo( self ):
        parent.foo( self )   # calls 'A.foo()'

2
คุณต้องใช้ class A(object): แทน class A(): แล้ว super () จะใช้ได้
blndev

ฉันไม่แน่ใจว่าฉันทำตาม คุณหมายถึงคำจำกัดความของชั้นเรียนหรือไม่? เห็นได้ชัดว่าคุณต้องระบุชั้นเรียนที่มีผู้ปกครองอย่างใดเพื่อที่จะคิดว่าคุณสามารถอ้างถึงผู้ปกครอง! ตั้งแต่ฉันโพสต์เมื่อ 9 เดือนที่ผ่านมาฉันมีรายละเอียดเล็กน้อย แต่ฉันคิดว่าฉันหมายถึงตัวอย่างจาก Arun Ghosh, lawrence, kkk ฯลฯ ไม่สามารถใช้งานได้ใน Py 2.x นี่เป็นวิธีที่ใช้งานได้และวิธีง่าย ๆ ในการอ้างถึงผู้ปกครองเพื่อให้ชัดเจนยิ่งขึ้นว่าคุณกำลังทำอะไรอยู่ (เช่นใช้ super)
BuvinJ

0
class department:
    campus_name="attock"
    def printer(self):
        print(self.campus_name)

class CS_dept(department):
    def overr_CS(self):
        department.printer(self)
        print("i am child class1")

c=CS_dept()
c.overr_CS()

-3
class a(object):
    def my_hello(self):
        print "hello ravi"

class b(a):
    def my_hello(self):
    super(b,self).my_hello()
    print "hi"

obj = b()
obj.my_hello()

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

-24

นี่เป็นวิธีที่เป็นนามธรรมมากขึ้น:

super(self.__class__,self).baz(arg)

15
self .__ class__ ไม่ทำงานอย่างถูกต้องกับ super เนื่องจากตัวเองเป็นอินสแตนซ์เสมอและคลาสของมันจะเป็นคลาสของอินสแตนซ์เสมอ ซึ่งหมายความว่าถ้าคุณใช้ super (self .__ class__ .. ในชั้นเรียนและคลาสนั้นสืบทอดมามันจะไม่ทำงานอีกต่อไป
xitrium

16
เพียงเพื่อเน้นอย่าใช้รหัสนี้ มันเอาชนะจุดประสงค์ทั้งหมดของ super () ซึ่งเป็นการสำรวจ MRO อย่างถูกต้อง
Eevee

1
stackoverflow.com/a/19257335/419348super(self.__class__, self).baz(arg)กล่าวว่าทำไมไม่โทร
AechoLiu
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.