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


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.