đối tượng null trong Python?


Câu trả lời:


1629

Trong Python, đối tượng 'null' là singleton None.

Cách tốt nhất để kiểm tra mọi thứ cho "Tính không đồng nhất" là sử dụng toán tử nhận dạng , is:

if foo is None:
    ...

125
Và lý do cho việc lựa chọn egg is Nonehơn egg == None: Sau này có thể bị quá tải, và có khả năng phá vỡ khi so sánh đối tượng có giá trị với None (phụ thuộc vào cách nó được thực hiện, nhưng bạn không mong đợi tất cả mọi người để có comparisions với Không vào tài khoản, phải không?) , trong khi isluôn hoạt động như nhau.

94
Tại sao các nhà thiết kế python không chọn "null". Chỉ cần khác biệt thôi bạn nhé! ;)
Vidar

35
@Vidar 'Không' không phải là thiếu một đối tượng (vì 'null' là một tham chiếu null), đó là một đối tượng thực tế. Bạn sẽ không bao giờ nhận được một đối tượng 'Không' đi qua cho một đối tượng thuộc loại bất kỳ thứ gì khác ngoài 'Không loại'. Nó cũng không phải là một khái niệm ngôn ngữ .. Nó không được tích hợp trong ngôn ngữ Python, nó là một phần của thư viện chuẩn của Python. Không có! == (ahem) Null
Rushyo

11
@ naught101 Nó sẽ không phục vụ mục đích; như không có khái niệm về con trỏ Nếu bạn đang sử dụng thư viện ctypes, Không ai sẽ được sử dụng làm từ đồng nghĩa cho địa chỉ 0 nhưng vẫn không có khái niệm ngôn ngữ nào về 'tham chiếu null'. Trong Python, bạn luôn để lại tham chiếu một số loại đối tượng, ngay cả khi đối tượng đó là Không có. Tôi nghi ngờ điều này rất đơn giản hóa ngôn ngữ.
Rushyo

5
bài thuyết trình tuyệt vời giải thích về 'sai lầm tỷ đô', đó là tài liệu tham khảo null: infoq.com/presentations/ . Các tùy chọn khác thay vì null là các loại Tùy chọn hoặc Có thể (tùy chỉnh).
Michael Trouw

134

None, Python là null?

Không có nullPython, thay vào đó là có None. Như đã nêu, cách chính xác nhất để kiểm tra rằng một cái gì đó đã được đưa ra Nonenhư một giá trị là sử dụng istoán tử nhận dạng, để kiểm tra hai biến tham chiếu đến cùng một đối tượng.

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

Những thứ cơ bản

Có và chỉ có thể là một None

Nonelà trường hợp duy nhất của lớp NoneTypevà bất kỳ nỗ lực nào nữa trong việc khởi tạo lớp đó sẽ trả về cùng một đối tượng, điều này tạo ra Nonemột singleton. Những người mới sử dụng Python thường thấy các thông báo lỗi đề cập NoneTypevà tự hỏi nó là gì. Theo ý kiến ​​cá nhân của tôi, những tin nhắn này chỉ có thể được đề cập Nonebằng tên bởi vì, như chúng ta sẽ thấy trong thời gian ngắn, Noneđể lại một khoảng trống nhỏ. Vì vậy, nếu bạn thấy một số TypeErrorthông báo đề cập rằng NoneTypekhông thể làm điều này hoặc không thể làm điều đó, chỉ cần biết rằng đó đơn giản là một trong những thông điệp Noneđược sử dụng theo cách mà nó không thể.

Ngoài ra, Nonelà một hằng số tích hợp, ngay khi bạn khởi động Python, nó có sẵn để sử dụng từ mọi nơi, cho dù trong mô-đun, lớp hoặc chức năng. NoneTypengược lại là không, trước tiên bạn cần có một tham chiếu đến nó bằng cách truy vấn Nonelớp của nó.

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

Bạn có thể kiểm tra Nonetính duy nhất với chức năng nhận dạng của Python id(). Nó trả về số duy nhất được gán cho một đối tượng, mỗi đối tượng có một. Nếu id của hai biến là như nhau, thì thực tế chúng trỏ đến cùng một đối tượng.

>>> 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 không thể ghi đè

Trong phiên bản cũ hơn của Python (trước 2.4), có thể gán lại None, nhưng không còn nữa. Thậm chí không phải là một thuộc tính lớp hoặc trong giới hạn của hàm.

# 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

Do đó, an toàn khi cho rằng tất cả các Nonetài liệu tham khảo đều giống nhau. Không có "tùy chỉnh" None.

Để kiểm tra Nonesử dụng istoán tử

Khi viết mã, bạn có thể muốn thử nghiệm tính Không giống như thế này:

if value==None:
    pass

Hoặc để kiểm tra sự giả dối như thế này

if not value:
    pass

Bạn cần hiểu ý nghĩa và lý do tại sao nó thường là một ý tưởng tốt để được rõ ràng.

Trường hợp 1: kiểm tra nếu một giá trị là None

tại sao làm điều này

value is None

thay vì

value==None

Đầu tiên là tương đương với:

id(value)==id(None)

Trong khi đó biểu thức value==Nonetrên thực tế được áp dụng như thế này

value.__eq__(None)

Nếu giá trị thực sự là Nonethì bạn sẽ nhận được những gì bạn mong đợi.

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

Trong hầu hết các trường hợp phổ biến, kết quả sẽ giống nhau, nhưng __eq__()phương thức mở ra một cánh cửa không có bất kỳ sự đảm bảo nào về tính chính xác, vì nó có thể được ghi đè trong một lớp để cung cấp hành vi đặc biệt.

Hãy xem xét lớp học này.

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

Vì vậy, bạn hãy thử nó Nonevà nó hoạt động

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

Nhưng sau đó nó cũng hoạt động trên chuỗi trống

>>> empty==''
True

Chưa hết

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

Trường hợp 2: Sử dụng Nonenhư một boolean

Hai bài kiểm tra sau

if value:
    # do something

if not value:
    # do something

trong thực tế được đánh giá là

if bool(value):
    # do something

if not bool(value):
    # do something

Nonelà một "falsey", có nghĩa là nếu được đúc thành boolean, nó sẽ quay trở lại Falsevà nếu được áp dụng nottoán tử, nó sẽ trả về True. Lưu ý tuy nhiên đó không phải là một tài sản duy nhất None. Ngoài ra False, thuộc tính được chia sẻ bởi các danh sách trống, bộ dữ liệu, bộ, ký tự, chuỗi, cũng như 0 và tất cả các đối tượng từ các lớp thực hiện __bool__()phương thức ma thuật để trả về 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

Vì vậy, khi kiểm tra các biến theo cách sau, hãy lưu ý thêm về những gì bạn bao gồm hoặc loại trừ khỏi kiểm tra:

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

Ở trên, bạn có nghĩa là gọi init_value()khi giá trị được đặt cụ thể thành None, hoặc bạn có nghĩa là một giá trị được đặt thành 0, hoặc chuỗi trống hoặc danh sách trống cũng sẽ kích hoạt khởi tạo. Như tôi đã nói, hãy chú ý. Vì nó thường là trường hợp trong Python rõ ràng là tốt hơn ngầm định .

None trong thực tế

None được sử dụng làm giá trị tín hiệu

Nonecó một trạng thái đặc biệt trong Python. Đó là một giá trị cơ bản yêu thích vì nhiều thuật toán coi nó là một giá trị đặc biệt. Trong các trường hợp như vậy, nó có thể được sử dụng làm cờ để báo hiệu rằng một điều kiện yêu cầu một số xử lý đặc biệt (chẳng hạn như cài đặt giá trị mặc định).

Bạn có thể gán Nonecho các đối số từ khóa của hàm và sau đó kiểm tra rõ ràng cho nó.

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

Bạn có thể trả nó về mặc định khi cố gắng truy cập thuộc tính của đối tượng và sau đó kiểm tra rõ ràng trước khi thực hiện điều gì đó đặc biệt.

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

Theo mặc định, get()phương thức của từ điển trả về Nonekhi cố gắng truy cập khóa không tồn tại:

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

Nếu bạn cố gắng truy cập nó bằng cách sử dụng ký hiệu đăng ký KeyErrorthì sẽ được nâng lên

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

Tương tự như vậy nếu bạn cố gắng bật một mục không tồn tại

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

mà bạn có thể chặn với giá trị mặc định thường được đặt thành None

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

None được sử dụng làm cờ và giá trị hợp lệ

Các mô tả sử dụng ở trên được Noneáp dụng khi nó không được coi là một giá trị hợp lệ, nhưng giống như một tín hiệu để làm điều gì đó đặc biệt. Tuy nhiên, có những tình huống đôi khi rất quan trọng để biết Nonenguồn gốc từ đâu vì mặc dù nó được sử dụng như một tín hiệu, nó cũng có thể là một phần của dữ liệu.

Khi bạn truy vấn một đối tượng cho thuộc tính của nó với getattr(some_obj, 'attribute_name', None)việc quay lại Nonesẽ không cho bạn biết nếu thuộc tính bạn đang cố truy cập được đặt thành Nonehoặc nếu nó hoàn toàn vắng mặt đối tượng. Tình trạng tương tự khi truy cập một khóa từ một từ điển như some_dict.get('some_key'), bạn không biết nếu some_dict['some_key']bị thiếu hoặc nếu nó chỉ được đặt thành None. Nếu bạn cần thông tin đó, cách thông thường để xử lý việc này là trực tiếp thử truy cập thuộc tính hoặc khóa từ bên trong try/exceptcấu trúc:

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)

Tương tự với 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)

Hai ví dụ trên cho thấy cách xử lý các trường hợp đối tượng và từ điển, còn các chức năng thì sao? Điều tương tự, nhưng chúng tôi sử dụng đối số từ khóa dấu sao đôi cho đến cuối:

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 chỉ được sử dụng như một giá trị hợp lệ

Nếu bạn thấy rằng mã của bạn được đặt với try/exceptmẫu ở trên chỉ đơn giản là để phân biệt giữa Nonecờ và Nonedữ liệu, thì chỉ cần sử dụng một giá trị thử nghiệm khác. Có một mẫu trong đó một giá trị nằm ngoài tập hợp các giá trị hợp lệ được chèn vào như một phần của dữ liệu trong cấu trúc dữ liệu và được sử dụng để kiểm soát và kiểm tra các điều kiện đặc biệt (ví dụ: ranh giới, trạng thái, v.v.). Một giá trị như vậy được gọi là sentinel và nó có thể được sử dụng theo cách Noneđược sử dụng làm tín hiệu. Việc tạo một sentinel trong Python là chuyện nhỏ.

undefined = object()

Đối undefinedtượng ở trên là duy nhất và không làm bất cứ điều gì có thể khiến chương trình quan tâm, do đó, đây là một sự thay thế tuyệt vời cho Nonemột lá cờ. Một số hãy cẩn thận áp dụng, thêm về điều đó sau khi mã.

Với chức năng

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

Với chính tả

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

Với một đối tượng

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

Như tôi đã đề cập, các tùy chỉnh trước đó đi kèm với một số cảnh báo. Đầu tiên, chúng không phải là từ khóa như thế None, nên python không bảo vệ chúng. Bạn có thể ghi đè lên phần undefinedtrên của bạn bất cứ lúc nào, bất cứ nơi nào trong mô-đun được xác định, vì vậy hãy cẩn thận với cách bạn phơi bày và sử dụng chúng. Tiếp theo, thể hiện được trả về bởi object()không phải là một singleton, nếu bạn thực hiện cuộc gọi đó 10 lần, bạn sẽ nhận được 10 đối tượng khác nhau. Cuối cùng, việc sử dụng một sentinel rất bình dị. Một sentinel dành riêng cho thư viện mà nó được sử dụng và vì vậy phạm vi của nó thường được giới hạn trong các phần bên trong của thư viện. Nó không nên "rò rỉ" ra ngoài. Mã bên ngoài chỉ nên biết về nó, nếu mục đích của chúng là mở rộng hoặc bổ sung API của thư viện.


Thế còn: Không == điều?
hkBst

@hkBst Tôi đoán câu hỏi của bạn là về cách python đánh giá sự bình đẳng. Hãy xem stackoverflow.com/questions3538776/
diệt

À, vậy IIUC mà thường vẫn sẽ kết thúc cuộc gọi .__ eq__? Trong trường hợp đó tôi rút lại đề nghị của tôi.
hkBst

@MichaelEkoka bạn có thể chia sẻ nguồn cho thông tin ấn tượng và hữu ích này.
Anidhya Bhatnagar

Tiêu chuẩn thực hành. Bạn có thể tìm thấy một số gợi ý về nó thông qua hướng dẫn Python , nhưng đọc mã nguồn của các dự án phổ biến là lời khuyên số một của tôi để nhanh chóng đắm mình vào Python thành ngữ .
Michael Ekoka

67

Nó không được gọi là null như trong các ngôn ngữ khác, nhưng None. Luôn luôn chỉ có một phiên bản của đối tượng này, vì vậy bạn có thể kiểm tra sự tương đương với x is None(so sánh danh tính) thay vì x == None, nếu bạn muốn.


25
Tôi sẽ tìm cách phá vỡ Python bằng cách khởi tạo lại None. Sau đó, tất cả những người thông minh so sánh NoneTypesẽ chiến thắng!
Zenexer

28

Trong Python, để biểu thị sự vắng mặt của một giá trị, bạn có thể sử dụng giá trị Không có ( loại.NoneType.None ) cho các đối tượng và "" (hoặc len () == 0 ) cho chuỗi. Vì thế:

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

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

Về sự khác biệt giữa "==" và "là", kiểm tra nhận dạng đối tượng bằng cách sử dụng "==" là đủ. Tuy nhiên, vì hoạt động "is" được định nghĩa là hoạt động nhận dạng đối tượng, nên có thể sử dụng nó đúng hơn là "==". Không chắc chắn nếu có sự khác biệt về tốc độ.

Dù sao, bạn có thể có một cái nhìn tại:


8
Theo như tôi biết, (0 == false) trả về true TRONG MỌI ngôn ngữ lập trình. Đó là bởi vì false được mã hóa thành 0 và "true" vì mọi thứ KHÔNG phải là 0. Tuy nhiên, xin vui lòng không phải là toán tử "is" không giống với toán tử "==". Điều này ít nhiều giống nhau giữa "==" và "bằng" trong Java. Toán tử "là", thực sự, kiểm tra xem HAI ĐỐI TƯỢNG có giống nhau không (cùng thể hiện và KHÔNG cùng nội dung)!
Paolo Rovelli

12
Paolo, FYI, (0 == false) không trả về true trong mọi ngôn ngữ lập trình. Một ví dụ truy cập nhanh sẽ là Scala, trong đó (0 == false) trả về false và tôi chắc chắn rằng Scala không phải là ngôn ngữ lập trình duy nhất trả về false khi so sánh một số với boolean.
Rob Wilton

2
OK đã nhận nó! Sau đó, hãy nói trong hầu hết các ngôn ngữ lập trình.
Paolo Rovelli

3
Trong JavaScript, lý do 0 == falsetrả về truelà vì toán tử hai số bằng loại cưỡng chế. Tuy nhiên, với toán tử ba số bằng, 0 === falsetrả về false, vì 'số' và 'boolean' là các loại khác nhau.
jkdev

1
@PaoloRovelli, ở Lua, Ruby và Clojure, 0 được coi là truetrong điều kiện. Trong Rust và Go, 0false là các loại khác nhau không thể so sánh cho sự bình đẳng.
xe tay ga-dangle

0

Null là một loại đối tượng đặc biệt như:

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

Bạn có thể kiểm tra xem một đối tượng có thuộc lớp 'noneType' không:

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

Thêm thông tin có sẵn tại Python Docs


-1

Mỗi thử nghiệm giá trị thật , 'Không' kiểm tra trực tiếp như FALSE, vì vậy biểu thức đơn giản nhất là đủ:

if not foo:

3
Không, nó sẽ không. Biểu thức đó sẽ trả về Truecho bất kỳ giá trị giả nào, không chỉ None.
insert_name_here

Đúng và đơn giản "nếu không foo:" sẽ không trả lời đúng câu hỏi ban đầu, như đã nêu. Tuy nhiên, python được gõ động và mời để giảm các giao thức cú pháp, do đó, nó có vẻ hợp lệ đối với tôi, để xem xét các cấu trúc tương tự, đơn giản hơn.
artejera
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.