Tại sao lại là người mà nếu không phải là someobj: thì tốt hơn là nếu một sốobj == Không có:


127

Tôi đã thấy một số ví dụ về mã như thế này:

if not someobj:
    #do something

Nhưng tôi tự hỏi tại sao không làm:

if someobj == None:
    #do something

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


13
Nói chung 'someobj là Không' thích hợp hơn 'someobj == Không'
Aaron Maenpaa

Còn mã giả thì sao nếu X không có? Hay là X != None?
Charlie Parker

câu hỏi này dài dòng hơn những câu hỏi khác ... kudos ...
alamin

Câu trả lời:


187

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

  1. Nếu đối tượng có một __nonzero__phương thức đặc biệt (cũng như tích hợp số intfloat), thì nó gọi phương thức này. Nó phải trả về một boolgiá trị được sử dụng trực tiếp hoặc intgiá trị được xem xét Falsenếu bằng 0.

  2. Ngược lại, nếu các đối tượng có một __len__phương pháp đặc biệt (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 Falsenếu nó là rỗng (chiều dài là zero).

  3. Mặt khác, đối tượng được xem xét Truetrừ khi nó nằm Nonetrong trường hợp nào, nó được xem xét False.

Trong thử nghiệm thứ hai, đối tượng được so sánh cho bằng None. Ở đây, chúng tôi đang hỏi đối tượng, "Bạn có bằng giá trị khác này không?" Điều này được thực hiện bằng thuật toán sau:

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

  2. Ngược lại, nếu các đối tượng có một __cmp__phương pháp, nó được gọi. Hàm này phải trả về một intchỉ thị thứ tự của hai đối tượng ( -1if self < other, 0if self == other, +1if self > other).

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

Có một thử nghiệm khác có thể sử dụng istoán tử. Chúng tôi sẽ hỏi đối tượng, "Bạn có phải là đối tượng đặc biệt 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 là số, để sử dụng thử nghiệm cho đẳng thức khi bạn muốn so sánh các đối tượng có cùng bản chất (hai chuỗi, hai số, ...) và chỉ kiểm tra nhận dạng khi sử dụng các giá trị sentinel ( Nonenghĩa là không được khởi tạo cho trường thành viên để lấy mẫu hoặc khi sử dụng getattrcác __getitem__phương thức hoặc ).

Để tóm tắt, chúng tôi 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 các bộ dữ liệu, danh sách, dicts, strs, unicodes, ints, float, v.v ... có một số khác . Việc dựa vào giá trị thật của loại tích hợp phổ biến hơn nhiều so với dựa vào phương pháp khác không tùy chỉnh .
ddaa

50

Đây thực sự là cả hai thực hành nghèo. Ngày xửa ngày xưa, việc coi ngẫu nhiên Không và Sai là tương tự nhau. Tuy nhiên, vì Python 2.2 nên đây không phải là chính sách tốt nhất.

Đầu tiên, khi bạn thực hiện một if xhoặc một if not xloại thử nghiệm, Python phải hoàn toàn chuyển đổi xsang boolean. Các quy tắc cho boolchức năng mô tả một bè của những thứ là Sai; mọi thứ khác đều đúng. Nếu giá trị của x không phải là boolean chính xác để bắt đầu, thì chuyển đổi ngầm định này thực sự không phải là cách rõ ràng nhất để nói.

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

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

Xem PEP 8, Hướng dẫn kiểu cho mã Python .

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

Có bao nhiêu singletons? Năm: None, True, False, NotImplementedEllipsis. Vì bạn thực sự không thể sử dụng NotImplementedhoặc Ellipsis, và bạn sẽ không bao giờ nói if x is True(vì đơn giản if xlà rõ ràng hơn nhiều), bạn sẽ chỉ kiểm tra None.


3
Các hình thức thứ hai là thực tế không xấu. PEP 8 khuyên bạn nên sử dụng nếu x hai lần . Đầu tiên cho chuỗi (thay vì sử dụng len) và sau đó cho Đúng và Sai (thay vì sử dụng là). Thực tế tất cả các mã Python tôi đã thấy sử dụng nếu x và nếu không x.
Antti Rasinen

35

Bởi vì Nonekhông phải là điều duy nhất đượ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, (), [], {}""tất cả đề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, {},, Vv tất cả khác nhau từ mỗi khác, nhưng tất cả họ đều đánh giá là False.

Đây là "ma thuật" đằng sau các biểu thức ngắn mạch như:

foo = bar and spam or eggs

đó là viết tắt của:

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

Liên quan đến câu hỏi cuối cùng của bạn, chúng là tương đương.
Sylvain Defresne

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

4

PEP 8 - Phong cách Hướng dẫn cho Python Mã khuyến cáo để sử dụng hoặc không phải là 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 kiểm tra nhiều hơn Không có, bạn nên sử dụng toán tử boolean.


3

Nếu bạn hỏi

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

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

__nonzero__ ( tự ) Được gọi để thực hiện kiểm tra giá trị thật và hoạt động tích hợp bool (); sẽ trả về Sai hoặc Đúng hoặc số nguyên tương đương của chúng 0 hoặ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 (xem bên dưới). Nếu một lớp định nghĩa không __len __ () hay __nonzero __ (), thì tất cả các thể hiện của nó được coi là đúng.

Nếu bạn hỏi

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

các __eq__ phương pháp 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 chỉnh, hãy xem thông tin về Python tại https://docs.python.org/reference/datamodel.html#basic-customization


2

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


1
Để hoàn toàn chính xác, lần thứ hai kiểm tra tính tương đương - với giá trị Không có ... "someobj là Không" kiểm tra danh tính.
Matthew Trevor

0

Đối với một ví dụ đầu tiên là 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 sự so sánh.


0

Câu trả lơi con phụ thuộc vao nhiêu thư".

Tôi sử dụng ví dụ đầu tiên nếu tôi coi 0, "", [] và Sai (danh sách không đầy đủ) tương đương với Không có trong ngữ cảnh này.


0

Cá nhân, tôi đã chọn một cách tiếp cận nhất quán giữa các ngôn ngữ: Tôi chỉ if (var)(hoặc tương đương) nếu 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ó một loại cụ thể). Tôi thậm chí còn thêm tiền tố vào các biến này bằng một b(vì vậy nó sẽ bVarthực sự) để chắc chắn rằng tôi sẽ không vô tình sử dụng một loại khác ở đây.
Tôi thực sự không thích truyền hình ngầm cho boolean, thậm chí ít hơn khi có 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 trong công việc của tôi (quá dư thừa theo sở thích của tôi!), Những người khác yêu thích quá nhiều cú pháp nhỏ gọn, đi while (line = getNextLine())(quá mơ hồ đối với tôi).

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.