kembali, kembali Tidak ada, dan tidak kembali sama sekali?

386

Pertimbangkan tiga fungsi:

def my_func1():
  print "Hello World"
  return None

def my_func2():
  print "Hello World"
  return

def my_func3():
  print "Hello World"

Mereka semua tampaknya tidak kembali. Apakah ada perbedaan antara bagaimana nilai yang dikembalikan dari fungsi ini berperilaku? Apakah ada alasan untuk memilih yang satu dibandingkan yang lain?

Clay Wardell
sumber
40
Perhatikan bahwa ada perbedaan gaya. return Nonemenyiratkan kepada saya bahwa fungsi kadang-kadang memiliki nilai non- Nonekembali, tetapi di lokasi return None, tidak ada nilai balik tersebut. Menulis tidak returnsama sekali menyiratkan kepada saya tidak pernah ada nilai pengembalian yang menarik, agak seperti "prosedur" yang bertentangan dengan "fungsi". returnmenyiratkan untuk ada lebih awal dari "prosedur" seperti pada poin sebelumnya.

Jawaban:

500

Pada perilaku aktual, tidak ada perbedaan. Mereka semua kembali Nonedan hanya itu. Namun, ada waktu dan tempat untuk semua ini. Instruksi berikut pada dasarnya adalah bagaimana metode yang berbeda harus digunakan (atau setidaknya bagaimana saya diajarkan mereka harus digunakan), tetapi mereka bukan aturan mutlak sehingga Anda dapat mencampurnya jika Anda merasa perlu.

Menggunakan return None

Ini mengatakan bahwa fungsi tersebut memang dimaksudkan untuk mengembalikan nilai untuk digunakan nanti, dan dalam hal ini kembali None. Nilai Noneini kemudian dapat digunakan di tempat lain. return Nonetidak pernah digunakan jika tidak ada kemungkinan nilai pengembalian lain dari fungsi.

Pada contoh berikut, kita kembali person's motherjika persondiberikan adalah manusia. Jika itu bukan manusia, kita kembali Nonekarena persontidak memiliki mother(misalkan hewan itu bukan hewan atau sesuatu).

def get_mother(person):
    if is_human(person):
        return person.mother
    else:
        return None

Menggunakan return

Ini digunakan untuk alasan yang sama seperti breakdi loop. Nilai kembali tidak masalah dan Anda hanya ingin keluar dari seluruh fungsi. Ini sangat berguna di beberapa tempat, meskipun Anda tidak terlalu membutuhkannya.

Kami punya 15 prisonersdan kami tahu salah satu dari mereka memiliki pisau. Kami memutari masing-masing prisonersatu per satu untuk memeriksa apakah mereka memiliki pisau. Jika kita memukul orang itu dengan pisau, kita bisa keluar dari fungsinya karena kita tahu hanya ada satu pisau dan tidak ada alasan untuk memeriksa sisa pisau itu prisoners. Jika kami tidak menemukan prisonerdengan pisau, kami meningkatkan peringatan. Ini bisa dilakukan dengan berbagai cara dan menggunakan returnmungkin bahkan bukan cara terbaik, tapi itu hanya contoh untuk menunjukkan cara menggunakan returnuntuk keluar dari suatu fungsi.

def find_prisoner_with_knife(prisoners):
    for prisoner in prisoners:
        if "knife" in prisoner.items:
            prisoner.move_to_inquisition()
            return # no need to check rest of the prisoners nor raise an alert
    raise_alert()

Catatan: Anda tidak boleh melakukannya var = find_prisoner_with_knife(), karena nilai pengembalian tidak dimaksudkan untuk ditangkap.

Tidak menggunakan returnsama sekali

Ini juga akan kembali None, tetapi nilai itu tidak dimaksudkan untuk digunakan atau ditangkap. Ini berarti bahwa fungsi berakhir dengan sukses. Ini pada dasarnya sama dengan returndi voidfungsi dalam bahasa seperti C ++ atau Java.

Dalam contoh berikut, kami menetapkan nama ibu seseorang dan kemudian fungsi keluar setelah selesai dengan sukses.

def set_mother(person, mother):
    if is_human(person):
        person.mother = mother

Catatan: Anda tidak boleh melakukannya var = set_mother(my_person, my_mother), karena nilai pengembalian tidak dimaksudkan untuk ditangkap.

Josh Downes
sumber
5
var = get_mother()tidak apa-apa jika Anda beralih varke fungsi lain yang menerima None- "tidak pernah" sedikit ekstrem
joelb
Bagaimana seharusnya seseorang menerima nilai kembali jika var = get_mother()tidak dapat digunakan? IMHO tidak apa-apa untuk melakukan itu. Anda hanya perlu memastikan untuk memeriksa Nonenilai yang tidak dengan if varsebelum menggunakannya.
winklerrr
Saya pikir penting untuk menyebutkan bahwa ini bukan hanya konvensi yang baik, itu diwajibkan oleh PEP8. Saya memposting jawaban untuk memuji milik Anda dan mengutip PEP8 tentang ini.
Fabiano
29

Ya, semuanya sama saja.

Kami dapat meninjau kode mesin yang ditafsirkan untuk mengkonfirmasi bahwa mereka semua melakukan hal yang sama persis.

import dis

def f1():
  print "Hello World"
  return None

def f2():
  print "Hello World"
  return

def f3():
  print "Hello World"

dis.dis(f1)
    4   0 LOAD_CONST    1 ('Hello World')
        3 PRINT_ITEM
        4 PRINT_NEWLINE

    5   5 LOAD_CONST    0 (None)
        8 RETURN_VALUE

dis.dis(f2)
    9   0 LOAD_CONST    1 ('Hello World')
        3 PRINT_ITEM
        4 PRINT_NEWLINE

    10  5 LOAD_CONST    0 (None)
        8 RETURN_VALUE

dis.dis(f3)
    14  0 LOAD_CONST    1 ('Hello World')
        3 PRINT_ITEM
        4 PRINT_NEWLINE            
        5 LOAD_CONST    0 (None)
        8 RETURN_VALUE      
David Marx
sumber
4
hati-hati di sini ... dis.dispengembalian None: -X
mgilson
Satu-satunya cara untuk mendapatkan output dis sebagai string adalah dengan mengarahkan ulang stdout ke semacam buffer IO (mis. StringIO). Bahkan kemudian, membandingkan secara langsung seperti itu mungkin tidak berhasil karena saya percaya dis.disjuga melaporkan beberapa nomor baris ...
mgilson
Itu tidak relevan apakah mereka menggunakan register yang sama persis atau tidak, jadi saya mengubah kode saya jadi kami hanya melihat mengotak-atik kode mesin sekarang daripada melakukan semacam perbandingan string konyol. Terima kasih untuk kepala yang sangat besar.
David Marx
19

Mereka masing-masing mengembalikan singleton yang sama None- Tidak ada perbedaan fungsional.

Saya pikir itu cukup idiomatis untuk meninggalkan returnpernyataan kecuali Anda perlu keluar dari fungsi lebih awal (dalam hal telanjang returnlebih umum), atau mengembalikan sesuatu selain None. Itu juga masuk akal dan tampaknya idiomatis untuk menulis return Noneketika itu dalam fungsi yang memiliki jalur lain yang mengembalikan sesuatu selain None. Menuliskan return Nonesecara eksplisit adalah isyarat visual kepada pembaca bahwa ada cabang lain yang mengembalikan sesuatu yang lebih menarik (dan bahwa kode panggilan mungkin perlu menangani kedua jenis nilai pengembalian).

Seringkali dalam Python, fungsi yang mengembalikan Nonedigunakan seperti voidfungsi di C - Tujuannya umumnya untuk beroperasi pada argumen input di tempat (kecuali jika Anda menggunakan data global ( gemetar )). Mengembalikan Nonebiasanya membuatnya lebih eksplisit bahwa argumen dimutasi. Ini membuatnya sedikit lebih jelas mengapa masuk akal untuk meninggalkan returnpernyataan dari sudut pandang "konvensi bahasa".

Yang mengatakan, jika Anda bekerja di basis kode yang sudah memiliki konvensi yang telah ditetapkan di sekitar hal-hal ini, saya pasti akan mengikuti untuk membantu basis kode tetap seragam ...

mgilson
sumber
1
Atau lebih umum, setiap kali suatu fungsi berakhir tanpa menekan sebuah returnpernyataan, ia kembali None.
Tidaklah idiomatis untuk menghilangkan pernyataan kembali jika fungsi diharapkan untuk mengembalikan nilai. Hanya fungsi yang beroperasi hanya melalui efek samping yang harus menghilangkan pernyataan pengembalian.
molekul
@ Molekul - Ya, saya pikir mungkin saya tidak melakukan pekerjaan terbaik dalam penjelasan saya di sini. Saya sudah mencoba memperbaruinya tanpa membuatnya menjadi tiruan lengkap dari (jauh lebih baik) jawaban di atas. Saya masih tidak begitu senang dengan posting ini ... Sebagian dari saya ingin menghapusnya (karena jawaban di atas jauh lebih baik) dan sebagian dari saya ingin menyimpannya - 9 orang sejauh ini berpikir itu baik untuk satu alasan atau lainnya. Mungkin saya menemukan sesuatu yang orang lain lewatkan?
mgilson
1
@ ZAB - Saya pikir itu tidak mungkin. Dalam python saya dapat menambal monyet apa saja dengan apa saja (ini adalah dengan desain dan itu fitur hebat jika digunakan hemat) Secara khusus, saya bisa menambal-monyet fungsi yang memiliki pengembalian dengan yang tidak. Semua pemeriksaan "bukan ekspresi" terjadi pada waktu parse, tetapi karena tambalan monyet, ini tidak dapat terjadi sampai runtime. TKI berbeda sekali. Secara pribadi, saya pikir perilaku saat ini cukup masuk akal (dan konsisten dengan bahasa tingkat tinggi lainnya), tetapi saya bukan orang yang perlu Anda meyakinkan :-).
mgilson
1
@ZAB - keduanya Rubydan Javascriptmengambil pendekatan yang sama dengan python. Saya yakin ada beberapa contoh bahasa tingkat tinggi yang mengatakan "batal" dalam ekspresi tetapi saya tidak dapat memikirkannya saat ini (mungkin jika Anda mempertimbangkan Javabahasa tingkat tinggi) ... Bagaimana menambal monyet fungsi (yang berguna untuk mengejek dalam pengujian antara lain) bekerja di dunia hipotetis di mana python mendapat voidkata kunci yang membuatnya jadi fungsi tidak mengembalikan apa pun? (Juga, seperti yang saya katakan sebelumnya, Anda tidak perlu meyakinkan saya bahwa ini adalah desain yang buruk. Saya tidak dapat melakukan apa pun meskipun Anda benar)
mgilson
4

Seperti yang lain menjawab, hasilnya persis sama, Nonedikembalikan dalam semua kasus.

Perbedaannya adalah gaya, tetapi harap dicatat bahwa PEP8 mengharuskan penggunaan agar konsisten:

Konsisten dalam pernyataan pengembalian. Entah semua pernyataan pengembalian dalam suatu fungsi harus mengembalikan ekspresi, atau tidak satupun dari itu seharusnya. Jika pernyataan pengembalian mengembalikan ekspresi, pernyataan pengembalian mana pun yang tidak ada nilai yang dikembalikan harus secara eksplisit menyatakan ini sebagai tidak ada, dan pernyataan pengembalian eksplisit harus ada di akhir fungsi (jika dapat dijangkau).

Iya:

def foo(x):
    if x >= 0:
        return math.sqrt(x)
    else:
        return None

def bar(x):
    if x < 0:
        return None
    return math.sqrt(x)

Tidak:

def foo(x):
    if x >= 0:
        return math.sqrt(x)

def bar(x):
    if x < 0:
        return
    return math.sqrt(x)

https://www.python.org/dev/peps/pep-0008/#programming-recomendations


Pada dasarnya, jika Anda pernah mengembalikan Nonenilai tidak dalam suatu fungsi, itu berarti nilai kembali memiliki makna dan dimaksudkan untuk ditangkap oleh penelepon. Jadi ketika Anda kembali None, itu juga harus eksplisit, untuk menyampaikan Nonedalam kasus ini memiliki makna, itu adalah salah satu nilai pengembalian yang mungkin.

Jika Anda tidak perlu kembali sama sekali, fungsi Anda pada dasarnya berfungsi sebagai prosedur alih-alih fungsi, jadi jangan sertakan returnpernyataan itu.

Jika Anda menulis fungsi seperti prosedur dan ada peluang untuk kembali lebih awal (yaitu Anda sudah selesai pada saat itu dan tidak perlu menjalankan sisa fungsi) Anda dapat menggunakan kosong dan returns untuk memberi sinyal kepada pembaca. itu hanyalah penyelesaian awal dan Nonenilai yang dikembalikan secara implisit tidak memiliki arti dan tidak dimaksudkan untuk ditangkap (fungsi seperti prosedur selalu kembali Nonepula).

Fabiano
sumber
3

Dalam hal fungsionalitas, semuanya sama, perbedaan di antara keduanya terletak pada pembacaan dan gaya kode (yang penting untuk dipertimbangkan)

Yehuda Schwartz
sumber