2010-05-24 22 views
6

thể trùng lặp:
least astonishment in python: the mutable default argumentPython constructor làm những việc kỳ lạ với các thông số tùy chọn

Tôi muốn hiểu về hành vi và tác động của python __init__ constructor. Nó có vẻ như khi có một tham số tùy chọn và bạn cố gắng và thiết lập một đối tượng hiện có cho một đối tượng mới, giá trị tùy chọn của đối tượng hiện có được bảo tồn và sao chép.

Xem ví dụ:

Trong mã bên dưới, tôi đang cố gắng tạo cấu trúc cây với các nút và có thể có nhiều trẻ em. Trong lớp đầu tiên NodeBad, hàm tạo có hai tham số, giá trị và mọi con có thể. Lớp thứ hai NodeGood chỉ lấy giá trị của nút làm tham số. Cả hai đều có phương thức addchild để thêm con vào nút.

Khi tạo cây với lớp NodeGood, nó hoạt động như mong đợi. Tuy nhiên, khi làm điều tương tự với lớp học NodeBad, có vẻ như một đứa trẻ chỉ có thể được thêm một lần!

Đoạn code dưới đây sẽ cho kết quả đầu ra sau đây:

Good Tree 
1 
2 
3 
[<3>] 
Bad Tree 
1 
2 
2 
[<2>, <3>] 

Quế Pasa?

Dưới đây là ví dụ:

#!/usr/bin/python 
class NodeBad: 
    def __init__(self, value, c=[]): 
    self.value = value 
    self.children = c 
    def addchild(self, node): 
    self.children.append(node) 
    def __str__(self): 
    return '< %s >' % self.value 
    def __repr__(self): 
    return '< %s >' % self.value 


class NodeGood: 
    def __init__(self, value): 
    self.value = value 
    self.children = [] 
    def addchild(self, node): 
    self.children.append(node) 
    def __str__(self): 
    return '< %s >' % self.value 
    def __repr__(self): 
    return '< %s >' % self.value 

if __name__ == '__main__': 
    print 'Good Tree' 
    ng = NodeGood(1) # Root Node 
    rootgood = ng 
    ng.addchild(NodeGood(2)) # 1nd Child 
    ng = ng.children[0] 
    ng.addchild(NodeGood(3)) # 2nd Child 

    print rootgood.value 
    print rootgood.children[0].value 
    print rootgood.children[0].children[0].value 
    print rootgood.children[0].children 

    print 'Bad Tree' 
    nb = NodeBad(1) # Root Node 
    rootbad = nb 
    nb.addchild(NodeBad(2)) # 1st Child 
    nb = nb.children[0] 
    nb.addchild(NodeBad(3)) # 2nd Child 

    print rootbad.value 
    print rootbad.children[0].value 
    print rootbad.children[0].children[0].value 
    print rootbad.children[0].children 
+0

bản sao của http://stackoverflow.com/questions/1132941/least-astonishment-in-python -the-mutable-default-argument – sth

Trả lời

12

Vấn đề là, giá trị mặc định của một đối số tùy chọn chỉ là một trường hợp duy nhất. Ví dụ: nếu bạn nói def __init__(self, value, c=[]):, cùng một danh sách [] sẽ được chuyển vào phương pháp mỗi khi một đối số tùy chọn được sử dụng bằng cách gọi mã.

Vì vậy, về cơ bản bạn chỉ nên sử dụng các loại ngày không thay đổi như None cho giá trị mặc định của đối số tùy chọn. Ví dụ:

def __init__(self, value, c=None): 

Sau đó, bạn chỉ có thể tạo ra một danh sách mới trong cơ thể phương pháp:

if c == None: 
    c = [] 
+3

Sự cố chỉ phát sinh nếu bạn sử dụng ** kiểu dữ liệu ** có thể thay đổi làm đối số mặc định. Không có vấn đề với bất kỳ loại ** bất biến ** (số nguyên, chuỗi, tuple, vv) (Tôi sẽ không đặt tên nó * giá trị hằng số *). –

+0

Điểm tốt, tôi vừa cập nhật câu trả lời của mình. Cảm ơn! –

+0

Giá trị làm rõ rằng điều này không cụ thể đối với '__init__', hoặc thực sự cho các lớp ở tất cả, nhưng đúng với bất kỳ hàm nào. –

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