2013-02-25 34 views
20

Tôi rất bối rối bởi hành vi bên dưới. Các trường hợp 1, 3 và 4 thực hiện như tôi mong đợi, nhưng trường hợp 2 thì không. Tại sao trường hợp 2 cho phép hàm thay đổi giá trị của mục nhập từ điển trên toàn cầu, mặc dù từ điển không bao giờ được hàm trả về? Lý do chính mà tôi đang sử dụng các hàm là cô lập mọi thứ trong hàm từ phần còn lại của mã, nhưng điều này dường như không thể nếu tôi chọn sử dụng cùng tên biến bên trong hàm. Tôi đã hiểu rằng bất cứ điều gì được định nghĩa rõ ràng trong một hàm là cục bộ cho hàm đó, nhưng điều này dường như không phải là trường hợp nếu từ điển là được xác định và được truyền làm đầu vào cho hàm.Từ điển python được truyền dưới dạng đầu vào cho một hàm hoạt động như một hàm toàn cầu trong hàm đó chứ không phải là một địa phương

Python 2.7.2+ (default, Oct 4 2011, 20:06:09) 
[GCC 4.6.1] on linux2 
Type "help", "copyright", "credits" or "license" for more information. 

============= Trường hợp 1 ===============

>>> def testfun1(a): 
...  a=2 
... 
>>> a=0 
>>> testfun1(a) 
>>> a 
0 

====== ======= Trường hợp 2 ===============

>>> def testfun2(b): 
...  b['test']=2 
... 
>>> b={} 
>>> testfun2(b) 
>>> b 
{'test': 2} 

============= Trường hợp 3 ==== ===========

>>> def testfun3(): 
...  c=2 
... 
>>> c=0 
>>> testfun3() 
>>> c 
0 

============= Trường hợp 4 =============== (giải thích bằng câu hỏi này: Global dictionaries don't need keyword global to modify them?)

>>> def testfun4(): 
...  d['test']=10 
... 
>>> d={} 
>>> testfun4() 
>>> d 
{'test': 10} 

Trả lời

35

Python's parameter passing acts a bit different than the languages you're probably used to. Thay vì có giá trị vượt qua bằng giá trị và chuyển qua ngữ nghĩa tham chiếu, python đã chuyển tên. Về cơ bản, bạn luôn truyền đối tượng đó và tính đột biến của đối tượng xác định có thể sửa đổi hay không. Danh sách và Dicts là các đối tượng có thể thay đổi. Số, Strings và Tuples thì không.

Bạn đang chuyển từ điển sang hàm, chứ không phải bản sao. Vì vậy, khi bạn sửa đổi nó, bạn cũng đang sửa đổi bản gốc.

Để tránh điều này, trước tiên bạn phải sao chép từ điển trước khi gọi hàm hoặc từ bên trong hàm (chuyển từ điển sang hàm dict sẽ làm điều đó).

+2

để sao chép từ điển, bạn nên sử dụng phương pháp sao chép bản sao của mô-đun sao chép ** copy.deepcopy (d) ** trả về bản sao sâu của d. để biết thêm thông tin về nông sâu và sâu, xin vui lòng tham khảo https://docs.python.org/2/library/copy.html –

+3

@PavanGupta Điều đó tất cả phụ thuộc vào cách 'sâu' của một bản sao bạn muốn. Nếu bạn có một dict có giá trị cũng là dicts, bạn có muốn tạo bản sao của những dicts, hoặc chỉ cho họ. Quy tắc/câu hỏi này áp dụng đệ quy. – Darthfett

1

Từ khóa toàn cầu là cần thiết chỉ dành cho chuyển nhượng (và có khả năng del, tôi chưa bao giờ thử nó). Các đột biến đối tượng hoàn toàn hợp lệ.

0

Bạn đã chuyển một đối tượng dict cho hàm và sửa đổi nó bên trong hàm, do đó tất nhiên nó sẽ được sửa đổi sau khi hàm trả về. Đối tượng không được sao chép, do đó bạn sửa đổi cùng một đối tượng mà bạn đã truyền, và câu hỏi này không liên quan gì tới việc đặt tên, các tên tương tự, phạm vi, vv khi bạn truyền đối tượng một cách rõ ràng.

0

Khi bạn truyền đối tượng cơ bản như số nguyên hoặc chuỗi cho hàm, nếu bạn thay đổi nó bên trong hàm, không có gì xảy ra với đối tượng tương ứng bên ngoài hàm vì khi bạn đang dẫn đầu với đối tượng cơ bản, giá trị.

Tuy nhiên, nếu bạn chuyển từ điển hoặc danh sách tới hàm, chúng sẽ được chuyển bằng tham chiếu, nghĩa là bạn sẽ có hành vi đó: đối tượng bên ngoài hàm được thay đổi như bạn đã thấy.

chỉnh sửa: Ngoài ra, có một sự khác biệt giữa đi qua bởi giá trị hoặc bằng cách tham khảo: theo giá trị, một "bản sao" của các đối tượng được thực hiện để được sử dụng trong chức năng; bằng cách tham chiếu, chính xác cùng một đối tượng được truyền qua tham chiếu và sửa đổi nó bên trong hàm có thể nhìn thấy bên ngoài. Theo định nghĩa, python truyền các đối tượng bất biến của nó theo giá trị và các đối tượng có thể thay đổi của nó bằng tham chiếu.

+0

Câu trả lời có thể được cải thiện bằng cách chỉ ra rằng các chuỗi là bất biến và từ điển có thể thay đổi được. Đó là sự khác biệt thực sự. Một đối tượng cơ bản thực sự không phải là "xác định" cho mỗi se. –

+0

@ScottLundberg Nhưng bằng các ngôn ngữ khác như chuỗi c/C++ có thể thay đổi được; thực tế là các chuỗi trong python là bất biến (tức là các đối tượng cơ bản) là một đặc tính của ngôn ngữ python [chúng có thể bởi một mảng hoặc một danh sách các ký tự - và các danh sách có thể thay đổi được]. (thông tin thêm trong câu trả lời cập nhật ...) –

+0

Tôi đồng ý với bạn, đó là lý do tại sao tôi đưa ra đề xuất để làm cho nó rõ ràng hơn. –