if A vs if A không phải là Không có:


154

Tôi có thể sử dụng:

if A:

thay vì

if A is not None:

Cái sau có vẻ dài dòng. Có sự khác biệt?

Câu trả lời:


149

Tuyên bố

if A:

sẽ gọi A.__nonzero__()(xem tài liệu tên phương thức đặc biệt ) và sử dụng giá trị trả về của hàm đó. Đây là tóm tắt:

object.__nonzero__(self)

Được gọi để thực hiện kiểm tra giá trị thật và hoạt động tích hợp bool(); nên trả về Falsehoặc True, hoặc số nguyên tương đương của chúng 0hoặc 1. Khi phương thức này không được xác định, __len__()được gọi, nếu nó được xác định và đối tượng được coi là đúng nếu kết quả của nó là khác không. Nếu một lớp định nghĩa không phải __len__()cũng không __nonzero__(), tất cả các thể hiện của nó được coi là đúng.

Mặt khác,

if A is not None:

chỉ so sánh các tham chiếu Avới Noneđể xem liệu nó có giống nhau hay không.


4
và như vậy A is not Nonelà nhanh hơn vì có rất ít việc phải làm
John La Rooy

40
@gnibbler Không theo thử nghiệm của tôi. if object(): passlà ~ 0,130 usec mỗi vòng, trong khi if object() is not None: passlà ~ 0,135 usec. Dù sao, bạn không nên sử dụng hiệu suất để lựa chọn giữa hai điều này, mà nên nhìn vào sự khác biệt trong cách chúng hoạt động, vì chúng không tương đương .
Lauritz V. Thaulow

1
@gnibbler if A is not Nonedường như chậm hơn vì đó là so sánh và cần tải singleton dựng sẵn Nonenhư một bước trung gian để so sánh với A(hãy xem dis.dis()). Sửa lỗi cho tôi nếu tôi sai nhưng if A:dường như hiệu quả hơn, ngay khi bạn thực sự muốn kiểm tra giá trị thật và không phải Nonedanh tính.
cedbeu

2
@cedbeu, Có vẻ như phụ thuộc vào giá trị của A. Tôi đã kiểm tra bây giờ python -m timeit -s"a=0" "if a: pass" "else: pass"nhanh hơn python -m timeit -s"a=0" "if a is None: pass" "else: pass"nhưng python -m timeit -s"a=1" "if a: pass" "else: pass"chậm hơn. Có thể phụ thuộc vào nền tảng, xem bạn có nhận được kết quả tương tự không
John La Rooy

2
@cedbeu, trong Python3 tất cả đều nhanh hơn nhiều, nhưng is Nonethử nghiệm thực sự là chậm nhất đối với tôi. Trong pypy tất cả đều đo chính xác như nhau :)
John La Rooy

51

Như được viết trong PEP8 :

  • Việc so sánh với những người độc thân như Không nên luôn luôn được thực hiện với 'là' hoặc 'không', không bao giờ là các toán tử đẳng thức .

    Ngoài ra, hãy cẩn thận khi viết "if x" khi bạn thực sự có nghĩa là "nếu x không phải là Không" - ví dụ: khi kiểm tra xem một biến hoặc đối số mặc định là Không được đặt thành một giá trị khác. Giá trị khác có thể có một loại (chẳng hạn như một thùng chứa) có thể sai trong bối cảnh boolean!


7
Mặc dù vậy, điều này không rõ ràng lắm và không phản hồi với OP. Tự động chuyển hướng đến PEP không phải lúc nào cũng là câu trả lời hay. Rất thường người ta không quan tâm đến bài kiểm tra danh tính với người độc thân None, mà chỉ kiểm tra giá trị thật. Trong trường hợp này, if A:có vẻ hiệu quả hơn (thực hiện dis.dis(), có thêm các bước tải nội dung Nonevà so sánh if A is not None:, trong khi chỉ có một jump_iftrong trường hợp khác).
cedbeu

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

vì vậy nó không giống như

if x is not None # which works only on None

17

Rất nhiều hàm trả về Không nếu không có kết quả phù hợp. Ví dụ: .first()phương thức của truy vấn SQLAlchemy trả về Không nếu không có hàng nào trong kết quả. Giả sử bạn đang chọn một giá trị có thể trả về 0 và cần biết liệu đó thực sự là 0 hay liệu truy vấn không có kết quả nào cả.

Một thành ngữ phổ biến là cung cấp cho đối số tùy chọn của hàm hoặc phương thức giá trị mặc định là Không, và sau đó kiểm tra giá trị đó là Không để xem nó có được chỉ định hay không. Ví dụ:

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

so sánh với:

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

Sau này, điều gì xảy ra nếu bạn gọi spam(0)hoặc spam([])? Hàm sẽ (không chính xác) phát hiện ra rằng bạn đã không chuyển vào một giá trị cho eggsvà sẽ tính toán một giá trị mặc định cho bạn. Đó có lẽ không phải là những gì bạn muốn.

Hoặc tưởng tượng một phương pháp như "trả về danh sách các giao dịch cho một tài khoản nhất định". Nếu tài khoản không tồn tại, nó có thể trả về Không có. Điều này khác với việc trả về một danh sách trống (có nghĩa là "tài khoản này tồn tại nhưng chưa được ghi lại giao dịch).

Cuối cùng, trở lại công cụ cơ sở dữ liệu. Có một sự khác biệt lớn giữa NULL và một chuỗi rỗng. Một chuỗi rỗng thường nói "có một giá trị ở đây và giá trị đó hoàn toàn không có gì". NULL nói "giá trị này chưa được nhập."

Trong mỗi trường hợp đó, bạn muốn sử dụng if A is None. Bạn đang kiểm tra một giá trị cụ thể - Không - không chỉ "bất kỳ giá trị nào xảy ra để chuyển thành Sai".


10

Họ làm những việc rất khác nhau .

Kiểm tra dưới đây nếu A có bất cứ điều gì ngoại trừ các giá trị False, [], None, ''0. Nó kiểm tra giá trị của A.

if A:

Dưới đây kiểm tra nếu A là một đối tượng khác với Không. Nó kiểm tra và so sánh tham chiếu (địa chỉ bộ nhớ) của A và Không có.

if A is not None:

CẬP NHẬT: Giải thích thêm

Nhiều lần cả hai dường như làm điều tương tự nên rất nhiều người sử dụng chúng thay thế cho nhau - đó là một ý tưởng thực sự tồi tệ. Lý do cả hai đưa ra cùng một kết quả là nhiều lần bởi sự trùng hợp thuần túy do tối ưu hóa trình thông dịch / trình biên dịch như thực tập hoặc một cái gì đó khác.

Với những tối ưu hóa đó, các số nguyên và chuỗi có cùng giá trị sẽ sử dụng cùng một không gian bộ nhớ. Điều đó có thể giải thích tại sao hai chuỗi riêng biệt hoạt động như thể chúng giống nhau.

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

Những thứ khác không hành xử giống nhau mặc dù ..

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

Hai danh sách rõ ràng có bộ nhớ riêng của họ. Đáng ngạc nhiên tuples hành xử như chuỗi.

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

Có lẽ điều này là do các bộ dữ liệu được đảm bảo không thay đổi, do đó, nó có ý nghĩa để sử dụng lại cùng một bộ nhớ.

Điểm mấu chốt của vấn đề này là bạn không thể dựa vào sự trùng hợp . Chỉ vì nó quẫy như một con vịt, nó không có nghĩa là nó là một con vịt. Sử dụng is==tùy thuộc vào những gì bạn thực sự muốn kiểm tra. Những điều này có thể khó gỡ lỗi vì isđọc như văn xuôi mà chúng ta thường chỉ đọc lướt qua nó.


Nonelà một singleton, nó không phải là một chi tiết triển khai (không giống như int hoặc chuỗi interning). Tôi không chắc là tôi làm theo ý bạn.
Lev Levitsky

@LevLevitsky Quan điểm của tôi không phải là Nonehành vi khác hơn inthoặc strdo thực tập. Quan điểm của tôi là is==đang kiểm tra những thứ khác nhau; đầu tiên kiểm tra một địa chỉ bộ nhớ, thứ hai kiểm tra nội dung của các địa chỉ bộ nhớ.
Pithikos

@LevLevitsky Tôi đã chỉnh sửa câu trả lời của mình ngay bây giờ để làm cho nó dễ tiêu hóa hơn một chút.
Pithikos

7

if A: sẽ chứng minh sai nếu A là 0, Sai, chuỗi trống, danh sách trống hoặc Không có, có thể dẫn đến kết quả không mong muốn.


1
Và nhiều giá trị khác nữa, chẳng hạn như danh sách trống, tập rỗng, trống rỗng tuple vv Về cơ bản, bất cứ điều gì đó không phải là truthy mỗi docs.python.org/3/library/stdtypes.html#truth-value-testing .
jarmod

6

Hầu hết các hướng dẫn tôi đã thấy đề xuất rằng bạn nên sử dụng

nếu một:

trừ khi bạn có một lý do để cụ thể hơn.

Có một số khác biệt nhỏ. Có các giá trị khác với Không có giá trị nào trả về Sai, ví dụ như danh sách trống hoặc 0, vì vậy hãy suy nghĩ về những gì bạn thực sự đang thử nghiệm.


5

Không có giá trị đặc biệt nào trong Python mà thường chỉ định một biến chưa được khởi tạo. Để kiểm tra xem A không có giá trị cụ thể này hay không, bạn sử dụng:

if A is not None

Giá trị Falsey là một lớp đối tượng đặc biệt trong Python (ví dụ: false, []). Để kiểm tra xem A có phải là falsey hay không:

if not A

Do đó, hai biểu thức không giống nhau Và tốt hơn hết bạn không nên coi chúng là từ đồng nghĩa.


PS Không cũng là falsey, vì vậy biểu thức đầu tiên ngụ ý thứ hai. Nhưng thứ hai bao gồm các giá trị falsey khác ngoài Không có. Bây giờ ... nếu bạn có thể chắc chắn rằng bạn không thể có các giá trị falsey khác ngoài Không có trong A, thì bạn có thể thay thế biểu thức đầu tiên bằng biểu thức thứ hai.


Không phải là bạn sai, nhưng câu trả lời này đã bao gồm điều này.
Makoto

boh, tôi muốn yêu cầu bạn loại bỏ downvote nếu bạn không phiền. Câu trả lời của tôi tương đương với tất cả những câu hỏi khác, có thể ít hơn với câu bạn đề cập, nhưng được trình bày từ một góc nhìn khác, và nó có thể dễ hiểu hơn đối với ai đó. hơn nữa, khi tôi thấy một downvote trên SO tôi giả sử một câu trả lời sai ... mà bạn thừa nhận không phải là trường hợp.
mircealungu

Vâng, giả định đó là không đầy đủ. Nó có thể sai hoặc đơn giản là không hữu ích. Vì câu trả lời của bạn như tôi đã nêu đã được bảo hiểm, tôi không tin rằng nó hữu ích.
Makoto

4

Nó phụ thuộc vào ngữ cảnh.

Tôi sử dụng if A:khi tôi mong đợi Alà một loại bộ sưu tập nào đó và tôi chỉ muốn thực thi khối nếu bộ sưu tập không trống. Điều này cho phép người gọi vượt qua bất kỳ bộ sưu tập nào hoạt động tốt, trống hay không và để nó thực hiện những gì tôi mong đợi. Nó cũng cho phép NoneFalsengăn chặn việc thực thi khối, đôi khi thuận tiện để gọi mã.

OTOH, nếu tôi dự kiến Alà một đối tượng hoàn toàn tùy ý nhưng nó có thể được mặc định None, thì tôi luôn sử dụng if A is not None, vì mã gọi có thể đã cố tình chuyển một tham chiếu đến một bộ sưu tập trống, chuỗi rỗng hoặc loại số có giá trị 0 hoặc boolean False, hoặc một số thể hiện lớp xảy ra là sai trong ngữ cảnh boolean.

Mặt khác, nếu tôi dự kiến Alà một số thứ cụ thể hơn (ví dụ: một lớp tôi sẽ gọi các phương thức của nó), nhưng nó có thể đã được mặc định Nonevà tôi coi chuyển đổi boolean mặc định là một thuộc tính của lớp tôi không ngại thực thi trên tất cả các lớp con, sau đó tôi sẽ chỉ sử dụng if A:để tiết kiệm cho ngón tay của mình gánh nặng khủng khiếp khi gõ thêm 12 ký tự.


3

Tôi đã tạo một tập tin được gọi test.pyvà chạy nó trên trình thông dịch. Bạn có thể thay đổi những gì bạn muốn, để kiểm tra chắc chắn mọi thứ đang diễn ra như thế nào đằng sau hậu trường.

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)

Đây là sự khác biệt của trình biên dịch:

Nguồn:

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

Cái trước là Pythonic hơn (mã ý thức hệ tốt hơn), nhưng sẽ không thực thi khối nếu A là Sai (không phải Không có).


7
-1. Như @k Meat đề cập, PEP8 nói sẽ sử dụng is/is not None. Theo sau PEP8 là Pythonic. Bên cạnh đó hai bài kiểm tra là khác nhau .
JC Bông

1

trăn> = 2,6,

nếu chúng ta viết như

if A:

sẽ tạo cảnh báo như,

FutureWarning: Hành vi của phương pháp này sẽ thay đổi trong các phiên bản trong tương lai. Thay vào đó, hãy sử dụng thử nghiệm 'len (elem)' hoặc 'elem không phải là Không' cụ thể.

Vì vậy, chúng ta có thể sử dụng

if A is not None:
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.