ฉันสามารถใช้:
if A:
แทน
if A is not None:
หลังดูเหมือน verbose ดังนั้น มีความแตกต่างหรือไม่?
ฉันสามารถใช้:
if A:
แทน
if A is not None:
หลังดูเหมือน verbose ดังนั้น มีความแตกต่างหรือไม่?
คำตอบ:
คำสั่ง
if A:
จะโทรA.__nonzero__()(ดูเอกสารวิธีการตั้งชื่อพิเศษ ) และใช้ค่าส่งคืนของฟังก์ชันนั้น นี่คือบทสรุป:
object.__nonzero__(self)เรียกว่าการดำเนินการทดสอบค่าความจริงและตัวในการดำเนินงาน
bool(); ควรกลับFalseหรือTrueหรือเทียบเท่าจำนวนเต็มของพวกเขาหรือ01เมื่อไม่ได้กำหนดเมธอดนี้จะ__len__()ถูกเรียกใช้ถ้าถูกกำหนดไว้และอ็อบเจ็กต์นั้นถูกพิจารณาว่าเป็นจริงถ้าผลลัพธ์ของมันไม่ใช่ศูนย์ หากมีการกำหนดระดับค่า__len__()มิได้__nonzero__()อินสแตนซ์ทั้งหมดได้รับการพิจารณาความจริง
ในทางกลับกัน,
if A is not None:
เปรียบเทียบเฉพาะการอ้างอิงAด้วยNoneเพื่อดูว่ามันเหมือนกันหรือไม่
if object(): passคือ ~ 0.130 usec ต่อวงขณะที่if object() is not None: pass~ 0.135 usec อย่างไรก็ตามคุณไม่ควรใช้ผลการดำเนินงานที่จะเลือกระหว่างสองคนนี้ แต่มองไปที่ความแตกต่างในวิธีที่พวกเขาทำงานเนื่องจากพวกเขาจะไม่เทียบเท่า
if A is not Noneดูเหมือนว่าจะช้ากว่าเพราะเป็นการเปรียบเทียบและจำเป็นต้องโหลด builtin singleton Noneเป็นขั้นตอนตัวกลางเพื่อเปรียบเทียบกับA(ดูที่dis.dis()) แก้ไขฉันถ้าฉันผิด แต่if A:ดูเหมือนว่าจะมีประสิทธิภาพมากขึ้นทันทีที่คุณต้องการทดสอบคุณค่าความจริงไม่ใช่Noneตัวตน
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"ช้ากว่า อาจขึ้นอยู่กับแพลตฟอร์มดูว่าคุณได้ผลลัพธ์แบบเดียวกันหรือไม่
is Noneทดสอบนั้นช้าที่สุดสำหรับฉัน ใน pypy พวกเขาทั้งหมดวัดตรง :) เดียวกัน
ตามที่เขียนไว้ในPEP8 :
การเปรียบเทียบกับซิงเกิลตันอย่างNone ควรทำด้วย 'is' หรือ 'ไม่ใช่' ไม่ควรใช้ตัวดำเนินการที่เท่าเทียมกันไม่เคยประกอบการเท่าเทียมกัน
นอกจากนี้ระวังการเขียน "ถ้า x" เมื่อคุณหมายถึง "ถ้า x ไม่ใช่ไม่ใช่" - เช่นเมื่อทดสอบว่าตัวแปรหรืออาร์กิวเมนต์ที่ค่าเริ่มต้นเป็นไม่มีถูกตั้งค่าเป็นค่าอื่น ค่าอื่นอาจมีประเภท (เช่นภาชนะ) ที่อาจเป็นเท็จในบริบทบูลีน!
Noneแต่ในการตรวจสอบค่าความจริง ในกรณีนี้if A:ดูเหมือนว่ามีประสิทธิภาพมากขึ้น (ใช้เวลาdis.dis()มีขั้นตอนพิเศษในการโหลดบิวด์อินNoneและเปรียบเทียบกับif A is not None:ขณะที่มีเพียงjump_ifในอีกกรณีหนึ่ง)
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
ฟังก์ชันจำนวนมากส่งคืนไม่มีถ้าไม่มีผลลัพธ์ที่เหมาะสม ตัวอย่างเช่น.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ในแต่ละกรณีดังกล่าวที่คุณต้องการที่จะใช้ คุณกำลังตรวจสอบค่าเฉพาะ - ไม่มี - ไม่ใช่แค่ "ค่าใด ๆ ที่เกิดขึ้นกับการแปลงค่าเป็นเท็จ"
พวกเขาทำสิ่งที่แตกต่างกันมาก
ด้านล่างตรวจสอบว่า 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) ฉันไม่แน่ใจว่าฉันทำตามสิ่งที่คุณหมายถึง
Noneทำงานแตกต่างจากintหรือเป็นstrเพราะการฝึกงาน ประเด็นของฉันคือisและ==กำลังตรวจสอบสิ่งต่าง ๆ ; ก่อนตรวจสอบที่อยู่หน่วยความจำที่สองตรวจสอบเนื้อหาของที่อยู่หน่วยความจำ
if A: จะพิสูจน์ว่าเป็นเท็จหาก A คือ 0, เท็จ, สตริงว่างเปล่า, รายการว่างเปล่าหรือไม่มีซึ่งสามารถนำไปสู่ผลลัพธ์ที่ไม่พึงประสงค์
คำแนะนำส่วนใหญ่ที่ฉันเคยเห็นแนะนำว่าคุณควรใช้
ถ้า A:
นอกเสียจากคุณจะมีเหตุผลที่เฉพาะเจาะจงมากขึ้น
มีความแตกต่างเล็กน้อย มีค่าอื่นที่ไม่ใช่ None ที่คืนค่า False ตัวอย่างเช่นรายการว่างหรือ 0 ดังนั้นลองคิดดูว่าคุณกำลังทดสอบอะไรอยู่
ไม่มีเป็นค่าพิเศษใน Pythonซึ่งโดยปกติจะกำหนดตัวแปรที่ไม่กำหนดค่าเริ่มต้น ในการทดสอบว่า A ไม่มีค่าเฉพาะที่คุณใช้หรือไม่:
if A is not None
ค่า Falsey เป็นคลาสพิเศษของวัตถุใน Python (เช่น false, []) ในการทดสอบว่า A ใช้เท็จหรือไม่:
if not A
ดังนั้นทั้งสองนิพจน์จึงไม่เหมือนกันและคุณควรที่จะไม่ถือว่ามันเป็นคำพ้องความหมาย
PS None ก็เท็จเช่นกันดังนั้นการแสดงออกครั้งแรกจึงหมายถึงวินาที แต่ค่าที่สองครอบคลุมค่าความเท็จอื่น ๆ นอกเหนือจากไม่มี ทีนี้ถ้าคุณมั่นใจได้ว่าคุณไม่มีค่าผิดพลาดอื่น ๆ นอกเหนือจากไม่มีใน A คุณสามารถแทนที่นิพจน์แรกด้วยอันที่สองได้
มันขึ้นอยู่กับบริบท
ฉันใช้if A:เมื่อฉันคาดว่าAจะเป็นคอลเลกชันบางประเภทและฉันต้องการรันบล็อกเฉพาะเมื่อคอลเลกชันไม่ว่างเปล่า วิธีนี้ช่วยให้ผู้โทรผ่านคอลเลกชันที่ประพฤติดีว่างเปล่าหรือไม่และมีสิ่งที่ฉันคาดหวัง นอกจากนี้ยังช่วยNoneและFalseระงับการเรียกใช้บล็อกซึ่งสะดวกในการเรียกรหัส
OTOH ถ้าฉันคาดว่าAจะเป็นวัตถุที่สมบูรณ์ แต่มันอาจเป็นค่าเริ่มNoneต้นแล้วฉันมักจะใช้if A is not Noneเพราะรหัสการโทรอาจตั้งใจส่งการอ้างอิงไปยังคอลเลกชันที่ว่างเปล่าสตริงว่างหรือประเภทตัวเลข 0 ที่มีค่าหรือ บูลีนFalseหรืออินสแตนซ์ของคลาสที่เกิดขึ้นเป็นเท็จในบริบทบูลีน
และในทางกลับกันถ้าฉันคาดหวังว่าAจะมีสิ่งที่เฉพาะเจาะจงมากขึ้น (เช่นอินสแตนซ์ของชั้นเรียนฉันจะเรียกวิธีการของ) แต่มันอาจได้รับการผิดนัดNoneและฉันคิดว่าการแปลงบูลีนเริ่มต้นเป็น คุณสมบัติของชั้นเรียนฉันไม่รังเกียจที่จะบังคับใช้กับคลาสย่อยทั้งหมดจากนั้นฉันจะใช้if A:เพื่อบันทึกนิ้วของฉันเป็นภาระอันยิ่งใหญ่ในการพิมพ์อักขระพิเศษ 12 ตัว
ฉันสร้างไฟล์ชื่อ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'>
อดีตคือ Pythonic มากกว่า (รหัสอุดมคติที่ดีกว่า) แต่จะไม่ดำเนินการบล็อกหาก A เป็นเท็จ (ไม่ใช่ None)
is/is not Noneพูดกับการใช้งาน การติดตาม PEP8 คือ Pythonic นอกจากนี้การทดสอบสองมีความแตกต่างกัน
หลาม> = 2.6,
ถ้าเราเขียนเช่น
if A:
จะสร้างคำเตือนว่า
FutureWarning: พฤติกรรมของวิธีนี้จะเปลี่ยนเป็นเวอร์ชันในอนาคต ใช้การทดสอบเฉพาะ 'len (elem)' หรือ 'elem ไม่ใช่ None' แทน
ดังนั้นเราสามารถใช้
if A is not None:
A is not Noneเร็วขึ้นเนื่องจากมีงานให้ทำน้อยกว่ามาก