2009-07-04 38 views
8

Trong Python, tôi muốn xây dựng một thể hiện của lớp của Child trực tiếp từ một cá thể của lớp Parent. Ví dụ:Kế thừa từ ví dụ trong Python

A = Parent(x, y, z) 
B = Child(A) 

Đây là một hack mà tôi nghĩ có thể làm việc:

class Parent(object): 

    def __init__(self, x, y, z): 
     print "INITILIZING PARENT" 
     self.x = x 
     self.y = y 
     self.z = z 

class Child(Parent): 

    def __new__(cls, *args, **kwds): 
     print "NEW'ING CHILD" 
     if len(args) == 1 and str(type(args[0])) == "<class '__main__.Parent'>": 
      new_args = [] 
      new_args.extend([args[0].x, args[0].y, args[0].z]) 
      print "HIJACKING" 
      return Child(*new_args) 
     print "RETURNING FROM NEW IN CHILD" 
     return object.__new__(cls, *args, **kwds) 

Nhưng khi tôi chạy

B = Child(A) 

tôi nhận được:

NEW'ING CHILD 
HIJACKING 
NEW'ING CHILD 
RETURNING FROM NEW IN CHILD 
INITILIZING PARENT 
Traceback (most recent call last): 
    File "classes.py", line 52, in <module> 
    B = Child(A) 
TypeError: __init__() takes exactly 4 arguments (2 given) 

Dường hack hoạt động giống như tôi mong đợi nhưng trình biên dịch ném một T ypeError ở cuối. Tôi đã tự hỏi nếu tôi có thể quá tải TypeError để làm cho nó bỏ qua B = Child (A) thành ngữ nhưng tôi đã không chắc chắn làm thế nào để làm điều đó. Trong mọi trường hợp, bạn vui lòng cho tôi các giải pháp của bạn để kế thừa từ các trường hợp?

Cảm ơn!

+1

Chỉ cần một FYI, "khởi tạo" được viết bằng chữ 'a' trong đó. Đã cố gắng chỉnh sửa điều này nhưng StackOverflow sẽ không cho phép chỉnh sửa dưới 6 chữ cái. – cfwschmidt

Trả lời

7

Khi __new__ trong lớp Child trả về một thể hiện của Child, Child.__init__ sẽ được gọi (với các đối số cùng __new__ đã được đưa ra) vào trường hợp đó - và dường như nó chỉ được thừa hưởng Parent.__init__, mà không mất tốt để được gọi với chỉ một arg (số khác Parent, A).

Nếu không có cách nào khác có thể thực hiện Child, bạn có thể xác định Child.__init__ chấp nhận một arg (bỏ qua) hoặc ba (trong trường hợp này gọi là Parent.__init__). Nhưng đơn giản hơn là bỏ qua __new__ và có tất cả logic trong Child.__init__, chỉ cần gọi Parent.__init__ một cách thích hợp!

Để làm cho bê tông này với một mã ví dụ:

class Parent(object): 

    def __init__(self, x, y, z): 
     print "INITIALIZING PARENT" 
     self.x = x 
     self.y = y 
     self.z = z 

    def __str__(self): 
     return "%s(%r, %r, %r)" % (self.__class__.__name__, 
      self.x, self.y, self.z) 


class Child(Parent): 

    _sentinel = object() 

    def __init__(self, x, y=_sentinel, z=_sentinel): 
     print "INITIALIZING CHILD" 
     if y is self._sentinel and z is self._sentinel: 
      print "HIJACKING" 
      z = x.z; y = x.y; x = x.x 
     Parent.__init__(self, x, y, z) 
     print "CHILD IS DONE!" 

p0 = Parent(1, 2, 3) 
print p0 
c1 = Child(p0) 
print c1 
c2 = Child(4, 5, 6) 
print c2 
3

Bạn không định nghĩa một constructor (init) cho trẻ em, vì vậy các nhà xây dựng Chánh được gọi là, mong 4 đối số trong khi chỉ có 2 trong số đó được thông qua năm (từ mới). Dưới đây là một cách để thực hiện những gì bạn muốn:

class Child(Parent): 
    def __init__(self, *args, **kwargs): 
     if len(args) == 1 and isinstance(args[0], Parent): 
      Parent.__init__(self, args[0].x, args[0].y, args[0].z) 

     else: 
      # do something else 
+0

+1: Đơn giản chỉ cần tạo một đứa trẻ bằng cách sao chép các thuộc tính của phụ huynh. –

0

Xin cảm ơn, các bạn đã nhanh chóng! đầu tiên tôi đọc comment của Alex và tôi viết lại của Child __init__ như

def __init__(self, *args, **kwds): 
    if len(args) == 1 and str(type(args[0])) == "<class '__main__.Parent'>": 
     new_args = [args[0].x, args[0].y, args[0].z] 
     super(Child, self).__init__(*new_args, **kwds) 
    else: 
     super(Child, self).__init__(*args, **kwds) 

đó rất giống với những gì abhinavg gợi ý (như tôi vừa phát hiện ra). Và nó hoạt động. Chỉ dòng của anh ấy và ars '

if len(args) == 1 and isinstance(args[0], Parent): 

sạch hơn tôi.

Cảm ơn bạn lần nữa !!

5

OK, vì vậy tôi đã không nhận ra rằng bạn hài lòng với một bản sao tĩnh của các đối số cho đến khi tôi đã được thực hiện một nửa với giải pháp của tôi. Nhưng tôi quyết định không lãng phí nó, vì vậy ở đây nó là anyway. Sự khác biệt từ các giải pháp khác là nó sẽ thực sự nhận được các thuộc tính từ cha mẹ ngay cả khi chúng được cập nhật.

_marker = object() 

class Parent(object): 

    def __init__(self, x, y, z): 
     self.x = x 
     self.y = y 
     self.z = z 

class Child(Parent): 

    _inherited = ['x', 'y', 'z'] 

    def __init__(self, parent): 
     self._parent = parent 
     self.a = "not got from dad" 

    def __getattr__(self, name, default=_marker): 
     if name in self._inherited: 
      # Get it from papa: 
      try: 
       return getattr(self._parent, name) 
      except AttributeError: 
       if default is _marker: 
        raise 
       return default 

     if name not in self.__dict__: 
      raise AttributeError(name) 
     return self.__dict__[name] 

Bây giờ nếu chúng ta làm điều này:

>>> A = Parent('gotten', 'from', 'dad') 
>>> B = Child(A) 
>>> print "a, b and c is", B.x, B.y, B.z 
a, b and c is gotten from dad 

>>> print "But x is", B.a 
But x is not got from dad 

>>> A.x = "updated!" 
>>> print "And the child also gets", B.x 
And the child also gets updated! 

>>> print B.doesnotexist 
Traceback (most recent call last): 
    File "acq.py", line 44, in <module> 
    print B.doesnotexist 
    File "acq.py", line 32, in __getattr__ 
    raise AttributeError(name) 
AttributeError: doesnotexist 

Đối với một phiên bản chung chung hơn về điều này, hãy nhìn vào gói http://pypi.python.org/pypi/Acquisition. Đó là trong thực tế, trong một số trường hợp một giải pháp nhu cầu đẫm máu.

2

Tôi biết đây là một chủ đề cực kỳ cũ, nhưng gần đây tôi đã gặp thách thức tương tự như Alexandra, và đây là chủ đề relavent nhất tôi có thể tìm thấy. Tôi có một lớp cha với rất nhiều thuộc tính, và tôi muốn bản chất 'vá' một thể hiện của nó, bằng cách giữ tất cả các phương thức và thuộc tính của nó, thêm một vài, và sửa đổi/ghi đè lên những người khác. Một lớp con đơn giản sẽ không hoạt động vì các thuộc tính sẽ được điền bởi người dùng trong suốt thời gian chạy và tôi không thể kế thừa các giá trị mặc định từ lớp cha. Sau rất nhiều tinkering tôi tìm thấy một cách rất sạch (mặc dù khá hacky) để làm điều đó bằng cách sử dụng __new__. Dưới đây là ví dụ:

class Parent(object): 
    def __init__(self): 
     # whatever you want here 
     self.x = 42 
     self.y = 5 
    def f(self): 
     print "Parent class, x,y =", self.x, self.y 

class Child(Parent): 
    def __new__(cls, parentInst): 
     parentInst.__class__ = Child 
     return parentInst 
    def __init__(self, parentInst): 
     # You don't call the Parent's init method here 
     self.y = 10 
    def f(self): 
     print "Child class, x,y =", self.x, self.y 

c = Parent() 
c.f() # Parent class, x,y = 42 5 
c.x = 13 
c.f() # Parent class, x,y = 13 5 
c = Child(c) 
c.f() # Child class, x,y = 13 10 

Phần đặc biệt duy nhất đang thay đổi thuộc tính cha mẹ trong hàm tạo con. Do đó, phương thức __init__ của Trẻ sẽ được gọi là bình thường và theo hiểu biết của tôi, lớp Trẻ em sẽ hoạt động chính xác như bất kỳ lớp nào được thừa hưởng khác.

+1

Tôi cũng chạy vào một cái gì đó tương tự: http://stackoverflow.com/questions/14177788/init-child-with-parent-instance – elyase

2

Tôi tìm thấy điều này (đóng gói) là cách sạch nhất:

class Child(object): 
    def __init__(self): 
     self.obj = Parent() 

    def __getattr__(self, attr): 
     return getattr(self.obj, attr) 

Bằng cách này bạn có thể sử dụng phương pháp tất cả của mẹ và của riêng bạn mà không cần chạy vào các vấn đề của thừa kế.

Các vấn đề liên quan