Pertanyaan Bagaimana Anda mengembalikan beberapa nilai dengan Python?


Cara kanonik mengembalikan banyak nilai dalam bahasa yang mendukungnya sering tupling.

Option: Menggunakan tuple

Pertimbangkan contoh sepele ini:

def f(x):
  y0 = x + 1
  y1 = x * 3
  y2 = y0 ** y3
  return (y0,y1,y2)

Namun, ini dengan cepat menjadi bermasalah karena jumlah nilai yang dikembalikan meningkat. Bagaimana jika Anda ingin mengembalikan empat atau lima nilai? Tentu, Anda bisa terus mengambilnya, tetapi mudah lupa mana nilainya. Ini juga agak buruk untuk membongkar mereka di mana pun Anda ingin menerimanya.

Opsi: Menggunakan kamus

Langkah logis berikutnya tampaknya adalah memperkenalkan semacam 'notasi catatan'. Dalam python, cara yang jelas untuk melakukan ini adalah dengan cara a dict.

Pertimbangkan yang berikut ini:

def g(x):
  y0 = x + 1
  y1 = x * 3
  y2 = y0 ** y3
  return {'y0':y0, 'y1':y1 ,'y2':y2 }

(edit - Hanya untuk menjadi jelas, y0, y1 dan y2 hanya dimaksudkan sebagai pengidentifikasi abstrak. Seperti yang ditunjukkan, dalam prakteknya Anda akan menggunakan pengidentifikasi yang berarti)

Sekarang, kita memiliki mekanisme di mana kita dapat memproyeksikan anggota tertentu dari objek yang dikembalikan. Sebagai contoh,

result['y0']

Opsi: Menggunakan kelas

Namun, ada opsi lain. Kami malah bisa mengembalikan struktur khusus. Saya telah membingkai ini dalam konteks Python, tetapi saya yakin itu juga berlaku untuk bahasa lain. Memang, jika Anda bekerja di C ini mungkin saja satu-satunya pilihan Anda. Di sini:

class ReturnValue(object):
  def __init__(self, y0, y1, y2):
     self.y0 = y0
     self.y1 = y1
     self.y2 = y2

def g(x):
  y0 = x + 1
  y1 = x * 3
  y2 = y0 ** y3
  return ReturnValue(y0, y1, y2)

Dalam python dua sebelumnya mungkin sangat mirip dalam hal plumbing - Lagi pula { y0, y1, y2 } hanya berakhir menjadi entri di internal __dict__ dari ReturnValue.

Ada satu fitur tambahan yang disediakan oleh Python meskipun untuk benda-benda kecil, yang __slots__ atribut. Kelas dapat dinyatakan sebagai:

class ReturnValue(object):
  __slots__ = ["y0", "y1", "y2"]
  def __init__(self, y0, y1, y2):
     self.y0 = y0
     self.y1 = y1
     self.y2 = y2

Dari Panduan Referensi Python:

Itu __slots__ deklarasi mengambil urutan variabel instan dan mencadangkan hanya cukup ruang di setiap instance untuk menyimpan nilai untuk setiap variabel. Ruang disimpan karena __dict__ tidak dibuat untuk setiap instance.

Opsi: Menggunakan daftar

Saran lain yang saya abaikan berasal dari Bill the Lizard:

def h(x):
  result = [x + 1]
  result.append(x * 3)
  result.append(y0 ** y3)
  return result

Ini adalah metode yang paling tidak saya sukai. Kurasa aku tercemar oleh paparan Haskell, tetapi gagasan daftar tipe campuran selalu terasa tidak nyaman bagiku. Dalam contoh khusus ini, daftar tersebut tidak-dicampur-jenis, tetapi bisa dibayangkan. Daftar yang digunakan dengan cara ini benar-benar tidak mendapatkan apa pun sehubungan dengan tupel sejauh yang saya tahu. Satu-satunya perbedaan nyata antara daftar dan tupel dalam Python adalah daftar tersebut yg mungkin berubah, wheras tupel tidak. Saya pribadi cenderung meneruskan konvensi dari pemrograman fungsional: gunakan daftar untuk sejumlah elemen dari jenis yang sama, dan tupel untuk sejumlah elemen tetap dari jenis yang telah ditentukan.

Pertanyaan

Setelah pembukaan yang panjang, muncul pertanyaan yang tak terelakkan. Metode mana (menurut Anda) yang terbaik?

Saya biasanya menemukan diri saya pergi ke rute kamus karena itu melibatkan lebih sedikit pekerjaan pengaturan. Namun, dari perspektif jenis, Anda mungkin lebih baik mengikuti rute kelas, karena itu dapat membantu Anda menghindari membingungkan apa yang diwakili oleh kamus. Di sisi lain, ada beberapa komunitas Python yang merasakan antarmuka tersirat harus lebih disukai daripada antarmuka eksplisit, pada titik mana jenis objek benar-benar tidak relevan, karena pada dasarnya Anda bergantung pada konvensi bahwa atribut yang sama akan selalu memiliki arti yang sama.

Jadi, bagaimana Anda mengembalikan beberapa nilai dengan Python?


804
2017-12-10 01:55


asal


Jawaban:


Tuple bernama ditambahkan dalam 2.6 untuk tujuan ini. Juga lihat os.stat untuk contoh bawaan yang serupa.

>>> import collections
>>> Point = collections.namedtuple('Point', ['x', 'y'])
>>> p = Point(1, y=2)
>>> p.x, p.y
1 2
>>> p[0], p[1]
1 2

511
2017-12-10 16:36



Untuk proyek-proyek kecil, saya merasa paling mudah untuk bekerja dengan tupel. Ketika itu menjadi terlalu sulit untuk dikelola (dan bukan sebelumnya) saya mulai mengelompokkan hal-hal ke dalam struktur logis, namun saya pikir penggunaan kamus dan objek RewValue yang disarankan Anda salah (atau terlalu sederhana).

Mengembalikan kamus dengan kunci y0, y1, y2 dll tidak menawarkan keuntungan apa pun atas tupel. Mengembalikan instance ReturnValue dengan properti .y0 .y1 .y2 dll tidak menawarkan keuntungan apa pun atas tupel. Anda perlu mulai menyebutkan hal-hal jika Anda ingin mendapatkan tempat, dan Anda dapat melakukannya dengan menggunakan tupel:

def getImageData(filename):
  [snip]
  return size, (format, version, compression), (width,height)
size, type, dimensions = getImageData(x)

IMHO, satu-satunya teknik yang baik di luar tuples adalah mengembalikan objek nyata dengan metode dan properti yang tepat, seperti yang Anda dapatkan re.match() atau open(file).


164
2017-12-10 02:22



Banyak jawaban yang menyarankan Anda perlu mengembalikan koleksi semacam, seperti kamus atau daftar. Anda bisa meninggalkan sintaks tambahan dan hanya menuliskan nilai kembali, dipisahkan koma. Catatan: ini secara teknis mengembalikan tuple.

def f():
    return True, False
x, y = f()
print(x)
print(y)

memberi:

True
False

122
2018-04-14 20:08



Saya memilih kamus.

Saya menemukan bahwa jika saya membuat suatu fungsi yang menghasilkan lebih dari 2-3 variabel, saya akan melipatnya dalam kamus. Kalau tidak, saya cenderung melupakan pesanan dan isi dari apa yang saya kembalikan.

Juga, memperkenalkan struktur 'khusus' membuat kode Anda lebih sulit untuk diikuti. (Orang lain harus mencari melalui kode untuk mencari tahu apa itu)

Jika Anda khawatir tentang jenis mencari, gunakan tombol kamus deskriptif, misalnya, 'daftar x-nilai'.

def g(x):
  y0 = x + 1
  y1 = x * 3
  y2 = y0 ** y3
  return {'y0':y0, 'y1':y1 ,'y2':y2 }

53
2017-12-10 02:42



Pilihan lain adalah menggunakan generator:

>>> def f(x):
        y0 = x + 1
        yield y0
        yield x * 3
        yield y0 ** 4


>>> a, b, c = f(5)
>>> a
6
>>> b
15
>>> c
1296

Meskipun IMHO tupel biasanya terbaik, kecuali dalam kasus di mana nilai yang dikembalikan adalah kandidat untuk enkapsulasi di kelas.


31
2018-02-23 15:26



>>> def func():
...    return [1,2,3]
...
>>> a,b,c = func()
>>> a
1
>>> b
2
>>> c
3

22
2018-01-21 20:57



Saya lebih suka menggunakan tupel setiap kali tupel terasa "alami"; Koordinat adalah contoh yang khas, di mana objek yang terpisah dapat berdiri sendiri, misalnya dalam perhitungan skala satu-sumbu saja, dan urutannya penting. Catatan: jika saya dapat mengurutkan atau mengacak item tanpa efek buruk pada arti grup, maka saya mungkin tidak seharusnya menggunakan tuple.

Saya menggunakan kamus sebagai nilai pengembalian hanya ketika objek yang dikelompokkan tidak selalu sama. Pikirkan header email opsional.

Untuk sisa kasus, di mana benda-benda yang dikelompokkan memiliki makna yang melekat di dalam kelompok atau objek yang lengkap dengan metode sendiri diperlukan, saya menggunakan kelas.


22
2017-12-10 02:40



aku lebih memilih

def g(x):
  y0 = x + 1
  y1 = x * 3
  y2 = y0 ** y3
  return {'y0':y0, 'y1':y1 ,'y2':y2 }

tampaknya segala sesuatu yang lain hanyalah kode tambahan untuk melakukan hal yang sama.


20
2017-12-10 02:00



Beri +1 atas saran S.Lott tentang kelas penampung yang bernama.

Untuk python 2.6 dan lebih tinggi, a bernama tuple menyediakan cara yang bermanfaat untuk membuat kelas penampung ini dengan mudah, dan hasilnya "ringan dan tidak memerlukan lebih banyak memori daripada tupel biasa".


13
2017-12-10 03:51



Tuple, dicts, dan objek Python menawarkan programmer kemudahan tradeoff antara formalitas dan kenyamanan untuk struktur data kecil ("hal"). Bagi saya, pilihan bagaimana merepresentasikan sesuatu ditentukan terutama oleh bagaimana saya akan menggunakan struktur. Dalam C ++, ini adalah konvensi umum untuk digunakan struct untuk item dan hanya data class untuk objek dengan metode, meskipun Anda dapat menempatkan metode secara legal pada struct; kebiasaan saya mirip dengan Python, dengan dict dan tuple di tempat struct.

Untuk set koordinat, saya akan menggunakan a tuple daripada sebuah poin class atau a dict (dan perhatikan bahwa Anda dapat menggunakan tuple sebagai kunci kamus, jadi dicts membuat array multidimensi sparse yang sangat kecil).

Jika saya akan melakukan iterasi atas daftar hal-hal, saya lebih suka membongkar tuples pada iterasi:

for score,id,name in scoreAllTheThings():
    if score > goodScoreThreshold:
        print "%6.3f #%6d %s"%(score,id,name)

... karena versi objek lebih rapi untuk dibaca:

for entry in scoreAllTheThings():
    if entry.score > goodScoreThreshold:
        print "%6.3f #%6d %s"%(entry.score,entry.id,entry.name)

... apalagi itu dict.

for entry in scoreAllTheThings():
    if entry['score'] > goodScoreThreshold:
        print "%6.3f #%6d %s"%(entry['score'],entry['id'],entry['name'])

Jika hal itu banyak digunakan, dan Anda menemukan diri Anda melakukan operasi non-sepele serupa di beberapa tempat dalam kode, maka biasanya bermanfaat untuk menjadikannya objek kelas dengan metode yang tepat.

Akhirnya, jika saya akan bertukar data dengan komponen sistem non-Python, saya akan sering menyimpannya di dalam dict karena itu paling cocok untuk serialisasi JSON.


13
2017-08-13 20:18