ส่งคืนส่งคืนไม่มีและไม่ส่งคืนเลย?


386

พิจารณาสามฟังก์ชั่น:

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

def my_func2():
  print "Hello World"
  return

def my_func3():
  print "Hello World"

พวกเขาทั้งหมดกลับไม่มี มีความแตกต่างระหว่างวิธีการคืนค่าของฟังก์ชั่นเหล่านี้หรือไม่? มีเหตุผลใดที่จะชอบอันหนึ่งกับอีกอัน?


40
โปรดทราบว่ามีความแตกต่างโวหาร return Noneบอกเป็นนัยว่าฟังก์ชั่นบางครั้งมีค่าที่ไม่ใช่Noneผลตอบแทน แต่ที่ตำแหน่งของreturn Noneมันไม่มีค่าตอบแทนดังกล่าว การเขียนไม่returnได้แปลว่าฉันไม่เคยมีค่าตอบแทนที่น่าสนใจเลยเช่นเดียวกับ "ขั้นตอน" เมื่อเทียบกับ "ฟังก์ชั่น" returnหมายถึงการมีอยู่ก่อนจาก "ขั้นตอน" ตามจุดก่อนหน้า

คำตอบ:


500

ในพฤติกรรมจริงไม่มีความแตกต่าง พวกเขากลับมาทั้งหมดNoneและนั่นคือมัน อย่างไรก็ตามมีเวลาและสถานที่สำหรับสิ่งเหล่านี้ คำแนะนำต่อไปนี้เป็นวิธีการใช้วิธีการที่แตกต่างกัน (หรืออย่างน้อยฉันได้รับการสอนให้ใช้) แต่ไม่ใช่กฎที่แน่นอนเพื่อให้คุณสามารถผสมผสานได้หากคุณรู้สึกว่าจำเป็น

การใช้ return None

Noneนี้จะบอกว่าฟังก์ชั่นที่มีความหมายแน่นอนค่าตอบแทนสำหรับการใช้ในภายหลังและในกรณีนี้ก็จะส่งกลับ ค่านี้Noneสามารถใช้ที่อื่นได้ return Noneไม่เคยถูกใช้หากไม่มีค่าตอบแทนที่เป็นไปได้อื่น ๆ จากฟังก์ชั่น

ในตัวอย่างต่อไปนี้เรากลับมาperson's motherถ้าpersonที่กำหนดเป็นมนุษย์ ถ้าไม่ใช่มนุษย์เรากลับมาNoneตั้งแต่personไม่มีmother(ลองสมมุติว่ามันไม่ใช่สัตว์หรืออะไร)

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

การใช้ return

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

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

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()

หมายเหตุ: คุณไม่ควรทำvar = find_prisoner_with_knife()เนื่องจากค่าส่งคืนไม่ได้ถูกจับ

ใช้ไม่returnได้ทั้งหมด

สิ่งนี้จะส่งคืนNoneเช่นกัน แต่ค่าดังกล่าวไม่ได้หมายถึงการใช้หรือจับ มันหมายความว่าฟังก์ชั่นจบลงด้วยความสำเร็จ มันเหมือนกับreturnในvoidฟังก์ชั่นในภาษาเช่น C ++ หรือ Java

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

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

หมายเหตุ: คุณไม่ควรทำvar = set_mother(my_person, my_mother)เนื่องจากค่าส่งคืนไม่ได้ถูกจับ


5
var = get_mother()ก็โอเคถ้าคุณกำลังส่งผ่านvarไปยังฟังก์ชั่นอื่น ๆ ที่ยอมรับNone- "ไม่เคย" เป็นสุดโต่งเล็กน้อย
joelb

หนึ่งควรยอมรับค่าตอบแทนอย่างไรถ้าvar = get_mother()ไม่สามารถใช้? IMHO เป็นเรื่องปกติมากที่จะทำเช่นนั้น คุณเพียงแค่ต้องตรวจสอบว่าไม่ใช่Noneค่าด้วยif varก่อนที่จะใช้
winklerrr

ฉันคิดว่ามันเป็นสิ่งสำคัญที่จะพูดถึงว่านี่ไม่ใช่แค่การประชุมที่ดี แต่เป็นสิ่งที่ PEP8 ต้องการ ฉันโพสต์คำตอบเพื่อชมเชยคุณและเสนอราคา PEP8 เกี่ยวกับเรื่องนี้
Fabiano

29

ใช่พวกเขาเหมือนกันทั้งหมด

เราสามารถตรวจสอบรหัสเครื่องตีความเพื่อยืนยันว่าพวกเขากำลังทำสิ่งเดียวกัน

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      

4
ระวังที่นี่ ... dis.disผลตอบแทนNone: -X
mgilson

วิธีเดียวที่จะได้รับผลลัพธ์ของ dis เป็นสตริงคือการเปลี่ยนเส้นทาง stdout ไปยังประเภทของบัฟเฟอร์ IO (เช่น StringIO) แล้วถึงแม้การเปรียบเทียบโดยตรงเช่นอาจจะว่าไม่ทำงานตามที่ผมเชื่อว่าdis.disยังมีรายงานหมายเลขบรรทัดบาง ...
mgilson

มันไม่เกี่ยวข้องเลยว่าพวกเขากำลังใช้รีจิสเตอร์เดียวกันหรือไม่ดังนั้นฉันจึงเปลี่ยนรหัสของฉันดังนั้นเราแค่ดูรหัสเครื่องตอนนี้แทนที่จะทำการเปรียบเทียบสตริงโง่ ๆ ขอบคุณสำหรับหัวใหญ่มากขึ้น
David Marx

19

พวกเขาแต่ละคนกลับมาคนเดียวที่เหมือนกันNone- ไม่มีความแตกต่างด้านหน้าที่

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

มักจะอยู่ใน Python, ฟังก์ชั่นที่กลับNoneถูกนำมาใช้เช่นvoidฟังก์ชั่นใน C - วัตถุประสงค์ของพวกเขาคือโดยทั่วไปในการดำเนินการเกี่ยวกับข้อโต้แย้งการป้อนข้อมูลในสถานที่ (ยกเว้นกรณีที่คุณกำลังใช้ข้อมูลทั่วโลก ( สั่น )) การกลับมาNoneมักจะทำให้มันชัดเจนมากขึ้นว่าข้อโต้แย้งที่กลายพันธุ์ สิ่งนี้ทำให้ชัดเจนขึ้นเล็กน้อยว่าทำไมจึงเหมาะสมที่จะละทิ้งreturnคำแถลงจากมุมมอง "การประชุมทางภาษา"

ที่กล่าวว่าหากคุณกำลังทำงานในรหัสฐานที่มีการประชุมที่กำหนดไว้ล่วงหน้าแล้วสิ่งเหล่านี้ฉันจะปฏิบัติตามเหมาะสมเพื่อช่วยให้รหัสฐานคงที่เหมือนกัน ...


1
หรืออื่น ๆ โดยทั่วไปทุกครั้งที่ฟังก์ชั่นปลายโดยไม่ต้องกดปุ่มคำสั่งก็จะส่งกลับreturn None

ไม่ใช่เรื่องผิดปกติที่จะละเว้นคำสั่ง return หากฟังก์ชันคาดว่าจะส่งคืนค่า เฉพาะฟังก์ชั่นที่ทำงานโดยใช้ผลข้างเคียงเท่านั้นที่ควรละเว้นข้อความสั่งคืน
โมเลกุล

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

1
@ZAB - ฉันไม่คิดว่าเป็นไปได้ ในงูหลามฉันสามารถแก้ไขอะไรกับลิง (นี่คือจากการออกแบบและมันเป็นคุณสมบัติที่ยอดเยี่ยมถ้าใช้เท่าที่จำเป็น) โดยเฉพาะฉันสามารถแก้ไขแพทช์ฟังก์ชั่นที่มีค่าตอบแทนที่ไม่มี การตรวจสอบ "ไม่ใช่นิพจน์" ทั้งหมดเกิดขึ้นในเวลาที่แยกวิเคราะห์ แต่เนื่องจากการปะแก้ลิงจึงไม่สามารถเกิดขึ้นได้จนกว่ารันไทม์ IOW มันแตกต่างอย่างสิ้นเชิง โดยส่วนตัวฉันคิดว่าพฤติกรรมปัจจุบันค่อนข้างสมเหตุสมผล (และสอดคล้องกับภาษาระดับสูงอื่น ๆ ) แต่ฉันไม่ใช่คนที่คุณต้องโน้มน้าวใจ :-)
mgilson

1
@ZAB - ทั้งคู่RubyและJavascriptใช้แนวทางเดียวกับ python ฉันแน่ใจว่ามีเป็นตัวอย่างของภาษาระดับสูงที่ว่า "เป็นโมฆะ" ในการแสดงออก แต่ฉันไม่สามารถคิดใด ๆ ในขณะนี้ (บางทีถ้าคุณพิจารณาJavaภาษาระดับสูง) ... วิธีจะลิงปะ ฟังก์ชั่น (ซึ่งมีประโยชน์สำหรับการเยาะเย้ยในการทดสอบในสิ่งอื่น ๆ ) ทำงานในโลกสมมุติที่ไพ ธ อนมีvoidคำสำคัญที่ทำให้มันฟังก์ชั่นไม่ได้กลับอะไร? (เช่นเดียวกับที่ฉันพูดก่อนหน้านี้คุณไม่จำเป็นต้องโน้มน้าวใจฉันว่านี่เป็นการออกแบบที่ไม่ดีฉันไม่สามารถทำอะไรได้แม้ว่าคุณจะพูดถูก)
mgilson

4

อย่างที่คนอื่น ๆ ตอบแล้วผลลัพธ์จะเหมือนกันNoneทุกประการ

ความแตกต่างนั้นมีสไตล์ แต่โปรดทราบว่าPEP8ต้องการการใช้งานที่สอดคล้องกัน:

มีความสอดคล้องในงบกลับ คำสั่ง return ทั้งหมดในฟังก์ชั่นควรส่งคืนค่า expression หรือไม่ควรใช้ หากคำสั่ง return ใด ๆ ส่งคืนนิพจน์คำสั่ง return ใด ๆ ที่ไม่มีค่าใดถูกส่งคืนควรระบุอย่างชัดเจนว่านี่เป็น Return None และคำชี้แจงการคืนอย่างชัดเจนควรปรากฏที่ท้ายฟังก์ชั่น (ถ้าเข้าถึงได้)

ใช่:

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)

No:

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-recommendations


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

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

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


3

ในแง่ของการทำงานสิ่งเหล่านี้เหมือนกันทั้งหมดความแตกต่างระหว่างพวกเขาคือในการอ่านโค้ดและสไตล์ (ซึ่งเป็นสิ่งสำคัญที่ต้องพิจารณา)

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