Truyền một số nguyên bằng tham chiếu trong Python


97

Làm cách nào để chuyển một số nguyên bằng tham chiếu trong Python?

Tôi muốn sửa đổi giá trị của một biến mà tôi đang chuyển cho hàm. Tôi đã đọc rằng mọi thứ trong Python đều được chuyển theo giá trị, nhưng phải có một mẹo dễ dàng. Ví dụ, trong Java bạn có thể vượt qua các loại tài liệu tham khảo của Integer, Longvv

  1. Làm cách nào để chuyển một số nguyên vào một hàm bằng tham chiếu?
  2. Các phương pháp hay nhất là gì?

hãy xem điều này để biết một cách hay nếu hơi phức tạp để bọc các int của bạn trong một lớp ẩn danh (có thể thay đổi) sẽ hoạt động giống như một 'tham chiếu': stackoverflow.com/a/1123054/409638 tức là ref = type ('', () , {'n': 1})
robert

Câu trả lời:


107

Nó không hoàn toàn hoạt động theo cách đó trong Python. Python chuyển các tham chiếu đến các đối tượng. Bên trong hàm của bạn, bạn có một đối tượng - Bạn có thể tự do thay đổi đối tượng đó (nếu có thể). Tuy nhiên, số nguyên là bất biến . Một cách giải quyết là chuyển số nguyên vào một vùng chứa có thể bị thay đổi:

def change(x):
    x[0] = 3

x = [1]
change(x)
print x

Điều này tốt nhất là xấu / vụng về, nhưng bạn sẽ không làm tốt hơn với Python. Lý do là vì trong Python, gán ( =) lấy bất kỳ đối tượng nào là kết quả của phía bên phải và liên kết nó với bất kỳ đối tượng nào ở phía bên trái * (hoặc chuyển nó đến hàm thích hợp).

Hiểu được điều này, chúng ta có thể thấy tại sao không có cách nào để thay đổi giá trị của một đối tượng không thể thay đổi bên trong một hàm - bạn không thể thay đổi bất kỳ thuộc tính nào của nó vì nó không thay đổi và bạn không thể chỉ định "biến" mới giá trị vì khi đó bạn thực sự đang tạo một đối tượng mới (khác với đối tượng cũ) và đặt cho nó tên mà đối tượng cũ có trong không gian tên cục bộ.

Thông thường, cách giải quyết là chỉ cần trả lại đối tượng mà bạn muốn:

def multiply_by_2(x):
    return 2*x

x = 1
x = multiply_by_2(x)

* Trong trường hợp ví dụ đầu tiên ở trên, 3thực sự được chuyển đến x.__setitem__.


11
"Python chuyển các đối tượng." Không. Python chuyển các tham chiếu (con trỏ đến các đối tượng).
user102008

6
@ user102008 - Điểm khá. Tôi cảm thấy ngữ nghĩa tại thời điểm này rất khó hiểu. Cuối cùng, chúng cũng không phải là "con trỏ". Ít nhất, chắc chắn không theo cùng nghĩa với bạn C. (Ví dụ: chúng không cần phải được tham chiếu). Trong mô hình tinh thần của tôi, việc nghĩ về mọi thứ là "Tên" và "Đối tượng" dễ dàng hơn. Phép gán liên kết (các) "Tên" ở bên trái với (các) "Đối tượng" ở bên phải. Khi bạn gọi một hàm, bạn truyền "Đối tượng" (liên kết hiệu quả nó với một tên cục bộ mới trong hàm).
mgilson

Tất nhiên, đây là mô tả về tham chiếu python là gì, nhưng không dễ để tìm ra cách mô tả ngắn gọn đối với những người không quen thuộc với thuật ngữ này.
mgilson

2
Không có gì ngạc nhiên khi chúng ta nhầm lẫn với thuật ngữ. Ở đây chúng tôi có nó được mô tả là gọi theo đối tượng và ở đây nó được mô tả là truyền theo giá trị . Ở những nơi khác nó được gọi là "pass-by-reference" với một dấu sao vào những gì mà thực sự có nghĩa là ... Về cơ bản, vấn đề là cộng đồng đã không tìm ra những gì để gọi nó
mgilson

1
Tham chiếu Python hoàn toàn giống với tham chiếu Java. Và các tham chiếu Java là theo các con trỏ JLS tới các đối tượng. Và về cơ bản có một sự phân biệt hoàn chỉnh giữa các tham chiếu Java / Python và các con trỏ C ++ đến các đối tượng trong ngữ nghĩa và không có gì khác mô tả ngữ nghĩa này. "Chúng không cần phải được tham chiếu" Chà, người .điều hành chỉ đơn giản là bỏ tham chiếu phía bên trái. Các nhà khai thác có thể khác nhau ở các ngôn ngữ khác nhau.
user102008

31

Hầu hết các trường hợp bạn cần chuyển bằng tham chiếu là nơi bạn cần trả lại nhiều hơn một giá trị cho người gọi. Một "phương pháp hay nhất" là sử dụng nhiều giá trị trả về, điều này dễ thực hiện hơn trong Python so với các ngôn ngữ như Java.

Đây là một ví dụ đơn giản:

def RectToPolar(x, y):
    r = (x ** 2 + y ** 2) ** 0.5
    theta = math.atan2(y, x)
    return r, theta # return 2 things at once

r, theta = RectToPolar(3, 4) # assign 2 things at once

9

Không chính xác truyền trực tiếp một giá trị, nhưng sử dụng nó như thể nó đã được truyền.

x = 7
def my_method():
    nonlocal x
    x += 1
my_method()
print(x) # 8

Lưu ý:

  • nonlocal đã được giới thiệu trong python 3
  • Nếu phạm vi bao quanh là phạm vi chung, hãy sử dụng globalthay vì nonlocal.

7

Thực sự, cách tốt nhất là lùi lại và hỏi xem bạn có thực sự cần làm điều này hay không. Tại sao bạn muốn sửa đổi giá trị của một biến mà bạn đang chuyển vào hàm?

Nếu bạn cần làm điều đó để hack nhanh, cách nhanh nhất là chuyển một listsố nguyên đang giữ và [0]cố gắng sử dụng nó, như câu trả lời của mgilson đã chứng minh.

Nếu bạn cần phải làm điều đó cho một cái gì đó quan trọng hơn, hãy viết một classmà có intnhư một thuộc tính, vì vậy bạn chỉ có thể thiết lập nó. Tất nhiên, điều này buộc bạn phải nghĩ ra một cái tên hay cho lớp và cho thuộc tính — nếu bạn không thể nghĩ ra gì, hãy quay lại và đọc lại câu đó một vài lần, sau đó sử dụng list.

Nói chung, nếu bạn đang cố gắng chuyển một số thành ngữ Java trực tiếp sang Python, bạn đang làm sai. Ngay cả khi có thứ gì đó tương ứng trực tiếp (như với static/ @staticmethod), bạn vẫn không muốn sử dụng nó trong hầu hết các chương trình Python chỉ vì bạn sử dụng nó trong Java.


JAVA không chuyển số nguyên bằng tham chiếu (số nguyên hoặc bất kỳ đối tượng nào. Các đối tượng đóng hộp cũng được thay thế khi gán).
Luis Masuelli

@LuisMasuelli Chà, các hộp primitves được xử lý giống như các đối tượng, điều duy nhất ngăn cản việc sử dụng chúng theo ý muốn của OP là thực tế là các hộp là bất biến (và vì bản thân các nguyên thủy cũng là bất biến, toàn bộ là bất biến và chỉ có thể thay đổi tại mức độ thay đổi)
Kroltan

Một trường hợp sử dụng tốt cho việc này là đếm số lượng lệnh gọi (tổng số, không phải độ sâu) bên trong một hàm đệ quy. Bạn cần một số có thể tăng lên trong tất cả các cuộc gọi rẽ nhánh. Một int chuẩn chỉ không cắt nó
z33k

4

Trong Python, mọi giá trị là một tham chiếu (một con trỏ tới một đối tượng), giống như các giá trị không phải là nguyên thủy trong Java. Ngoài ra, giống như Java, Python chỉ có giá trị truyền. Vì vậy, về mặt ngữ nghĩa, chúng khá giống nhau.

Vì bạn đề cập đến Java trong câu hỏi của mình, tôi muốn biết cách bạn đạt được những gì bạn muốn trong Java. Nếu bạn có thể hiển thị nó bằng Java, tôi có thể chỉ cho bạn cách thực hiện nó một cách chính xác tương đương trong Python.


4

Một mảng phần tử đơn NumPy là có thể thay đổi nhưng đối với hầu hết các mục đích, nó có thể được đánh giá như thể nó là một biến python số. Do đó, nó là một vùng chứa số tham chiếu theo tham chiếu thuận tiện hơn là một danh sách một phần tử.

    import numpy as np
    def triple_var_by_ref(x):
        x[0]=x[0]*3
    a=np.array([2])
    triple_var_by_ref(a)
    print(a+1)

đầu ra:

3

3
class PassByReference:
    def Change(self, var):
        self.a = var
        print(self.a)
s=PassByReference()
s.Change(5)     

3

Có thể đó không phải là cách của con trăn, nhưng bạn có thể làm điều này

import ctypes

def incr(a):
    a += 1

x = ctypes.c_int(1) # create c-var
incr(ctypes.ctypes.byref(x)) # passing by ref

Con người thật thông minh.
xilpex


0

Trong Python, mọi thứ được truyền theo giá trị, nhưng nếu bạn muốn sửa đổi một số trạng thái, bạn có thể thay đổi giá trị của một số nguyên bên trong danh sách hoặc đối tượng được truyền cho một phương thức.


3
Tôi nghĩ, bởi vì everything is passed by valuenó không thực sự đúng. Trích dẫn tài liệu:arguments are passed using call by value (where the value is always an object reference, not the value of the object)
Astery

1
Danh sách, các đối tượng, và từ điển được thông qua tham khảo
AN88

2
Nếu điều đó là đúng, việc gán cho một tham số trong một hàm sẽ được phản ánh tại trang web cuộc gọi. Như Astery được trích dẫn truyền là theo giá trị và các giá trị đó là tham chiếu đối tượng.
đệ quy

0

Câu trả lời đúng là sử dụng một lớp và đặt giá trị bên trong lớp, điều này cho phép bạn chuyển qua tham chiếu chính xác như bạn mong muốn.

class Thing:
  def __init__(self,a):
    self.a = a
def dosomething(ref)
  ref.a += 1

t = Thing(3)
dosomething(t)
print("T is now",t.a)

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.