2008-09-19 17 views
98

Tôi đã nhìn thấy một vài ví dụ về các mã như thế này:Tại sao "if not someobj:" tốt hơn "if someobj == None:" bằng Python?

if not someobj: 
    #do something 

Nhưng tôi tự hỏi tại sao không thực hiện:

if someobj == None: 
    #do something 

Có sự khác biệt? Có ai có lợi thế hơn người kia không?

+13

chung 'someobj là Không' là thích hợp hơn để 'someobj == Không' –

+0

gì về giả nếu X không phải là Không? Hoặc 'X! = None'? –

+0

câu hỏi này chi tiết hơn so với những người khác ... kudo ... – alamin

Trả lời

157

Trong thử nghiệm đầu tiên, Python cố gắng chuyển đổi đối tượng thành giá trị bool nếu nó chưa phải là một. Khoảng, chúng ta đang hỏi đối tượng: bạn có ý nghĩa hay không? này được thực hiện bằng cách sử dụng thuật toán sau đây:

  1. Nếu đối tượng có một phương pháp đặc biệt __nonzero__ (cũng như số built-in, intfloat), nó gọi phương pháp này. Giá trị này phải trả về giá trị bool sau đó được sử dụng trực tiếp hoặc giá trị int được coi là False nếu bằng không.

  2. Ngược lại, nếu đối tượng có một phương pháp đặc biệt __len__ (như làm thùng chứa được xây dựng-in, list, dict, set, tuple ...), nó gọi phương pháp này, xem xét một container False nếu nó là rỗng (chiều dài bằng không).

  3. Nếu không, đối tượng được coi là True trừ khi nó là None trong trường hợp này, nó được coi là False.

Trong thử nghiệm thứ hai, đối tượng được so sánh để bình đẳng với None. Ở đây, chúng tôi đang yêu cầu đối tượng, "Bạn có bằng với giá trị khác này không?" này được thực hiện bằng cách sử dụng thuật toán sau đây:

  1. Nếu đối tượng có một phương pháp __eq__, nó được gọi, và giá trị trả về sau đó được chuyển thành một giá trị bool và dùng để xác định kết quả của if.

  2. Nếu không, nếu đối tượng có phương thức __cmp__, nó sẽ được gọi. Hàm này phải trả lại một số int cho biết thứ tự của hai đối tượng (-1 nếu self < other, 0 nếu self == other, +1 nếu self > other).

  3. Nếu không, đối tượng sẽ được so sánh với danh tính (nghĩa là chúng được tham chiếu đến cùng một đối tượng, có thể được kiểm tra bởi toán tử is).

Có thể thực hiện kiểm tra khác bằng toán tử is. Chúng tôi sẽ hỏi đối tượng, "Bạn có phải là đối tượng cụ thể này không?"

Nói chung, tôi khuyên bạn nên sử dụng thử nghiệm đầu tiên với các giá trị không phải số, để sử dụng phép thử cho sự bình đẳng khi bạn muốn so sánh các đối tượng có cùng tính chất (hai chuỗi, hai số, ...) và chỉ kiểm tra danh tính khi sử dụng giá trị sentinel (None có nghĩa là không được khởi tạo cho trường thành viên cho ví dụ hoặc khi sử dụng phương thức getattr hoặc __getitem__).

Nói tóm lại, chúng ta có:

>>> class A(object): 
... def __repr__(self): 
...  return 'A()' 
... def __nonzero__(self): 
...  return False 

>>> class B(object): 
... def __repr__(self): 
...  return 'B()' 
... def __len__(self): 
...  return 0 

>>> class C(object): 
... def __repr__(self): 
...  return 'C()' 
... def __cmp__(self, other): 
...  return 0 

>>> class D(object): 
... def __repr__(self): 
...  return 'D()' 
... def __eq__(self, other): 
...  return True 

>>> for obj in ['',(), [], {}, 0, 0., A(), B(), C(), D(), None]: 
...  print '%4s: bool(obj) -> %5s, obj == None -> %5s, obj is None -> %5s' % \ 
...   (repr(obj), bool(obj), obj == None, obj is None) 
    '': bool(obj) -> False, obj == None -> False, obj is None -> False 
(): bool(obj) -> False, obj == None -> False, obj is None -> False 
    []: bool(obj) -> False, obj == None -> False, obj is None -> False 
    {}: bool(obj) -> False, obj == None -> False, obj is None -> False 
    0: bool(obj) -> False, obj == None -> False, obj is None -> False 
0.0: bool(obj) -> False, obj == None -> False, obj is None -> False 
A(): bool(obj) -> False, obj == None -> False, obj is None -> False 
B(): bool(obj) -> False, obj == None -> False, obj is None -> False 
C(): bool(obj) -> True, obj == None -> True, obj is None -> False 
D(): bool(obj) -> True, obj == None -> True, obj is None -> False 
None: bool(obj) -> False, obj == None -> True, obj is None -> True 
+4

Mặc dù về mặt kỹ thuật chính xác, điều này không giải thích rằng bộ dữ liệu, danh sách, dicts, strs, unicodes, ints, float, vv có __nonzero__. Nó phổ biến hơn nhiều để dựa vào giá trị chân lý của kiểu dựng sẵn hơn là dựa vào phương thức __nonzero__ tùy chỉnh. – ddaa

32

None không phải là điều duy nhất mà được coi là sai.

if not False: 
    print "False is false." 
if not 0: 
    print "0 is false." 
if not []: 
    print "An empty list is false." 
if not(): 
    print "An empty tuple is false." 
if not {}: 
    print "An empty dict is false." 
if not "": 
    print "An empty string is false." 

False, 0, (), [], {}"" đều khác nhau từ None, vì vậy hai đoạn mã của bạn là không tương đương.

Hơn nữa, hãy xem xét những điều sau đây:

>>> False == 0 
True 
>>> False ==() 
False 

if object:không một kiểm tra bình đẳng. 0, (), [], None, {}, v.v. tất cả khác nhau, nhưng tất cả đều là đánh giá thành Sai.

Đây là "kỳ diệu" đằng sau biểu ngắn mạch như:

foo = bar and spam or eggs 

mà là viết tắt cho:

if bar: 
    foo = spam 
else: 
    foo = eggs 

mặc dù bạn thực sự nên viết:

foo = spam if bar else egg 
+0

Về câu hỏi cuối cùng của bạn, chúng tương đương nhau. –

+0

Và cả hai không chính xác vì "" là Sai. Người thứ hai nên đọc '("",) hoặc ("s",)'. Dù sao, phiên bản hiện đại của python có một nhà điều hành ternary thích hợp. Điều này dễ bị lỗi hack nên bị trục xuất. – ddaa

+0

Cảm ơn. Đã xóa phần không chính xác. – badp

2

Hai so sánh phục vụ các mục đích khác nhau. Các kiểm tra trước đây cho giá trị boolean của một cái gì đó, kiểm tra thứ hai cho danh tính với giá trị None.

+1

Để hoàn toàn chính xác, kiểm tra thứ hai cho sự bất bình đẳng- với một giá trị None ... "someobj is None" kiểm tra danh tính. –

0

Đối với một ví dụ đầu tiên ngắn hơn và trông đẹp hơn. Theo các bài viết khác những gì bạn chọn cũng phụ thuộc vào những gì bạn thực sự muốn làm với so sánh.

3

PEP 8 -- Style Guide for Python Code khuyến cáo sử dụng hoặc không nếu bạn đang thử nghiệm cho Không-Ness

- Comparisons to singletons like None should always be done with 
    'is' or 'is not', never the equality operators. 

Mặt khác nếu bạn đang thử nghiệm cho hơn Không có-Ness, bạn nên sử dụng toán tử boolean.

0

Câu trả lời là "nó phụ thuộc".

Tôi sử dụng ví dụ đầu tiên nếu tôi xem 0, "", [] và False (liệt kê không đầy đủ) tương đương với Không trong ngữ cảnh này.

0

Cá nhân, tôi đã chọn cách tiếp cận nhất quán giữa các ngôn ngữ: Tôi làm if (var) (hoặc tương đương) chỉ khi var được khai báo là boolean (hoặc được định nghĩa như vậy, trong C chúng tôi không có loại cụ thể).Tôi thậm chí tiền tố các biến này với một b (vì vậy nó sẽ là bVar thực sự) để chắc chắn tôi sẽ không vô tình sử dụng loại khác ở đây.
Tôi không thực sự thích đúc ngầm để boolean, thậm chí ít hơn khi có rất nhiều, quy tắc phức tạp.

Tất nhiên, mọi người sẽ không đồng ý. Một số đi xa hơn, tôi thấy if (bVar == true) trong mã Java tại nơi làm việc của tôi (quá dư thừa cho khẩu vị của tôi!), Những người khác yêu cú pháp quá nhỏ gọn, đi while (line = getNextLine()) (quá mơ hồ đối với tôi).

42

Đây thực sự là cả thực tiễn kém. Đã có một thời gian, nó được coi là OK để tình cờ đối xử với None và False là tương tự. Tuy nhiên, vì Python 2.2 đây không phải là chính sách tốt nhất.

Trước tiên, khi bạn thực hiện thử nghiệm if x hoặc if not x, Python phải chuyển đổi hoàn toàn x thành boolean. Các quy tắc cho hàm bool mô tả một loạt các thứ sai; mọi thứ khác là True. Nếu giá trị của x không đúng là boolean để bắt đầu, thì chuyển đổi ngầm này không thực sự là cách rõ ràng nhất để nói mọi thứ.

Trước Python 2.2, không có hàm bool, vì vậy nó thậm chí còn ít rõ ràng hơn.

Thứ hai, bạn không nên thực sự kiểm tra với == None. Bạn nên sử dụng is Noneis not None.

Xem PEP 8, Style Guide for Python Code.

- Comparisons to singletons like None should always be done with 
    'is' or 'is not', never the equality operators. 

    Also, beware of writing "if x" when you really mean "if x is not None" 
    -- e.g. when testing whether a variable or argument that defaults to 
    None was set to some other value. The other value might have a type 
    (such as a container) that could be false in a boolean context! 

bao nhiêu độc thân nào nữa? Năm: None, True, False, NotImplementedEllipsis. Vì bạn thực sự không thể sử dụng NotImplemented hoặc Ellipsis và bạn sẽ không bao giờ nói if x is True (vì chỉ đơn giản là if x rõ ràng hơn rất nhiều), bạn sẽ chỉ bao giờ thử nghiệm None.

+3

Dạng thứ hai không phải là thực hành không tốt. PEP 8 khuyến nghị sử dụng nếu x _twice_. Đầu tiên cho chuỗi (thay vì sử dụng len) và sau đó cho True và False (thay vì sử dụng là). Thực tế tất cả các mã Python tôi đã nhìn thấy sử dụng nếu x và nếu không x. –

3

Nếu bạn hỏi

if not spam: 
    print "Sorry. No SPAM." 

phương pháp __nonzero__ của thư rác được gọi. Từ hướng dẫn Python:

__nonzero__ (tự) gọi để thực hiện kiểm tra giá trị thật, và được xây dựng trong bool hoạt động(); nên trả về False hoặc True, hoặc số nguyên tương đương 0 hoặc 1. Khi phương thức này không được định nghĩa, __len __() được gọi, nếu nó được định nghĩa (xem bên dưới). Nếu một lớp định nghĩa không __len __() và __nonzero __(), tất cả các cá thể của nó được coi là đúng.

Nếu bạn hỏi

if spam == None: 
    print "Sorry. No SPAM here either." 

phương pháp __eq__ của thư rác được gọi với đối số Không.

Để biết thêm thông tin về các khả năng tùy biến có một cái nhìn tại documenation Python tại https://docs.python.org/reference/datamodel.html#basic-customization

Các vấn đề liên quan