Gọi phương thức của lớp cha từ lớp con?


608

Khi tạo một hệ thống phân cấp đối tượng đơn giản trong Python, tôi muốn có thể gọi các phương thức của lớp cha từ một lớp dẫn xuất. Trong Perl và Java, có một từ khóa cho cái này ( super). Ở Perl, tôi có thể làm điều này:

package Foo;

sub frotz {
    return "Bamf";
}

package Bar;
@ISA = qw(Foo);

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

Trong Python, có vẻ như tôi phải đặt tên lớp cha rõ ràng từ đứa trẻ. Trong ví dụ trên, tôi phải làm một cái gì đó như Foo::frotz().

Điều này có vẻ không đúng vì hành vi này khiến cho việc phân cấp sâu trở nên khó khăn. Nếu trẻ em cần biết lớp nào định nghĩa một phương thức kế thừa, thì tất cả các loại đau thông tin được tạo ra.

Đây có phải là một giới hạn thực sự trong python, một khoảng cách trong sự hiểu biết của tôi hoặc cả hai?


3
Tôi nghĩ rằng việc đặt tên của lớp cha mẹ không phải là một ý tưởng tồi. Nó có thể giúp đỡ khi lớp con kế thừa từ nhiều hơn một cha mẹ, vì bạn đặt tên rõ ràng cho lớp cha.

7
Mặc dù một tùy chọn để đặt tên cho một lớp học không phải là một ý tưởng tồi, nhưng chắc chắn bị buộc phải làm như vậy.
johndodo

Hãy nhận biết các thay đổi trong siêu xử lý giữa Python 2 và Python 3 https://www.python.org/dev/peps/pep-3135/ . Đặt tên lớp không còn cần thiết nữa (mặc dù đôi khi vẫn có thể là một ý tưởng của thượng đế, ít nhất là đôi khi).
jwpfox

Câu trả lời:


736

Có, nhưng chỉ với các lớp học kiểu mới . Sử dụng super()chức năng:

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

Đối với trăn <3, sử dụng:

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

53
Bạn có thể không làm "return Bar.baz (self, arg)" không? Nếu vậy, cái nào sẽ tốt hơn?

15
Có, bạn có thể, nhưng OP đã hỏi làm thế nào để làm điều đó mà không đặt tên rõ ràng cho siêu lớp tại trang web cuộc gọi (Bar, trong trường hợp này).
Adam Rosenfield

7
super () là lạ với nhiều thừa kế. Nó gọi lớp "tiếp theo". Đó có thể là lớp cha hoặc có thể là một lớp hoàn toàn không liên quan xuất hiện ở nơi khác trong hệ thống phân cấp.
Steve Jessop

6
Tôi sử dụng Python 2.x và tôi nhận được "Lỗi: phải là loại chứ không phải classobj" khi tôi làm điều này
Chris F

28
Cú pháp super(Foo, self)dành cho Python 2.x có đúng không? Trong Python 3.x, super()được ưu tiên đúng?
Trevor Boyd Smith

144

Python cũng có siêu :

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

Trả về một đối tượng proxy ủy nhiệm các cuộc gọi phương thức cho một kiểu cha mẹ hoặc anh chị em của kiểu. Điều này rất hữu ích để truy cập các phương thức được kế thừa đã bị ghi đè trong một lớp. Thứ tự tìm kiếm giống như thứ tự được sử dụng bởi getattr () ngoại trừ loại chính nó bị bỏ qua.

Thí dụ:

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
Ví dụ tốt hơn Adam vì bạn đã thể hiện bằng cách sử dụng lớp kiểu mới với lớp cơ sở đối tượng. Chỉ thiếu liên kết đến các lớp phong cách mới.
Samuel

83
ImmediateParentClass.frotz(self)

sẽ ổn thôi, cho dù lớp cha ngay lập tức frotztự xác định hay kế thừa nó. superchỉ cần cho sự hỗ trợ thích hợp của nhiều kế thừa (và sau đó nó chỉ hoạt động nếu mọi lớp sử dụng đúng cách). Nói chung, AnyClass.whateversẽ tìm kiếm whatevertrong AnyClasstổ tiên nếu AnyClasskhông định nghĩa / ghi đè lên nó và điều này đúng với "phương thức gọi lớp của cha mẹ" như bất kỳ trường hợp nào khác!


Điều này bắt buộc Im ImParentClass cũng phải có mặt trong phạm vi để câu lệnh này được thực thi. Chỉ cần có đối tượng được nhập từ một mô-đun sẽ không giúp đỡ.
Tushar Vazirani

Điều gì nếu nó là một phương thức lớp?
Shiplu Mokaddim

@TusharVazirani nếu lớp cha là ngay lập tức parent, nó nằm trong phạm vi của cả lớp, phải không?
Yaroslav Nikitenko

1
@YaroslavNikitenko Tôi đang nói về việc nhập một thể hiện, không phải lớp.
Tushar Vazirani

1
@TusharVazirani giờ tôi hiểu rồi, cảm ơn bạn. Tôi đã suy nghĩ về việc gọi từ phương thức của lớp hiện tại (như đã nêu của OP).
Yar Tư Nikitenko

75

Python 3 có một cú pháp khác nhau và đơn giản hơn để gọi phương thức cha.

Nếu Foolớp kế thừa từ Bar, thì từ Bar.__init__có thể được gọi từ Foothông qua super().__init__():

class Foo(Bar):

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

5
Thật tốt khi nghe python 3 có một giải pháp tốt hơn. Câu hỏi đến từ python 2.X. Nhờ đề nghị mặc dù.
jjohn

46

Nhiều câu trả lời đã giải thích làm thế nào để gọi một phương thức từ cha mẹ đã bị ghi đè ở trẻ.

Tuy nhiên

"làm thế nào để bạn gọi phương thức của lớp cha từ lớp con?"

cũng có thể có nghĩa là:

"làm thế nào để bạn gọi các phương thức kế thừa?"

Bạn có thể gọi các phương thức được kế thừa từ một lớp cha giống như chúng là các phương thức của lớp con, miễn là chúng không bị ghi đè.

ví dụ trong trăn 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"

vâng, điều này có thể khá rõ ràng, nhưng tôi cảm thấy rằng nếu không chỉ ra điều này, mọi người có thể rời khỏi chủ đề này với ấn tượng bạn phải nhảy qua những cái vòng lố bịch chỉ để truy cập các phương thức được kế thừa trong python. Đặc biệt là câu hỏi này có tỷ lệ cao trong các tìm kiếm về "cách truy cập phương thức của lớp cha mẹ trong Python" và OP được viết từ quan điểm của một người mới biết về python.

Tôi đã tìm thấy: https://docs.python.org/3/tutorial/groupes.html#inherribution sẽ hữu ích trong việc hiểu cách bạn truy cập các phương thức được kế thừa.


@gizzmole trong tình huống nào không hoạt động? Tôi rất vui khi thêm bất kỳ cảnh báo hoặc cảnh báo nào có liên quan, nhưng tôi không thể tìm ra cách liên kết bạn đăng có liên quan đến câu trả lời này.
Ben

@Ben Xin lỗi, tôi không đọc kỹ câu trả lời của bạn. Câu trả lời này không trả lời câu hỏi về cách quá tải các hàm, nhưng trả lời một câu hỏi khác. Tôi đã xóa bình luận ban đầu của tôi.
gizzmole

@gizzmole ah, không phải lo lắng. Tôi nhận ra rằng câu trả lời này không áp dụng cho các hàm quá tải, tuy nhiên câu hỏi không đề cập rõ ràng đến quá tải (mặc dù ví dụ perl đưa ra không cho thấy trường hợp quá tải). Phần lớn các câu trả lời khác bao gồm quá tải - câu trả lời này dành cho những người không cần nó.
Ben

@Ben nếu tôi muốn gọi B (). Baz () từ một lớp khác có tên 'C' bằng cách sử dụng các vars được định nghĩa trong C?. Tôi đã thử làm B (). Baz () trong lớp C, trong đó self.opes là một val khác. Nó đã cho tôi không, khi tôi thực hiện
joey

@joey Tôi không chắc chắn những gì bạn đang cố gắng làm, hoặc nếu nó nằm trong phạm vi của câu hỏi này. Chuỗi là một biến cục bộ - cài đặt self.stringsẽ không làm gì cả. Tuy nhiên, trong lớp C, bạn vẫn có thể gọiB().bar(string)
Ben

27

Dưới đây là một ví dụ về việc sử dụng 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

Đây không phải là một ví dụ xấu? Tôi tin rằng nó không thực sự an toàn khi sử dụng Super trừ khi toàn bộ hệ thống phân cấp của bạn là các lớp "kiểu mới" và gọi đúng cách Super (). Init ()
Jaykul

14

Cũng có một siêu () trong Python. Đó là một chút rắc rối, bởi vì các lớp kiểu cũ và kiểu mới của Python, nhưng được sử dụng khá phổ biến, ví dụ như trong các hàm tạo:

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

10

Tôi khuyên bạn nên sử dụng CLASS.__bases__ một cái gì đó như thế này

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
Hãy nhớ rằng, như bạn có thể nói từ khối lượng tài liệu tham khảo cho siêu ở đây, rằng mọi người thực sự ghê tởm khi sử dụng căn cứ trừ khi bạn đang làm một cái gì đó thực sự sâu sắc. Nếu bạn không thích siêu , hãy nhìn vào mro . Lấy mục đầu tiên trong mro sẽ an toàn hơn so với việc theo dõi mục nào trong các cơ sở sẽ sử dụng. Nhiều kế thừa trong Python quét một số thứ nhất định lạc hậu và những thứ khác về phía trước, vì vậy hãy để ngôn ngữ thư giãn quá trình này là an toàn nhất.
Jon Jay Obermark


5

Có một siêu () trong python cũng.

Ví dụ về cách một phương thức siêu lớp được gọi từ một phương thức lớp phụ

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()) 

Ví dụ này tương tự như đã giải thích ở trên. Tuy nhiên, có một điểm khác biệt là siêu không có bất kỳ đối số nào được truyền cho nó. Đoạn mã trên có thể được thực thi trong phiên bản python 3.4.


3

Trong ví dụ cafec_paramnày là lớp cơ sở (lớp cha) và abclà lớp con. abcgọi AWCphương thức trong lớp cơ sở.

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())

Đầu ra

56

56

56

2

Trong Python 2, tôi đã không gặp nhiều may mắn với super (). Tôi đã sử dụng câu trả lời từ jimifiki trên luồng SO này làm thế nào để tham khảo một phương thức cha trong python? . Sau đó, tôi đã thêm thắt nhỏ của riêng mình vào nó, mà tôi nghĩ là một sự cải thiện về khả năng sử dụng (Đặc biệt là nếu bạn có tên lớp dài).

Xác định lớp cơ sở trong một mô-đun:

 # myA.py

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

Sau đó nhập lớp vào các mô-đun khác as parent:

# myB.py

from myA import A as parent

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

2
Bạn phải sử dụng class A(object): thay vì class A(): Then super () sẽ hoạt động
blndev

Tôi không chắc là tôi làm theo. Bạn có nghĩa là trong định nghĩa của lớp học? Rõ ràng, bạn phải chỉ ra một lớp có cha mẹ bằng cách nào đó để nghĩ rằng bạn có thể đề cập đến cha mẹ! Kể từ khi tôi đăng bài này 9 tháng trước, tôi hơi mơ hồ về các chi tiết, nhưng tôi nghĩ ý tôi là các ví dụ từ Arun Ghosh, lawrence, kkk, v.v. không được áp dụng trong Py 2.x. Thay vào đó, đây là một phương pháp hoạt động và một cách dễ dàng để giới thiệu cho phụ huynh để nó rõ ràng hơn một chút về những gì bạn đang làm. (thích sử dụng siêu)
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
Chào mừng đến với stackoverflow. Câu hỏi này đã được trả lời chi tiết gần mười năm trước, câu trả lời được chấp nhận và phần lớn được nêu lên, và câu trả lời của bạn không thêm bất cứ điều gì mới. Thay vào đó, bạn có thể muốn thử và trả lời các câu hỏi gần đây hơn (và tốt nhất là chưa được trả lời). Là một lưu ý phụ, có một nút trong trình chỉnh sửa để áp dụng định dạng mã phù hợp.
bruno Desthuilliers

-24

Đây là một phương pháp trừu tượng hơn:

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

15
self .__ class__ không hoạt động đúng với siêu vì bản thân luôn là ví dụ và lớp của nó luôn là lớp của thể hiện. Điều này có nghĩa là nếu bạn sử dụng siêu (self .__ class__ .. trong một lớp và lớp đó được kế thừa từ đó, nó sẽ không hoạt động nữa.
xitrium

16
Chỉ cần nhấn mạnh, KHÔNG SỬ DỤNG MÃ NÀY. Nó đánh bại toàn bộ mục đích của super (), đó là vượt qua chính xác MRO.
Eevee

1
stackoverflow.com/a/19257335/419348 nói tại sao không gọi super(self.__class__, self).baz(arg).
AechoLiu
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.