Python'da referansa göre bir tamsayı geçirme


97

Python'da bir tamsayıyı referans olarak nasıl geçirebilirim?

İşleve aktardığım bir değişkenin değerini değiştirmek istiyorum. Python'daki her şeyin değere göre geçtiğini okudum, ancak kolay bir numara olması gerekiyor. Örneğin, Java sen referans tiplerini geçebileceği Integer, Longvb

  1. Bir tamsayıyı referans olarak bir işleve nasıl geçirebilirim?
  2. En iyi uygulamalar nelerdir?

İnts'lerinizi bir 'referans' gibi davranacak olan anonim bir sınıfa (değişebilir) sarmanın güzel ama biraz kıvrımlı bir yolu için buna bakın: stackoverflow.com/a/1123054/409638 yani ref = type ('', () , {'n': 1})
robert

Yanıtlar:


107

Python'da pek bu şekilde çalışmıyor. Python, referansları nesnelere aktarır. İşlevinizin içinde bir nesne var - Bu nesneyi değiştirmekte özgürsünüz (mümkünse). Ancak tamsayılar değişmezdir . Çözümlerden biri, tamsayıyı mutasyona uğratılabilen bir kapta geçirmektir:

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

x = [1]
change(x)
print x

Bu en iyi ihtimalle çirkin / beceriksizdir, ancak Python'da daha iyisini yapamazsınız. Bunun nedeni, Python'da, assignment ( =) sağ tarafın sonucu olan nesneyi alır ve onu sol taraftaki * her şeye bağlar (veya onu uygun işleve geçirir).

Bunu anladığımızda, bir fonksiyonun içindeki değişmez bir nesnenin değerini değiştirmenin neden bir yolu olmadığını görebiliriz - onun niteliklerinden hiçbirini değiştiremezsiniz çünkü değişmezdir ve sadece "değişken" e yeni bir atayamazsınız. değer çünkü o zaman aslında (eskisinden farklı olan) yeni bir nesne oluşturuyorsunuz ve ona eski nesnenin yerel ad alanında sahip olduğu adı veriyorsunuz.

Genellikle geçici çözüm, istediğiniz nesneyi döndürmektir :

def multiply_by_2(x):
    return 2*x

x = 1
x = multiply_by_2(x)

* Yukarıdaki ilk örnek durumda, 3aslında geçilir x.__setitem__.


11
"Python nesneleri aktarır." Hayır. Python referansları iletir (nesnelere işaretçiler).
user102008

6
@ user102008 - Adil nokta. Bu noktada anlambilim çok çamurlu oluyor. Sonuçta, onlar da "işaretçi" değiller. En azından, kesinlikle sahip olduğun anlamda değil C. (Örneğin, referans alınmasına gerek yoktur). Zihinsel modelimde, şeyleri "İsimler" ve "Nesneler" olarak düşünmek daha kolaydır. Atama, soldaki bir "İsim" (ler) i sağdaki "Nesne" (ler) e bağlar. Bir işlevi çağırdığınızda, "Nesne" yi iletirsiniz (onu işlev içinde yeni bir yerel isme etkin bir şekilde bağlama).
mgilson

Bu elbette python referanslarının ne olduğunun bir açıklamasıdır , ancak terminolojiye aşina olmayanlara bunu kısa ve öz bir şekilde tanımlamanın bir yolunu bulmak kolay değildir.
mgilson

2
Terminoloji ile karıştırılmamıza şaşmamalı. Burada onu nesne bazında çağrı olarak tanımladık ve burada değerle geçiş olarak tanımlandı . Başka bir yerde bu "pass-by-reference" konulu bir yıldız ile ne denir aslında araçlar ... Temel olarak, sorun topluluk anladım almamış olmasıdır diyoruz ne
mgilson

1
Python referansları Java referansları ile tamamen aynıdır. Ve Java referansları, nesnelerin JLS işaretçilerine göredir. Ve temelde Java / Python referansları ile anlambilimdeki nesnelere yönelik C ++ işaretçileri arasında tam bir bağlantı vardır ve bu semantiği de başka hiçbir şey tanımlamaz. "Bunların referans alınmasına gerek yok" .Operatör basitçe sol tarafı referans alıyor. Operatörler farklı dillerde farklı olabilir.
user102008

31

Referans olarak geçmeniz gereken çoğu durum, birden fazla değeri arayana geri döndürmeniz gereken yerlerdir. "En iyi uygulama", Python'da Java gibi dillerden çok daha kolay olan çoklu dönüş değerleri kullanmaktır.

İşte basit bir örnek:

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

Tam olarak bir değeri doğrudan iletmek değil, onu iletilmiş gibi kullanmak.

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

Uyarılar:

  • nonlocal python 3'te tanıtıldı
  • Çevreleyen kapsam global ise, globalyerine kullanın nonlocal.

7

Gerçekten, en iyi uygulama geri adım atmak ve bunu gerçekten yapmanız gerekip gerekmediğini sormaktır. İşleve aktardığınız bir değişkenin değerini neden değiştirmek istiyorsunuz?

Hızlı bir kesmek için yapmanız gerekiyorsa, en hızlı yol, bir listtamsayıyı tutan bir geçiş yapmak ve bir[0] mgilson'un cevabının da gösterdiği gibi, her kullanımda bir tutturmaktır.

Eğer daha önemli bir şey için bunu yapmak gerekirse, bir yazma classbir olduğunu intsadece bunu ayarlayabilirsiniz böylece, nitelik olarak. Elbette bu, sizi sınıf ve nitelik için iyi bir isim bulmaya zorlar - eğer bir şey düşünemiyorsanız, geri dönüp cümleyi birkaç kez tekrar okuyun ve sonra list.

Daha genel olarak, bazı Java deyimlerini doğrudan Python'a aktarmaya çalışıyorsanız, yanlış yapıyorsunuz demektir. Doğrudan karşılık gelen bir şey olsa bile ( static/ ile olduğu gibi @staticmethod), onu Java'da kullandığınız için çoğu Python programında kullanmak istemezsiniz.


JAVA tamsayıları referans olarak geçirmez (tamsayılar veya herhangi bir nesne. Kutulu nesneler de atamanın ardından değiştirilir).
Luis Masuelli

@LuisMasuelli Evet, kutulu primitves tıpkı nesneler gibi ele alınır, OP'nin istediği gibi kullanımlarını engelleyen tek şey, kutuların değişmez olmasıdır (ve ilkellerin kendileri de değişmez olduğu için, her şey değişmezdir ve yalnızca değişken düzey)
Kroltan

Bunun için iyi bir kullanım durumu, özyinelemeli bir işlev içinde bir dizi çağrıyı (toplam, derinlik değil) saymaktır. Dallanan tüm aramalarda artırılabilen bir numaraya ihtiyacınız var. Standart bir int yeterli değil
z33k

4

Python'da her değer, tıpkı Java'daki ilkel olmayanlar gibi bir referanstır (bir nesneye işaretçi). Ayrıca, Java gibi, Python da yalnızca değere göre geçişe sahiptir. Yani anlamsal olarak hemen hemen aynıdırlar.

Sorunuzda Java'dan bahsettiğinize göre, Java'da istediğinizi nasıl elde ettiğinizi görmek isterim. Java'da gösterebilirseniz, size Python'da tam olarak aynı şekilde nasıl yapılacağını gösterebilirim.


4

Bir numpy tek öğeli bir dizi değişken ve sayısal bir piton değişkeni ise henüz çok amaç için, bu değerlendirilebilir olup. Bu nedenle, tek öğeli bir listeden daha uygun bir referans numarası kapsayıcıdır.

    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)

çıktı:

3

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

3

Belki pitonik bir yol değil, ama bunu yapabilirsin

import ctypes

def incr(a):
    a += 1

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

Akıllı olan adam.
xilpex


0

Python'da her şey değere göre aktarılır, ancak bir durumu değiştirmek istiyorsanız, bir listenin veya bir yönteme iletilen nesnenin içindeki bir tamsayının değerini değiştirebilirsiniz.


3
Sanırım, çünkü everything is passed by valuegerçekten doğru değil. Alıntı dokümanlar:arguments are passed using call by value (where the value is always an object reference, not the value of the object)
Astery

1
Listeler, nesneler ve sözlükler referans olarak aktarılır
AN88

2
Bu doğru olsaydı, bir işlevdeki bir parametreye atama çağrı sitesinde yansıtılırdı. Astery olarak alıntılanan geçiş değer bazındadır ve bu değerler nesne referanslarıdır.
yinelemeli

0

Doğru cevap, bir sınıf kullanmak ve değeri sınıfın içine koymaktır, bu tam olarak istediğiniz gibi referansla geçmenizi sağlar.

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)

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.