ถ้า A กับ A ถ้าไม่ใช่ A:


154

ฉันสามารถใช้:

if A:

แทน

if A is not None:

หลังดูเหมือน verbose ดังนั้น มีความแตกต่างหรือไม่?

คำตอบ:


149

คำสั่ง

if A:

จะโทรA.__nonzero__()(ดูเอกสารวิธีการตั้งชื่อพิเศษ ) และใช้ค่าส่งคืนของฟังก์ชันนั้น นี่คือบทสรุป:

object.__nonzero__(self)

เรียกว่าการดำเนินการทดสอบค่าความจริงและตัวในการดำเนินงานbool(); ควรกลับFalseหรือTrueหรือเทียบเท่าจำนวนเต็มของพวกเขาหรือ0 1เมื่อไม่ได้กำหนดเมธอดนี้จะ__len__()ถูกเรียกใช้ถ้าถูกกำหนดไว้และอ็อบเจ็กต์นั้นถูกพิจารณาว่าเป็นจริงถ้าผลลัพธ์ของมันไม่ใช่ศูนย์ หากมีการกำหนดระดับค่า__len__()มิได้__nonzero__()อินสแตนซ์ทั้งหมดได้รับการพิจารณาความจริง

ในทางกลับกัน,

if A is not None:

เปรียบเทียบเฉพาะการอ้างอิงAด้วยNoneเพื่อดูว่ามันเหมือนกันหรือไม่


4
และA is not Noneเร็วขึ้นเนื่องจากมีงานให้ทำน้อยกว่ามาก
John La Rooy

40
@gnibbler ไม่เป็นไปตามการทดสอบของฉัน if object(): passคือ ~ 0.130 usec ต่อวงขณะที่if object() is not None: pass~ 0.135 usec อย่างไรก็ตามคุณไม่ควรใช้ผลการดำเนินงานที่จะเลือกระหว่างสองคนนี้ แต่มองไปที่ความแตกต่างในวิธีที่พวกเขาทำงานเนื่องจากพวกเขาจะไม่เทียบเท่า
Lauritz V. Thaulow

1
@gnibbler if A is not Noneดูเหมือนว่าจะช้ากว่าเพราะเป็นการเปรียบเทียบและจำเป็นต้องโหลด builtin singleton Noneเป็นขั้นตอนตัวกลางเพื่อเปรียบเทียบกับA(ดูที่dis.dis()) แก้ไขฉันถ้าฉันผิด แต่if A:ดูเหมือนว่าจะมีประสิทธิภาพมากขึ้นทันทีที่คุณต้องการทดสอบคุณค่าความจริงไม่ใช่Noneตัวตน
cedbeu

2
@cedbeu ดูเหมือนว่าจะขึ้นอยู่กับค่าของ A. ฉันทดสอบตอนนี้python -m timeit -s"a=0" "if a: pass" "else: pass"เร็วกว่าpython -m timeit -s"a=0" "if a is None: pass" "else: pass"แต่python -m timeit -s"a=1" "if a: pass" "else: pass"ช้ากว่า อาจขึ้นอยู่กับแพลตฟอร์มดูว่าคุณได้ผลลัพธ์แบบเดียวกันหรือไม่
John La Rooy

2
@cedbeu ใน Python3 พวกเขาทั้งหมดเร็วกว่ามาก แต่การis Noneทดสอบนั้นช้าที่สุดสำหรับฉัน ใน pypy พวกเขาทั้งหมดวัดตรง :) เดียวกัน
จอห์นลา Rooy

51

ตามที่เขียนไว้ในPEP8 :

  • การเปรียบเทียบกับซิงเกิลตันอย่างNone ควรทำด้วย 'is' หรือ 'ไม่ใช่' ไม่ควรใช้ตัวดำเนินการที่เท่าเทียมกันไม่เคยประกอบการเท่าเทียมกัน

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


7
Mmmh นี้ไม่ชัดเจนมาก แต่และไม่ตอบสนองต่อ OP การเปลี่ยนเส้นทางอัตโนมัติไปยัง PEP ไม่ใช่คำตอบที่ดีเสมอไป บ่อยครั้งที่หนึ่งไม่ได้สนใจในการทดสอบตัวตนด้วยซิงเกิลNoneแต่ในการตรวจสอบค่าความจริง ในกรณีนี้if A:ดูเหมือนว่ามีประสิทธิภาพมากขึ้น (ใช้เวลาdis.dis()มีขั้นตอนพิเศษในการโหลดบิวด์อินNoneและเปรียบเทียบกับif A is not None:ขณะที่มีเพียงjump_ifในอีกกรณีหนึ่ง)
cedbeu

29
if x: #x is treated True except for all empty data types [],{},(),'',0 False, and None

ดังนั้นจึงไม่เหมือนกัน

if x is not None # which works only on None

17

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

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

def spam(eggs=None):
    if eggs is None:
        eggs = retrievefromconfigfile()

เปรียบเทียบกับ:

def spam(eggs=None):
    if not eggs:
        eggs = retrievefromconfigfile()

ในระยะหลังจะเกิดอะไรขึ้นถ้าคุณโทรspam(0)หรือspam([])? ฟังก์ชั่นนี้จะ (ตรวจสอบไม่ถูกต้อง) ตรวจพบว่าคุณไม่ได้ส่งผ่านค่าeggsและจะคำนวณค่าเริ่มต้นสำหรับคุณ นั่นอาจไม่ใช่สิ่งที่คุณต้องการ

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

สุดท้ายกลับไปที่ฐานข้อมูล มีความแตกต่างใหญ่ระหว่าง NULL และสตริงว่างเปล่า สตริงที่ว่างเปล่ามักจะพูดว่า "มีค่าอยู่ที่นี่และค่านั้นไม่มีค่าอะไรเลย" NULL บอกว่า "ยังไม่ได้ป้อนค่านี้"

if A is Noneในแต่ละกรณีดังกล่าวที่คุณต้องการที่จะใช้ คุณกำลังตรวจสอบค่าเฉพาะ - ไม่มี - ไม่ใช่แค่ "ค่าใด ๆ ที่เกิดขึ้นกับการแปลงค่าเป็นเท็จ"


10

พวกเขาทำสิ่งที่แตกต่างกันมาก

ด้านล่างตรวจสอบว่า A มีอะไรยกเว้นค่าFalse, [], None, และ'' 0มันตรวจสอบค่าของ A.

if A:

ตรวจสอบด้านล่างว่า A เป็นวัตถุที่แตกต่างจากไม่มี มันจะตรวจสอบและเปรียบเทียบการอ้างอิง (ที่อยู่หน่วยความจำ) ของ A และไม่มี

if A is not None:

UPDATE: คำอธิบายเพิ่มเติม

หลายครั้งที่ทั้งสองดูเหมือนจะทำสิ่งเดียวกันดังนั้นผู้คนจำนวนมากใช้มันสลับกันได้ - นั่นเป็นความคิดที่แย่จริงๆ เหตุผลที่ทั้งสองให้ผลลัพธ์เหมือนกันหลายครั้งด้วยเหตุบังเอิญอันบริสุทธิ์เนื่องจากการเพิ่มประสิทธิภาพของล่าม / คอมไพเลอร์เช่นการฝึกงานหรืออย่างอื่น

ด้วยการเพิ่มประสิทธิภาพเหล่านั้นในใจจำนวนเต็มและสตริงของค่าเดียวกันจบลงโดยใช้พื้นที่หน่วยความจำเดียวกัน นั่นอาจอธิบายได้ว่าทำไมสองสายแยกทำหน้าที่เหมือนกัน

> a = 'test'
> b = 'test'
> a is b
True
> a == b
True

สิ่งอื่น ๆ ไม่ทำงานเหมือนกัน ..

> a = []
> b = []
> a is b
False
> a == b
True

ทั้งสองรายการมีหน่วยความจำของตัวเองอย่างชัดเจน สิ่งอันดับที่น่าแปลกใจทำตัวเหมือนสตริง

> a = ()
> b = ()
> a is b
True
> a == b
True

อาจเป็นเพราะ tuples รับประกันว่าจะไม่เปลี่ยนแปลงดังนั้นจึงเหมาะสมที่จะใช้หน่วยความจำเดียวกันซ้ำ

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


Noneเป็น singleton ไม่ใช่รายละเอียดการใช้งาน (ต่างจาก int หรือ string interning) ฉันไม่แน่ใจว่าฉันทำตามสิ่งที่คุณหมายถึง
Lev Levitsky

@LevLevitsky จุดของฉันไม่ได้Noneทำงานแตกต่างจากintหรือเป็นstrเพราะการฝึกงาน ประเด็นของฉันคือisและ==กำลังตรวจสอบสิ่งต่าง ๆ ; ก่อนตรวจสอบที่อยู่หน่วยความจำที่สองตรวจสอบเนื้อหาของที่อยู่หน่วยความจำ
Pithikos

@LevLevitsky ฉันได้แก้ไขคำตอบของฉันตอนนี้เพื่อให้ย่อยได้มากขึ้น
Pithikos

7

if A: จะพิสูจน์ว่าเป็นเท็จหาก A คือ 0, เท็จ, สตริงว่างเปล่า, รายการว่างเปล่าหรือไม่มีซึ่งสามารถนำไปสู่ผลลัพธ์ที่ไม่พึงประสงค์


1
และค่าอื่น ๆ มากเกินไปเช่นรายการที่ว่างเปล่า, ชุดที่ว่างเปล่าว่างเปล่า tuple ฯลฯ เป็นหลักสิ่งที่ไม่ truthy ต่อdocs.python.org/3/library/stdtypes.html#truth-value-testing
jarmod

6

คำแนะนำส่วนใหญ่ที่ฉันเคยเห็นแนะนำว่าคุณควรใช้

ถ้า A:

นอกเสียจากคุณจะมีเหตุผลที่เฉพาะเจาะจงมากขึ้น

มีความแตกต่างเล็กน้อย มีค่าอื่นที่ไม่ใช่ None ที่คืนค่า False ตัวอย่างเช่นรายการว่างหรือ 0 ดังนั้นลองคิดดูว่าคุณกำลังทดสอบอะไรอยู่


5

ไม่มีเป็นค่าพิเศษใน Pythonซึ่งโดยปกติจะกำหนดตัวแปรที่ไม่กำหนดค่าเริ่มต้น ในการทดสอบว่า A ไม่มีค่าเฉพาะที่คุณใช้หรือไม่:

if A is not None

ค่า Falsey เป็นคลาสพิเศษของวัตถุใน Python (เช่น false, []) ในการทดสอบว่า A ใช้เท็จหรือไม่:

if not A

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


PS None ก็เท็จเช่นกันดังนั้นการแสดงออกครั้งแรกจึงหมายถึงวินาที แต่ค่าที่สองครอบคลุมค่าความเท็จอื่น ๆ นอกเหนือจากไม่มี ทีนี้ถ้าคุณมั่นใจได้ว่าคุณไม่มีค่าผิดพลาดอื่น ๆ นอกเหนือจากไม่มีใน A คุณสามารถแทนที่นิพจน์แรกด้วยอันที่สองได้


ไม่ใช่ว่าคุณผิด แต่คำตอบนี้ครอบคลุมไปแล้ว
Makoto

boh ฉันอยากจะขอให้คุณลบ downvote ถ้าคุณไม่รังเกียจ คำตอบของฉันเทียบเท่ากับคนอื่น ๆ ทั้งหมดอาจจะน้อยกว่าที่คุณพูดถึง แต่นำเสนอจากมุมมองที่ต่างออกไปและมันอาจจะง่ายกว่าที่จะเข้าใจใครสักคน นอกจากนี้เมื่อฉันเห็น downvote บนดังนั้นฉันถือว่าคำตอบที่ผิด ... ซึ่งคุณยอมรับไม่ใช่กรณี
mircealungu

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

4

มันขึ้นอยู่กับบริบท

ฉันใช้if A:เมื่อฉันคาดว่าAจะเป็นคอลเลกชันบางประเภทและฉันต้องการรันบล็อกเฉพาะเมื่อคอลเลกชันไม่ว่างเปล่า วิธีนี้ช่วยให้ผู้โทรผ่านคอลเลกชันที่ประพฤติดีว่างเปล่าหรือไม่และมีสิ่งที่ฉันคาดหวัง นอกจากนี้ยังช่วยNoneและFalseระงับการเรียกใช้บล็อกซึ่งสะดวกในการเรียกรหัส

OTOH ถ้าฉันคาดว่าAจะเป็นวัตถุที่สมบูรณ์ แต่มันอาจเป็นค่าเริ่มNoneต้นแล้วฉันมักจะใช้if A is not Noneเพราะรหัสการโทรอาจตั้งใจส่งการอ้างอิงไปยังคอลเลกชันที่ว่างเปล่าสตริงว่างหรือประเภทตัวเลข 0 ที่มีค่าหรือ บูลีนFalseหรืออินสแตนซ์ของคลาสที่เกิดขึ้นเป็นเท็จในบริบทบูลีน

และในทางกลับกันถ้าฉันคาดหวังว่าAจะมีสิ่งที่เฉพาะเจาะจงมากขึ้น (เช่นอินสแตนซ์ของชั้นเรียนฉันจะเรียกวิธีการของ) แต่มันอาจได้รับการผิดนัดNoneและฉันคิดว่าการแปลงบูลีนเริ่มต้นเป็น คุณสมบัติของชั้นเรียนฉันไม่รังเกียจที่จะบังคับใช้กับคลาสย่อยทั้งหมดจากนั้นฉันจะใช้if A:เพื่อบันทึกนิ้วของฉันเป็นภาระอันยิ่งใหญ่ในการพิมพ์อักขระพิเศษ 12 ตัว


3

ฉันสร้างไฟล์ชื่อtest.pyและเรียกใช้บนล่าม คุณอาจเปลี่ยนสิ่งที่คุณต้องการทดสอบเพื่อให้แน่ใจว่าสิ่งต่าง ๆ เกิดขึ้นเบื้องหลังอย่างไร

import dis

def func1():

    matchesIterator = None

    if matchesIterator:

        print( "On if." );

def func2():

    matchesIterator = None

    if matchesIterator is not None:

        print( "On if." );

print( "\nFunction 1" );
dis.dis(func1)

print( "\nFunction 2" );
dis.dis(func2)

นี่คือความแตกต่างของแอสเซมเบลอร์:

ที่มา:

>>> import importlib
>>> reload( test )

Function 1
  6           0 LOAD_CONST               0 (None)
              3 STORE_FAST               0 (matchesIterator)

  8           6 LOAD_FAST                0 (matchesIterator)
              9 POP_JUMP_IF_FALSE       20

 10          12 LOAD_CONST               1 ('On if.')
             15 PRINT_ITEM
             16 PRINT_NEWLINE
             17 JUMP_FORWARD             0 (to 20)
        >>   20 LOAD_CONST               0 (None)
             23 RETURN_VALUE

Function 2
 14           0 LOAD_CONST               0 (None)
              3 STORE_FAST               0 (matchesIterator)

 16           6 LOAD_FAST                0 (matchesIterator)
              9 LOAD_CONST               0 (None)
             12 COMPARE_OP               9 (is not)
             15 POP_JUMP_IF_FALSE       26

 18          18 LOAD_CONST               1 ('On if.')
             21 PRINT_ITEM
             22 PRINT_NEWLINE
             23 JUMP_FORWARD             0 (to 26)
        >>   26 LOAD_CONST               0 (None)
             29 RETURN_VALUE
<module 'test' from 'test.py'>

2

อดีตคือ Pythonic มากกว่า (รหัสอุดมคติที่ดีกว่า) แต่จะไม่ดำเนินการบล็อกหาก A เป็นเท็จ (ไม่ใช่ None)


7
-1 ในฐานะที่เป็น @klesh กล่าวถึง PEP8 is/is not Noneพูดกับการใช้งาน การติดตาม PEP8 คือ Pythonic นอกจากนี้การทดสอบสองมีความแตกต่างกัน
JCotton

1

หลาม> = 2.6,

ถ้าเราเขียนเช่น

if A:

จะสร้างคำเตือนว่า

FutureWarning: พฤติกรรมของวิธีนี้จะเปลี่ยนเป็นเวอร์ชันในอนาคต ใช้การทดสอบเฉพาะ 'len (elem)' หรือ 'elem ไม่ใช่ None' แทน

ดังนั้นเราสามารถใช้

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