วัตถุ null ใน Python?


คำตอบ:


1628

ใน Python วัตถุ 'null' คือ singleton Noneวัตถุเดี่ยว

วิธีที่ดีที่สุดในการตรวจสอบสิ่งต่าง ๆ สำหรับ "Noneness" คือการใช้ตัวดำเนินการระบุตัวตนis:

if foo is None:
    ...

125
และเหตุผลในการเลือกegg is Noneมากกว่าegg == None: หลังสามารถมากเกินไปและมีแนวโน้มที่จะทำลายเมื่อเปรียบเทียบกับวัตถุที่ถูกต้องกับไม่มี (ขึ้นอยู่กับวิธีการที่จะดำเนินการ แต่คุณไม่ได้คาดหวังว่าทุกคนที่จะเปรียบเทียบเช่นเดียวกันกับที่ไม่มีใครเข้าบัญชีคุณ?) ในขณะที่isทำงานเหมือนเดิมเสมอ

94
ทำไมนักออกแบบงูใหญ่ไม่เพียงแค่เลือก "null" เพียงแค่จะต้องแตกต่างกันไม่ได้คุณ! ;)
Vidar

35
@Vidar 'ไม่มี' ไม่ได้ขาดวัตถุ (ตามที่ 'null' คือการอ้างอิงเป็นโมฆะ) มันเป็นวัตถุจริง คุณจะไม่ได้รับวัตถุ 'ไม่มี' สำหรับวัตถุที่เป็นประเภทอื่นนอกเหนือจาก 'NoneType' มันไม่ใช่แนวคิดทางภาษา .. มันไม่ได้สร้างขึ้นในภาษาไพ ธ อนมันเป็นส่วนหนึ่งของห้องสมุดมาตรฐานของไพ ธ อน ไม่มี! == (ahem) Null
Rushyo

11
@ naught101 มันจะไม่ให้บริการวัตถุประสงค์ เนื่องจากไม่มีแนวคิดของพอยน์เตอร์ หากคุณกำลังใช้ไลบรารี ctypes จะไม่มีการใช้คำใดคำหนึ่งแทนที่อยู่ศูนย์ แต่ยังไม่มีแนวคิดภาษาของ 'การอ้างอิงที่เป็นโมฆะ' ใน Python คุณมักจะอ้างอิงวัตถุบางชนิดแม้ว่าวัตถุนั้นจะเป็น None ฉันสงสัยว่านี่ทำให้ภาษาง่ายขึ้นอย่างมาก
Rushyo

5
การนำเสนอที่ยอดเยี่ยมอธิบายถึงข้อผิดพลาด 'พันล้านดอลลาร์' ซึ่งเป็นข้อมูลอ้างอิงที่เป็นโมฆะ: infoq.com/presentations/… . ตัวเลือกอื่น ๆ แทน null เป็นตัวเลือกหรืออาจ (กำหนดเอง) ประเภท
Michael Trouw

133

None, Python ว่างเปล่า?

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

>>> foo is None
True
>>> foo = 'bar' 
>>> foo is None
False

พื้นฐาน

มีและสามารถเป็นหนึ่งเดียวเท่านั้น None

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

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

>>> NoneType
NameError: name 'NoneType' is not defined
>>> type(None)
NoneType

คุณสามารถตรวจสอบNone's id()เอกลักษณ์ที่มีฟังก์ชั่นตัวตนของงูใหญ่ มันจะส่งกลับจำนวนที่ไม่ซ้ำกันที่กำหนดให้กับวัตถุแต่ละวัตถุมีหนึ่ง หากรหัสของสองตัวแปรเหมือนกันดังนั้นพวกเขาชี้ไปที่วัตถุเดียวกัน

>>> NoneType = type(None)
>>> id(None)
10748000
>>> my_none = NoneType()
>>> id(my_none)
10748000
>>> another_none = NoneType()
>>> id(another_none)
10748000
>>> def function_that_does_nothing(): pass
>>> return_value = function_that_does_nothing()
>>> id(return_value)
10748000

None ไม่สามารถเขียนทับได้

ใน Python เวอร์ชั่นเก่ากว่า (ก่อนหน้า 2.4) เป็นไปได้ที่จะมอบหมายใหม่Noneแต่ไม่สามารถกำหนดได้อีกต่อไป ไม่ได้เป็นคุณลักษณะคลาสหรือในขอบเขตของฟังก์ชั่น

# In Python 2.7
>>> class SomeClass(object):
...     def my_fnc(self):
...             self.None = 'foo'
SyntaxError: cannot assign to None
>>> def my_fnc():
        None = 'foo'
SyntaxError: cannot assign to None

# In Python 3.5
>>> class SomeClass:
...     def my_fnc(self):
...             self.None = 'foo'
SyntaxError: invalid syntax
>>> def my_fnc():
        None = 'foo'
SyntaxError: cannot assign to keyword

ดังนั้นจึงปลอดภัยที่จะสมมติว่าNoneการอ้างอิงทั้งหมดเหมือนกัน ไม่มี None"กำหนดเอง"

เพื่อทดสอบการNoneใช้งานisโอเปอเรเตอร์

เมื่อเขียนโค้ดคุณอาจถูกล่อลวงให้ทดสอบNonenessเช่นนี้:

if value==None:
    pass

หรือเพื่อทดสอบความเท็จเช่นนี้

if not value:
    pass

คุณต้องเข้าใจความหมายและทำไมมันมักจะเป็นความคิดที่ดีที่จะชัดเจน

กรณีที่ 1: ทดสอบว่ามีค่าหรือไม่ None

ทำไมต้องทำเช่นนี้

value is None

ค่อนข้างมากกว่า

value==None

ครั้งแรกเทียบเท่ากับ:

id(value)==id(None)

value==Noneในความเป็นจริงแล้วนิพจน์นั้นถูกนำไปใช้เช่นนี้

value.__eq__(None)

หากมูลค่าเป็นจริงNoneคุณจะได้รับสิ่งที่คาดหวัง

>>> nothing = function_that_does_nothing()
>>> nothing.__eq__(None)
True

ในกรณีทั่วไปส่วนใหญ่ผลลัพธ์จะเหมือนกัน แต่ __eq__()วิธีการเปิดประตูที่เป็นช่องว่างที่รับประกันความถูกต้องเนื่องจากมันสามารถถูกแทนที่ในชั้นเรียนเพื่อให้พฤติกรรมพิเศษ

พิจารณาคลาสนี้

>>> class Empty(object):
...     def __eq__(self, other):
...         return not other

ดังนั้นคุณลองNoneและใช้งานได้

>>> empty = Empty()
>>> empty==None
True

แต่แล้วมันก็ยังใช้งานได้กับสตริงว่าง

>>> empty==''
True

และยัง

>>> ''==None
False
>>> empty is None
False

กรณีที่ 2: ใช้Noneเป็นบูลีน

การทดสอบสองครั้งต่อไปนี้

if value:
    # do something

if not value:
    # do something

ในความเป็นจริงการประเมินเป็น

if bool(value):
    # do something

if not bool(value):
    # do something

Noneคือ "falsey" หมายถึงว่าถ้าโยนบูลมันจะกลับมาFalseและถ้านำไปใช้ประกอบการก็จะกลับnot แต่ทราบว่ามันไม่ใช่คุณสมบัติที่จะไม่ซ้ำกันTrue NoneนอกเหนือไปจากFalseตัวเองทรัพย์สินที่ใช้ร่วมกันโดยรายการที่ว่างเปล่า tuples, ชุด dicts สายเช่นเดียวกับ 0 และวัตถุทั้งหมดออกจากชั้นเรียนที่ใช้วิธีวิเศษที่จะกลับมา__bool__()False

>>> bool(None)
False
>>> not None
True

>>> bool([])
False
>>> not []
True

>>> class MyFalsey(object):
...     def __bool__(self):
...         return False
>>> f = MyFalsey()
>>> bool(f)
False
>>> not f
True

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

def some_function(value=None):
    if not value:
        value = init_value()

ในข้างต้นคุณหมายถึงการโทรinit_value()เมื่อค่าถูกตั้งค่าเป็นพิเศษNoneหรือคุณหมายถึงค่าที่ตั้งเป็น0หรือสตริงว่างหรือรายการว่างควรเรียกการเริ่มต้น อย่างที่ฉันพูด ในขณะที่มันมักจะเป็นในกรณีที่งูใหญ่อย่างชัดเจนจะดีกว่าโดยปริยาย

None ในทางปฏิบัติ

None ใช้เป็นค่าสัญญาณ

Noneมีสถานะพิเศษใน Python มันเป็นค่าพื้นฐานที่ชื่นชอบเพราะอัลกอริทึมจำนวนมากถือว่าเป็นค่าที่ไม่ธรรมดา ในสถานการณ์เช่นนี้มันสามารถใช้เป็นธงเพื่อส่งสัญญาณว่าเงื่อนไขนั้นต้องการการจัดการพิเศษ (เช่นการตั้งค่าเริ่มต้น)

คุณสามารถกำหนดNoneให้กับอาร์กิวเมนต์คำหลักของฟังก์ชันและทดสอบอย่างชัดเจน

def my_function(value, param=None):
    if param is None:
        # do something outrageous!

คุณสามารถส่งคืนเป็นค่าเริ่มต้นเมื่อพยายามเข้าถึงแอตทริบิวต์ของวัตถุแล้วทดสอบอย่างชัดเจนก่อนทำสิ่งพิเศษ

value = getattr(some_obj, 'some_attribute', None)
if value is None:
    # do something spectacular!

ตามค่าเริ่มต้นget()เมธอดของพจนานุกรมจะส่งคืนNoneเมื่อพยายามเข้าถึงคีย์ที่ไม่มีอยู่:

>>> some_dict = {}
>>> value = some_dict.get('foo')
>>> value is None
True

หากคุณพยายามที่จะเข้าถึงมันโดยใช้เครื่องหมายห้อยKeyErrorจะเพิ่มขึ้น

>>> value = some_dict['foo']
KeyError: 'foo'

ในทำนองเดียวกันหากคุณพยายามที่จะปรากฏรายการที่ไม่มีอยู่

>>> value = some_dict.pop('foo')
KeyError: 'foo'

ซึ่งคุณสามารถระงับด้วยค่าเริ่มต้นที่มักจะตั้งค่า None

value = some_dict.pop('foo', None)
if value is None:
    # booom!

None ใช้เป็นทั้งค่าสถานะและค่าที่ถูกต้อง

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

เมื่อคุณเคียวรีวัตถุสำหรับแอททริบิวต์ที่มีgetattr(some_obj, 'attribute_name', None)การเรียกคืนNoneไม่ได้บอกคุณว่าแอททริบิวที่คุณพยายามเข้าถึงนั้นถูกตั้งค่าเป็นNoneหรือว่ามันหายไปจากวัตถุโดยสิ้นเชิง สถานการณ์เดียวกันเมื่อมีการเข้าถึงที่สำคัญจากพจนานุกรมเหมือนที่some_dict.get('some_key')คุณไม่ทราบว่าจะหายไปหรือถ้ามันตั้งเพียงเพื่อsome_dict['some_key'] Noneหากคุณต้องการข้อมูลนั้นวิธีปกติในการจัดการนี้คือพยายามเข้าถึงแอตทริบิวต์หรือคีย์โดยตรงจากภายในtry/exceptโครงสร้าง:

try:
    # equivalent to getattr() without specifying a default
    # value = getattr(some_obj, 'some_attribute')
    value = some_obj.some_attribute
    # now you handle `None` the data here
    if value is None:
        # do something here because the attribute was set to None
except AttributeError:
    # we're now hanling the exceptional situation from here.
    # We could assign None as a default value if required.
    value = None 
    # In addition, since we now know that some_obj doesn't have the
    # attribute 'some_attribute' we could do something about that.
    log_something(some_obj)

ในทำนองเดียวกันกับ Dict:

try:
    value = some_dict['some_key']
    if value is None:
        # do something here because 'some_key' is set to None
except KeyError:
    # set a default 
    value = None
    # and do something because 'some_key' was missing
    # from the dict.
    log_something(some_dict)

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

def my_function(**kwargs):
    try:
        value = kwargs['some_key'] 
        if value is None:
            # do something because 'some_key' is explicitly 
            # set to None
    except KeyError:
        # we assign the default
        value = None
        # and since it's not coming from the caller.
        log_something('did not receive "some_key"')

None ใช้เป็นค่าที่ถูกต้องเท่านั้น

หากคุณพบว่าโค้ดของคุณถูกทิ้งด้วยtry/exceptรูปแบบข้างต้นเพียงเพื่อแยกความแตกต่างระหว่างNoneค่าสถานะและNoneข้อมูลจากนั้นใช้ค่าทดสอบอื่น มีรูปแบบที่แทรกค่าที่อยู่นอกชุดของค่าที่ถูกต้องซึ่งเป็นส่วนหนึ่งของข้อมูลในโครงสร้างข้อมูลและใช้เพื่อควบคุมและทดสอบเงื่อนไขพิเศษ (เช่นขอบเขตรัฐ ฯลฯ ) ค่าดังกล่าวเรียกว่าSentinelและสามารถใช้วิธีที่Noneใช้เป็นสัญญาณได้ มันง่ายมากที่จะสร้าง Sentinel ใน Python

undefined = object()

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

ด้วยฟังก์ชั่น

def my_function(value, param1=undefined, param2=undefined):
    if param1 is undefined:
        # we know nothing was passed to it, not even None
        log_something('param1 was missing')
        param1 = None


    if param2 is undefined:
        # we got nothing here either
        log_something('param2 was missing')
        param2 = None

พร้อม dict

value = some_dict.get('some_key', undefined)
if value is None:
    log_something("'some_key' was set to None")

if value is undefined:
    # we know that the dict didn't have 'some_key'
    log_something("'some_key' was not set at all")
    value = None

ด้วยวัตถุ

value = getattr(obj, 'some_attribute', undefined) 
if value is None:
    log_something("'obj.some_attribute' was set to None")
if value is undefined:
    # we know that there's no obj.some_attribute
    log_something("no 'some_attribute' set on obj")
    value = None

อย่างที่ฉันได้กล่าวถึงยามรักษาการณ์ก่อนหน้านี้มาพร้อมกับคำเตือนบางอย่าง ก่อนอื่นพวกเขาไม่ใช่คำหลักNoneดังนั้น python จึงไม่ปกป้อง คุณสามารถเขียนทับข้อความundefinedด้านบนของคุณได้ทุกที่ทุกเวลาในโมดูลที่กำหนดไว้ดังนั้นโปรดใช้ความระมัดระวังในการเปิดเผยและใช้งาน ถัดไปอินสแตนซ์ที่ส่งกลับโดยobject()ไม่ใช่ซิงเกิลตันถ้าคุณทำการโทรนั้น 10 ครั้งคุณจะได้รับ 10 วัตถุที่แตกต่างกัน ในที่สุดการใช้ Sentinel นั้นมีความเป็นไปได้สูงมาก Sentinel มีความเฉพาะเจาะจงกับไลบรารีที่ใช้ในและโดยทั่วไปขอบเขตดังกล่าวควรถูก จำกัด ไว้ที่ internals ของห้องสมุด ไม่ควร "รั่ว" ออก รหัสภายนอกควรตระหนักถึงมันหากวัตถุประสงค์ของพวกเขาคือการขยายหรือเสริม API ของห้องสมุด


เกี่ยวกับ: ไม่มี == สิ่ง?
hkBst

@hkBst ฉันเดาว่าคำถามของคุณเกี่ยวกับวิธีที่ไพ ธ อนประเมินความเสมอภาค ดูstackoverflow.com/questions/3588776/
Michael Ekoka

อ่า IIUC ที่มักจะยังคงจบลงด้วยการเรียกสิ่งต่าง ๆ ในกรณีนี้ฉันเพิกถอนคำแนะนำของฉัน
hkBst

@MichaelEkoka คุณสามารถแบ่งปันแหล่งข้อมูลที่น่าประทับใจและมีประโยชน์นี้ได้
Anidhya Bhatnagar

มาตรฐานการปฏิบัติ คุณสามารถค้นหาคำแนะนำของมันจากการที่จะผ่านหลามกวดวิชาแต่การอ่านรหัสที่มาโครงการที่ได้รับความนิยมเป็นอันดับหนึ่งของฉันคำแนะนำได้อย่างรวดเร็วดื่มด่ำเข้าไปในสำนวนหลาม
Michael Ekoka

67

มันไม่ได้เรียกว่าเป็นโมฆะในภาษาอื่น ๆ Noneแต่ มีเพียงอินสแตนซ์เดียวของวัตถุนี้เสมอดังนั้นคุณสามารถตรวจสอบความเทียบเท่ากับx is None(การเปรียบเทียบตัวตน) แทนที่จะเป็นx == Noneหากคุณต้องการ


25
ฉันจะหาวิธีที่จะทำลาย Python โดยการสร้างอินสแตนซ์Noneใหม่ ถ้าอย่างนั้นคนฉลาดที่เปรียบเทียบกับทุกคนNoneTypeจะได้ชัยชนะ!
Zenexer

28

ใน Python เพื่อแสดงถึงการไม่มีค่าคุณสามารถใช้ค่าNone ( types.NoneType.None ) สำหรับวัตถุและ"" (หรือlen () == 0 ) สำหรับสตริง ดังนั้น:

if yourObject is None:  # if yourObject == None:
    ...

if yourString == "":  # if yourString.len() == 0:
    ...

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

อย่างไรก็ตามคุณสามารถดู:


8
เท่าที่ฉันรู้ (0 == false) จะส่งกลับค่าจริงในทุกภาษาการเขียนโปรแกรม นั่นเป็นเพราะ false มีการเข้ารหัสเป็น 0 และ "true" เนื่องจากทุกอย่างที่ไม่ใช่ 0 อย่างไรก็ตามโปรดอย่าใช้ตัวดำเนินการ "is" นั้นไม่เหมือนกับ "==" นี่เป็นความแตกต่างระหว่าง "==" และ "เท่ากับ" ใน Java มากขึ้นหรือน้อยลง ผู้ประกอบการ "คือ" แน่นอนตรวจสอบว่าสองวัตถุที่เหมือนกัน (เช่นเดียวกันและไม่เนื้อหาเดียวกัน)!
เปาโล Rovelli

12
Paolo, FYI, (0 == false) ไม่ส่งคืนจริงในทุกภาษาการเขียนโปรแกรม ตัวอย่างตัวนับด่วนคือ Scala โดยที่ (0 == false) คืนค่า false และฉันแน่ใจว่า Scala ไม่ใช่ภาษาโปรแกรมภาษาเดียวที่ส่งกลับค่า false เมื่อเปรียบเทียบตัวเลขกับบูลีน
Rob Wilton

2
ตกลงเข้าใจแล้ว! ถ้าอย่างนั้นเรามาพูดกันในภาษาโปรแกรมส่วนใหญ่
เปาโล Rovelli

3
ใน JavaScript เหตุผล0 == falseส่งคืนtrueเนื่องจากตัวดำเนินการ double-equals พิมพ์ coercion อย่างไรก็ตามด้วยตัวดำเนินการสามเท่า0 === falseจะส่งคืนfalseเนื่องจาก 'number' และ 'boolean' เป็นประเภทที่แตกต่างกัน
jkdev

1
@PaoloRovelli ใน Lua, Ruby และ Clojure, 0 ถือว่าเป็นtrueเงื่อนไข ใน Rust and Go 0และfalse เป็นประเภทที่แตกต่างกันซึ่งไม่สามารถนำมาเปรียบเทียบเพื่อความเท่าเทียมกันได้
สกูตเตอร์ห้อย

0

Null เป็นวัตถุชนิดพิเศษเช่น:

>>>type(None)
<class 'NoneType'>

คุณสามารถตรวจสอบว่าวัตถุอยู่ในคลาส 'NoneType' หรือไม่:

>>>variable = None
>>>variable is None
True

ข้อมูลเพิ่มเติมสามารถดูได้ที่Python Docs


-1

ตามการทดสอบค่าความจริง 'ไม่มี' ทดสอบโดยตรงเป็น FALSE ดังนั้นนิพจน์ที่ง่ายที่สุดจะพอเพียง:

if not foo:

3
ไม่มันจะไม่ การแสดงออกที่จะกลับมาTrueสำหรับการใด ๆค่า falsy Noneไม่เพียง
insert_name_here

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