Защо ключовите думи не работят тук?

Знам, че is се използва за сравнение, ако два обекта са еднакви, но == е за равенство. От моя опит is винаги работеше за числа, защото Python използва повторно числа. например:

>>>a = 3
>>>a is 3
True

И съм свикнал да използвам is винаги, когато сравнявам нещо с число. Но is не работи за тази програма по-долу:

from collections import namedtuple
# Code taken directly from [Udacity site][1].
# make a basic Link class
Link = namedtuple('Link', ['id', 'submitter_id', 'submitted_time', 'votes',
                           'title', 'url'])

# list of Links to work with
links = [
    Link(0, 60398, 1334014208.0, 109,
         "C overtakes Java as the No. 1 programming language in the TIOBE index.",
         "http://pixelstech.net/article/index.php?id=1333969280"),
    Link(1, 60254, 1333962645.0, 891,
         "This explains why technical books are all ridiculously thick and overpriced",
         "http://prog21.dadgum.com/65.html"),
    Link(23, 62945, 1333894106.0, 351,
         "Learn Haskell Fast and Hard",
         "http://yannesposito.com/Scratch/en/blog/Haskell-the-Hard-Way/"),
    Link(2, 6084, 1333996166.0, 81,
         "Announcing Yesod 1.0- a robust, developer friendly, high performance web framework for Haskell",
         "http://www.yesodweb.com/blog/2012/04/announcing-yesod-1-0"),
    Link(3, 30305, 1333968061.0, 270,
         "TIL about the Lisp Curse",
         "http://www.winestockwebdesign.com/Essays/Lisp_Curse.html"),
    Link(4, 59008, 1334016506.0, 19,
         "The Downfall of Imperative Programming. Functional Programming and the Multicore Revolution",
         "http://fpcomplete.com/the-downfall-of-imperative-programming/"),
    Link(5, 8712, 1333993676.0, 26,
         "Open Source - Twitter Stock Market Game - ",
         "http://www.twitstreet.com/"),
    Link(6, 48626, 1333975127.0, 63,
         "First look: Qt 5 makes JavaScript a first-class citizen for app development",
         "http://arstechnica.com/business/news/2012/04/an-in-depth-look-at-qt-5-making-javascript-a-first-class-citizen-for-native-cross-platform-developme.ars"),
    Link(7, 30172, 1334017294.0, 5,
         "Benchmark of Dictionary Structures", "http://lh3lh3.users.sourceforge.net/udb.shtml"),
    Link(8, 678, 1334014446.0, 7,
         "If It's Not on Prod, It Doesn't Count: The Value of Frequent Releases",
         "http://bits.shutterstock.com/?p=165"),
    Link(9, 29168, 1334006443.0, 18,
         "Language proposal: dave",
         "http://davelang.github.com/"),
    Link(17, 48626, 1334020271.0, 1,
         "LispNYC and EmacsNYC meetup Tuesday Night: Large Scale Development with Elisp ",
         "http://www.meetup.com/LispNYC/events/47373722/"),
    Link(101, 62443, 1334018620.0, 4,
         "research!rsc: Zip Files All The Way Down",
         "http://research.swtch.com/zip"),
    Link(12, 10262, 1334018169.0, 5,
         "The Tyranny of the Diff",
         "http://michaelfeathers.typepad.com/michael_feathers_blog/2012/04/the-tyranny-of-the-diff.html"),
    Link(13, 20831, 1333996529.0, 14,
         "Understanding NIO.2 File Channels in Java 7",
         "http://java.dzone.com/articles/understanding-nio2-file"),
    Link(15, 62443, 1333900877.0, 1244,
         "Why vector icons don't work",
         "http://www.pushing-pixels.org/2011/11/04/about-those-vector-icons.html"),
    Link(14, 30650, 1334013659.0, 3,
         "Python - Getting Data Into Graphite - Code Examples",
         "http://coreygoldberg.blogspot.com/2012/04/python-getting-data-into-graphite-code.html"),
    Link(16, 15330, 1333985877.0, 9,
         "Mozilla: The Web as the Platform and The Kilimanjaro Event",
         "https://groups.google.com/forum/?fromgroups#!topic/mozilla.dev.planning/Y9v46wFeejA"),
    Link(18, 62443, 1333939389.0, 104,
         "github is making me feel stupid(er)",
         "http://www.serpentine.com/blog/2012/04/08/github-is-making-me-feel-stupider/"),
    Link(19, 6937, 1333949857.0, 39,
         "BitC Retrospective: The Issues with Type Classes",
         "http://www.bitc-lang.org/pipermail/bitc-dev/2012-April/003315.html"),
    Link(20, 51067, 1333974585.0, 14,
         "Object Oriented C: Class-like Structures",
         "http://cecilsunkure.blogspot.com/2012/04/object-oriented-c-class-like-structures.html"),
    Link(10, 23944, 1333943632.0, 188,
         "The LOVE game framework version 0.8.0 has been released - with GLSL shader support!",
         "https://love2d.org/forums/viewtopic.php?f=3&t=8750"),
    Link(22, 39191, 1334005674.0, 11,
         "An open letter to language designers: Please kill your sacred cows. (megarant)",
         "http://joshondesign.com/2012/03/09/open-letter-language-designers"),
    Link(21, 3777, 1333996565.0, 2,
         "Developers guide to Garage48 hackatron",
         "http://martingryner.com/developers-guide-to-garage48-hackatron/"),
    Link(24, 48626, 1333934004.0, 17,
         "An R programmer looks at Julia",
         "http://www.r-bloggers.com/an-r-programmer-looks-at-julia/")]


# links is a list of Link objects. Links have a handful of properties. For
# example, a Link's number of votes can be accessed by link.votes if "link" is a
# Link.

# make the function query() return a list of Links submitted by user 62443, by
# submission time ascending

def query():
    print "hello"
    print [link for link in links if link.submitter_id == 62443] # is does not work
    return sorted([link for link in links if link.submitter_id == 62443],key = lambda x: x[2])
query()

Когато използвах is във функцията за заявка като тази [link for link in links if link.submitter_id is 62443], ще получа празен списък. Но ако използвам ==, работи добре.

В по-голямата си част кодът беше взет директно от сайта udacity, но аз също го изпробвах на моята локална машина. Същият резултат. Така че мисля, че числата сега са различни обекти в този случай, но защо? Има ли нужда от това?

РЕДАКТИРАНЕ: Да. Признавам, че този въпрос е дублиран и трябва да бъде затворен. Но е дубликат с първата публикация не вторият. Не знаех този въпрос, преди да публикувам това.

Проблемът ми беше, че мислех, че числовите обекти винаги ще се използват повторно.

Благодаря на всички, отървах се от лошия навик.


person Gnijuohz    schedule 19.12.2013    source източник
comment
Свикнал съм да използвам is винаги, когато сравнявам нещо с число. Тогава трябва да очаквате вашият код да се счупи по мистериозен начин. Python никога не е гарантирал, че числата са единични - а понякога не са. Преодолей го и използвай == според намерението на Гуидо ;-)   -  person Tim Peters    schedule 19.12.2013
comment
stackoverflow.com/ въпроси/2987958/   -  person Buddhima Gamlath    schedule 19.12.2013
comment
Като да тичаш през пътя, без да гледаш. Знаете, че това е грешното нещо, но изглежда е работило в миналото, така че...   -  person John La Rooy    schedule 19.12.2013
comment
@Gnijuohz: Опитайте вашия пример с число с число като 1023 вместо 3 и ще видите False вместо True.   -  person Matthias    schedule 19.12.2013
comment
@TimPeters Винаги съм го използвал за сравняване на числа като -1 или 0, което се използва като върната стойност за някои функции. Затова винаги са работили. Сега виждам смисъла!   -  person Gnijuohz    schedule 19.12.2013


Отговори (6)


Няма отговор на въпроса ви, освен да се заровите в подробности за изпълнението на конкретната версия на Python, която използвате. Нищо не е определено дали a == b предполага a is b, когато a и b са числа. Често е вярно, и особено за „малки цели числа“, поради това, че CPython пази кеш от малки целочислени обекти и обикновено (не винаги!) връща същия обект за дадена малка целочислена стойност. Но нищо в това не е дефинирано, гарантирано или дори винаги едно и също във всички версии.

Мисленето за адресите на паметта може да бъде леко полезно, тъй като това е начинът, по който id() е имплементиран в CPython. Но други реализации използват различни реализации за id(). Например, казаха ми, че id() е голяма трудност за внедряване в Jython (Python имплементиран в Java), тъй като Java е свободна да премества обекти в паметта по време на събиране на отпадъци (CPython не го прави: в CPython обект винаги заема паметта първоначално разпределени за него, докато обектът стане боклук).

Единственото предвидено - и поддържано - използване за is е да се провери дали две имена на обекти всъщност се преобразуват в един и същ обект. Например, независимо от вида на b, след

a = b

трябва да е така

a is b

е True. И това понякога е полезно.

_sentinel = object() # create a unique object

def somefunc(optional=_sentinel):
    if optional is _sentinel:  # we know for sure nothing was passed
        ...

Другата основна употреба е за шепата обекти, гарантирано че са единични. None, True и False са примери за това и наистина е идиоматично да се пише:

if a is None:

вместо:

if a == None:

Първият начин е успешен, ако и само ако a е действително обвързан с единичен обект None, но вторият начин може да успее, ако a е от произволен тип, така че a.__eq__(None) връща True.

Не използвайте is за числа. Това е лудост ;-)

person Tim Peters    schedule 19.12.2013
comment
Страхотен отговор. Имах впечатлението, че Python винаги ще използва повторно обекти за числа. Сега знам, че е грешно. - person Gnijuohz; 19.12.2013
comment
Е, това би било изключително скъпо. Например след a = 10**10000 и b=10**10000. След като изчисли 10**10000 за втори път, Python ще трябва да претърси всички съществуващи числа, за да разбере, че a вече има същата стойност. Не и през нашия живот ;-) - person Tim Peters; 19.12.2013
comment
Разбирам мисълта ти! Благодаря! - person Gnijuohz; 19.12.2013

Имате право, че is проверява идентичността, ако двете променливи са един и същ обект, и че == се използва за проверка на евенството, ако обектите са еднакви. (Какви равни средства се решават от участващите класове).

И вие сте прави, че използването на is за проверка дали две числа са равни често работи, защото Python използва повторно числа, така че често, когато са равни, те също са идентични.

Но забелязвате как звучи това. Трябва ли да проверите дали две числа са равни, като използвате проверката за самоличност? Не разбира се, че не. Трябва да използвате проверката за равенство, за да проверите дали обектите са еднакви. Толкова е просто.

Това, че често можете да използвате проверката на самоличността, за да проверите равенството на числата, е само страничен ефект от повторното използване на числа в Python, което той прави, за да спести памет, и което е детайл от изпълнението.

Освен това в Python 3 == 3.0, но 3 is not 3.0. Така че трябва да използвате == поради тази причина.

person Lennart Regebro    schedule 19.12.2013
comment
Да, трябва да използвам ==. Но аз използвах е с малки числа и те винаги работеха. стана навик. Благодаря! - person Gnijuohz; 19.12.2013

Този въпрос има две части

  1. is не проверява за равенство, а само за идентичност. Два обекта имат една и съща идентичност, ако са едни и същи обекти (с една и съща идентичност, т.е. id(a) == id(b))

    >>> a = 10
    >>> b = a
    >>> id(a), id(b)
    (30388628, 30388628)
    
  2. CPython като имплементиран (може да е за други) определен диапазон от числа в рамките на определен лимит, те се кешират, така че въпреки че са различни обекти, те имат една и съща идентичност

По този начин

>>> a is 200
True
>>> a = 2000
>>> a is 2000
False
person Abhijit    schedule 19.12.2013
comment
Така че накратко за големи числа is няма да работи. - person Gnijuohz; 19.12.2013
comment
@Gnijuohz Така че накратко причината, поради която работи понякога, е вътрешна подробност за изпълнение на CPython. Не се очаква да работи и не трябва да разчитате, че работи. - person Lennart Regebro; 19.12.2013
comment
Така че, въпреки че са различни обекти, те имат една и съща идентичност - Грешка. Това е същият обект. Иначе щяха да имат различни самоличности. - person Lennart Regebro; 19.12.2013
comment
@LennartRegebro добре, за малки числа CPython използва повторно същия обект, но за големи числа не го прави. Правилно ли е това твърдение? - person Gnijuohz; 19.12.2013
comment
@Gnijuohz Да. Въпреки че мисля, че може да има случаи, когато не са еднакви, купете, не помня кога е било. Това е без значение, тъй като това е детайл от изпълнението, за който не е нужно да се интересувате. - person Lennart Regebro; 19.12.2013

Това е така, защото is сравнява самоличностите:

>>> a = 10
>>> id(a)
30967348
>>> id(10)
30967348
>>> a is 10
True
>>> a += 1
>>> a
11
>>> id(a)
30967336
>>> id(11)
30967336
>>> a is 11
True
>>> a = 106657.334
>>> id(a)
44817088
>>> id(106657.334)
31000752
>>> a is 106657.334
False
>>> a == 106657.334
True
person Games Brainiac    schedule 19.12.2013

is се използва за сравняване на самоличността.

In [26]: a = 3

In [27]: a is 3
Out[27]: True

In [28]: id(a)
Out[28]: 140479182211448

In [29]: id(3)
Out[29]: 140479182211448

Разширяване на същото към горния пример.

In [32]: for link in links:
    print id(link.submitter_id), id(62443), id(link.submitter_id) == id(62443), link.submitter_id

....:

140479184066728 140479184065152 False 60398
140479184066872 140479184065152 False 60254
140479184065688 140479184065152 False 62945
140479184064984 140479184065152 False 6084
140479184064648 140479184065152 False 30305
140479184063416 140479184065152 False 59008
140479184063608 140479184065152 False 8712
140479184063752 140479184065152 False 48626
140479184064352 140479184065152 False 30172
140479185936456 140479184065152 False 678
140479185966096 140479184065152 False 29168
140479184063752 140479184065152 False 48626
140479185936888 140479184065152 False 62443
140479184052336 140479184065152 False 10262
140479184061232 140479184065152 False 20831
140479185936888 140479184065152 False 62443
140479184057712 140479184065152 False 30650
140479185957880 140479184065152 False 15330
140479185936888 140479184065152 False 62443
140479185959760 140479184065152 False 6937
140479184061528 140479184065152 False 51067
140479184058728 140479184065152 False 23944
140479185944264 140479184065152 False 39191
140479184062568 140479184065152 False 3777
140479184063752 140479184065152 False 48626

Използвайте is, когато проверявате за самоличност.

Справка: Сравнение на низове в Python: е срещу ==

person Kracekumar    schedule 19.12.2013

Операторът is се използва за сравняване на идентичностите на двата обекта. Така че основно сравнявате дали обектите имат еднаква идентичност, а не дали са еднакви

Така че, ако отпечатате идентификаторите на включените обекти с помощта на функцията id(), техните идентификатори са различни, така че операторът is не работи в този случай:

>>>print [(id(link),id(62443)) for link in links if link.submitter_id == 62443]
[(28741560, 21284824), (28860576, 21284824), (28860744, 21284824)]

Въпреки че само защото два обекта са подобни, идентичностите може да не са еднакви


Забележка: След като даден обект бъде изхвърлен, неговият id е достъпен за повторно използване. Така че използването на оператора is всъщност донякъде не се препоръчва

person K DawG    schedule 19.12.2013
comment
защо гласуването против? BTW коригирано, ако причинява объркване по някакъв начин @user2864740 - person K DawG; 19.12.2013
comment
@KDawG Това е наистина лош начин да правите нещата, приятел. - person Games Brainiac; 19.12.2013
comment
OP започва въпроса си с знам, че се използва за сравнение дали два обекта са еднакви, но == е за равенство. Значи той вече знае това. Просто казвам'. - person Lennart Regebro; 19.12.2013
comment
Вашият пример изглежда доказва обратното на това, което обяснявате по-горе. Късите низове се интернират (ако отговарят на определени правила). - person Tim Pietzcker; 19.12.2013
comment
Забележка: След като даден обект бъде изхвърлен, неговият id е достъпен за повторно използване. Така че използването на оператора is всъщност донякъде се обезсърчава - Ех. КАКВО? Кой ти каза това? Това няма смисъл. Върви да им се смееш. - person Lennart Regebro; 19.12.2013
comment
@LennartRegebro, техните идентификатори всъщност са достъпни за повторно използване, така че може да доведе до неочаквани резултати, нали? - person K DawG; 19.12.2013
comment
@KDawG: Не, може и да не е така. Обектът е само събиран боклук, когато вече нямате препратки към него. - person Lennart Regebro; 19.12.2013