Python'da "if someobj:" neden "if someobj == None:" seçeneğinden daha iyidir?


127

Bunun gibi birkaç kod örneği gördüm:

if not someobj:
    #do something

Ama neden yapmadığımı merak ediyorum:

if someobj == None:
    #do something

Herhangi bir fark var mı? Birinin diğerine göre avantajı var mı?


13
Genellikle 'someobj is None' yerine 'someobj == None' tercih edilir
Aaron Maenpaa

X None değilse sözde kod ne olacak? Veya X != None?
Charlie Parker

bu soru diğerlerinden daha ayrıntılı ... tebrikler ...
alamin

Yanıtlar:


187

İlk testte Python, nesneyi boolzaten bir değere dönüştürmeyi dener . Kabaca, nesneye soruyoruz: anlamlı mısınız, değil misiniz? Bu, aşağıdaki algoritma kullanılarak yapılır:

  1. Nesnenin __nonzero__özel bir yöntemi varsa (sayısal yerleşikler intve gibi float), bu yöntemi çağırır. Ya booldaha sonra doğrudan kullanılan bir intdeğer ya da Falsesıfıra eşit olduğu düşünülen bir değer döndürmelidir .

  2. Nesne varsa Aksi takdirde, __len__özel bir yöntem (aynı do konteyner gömmeler, list, dict, set, tuple, ...), bir konteyneri düşünüldüğünde bu yöntemi çağırırFalse o (uzunluk sıfır) boşsa.

  3. Aksi takdirde nesne, bu durumda Trueolmadığı sürece Nonedeğerlendirmeye alınır False.

İkinci testte, nesne eşitlik açısından karşılaştırılır None. Burada, "Bu diğer değere eşit misiniz?" Bu, aşağıdaki algoritma kullanılarak yapılır:

  1. Nesnenin bir __eq__yöntemi varsa, çağrılır ve dönüş değeri daha sonra bir booldeğere dönüştürülür ve sonucun belirlenmesi için kullanılır.if .

  2. Aksi takdirde, nesnenin bir __cmp__yöntemi varsa, çağrılır. Bu işlev int, iki nesnenin sırasını gösteren bir döndürmelidir ( -1eğer self < other, 0eğer self == other, +1eğerself > other ).

  3. Aksi takdirde, nesne kimlik açısından karşılaştırılır (yani, isoperatör tarafından test edilebilen aynı nesneye referans olurlar ).

isOperatör kullanılarak olası başka bir test var . "Bu özel nesne misiniz?"

Genel olarak, ilk testi sayısal olmayan değerlerle kullanmanızı, aynı nitelikteki nesneleri (iki dizi, iki sayı, ...) karşılaştırmak istediğinizde eşitlik testini kullanmanızı ve yalnızca aşağıdaki durumlarda kimliği kontrol etmenizi öneririm. Sentinel değerleri kullanarak (örneğin None, bir üye alanı için başlatılmamış anlamına gelir veyagetattr veya __getitem__yöntemleri ).

Özetlemek gerekirse, elimizde:

>>> 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
Teknik olarak doğru olmasına rağmen, bu, tuples, list, dicts, strs, unicodes, ints, floats, vb. Nin sıfırdan farklı olduğunu açıklamaz . Yerleşik türün doğruluk değerine güvenmek, sıfırdan farklı bir özel yönteme güvenmekten çok daha yaygındır .
ddaa

50

Bunların ikisi de aslında kötü uygulamalardır. Bir zamanlar, Yok ve Yanlış'ı benzer olarak rastgele ele almanın uygun olduğu düşünülüyordu. Ancak, Python 2.2'den beri bu en iyi politika değildir.

İlk olarak, bir if xveya bir if not xçeşit test yaptığınızda, Python örtük xolarak boolean'a dönüştürmelidir . boolİşlevin kuralları , Yanlış olan bir yığın şeyi açıklar; diğer her şey True. X'in değeri başlangıçta doğru bir şekilde boole değilse, bu örtük dönüştürme gerçekten bir şeyleri söylemenin en net yolu değildir.

Python 2.2'den önce bool işlevi yoktu, bu yüzden daha da açık değildi.

İkincisi, gerçekten test etmemelisiniz == None. Sen kullanmalıdır is Noneve is not None.

Bkz. PEP 8, Python Kodu için Stil Kılavuzu .

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

Kaç tane singleton var? Beş: None, True, False, NotImplementedve Ellipsis. Gerçekten olası olduğuna göre kullanmak NotImplementedveya Ellipsisve demezdim if x is True(çünkü if xçok nettir), sen olacak sadece hiç bir test None.


3
İkinci biçim, kategorik olarak kötü bir uygulama değildir. PEP 8, if x'in iki kez kullanılmasını önerir . Önce diziler için (len kullanmak yerine) ve sonra Doğru ve Yanlış için (is kullanmak yerine). Gördüğüm neredeyse tüm Python kodu x ve değilse x kullanıyor.
Antti Rasinen

35

Çünkü Noneyanlış kabul edilen tek şey değil.

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, (), [], {}Ve ""bu bakımdan farklılar Noneda iki kod parçacıkları yani, değil eşdeğeri.

Ayrıca şunları da göz önünde bulundurun:

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

if object:olduğu değil bir eşitlik kontrolü. 0, (), [], None, {}, Vb vardır birbirinden farklı, ama hepsi değerlendirmek False.

Bu, aşağıdaki gibi kısa süreli ifadelerin arkasındaki "sihir" dir:

foo = bar and spam or eggs

hangisinin kısaltması:

if bar:
    foo = spam
else:
    foo = eggs

gerçekten yazman gerekse de:

foo = spam if bar else egg

Son sorunuzla ilgili olarak, bunlar eşdeğerdir.
Sylvain Defresne

Ve ikisi de yanlış çünkü "" Yanlış. İkincisi '("",) veya ("s",)' olmalıdır. Her neyse, python'un modern sürümlerinde uygun bir üçlü operatör vardır. Bu hataya meyilli hack, yasaklanmalıdır.
ddaa


3

Eğer sorarsan

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

__nonzero__ yöntemi istenmeyen çağrılır. Python kılavuzundan:

__nonzero__ ( self ) Doğruluk değeri testini ve yerleşik bool () işlemini uygulamak için çağrılır; False veya True ya da bunların tamsayı eşdeğerleri 0 veya 1 döndürmelidir. Bu yöntem tanımlanmadığında, tanımlanmışsa __len __ () çağrılır (aşağıya bakın). Bir sınıf __len __ () veya __nonzero __ () tanımlamıyorsa, tüm örnekleri doğru olarak kabul edilir.

Eğer sorarsan

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

__eq__ yöntemi Spam argümanı ile çağrılan Yok .

Özelleştirme olasılıkları hakkında daha fazla bilgi için, https://docs.python.org/reference/datamodel.html#basic-customization adresindeki Python belgelerine bakın.


2

Bu iki karşılaştırma farklı amaçlara hizmet eder. İlki, bir şeyin boole değerini kontrol eder, ikincisi ise None değerine sahip kimliği kontrol eder.


1
Kesinlikle doğru olmak gerekirse, ikincisi -Eşitliği- Yok değeriyle kontrol eder ... "someobj, Yoktur" kimliği kontrol eder.
Matthew Trevor

0

Birincisi, ilk örnek daha kısa ve daha güzel görünüyor. Diğer gönderilere göre ne seçeceğiniz, karşılaştırmada gerçekten ne yapmak istediğinize de bağlıdır.


0

Cevap, duruma bağlı".

Bu bağlamda 0, "", [] ve False (liste ayrıntılı değil) 'nin Yok'a eşdeğer olduğunu düşünürsem ilk örneği kullanırım.


0

Şahsen, diller arasında tutarlı bir yaklaşım seçtim: if (var)Yalnızca var (veya eşdeğeri) boole olarak bildirilirse (veya C'de belirli bir türe sahip değilsek). Burada yanlışlıkla başka bir tür kullanmayacağımdan emin olmak için bu değişkenlerin bönüne (yani bVaraslında öyle olurdu ) bile ekledim .
Boolean'a örtük çevirmeyi gerçekten sevmiyorum, çok sayıda karmaşık kural olduğunda daha da az.

Tabii ki insanlar aynı fikirde olmayacak. Bazıları daha ileri gidiyor if (bVar == true), işimde Java kodunda görüyorum (zevkime göre çok fazla!), Diğerleri çok fazla kompakt sözdizimini seviyor, gidiyor while (line = getNextLine())(benim için çok belirsiz).

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.