2017-07-31 20 views
7

Có mã.Làm cách nào tôi có thể kế thừa defaultdict và sử dụng phương thức sao chép của nó trong phương thức lớp con?

from collections import defaultdict 
class A(defaultdict): 
    def __init__(self): 
    super(A, self).__init__(lambda :0) 
    self.x = 1 

    def my_copy(self): 
    return self.copy() 

if __name__ == '__main__': 
    a = defaultdict(lambda :0) 
    b = a.copy() # no error when using the base class directly 
    a = A() 
    b = a.my_copy() 

Có lỗi:

Traceback (most recent call last): 
    File "/Applications/PyCharm.app/Contents/helpers/pydev/pydevd.py", line 1591, in <module> 
    globals = debugger.run(setup['file'], None, None, is_module) 
    File "/Applications/PyCharm.app/Contents/helpers/pydev/pydevd.py", line 1018, in run 
    pydev_imports.execfile(file, globals, locals) # execute the script 
    File "/Applications/PyCharm.app/Contents/helpers/pydev/_pydev_imps/_pydev_execfile.py", line 18, in execfile 
    exec(compile(contents+"\n", file, 'exec'), glob, loc) 
    File "/Users/liu/project/scir/pytorch_test/t.py", line 14, in <module> 
    b = a.my_copy() 
    File "/Users/liu/project/scir/pytorch_test/t.py", line 8, in my_copy 
    return self.copy() 
TypeError: __init__() takes 1 positional argument but 3 were given 

Tôi không biết làm thế nào để kế thừa phương pháp sao chép và cũng không biết tại sao tôi cho 3 đối số.

Trả lời

7

Khi gọi copy, defaultdict gọi một hàm tạo với các đối số, để chuyển hàm default_factory và dữ liệu.

Nhà xây dựng của bạn không có bất kỳ đối số nào, do đó, nó chỉ có thể xây dựng các dicts trống với nhà máy cố định.

Fix constructor của bạn như thế này:

def __init__(self,*args): 

Nhưng bạn phải vượt qua args đến lớp mẹ hoặc từ điển sao chép của bạn sẽ trống (không mà bạn muốn).

Vì bạn đang chuyên cung cấp các nhà máy mặc định, bạn phải thực hiện một trường hợp đặc biệt nếu args là rỗng:

class A(defaultdict): 
    def __init__(self,*args): 
    if args: 
     super(A, self).__init__(*args) 
    else: 
     super(A, self).__init__(int) # better than lambda : 0 

Hoặc có thể đơn giản hơn với một ternary:

class A(defaultdict): 
    def __init__(self,*args): 
    super(A, self).__init__(*(args or (int,))) 
  • Khi args không được để trống (được gọi là từ copy), sau đó bản sao có các thuộc tính của bản gốc (chức năng & dữ liệu).
  • Khi args trống, điều đó có nghĩa là bạn đang tạo một dict mới, do đó, nó chỉ sửa đối số nhà máy mặc định.

Ngoài ra: bạn có thể thay thế (lambda :0) bởi (int).

EDIT: một cách phức tạp hơn nhưng mà làm cho chắc chắn rằng người dùng không thể thay đổi mặc định sẽ được bỏ qua đối số đầu tiên và lực lượng int (có thể với một cảnh báo nếu đối số đầu tiên không phải là int):

super(A, self).__init__(*([int]+list(args[1:]))) 

Đó sẽ làm việc, nhưng tôi không thích ý tưởng bỏ qua một đối số nhiều. Như một kết luận, kế thừa cho các loại built-in nói chung là khó khăn và nên được sử dụng một cách thận trọng (xem ví dụ khác cố gắng để làm điều đó với một pandas dataframe: building a class from an existing one). Đôi khi tạo một lớp với một đối số là defaultdict và bắt chước/chuyển tiếp chỉ các phương pháp bạn định sử dụng sẽ dẫn đến ít tác dụng phụ hơn.

+0

Điều này cho phép người dùng chỉ định một nhà máy khác, có thể không phải là những gì OP muốn. –

+1

@brunodesthuilliers xem chỉnh sửa của tôi. Nhưng kết luận của tôi là: thật khó để kế thừa một cách an toàn từ kiểu tích hợp. –

2

defaultdict.__init__() có ba đối số: self (tất nhiên) , an optional factory callable for missing keys and an optional set of key:values (which cand be either a dict or a sequence of (khóa, giá trị) `cặp).

defaultdict.copy() sẽ tạo một phiên bản defaultdict mới và chuyển nó là factory có thể gọi VÀ bản sao nông của khóa hiện tại của nó: giá trị được đặt.

Lớp con của bạn __init__ chỉ mất self làm đối số, nhưng kết thúc bằng cách được gọi với ba.

Việc sửa chữa ở đây là để viết lại A.__init__ để nó có thể xử lý cả hai trường hợp:

class A(defaultdict): 
    def __init__(self, *args): 
     # make sure we force the factory 
     args = (int,) + args[1:] 
     super(A, self).__init__(*args) 
1

tôi quyết định mở rộng một lời nhận xét nhỏ để một câu trả lời là gì. Trong khi một phân tích hoàn hảo đã được đưa ra trong các câu trả lời đã được đưa ra, tôi không thích sửa đổi đối số được đề xuất. Cả hai defaultdict và dict cơ bản có một chữ ký không tầm thường (sử dụng các đối số). Mã bên dưới không chạm vào các đối số và chuyển chúng không thay đổi đến triển khai ban đầu:

def __init__(self, *args, **kwargs): 
    super(A, self).__init__(*args, **kwargs) 
    self.default_factory = int 

Ngoài ra, các kwarg được giữ nguyên, ví dụ: A(a=1,b=2) hoạt động.

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