Câu trả lời ngắn gọn là, Python luôn luôn chuyển theo giá trị, nhưng mọi biến Python thực sự là một con trỏ tới một số đối tượng, vì vậy đôi khi nó trông giống như tham chiếu chuyển qua.
Trong Python, mọi đối tượng đều có thể thay đổi hoặc không thể thay đổi. ví dụ: danh sách, dicts, mô-đun và khung dữ liệu Pandas có thể thay đổi được và int, chuỗi và bộ giá trị là không thể thay đổi. Các đối tượng có thể thay đổi có thể được thay đổi bên trong (ví dụ: thêm một phần tử vào danh sách), nhưng các đối tượng không thể thay đổi thì không thể.
Như tôi đã nói ở phần đầu, bạn có thể coi mọi biến Python như một con trỏ đến một đối tượng. Khi bạn truyền một biến cho một hàm, biến (con trỏ) trong hàm luôn là bản sao của biến (con trỏ) đã được chuyển vào. Vì vậy, nếu bạn gán một cái gì đó mới cho biến nội bộ, tất cả những gì bạn đang làm là thay đổi biến cục bộ để trỏ đến một đối tượng khác. Điều này không làm thay đổi (đột biến) đối tượng ban đầu mà biến trỏ đến, cũng không làm cho biến bên ngoài trỏ đến đối tượng mới. Tại thời điểm này, biến bên ngoài vẫn trỏ đến đối tượng ban đầu, nhưng biến bên trong trỏ đến đối tượng mới.
Nếu bạn muốn thay đổi đối tượng ban đầu (chỉ có thể với các kiểu dữ liệu có thể thay đổi), bạn phải làm điều gì đó thay đổi đối tượng mà không chỉ định một giá trị hoàn toàn mới cho biến cục bộ. Đây là lý do tại sao letgo()
và letgo3()
để vật phẩm bên ngoài không thay đổi, nhưng letgo2()
thay đổi nó.
Như @ursan đã chỉ ra, nếu letgo()
sử dụng thứ gì đó như thế này thay thế, thì nó sẽ thay đổi (biến đổi) đối tượng ban đầu df
trỏ đến, điều này sẽ thay đổi giá trị được nhìn thấy qua a
biến toàn cục:
def letgo(df):
df.drop('b', axis=1, inplace=True)
a = pd.DataFrame({'a':[1,2], 'b':[3,4]})
letgo(a)
Trong một số trường hợp, bạn hoàn toàn có thể làm rỗng biến ban đầu và điền vào nó bằng dữ liệu mới mà không thực sự thực hiện phép gán trực tiếp, ví dụ: điều này sẽ thay đổi đối tượng ban đầu v
trỏ đến, điều này sẽ thay đổi dữ liệu được thấy khi bạn sử dụng v
sau này:
def letgo3(x):
x[:] = np.array([[3,3],[3,3]])
v = np.empty((2, 2))
letgo3(v)
Lưu ý rằng tôi không chỉ định một cái gì đó trực tiếp cho x
; Tôi đang chỉ định một cái gì đó cho toàn bộ phạm vi nội bộ của x
.
Nếu bạn nhất thiết phải tạo một đối tượng hoàn toàn mới và làm cho nó hiển thị ra bên ngoài (trường hợp này đôi khi xảy ra với gấu trúc), bạn có hai lựa chọn. Tùy chọn 'sạch' sẽ chỉ để trả về đối tượng mới, ví dụ:
def letgo(df):
df = df.drop('b',axis=1)
return df
a = pd.DataFrame({'a':[1,2], 'b':[3,4]})
a = letgo(a)
Một tùy chọn khác là tiếp cận bên ngoài hàm của bạn và trực tiếp thay đổi một biến toàn cục. Điều này thay đổi a
để trỏ đến một đối tượng mới và bất kỳ hàm nào tham chiếu đến a
sau đó sẽ thấy đối tượng mới đó:
def letgo():
global a
a = a.drop('b',axis=1)
a = pd.DataFrame({'a':[1,2], 'b':[3,4]})
letgo()
Thay đổi trực tiếp các biến toàn cục thường là một ý tưởng tồi, bởi vì bất kỳ ai đọc mã của bạn đều sẽ gặp khó khăn trong việc tìm hiểu xem a
đã thay đổi như thế nào . (Tôi thường sử dụng các biến toàn cục cho các tham số dùng chung được sử dụng bởi nhiều hàm trong một tập lệnh, nhưng tôi không để chúng thay đổi các biến toàn cục đó.)