การส่งจำนวนเต็มโดยการอ้างอิงใน Python


97

ฉันจะส่งจำนวนเต็มโดยอ้างอิงใน Python ได้อย่างไร

ฉันต้องการแก้ไขค่าของตัวแปรที่ฉันส่งผ่านไปยังฟังก์ชัน ฉันได้อ่านแล้วว่าทุกอย่างใน Python ส่งผ่านค่า แต่ต้องมีเคล็ดลับง่ายๆ ยกตัวอย่างเช่นใน Java คุณสามารถผ่านประเภทอ้างอิงInteger, Longฯลฯ

  1. ฉันจะส่งจำนวนเต็มไปยังฟังก์ชันโดยการอ้างอิงได้อย่างไร
  2. อะไรคือการปฏิบัติที่ดีที่สุด?

ดูสิ่งนี้เป็นวิธีที่ดีหากวิธีที่ซับซ้อนเล็กน้อยในการตัด ints ของคุณในคลาสที่ไม่ระบุชื่อ (ซึ่งไม่แน่นอน) ซึ่งจะทำงานเหมือน 'การอ้างอิง': stackoverflow.com/a/1123054/409638เช่น ref = type ('', () , {'n': 1})
เบิร์ต

คำตอบ:


107

มันไม่ค่อยได้ผลใน Python Python ส่งผ่านการอ้างอิงไปยังวัตถุ ภายในฟังก์ชันของคุณคุณมีวัตถุ - คุณมีอิสระที่จะกลายพันธุ์วัตถุนั้น (ถ้าเป็นไปได้) อย่างไรก็ตามจำนวนเต็มมีไม่เปลี่ยนรูป วิธีแก้ปัญหาอย่างหนึ่งคือการส่งผ่านจำนวนเต็มในคอนเทนเนอร์ซึ่งสามารถกลายพันธุ์ได้:

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

x = [1]
change(x)
print x

นี่เป็นสิ่งที่น่าเกลียด / เงอะงะ แต่คุณจะไม่ทำอะไรให้ดีขึ้นใน Python เหตุผลก็เพราะว่าใน Python นั้นการกำหนด ( =) จะใช้วัตถุใด ๆ ก็ตามที่เป็นผลลัพธ์ของทางด้านขวามือและผูกเข้ากับสิ่งที่อยู่ทางซ้ายมือ * (หรือส่งต่อไปยังฟังก์ชันที่เหมาะสม)

เมื่อเข้าใจสิ่งนี้เราจะเห็นว่าเหตุใดจึงไม่มีวิธีเปลี่ยนค่าของวัตถุที่ไม่เปลี่ยนรูปภายในฟังก์ชันคุณไม่สามารถเปลี่ยนแอตทริบิวต์ใด ๆ ได้เนื่องจากไม่เปลี่ยนรูปและคุณไม่สามารถกำหนด "ตัวแปร" ใหม่ได้ มูลค่าเนื่องจากคุณกำลังสร้างวัตถุใหม่ (ซึ่งแตกต่างจากวัตถุเก่า) และตั้งชื่อให้กับวัตถุเก่าในเนมสเปซในเครื่อง

โดยปกติวิธีแก้ปัญหาคือเพียงส่งคืนวัตถุที่คุณต้องการ:

def multiply_by_2(x):
    return 2*x

x = 1
x = multiply_by_2(x)

* ในตัวอย่างกรณีแรกข้างต้นจริงได้รับการส่งผ่านไป3x.__setitem__


11
"Python ส่งผ่านวัตถุ" ไม่ Python ส่งผ่านการอ้างอิง (ตัวชี้ไปยังวัตถุ)
user102008

6
@ user102008 - จุดยุติธรรม. ความหมาย ณ จุดนี้ทำให้ฉันรู้สึกสับสนมาก ท้ายที่สุดแล้วก็ไม่ใช่ "ตัวชี้" เช่นกัน Cอย่างน้อยไม่แน่นอนในความรู้สึกเช่นเดียวกับที่คุณมีใน (ตัวอย่างเช่นไม่จำเป็นต้องอ้างถึง) ในแบบจำลองทางจิตของฉันการคิดสิ่งต่าง ๆ เป็น "ชื่อ" และ "วัตถุ" นั้นง่ายกว่า การมอบหมายจะผูก "ชื่อ" ทางด้านซ้ายกับ "วัตถุ" ทางด้านขวา เมื่อคุณเรียกใช้ฟังก์ชันคุณจะส่ง "Object" (ผูกกับชื่อท้องถิ่นใหม่ภายในฟังก์ชันได้อย่างมีประสิทธิภาพ)
mgilson

แน่นอนว่านี่เป็นคำอธิบายว่าการอ้างอิง python คืออะไร แต่ไม่ใช่เรื่องง่ายที่จะหาวิธีอธิบายอย่างรวบรัดสำหรับผู้ที่ไม่คุ้นเคยกับคำศัพท์
mgilson

2
ไม่น่าแปลกใจที่เราสับสนกับคำศัพท์ ที่นี่เรามีมันอธิบายว่าการเรียกร้องโดยวัตถุและนี่ก็จะอธิบายว่าผ่านโดยค่า ที่อื่นเรียกว่า "pass-by-reference" พร้อมเครื่องหมายดอกจันว่าหมายถึงอะไร ... โดยพื้นฐานแล้วปัญหาคือชุมชนยังไม่ทราบว่าจะเรียกว่าอะไร
mgilson

1
การอ้างอิง Python เหมือนกับการอ้างอิง Java ทุกประการ และการอ้างอิง Java เป็นไปตามตัวชี้ JLS ไปยังวัตถุ และโดยพื้นฐานแล้วจะมี bijection ที่สมบูรณ์ระหว่างการอ้างอิง Java / Python และตัวชี้ C ++ ไปยังอ็อบเจ็กต์ในความหมายและไม่มีอะไรอื่นที่อธิบายความหมายนี้เช่นกัน "พวกเขาไม่ต้องอ้างถึง" .โอเปอเรเตอร์เพียงแค่ dereferences ทางด้านซ้าย ตัวดำเนินการอาจแตกต่างกันในภาษาต่างๆ
user102008

31

กรณีส่วนใหญ่ที่คุณต้องส่งต่อโดยอ้างอิงคือที่ที่คุณต้องส่งคืนค่ามากกว่าหนึ่งค่ากลับไปยังผู้โทร "แนวทางปฏิบัติที่ดีที่สุด" คือการใช้ค่าส่งคืนหลายค่าซึ่งทำได้ง่ายกว่าในภาษา Python มากกว่าภาษาเช่น Java

นี่คือตัวอย่างง่ายๆ:

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

ไม่ใช่การส่งผ่านค่าโดยตรง แต่ใช้ราวกับว่ามันถูกส่งผ่าน

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

ข้อควรระวัง:

  • nonlocal ถูกนำมาใช้ใน python 3
  • ถ้าขอบเขตการปิดล้อมเป็นหนึ่งที่ทั่วโลกใช้แทนglobalnonlocal

7

จริงๆแล้วแนวทางปฏิบัติที่ดีที่สุดคือถอยหลังและถามว่าคุณจำเป็นต้องทำสิ่งนี้จริงๆหรือไม่ เหตุใดคุณจึงต้องการแก้ไขค่าของตัวแปรที่คุณส่งผ่านไปยังฟังก์ชัน

หากคุณต้องการแฮ็คอย่างรวดเร็ววิธีที่เร็วที่สุดคือการlistถือจำนวนเต็มและ[0]ใช้ทุกครั้งตามที่คำตอบของ mgilson แสดงให้เห็น

หากคุณต้องการทำเพื่อสิ่งที่สำคัญกว่านั้นให้เขียนclassว่ามีintเป็นแอตทริบิวต์เพื่อให้คุณสามารถตั้งค่าได้ แน่นอนว่าสิ่งนี้บังคับให้คุณต้องตั้งชื่อที่ดีสำหรับชั้นเรียนและสำหรับแอตทริบิวต์ - ถ้าคุณคิดอะไรไม่ออกให้กลับไปอ่านประโยคอีกครั้งสองสามครั้งจากนั้นใช้list.

โดยทั่วไปหากคุณพยายามพอร์ต Java idiom ไปยัง Python โดยตรงแสดงว่าคุณทำผิด แม้ว่าจะมีบางสิ่งที่เกี่ยวข้องโดยตรง (เช่นเดียวกับstatic/@staticmethod ) คุณก็ยังไม่ต้องการใช้มันในโปรแกรม Python ส่วนใหญ่เพียงเพราะคุณใช้ใน Java


JAVA ไม่ส่งผ่านจำนวนเต็มโดยการอ้างอิง (จำนวนเต็มหรืออ็อบเจกต์ใด ๆ อ็อบเจ็กต์แบบบรรจุกล่องจะถูกแทนที่ด้วยเช่นกันเมื่อได้รับมอบหมาย)
Luis Masuelli

@LuisMasuelli ดี primitves ชนิดบรรจุกล่องได้รับการปฏิบัติเช่นเดียวกับวัตถุสิ่งเดียวที่ป้องกันการใช้งานตามที่ OP ต้องการคือความจริงที่ว่ากล่องไม่เปลี่ยนรูป (และเนื่องจากตัวดั้งเดิมเองก็ไม่เปลี่ยนรูปเช่นกันสิ่งทั้งหมดจึงไม่เปลี่ยนรูปและสามารถเปลี่ยนแปลงได้เฉพาะที่ ระดับตัวแปร)
Kroltan

กรณีการใช้งานที่ดีสำหรับสิ่งนี้คือการนับจำนวนการโทร (รวมไม่ใช่ความลึก) ภายในฟังก์ชันเรียกซ้ำ คุณต้องการหมายเลขที่สามารถเพิ่มได้ในการโทรแบบแยกสายทั้งหมด int มาตรฐานไม่ได้ตัดมัน
z33k

4

ใน Python ทุกค่าเป็นการอ้างอิง (ตัวชี้ไปยังวัตถุ) เช่นเดียวกับ non-primitives ใน Java นอกจากนี้เช่นเดียวกับ Java Python จะส่งผ่านค่าเท่านั้น ดังนั้นในทางความหมายพวกมันก็ค่อนข้างเหมือนกัน

เนื่องจากคุณพูดถึง Java ในคำถามของคุณฉันต้องการทราบว่าคุณบรรลุสิ่งที่คุณต้องการใน Java ได้อย่างไร หากคุณสามารถแสดงใน Java ฉันสามารถแสดงวิธีทำใน Python ได้


4

numpy อาร์เรย์องค์ประกอบเดียวคือไม่แน่นอนและยังเพื่อวัตถุประสงค์ส่วนใหญ่ก็สามารถได้รับการประเมินเป็นถ้ามันเป็นตัวแปรหลามตัวเลข ดังนั้นจึงเป็นที่เก็บหมายเลขอ้างอิงที่สะดวกกว่ารายการองค์ประกอบเดียว

    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)

เอาต์พุต:

3



2

บางทีการจัดทำเอกสารด้วยตัวเองเล็กน้อยกว่าเคล็ดลับ list-of-length-1 คือเคล็ดลับประเภทว่างเปล่าแบบเก่า:

def inc_i(v):
    v.i += 1

x = type('', (), {})()
x.i = 7
inc_i(x)
print(x.i)

หรือห่อหมายเลขในชั้นเรียน: github.com/markrages/python_mutable_number
markrages

0

ใน Python ทุกอย่างจะถูกส่งผ่านด้วยค่า แต่ถ้าคุณต้องการแก้ไขบางสถานะคุณสามารถเปลี่ยนค่าของจำนวนเต็มภายในรายการหรือวัตถุที่ส่งผ่านไปยังเมธอดได้


3
ฉันคิดว่าเพราะeverything is passed by valueไม่เป็นความจริง เอกสารอ้างอิง:arguments are passed using call by value (where the value is always an object reference, not the value of the object)
Astery

1
รายการวัตถุและพจนานุกรมถูกส่งผ่านโดยการอ้างอิง
AN88

2
หากเป็นจริงการกำหนดให้กับพารามิเตอร์ในฟังก์ชันจะแสดงที่ไซต์การโทร เนื่องจาก Astery ที่ยกมานั้นเป็นไปตามค่าและค่าเหล่านั้นเป็นการอ้างอิงวัตถุ
ออกอากาศซ้ำ

0

คำตอบที่ถูกต้องคือการใช้คลาสและใส่ค่าลงในคลาสซึ่งจะช่วยให้คุณส่งผ่านข้อมูลอ้างอิงได้ตรงตามที่คุณต้องการ

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)

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.