Pourquoi un floating point clé de dictionnaire remplacer une clé entière avec la même valeur?
Je suis en train de travailler à travers http://www.mypythonquiz.com, et question n ° 45 demande pour la sortie de le code suivant:
confusion = {}
confusion[1] = 1
confusion['1'] = 2
confusion[1.0] = 4
sum = 0
for k in confusion:
sum += confusion[k]
print sum
La sortie est 6
, puisque la clé 1.0
remplace 1
. C'est un peu dangereux pour moi, est-ce toujours utile pour le langage?
- en utilisant le haut-
sum
comme une variable est aussi (légèrement) à confusion. Je méfiance code qui tente d'utiliser des flotteurs que les int clés, même si le langage le permet, même si je suis sûr qu'il y a certains cas où il pourrait être utile. - Cela ne ferait que vous faire du mal dans les situations où vous pourriez envisager
1.0
et1
être différent. Compte tenu de l' (apparemment) la rareté de ces situations, il est compréhensible que le comportement par défaut est de les traiter comme des égaux. - Je serais méfiant de tout dictionnaire qui contient disjoints types de clés.
- Bon point. 99% des fois j'ai juste utiliser des chaînes ou ints (ou de divers tuples construit à partir d'eux), de toute façon. Je n'ai pas vraiment confiance en float clés.
- Votre titre modifier a changé l'esprit de la question, mon intérêt est surtout de savoir si ce comportement est utile/comment il s'intègre avec le reste du langage Python, plutôt que de simplement 'pourquoi' ce comportement se produit. Encore, beaucoup de bonnes réponses ont surgi au sujet de la question d'origine.
- Le titre et les questions sont deux choses différentes. La question du corps ne devrait pas dépendre sur le titre. Le titre devrait être une phrase anglaise en résumant les principaux points de la question (voir la section ici). Un titre comme "Tag2 en python Tag3 vs Tag4" n'est pas un bon titre, même si il peut sembler accrocheur. Si vous pensez que le titre actuel ne peut pas exprimer ce que vous vouliez à l'origine-à-dire le modifier, mais il devrait être une phrase. Plus important encore, si vous pensez que la question a obtenu un "mal tourner" dans ses réponses, vous devez préciser dans son corps avec une déclaration explicite.
Vous devez vous connecter pour publier un commentaire.
Vous devez considérer que le
dict
vise à stocker des données en fonction de la logique numérique de la valeur, et non pas sur la façon dont vous la représentait.La différence entre
int
s etfloat
s est en effet juste un détail d'implémentation, et non conceptuel. Idéalement, le seul type de numéro devrait être une précision arbitraire nombre avec la surabondance de l'exactitude de la même sous-unité... ce n'est cependant difficile à mettre en œuvre sans avoir de problèmes... mais peut-être que sera le seul avenir numérique de type pour Python.Donc, tout en ayant de différents types, pour des raisons techniques Python essaie de cacher ces détails de mise en œuvre et
int
->float
la conversion est automatique.Il serait beaucoup plus étonnant que dans un programme en Python
if x == 1: ...
n'allait pas être prises lors de l'x
est unfloat
avec la valeur 1.Note également avec Python 3 la valeur de
1/2
est0.5
(la division de deux nombres entiers) et que les typeslong
et non-unicode chaîne ont été déposés avec la même tentative de cacher les détails d'implémentation.int->float
promotion doit être automatique dans un contexte où il est nécessaire, mais dans ce cas je dirais qu'il ne l'est pas. Ce qui se passe lorsque vous essayez de mettre unint
dans un dictionnaire qui est à l'extérieur des limites d'unefloat
, ou qui ne peuvent pas aller-retour?int
etfloats
et d'autres uniquement avec l'un ou l'autre..bit_length()
méthode. Les flotteurs sont également pas autorisés à être utilisés comme indices de tableau -- si elles étaient destinées à être ils devraient mettre en œuvre__index__
et seulement de relever une erreur pour les valeurs non entières. Ce sont certainement des différences conceptuelles, et pas seulement de la mise en œuvre des différences.3.0
comme un index de tableau est un bug et non pas une fonction, mais peut être Guido n'est pas d'accord sur ce point. Bien sûr il y a des différences (par exemple,type(3)
ettype(3.0)
ne sont pas les mêmes)... le point est que si ils sont accessoires différences (que nous serions ravis de se débarrasser de) ou si elles sont désiré les différences...3.000000000000001
comme un index de tableau? Ou2.999999999999999
, ou3.141592653589793
? Si non, je ne pense pas que vous devriez être heureux avec3.0
soit.int
etfloat
être de différents types, alors vous ne devriez pas être heureux avec3 == 3.0
soit; mais c'est de l'OMI très gênant (même si OCaml gars pensent différemment). Si3 == 3.0
puisx[3]
doit être le même quex[3.0]
trop.3.0000000001
sur l'autre main est quelque chose de différent et avoir à élever une erreur pourrait aider à résoudre des problèmes. BTW noter que les nombres double précision peut représenter exactement tous les nombres entiers avec une valeur absolue inférieure à 2^53... c'est à dire 9,007,199,254,740,992 (nous n'allons pas avoir des matrices de grande autour pendant un bon moment).6 * cos(pi/4)**2
. C'est mathématiquement exactement trois, mais à virgule flottante peut pas représentent de ce fait. Exemple artificiel, bien sûr, mais si vous n'avez pas besoin d'une telle “inexact” calculs alors pourquoi voulez-vous à virgule flottante en premier lieu? C'est un peu une question philosophique; ma position est que les flotteurs jamais représenter les nombres exactement, mais ils représentent toujours un petit intervalle de nombres. Le coulage d'un type exact commeint
flotter signifie que vous êtes sûr de choisir un intervalle qui contient de type int, mais l'inverse n'est pas (uniquement) possible.Tout d'abord: le comportement est documenté de manière explicite dans la documentation pour l' de hachage fonction:
Deuxièmement, une limitation de hachage est indiqué dans la documentation pour
objet.__hash__
Ce n'est pas spécifique à python. Java a la même mise en garde: si vous mettez en œuvre
hashCode
ensuite, pour que les choses fonctionnent correctement, vous doit le mettre en œuvre de manière à ce que:x.equals(y)
impliquex.hashCode() == y.hashCode()
.Donc, python a décidé que
1.0 == 1
détient, par conséquent, il est forcé pour fournir une implémentation pourhash
tels quehash(1.0) == hash(1)
. L'effet secondaire est que1.0
et1
agissent exactement de la même manière quedict
clés, d'où le comportement.En d'autres termes, le comportement en lui-même ne doit pas être utilisé ou utile en quelque sorte. Il est nécessaire. Sans ce comportement, il y aurait des cas où vous pourriez accidentellement écraser une clé différente.
Si nous avions
1.0 == 1
maishash(1.0) != hash(1)
, on pourrait encore avoir un collision. Et si1.0
et1
entrent en collision, ladict
utilisera l'égalité pour être sûr qu'ils sont de la même clé ou pas et kaboom la valeur est écrasée, même si vous en aviez l'intention d'être différent.La seule façon de l'éviter serait d'avoir
1.0 != 1
, de sorte que ledict
est capable de distinguer entre eux, même en cas de collision. Mais il a été jugé plus important d'avoir1.0 == 1
que pour éviter le comportement que vous voyez, depuis que vous avez pratiquement jamais utiliserfloat
s etint
s comme les clés de dictionnaire, de toute façon.Depuis python essaie de cacher la distinction entre les nombres en convertissant automatiquement lorsque nécessaire (par exemple,
1/2 -> 0.5
) il est logique que ce comportement se reflète même dans de telles circonstances. C'est plus cohérent avec le reste de python.Ce comportement apparaît dans tout mise en œuvre lorsque la mise en correspondance des touches est au moins en partie (comme dans un tableau associatif) fondée sur des comparaisons.
Par exemple, si un
dict
a été mis en œuvre à l'aide d'un rouge-noir arbre ou un autre type d'équilibre du BST, lorsque la clé1.0
est regardé les comparaisons avec d'autres touches, serait de retour les mêmes résultats que pour1
et afin qu'ils continuent à agir de la même manière.De hachage cartes nécessitent un soin encore plus grand en raison du fait que c'est la valeur de hachage est utilisé pour trouver l'entrée de la clé et des comparaisons sont faites seulement par la suite. Afin de briser la règle présentée ci-dessus signifie que vous voudrais vous présenter un bug qui est assez difficile à repérer car à la fois la
dict
semblent fonctionner comme vous le souhaitez, et à d'autres moments, lorsque les changements de taille, elle commence à se comporter de manière incorrecte.Noter qu'il n'y serait être un moyen de résoudre ce problème: avoir un hachage à la carte/BST pour chaque type inséré dans le dictionnaire. De cette façon, il ne pouvait pas être tout les collisions entre les objets de type différent et comment
==
compare ne serait pas question lorsque les arguments de différents types.Toutefois, cela ne ferait que compliquer la mise en œuvre, il serait probablement inefficace depuis hachage cartes ont à garder tout à fait à quelques endroits afin d'avoir O(1) les temps d'accès. Si ils sont trop pleins les performances diminuent. Le fait d'avoir plusieurs cartes de hachage moyen de gaspiller plus d'espace et aussi, vous devez d'abord choisir les hash map à regarder avant même de commencer la réelle recherche de la clé.
Si vous avez utilisé techniciennes se chargent de vous aimerais tout d'abord avoir à rechercher le type et le effectuer un second de recherche. Donc, si vous allez à l'utilisation de nombreux types de vous finirais avec deux fois plus de travail (et de la recherche permettrait de prendre en O(log n) au lieu de O(1)).
42
pour chaque valeur doit être valide mais inefficace de hachage (donc vous ne pouvez jamais décider quoi que ce soit basé sur la valeur de hachage). Le point essentiel est qu'en Python3 == 3.0
et le dictionnaire des œuvres sur l'égalité.dict
et non pas un concept générique de la cartographie, je pense que le hachage est tout à fait pertinente.3==3.0
et parfois ne serait pas une mauvaise chose, et de l'empêcher de force - vous de leur donner le même hash.3 == 3.0
. Un dict en python utilise la comparaison d'égalité pour faire correspondre les touches (ce est le point clé) et c'est pourquoix[3]
etx[3.0]
sont les mêmes pour un dict. Il serait le même, même sans hachage et juste avec linéaire de la numérisation.dict
est pas documentée comme une cartographie simple, mais comme un hachage de la carte, donc le hachage de la carte se comporte de la est pertinentes de façon indépendante sur la façon dont d'autres structures se comportent. En plus, j'ai déjà ajouté ce que vous appelez votre point principal, de sorte que je suis pas d'aller retirer de l'information utile à partir de la réponse.dict
ne peut pas contenir à la fois des 3 et 3.0 dans différents emplacements... la question est dans__eq__
, pas dans__hash__
.En python:
C'est à cause de la conversion implicite
Cependant:
Je peux voir pourquoi automatiques de moulage entre
float
etint
est à portée de main, Il est relativement sûr de jeterint
enfloat
, et pourtant il y a d'autres langues (par exemple) qui rester à l'écart de conversion implicite.Il est en fait un langage de conception de décision et une question de goût plus que les différentes fonctionnalités
is
avec des chiffres n'est pas une bonne idée... par exemple aprèsx=1000000
l'expressionx is 1000000
estFalse
.Dictionnaires sont mis en œuvre avec une table de hachage. Chercher quelque chose dans une table de hachage, vous commencez à la position indiquée par la valeur de hachage, puis de rechercher les différents lieux jusqu'à ce que vous trouver une clé de valeur égale ou un seau vide.
Si vous avez deux valeurs clés qui permettent de comparer l'égalité des différentes tables de hachage, vous pouvez obtenir des résultats contradictoires en fonction de la valeur de la clé était dans la perquisitionnés ou pas. Par exemple ce serait plus probable que la table est pleine. C'est quelque chose que vous voulez éviter. Il semble que les développeurs Python avait cela à l'esprit, depuis le haut-
hash
fonction renvoie la même valeur de hachage pour l'équivalent de valeurs numériques, peu importe si ces valeurs sontint
oufloat
. Notez que cela s'étend à d'autres types numériques,False
est égal à0
etTrue
est égal à1
. Mêmefractions.Fraction
etdecimal.Decimal
respecter cette propriété.L'exigence que si
a == b
puishash(a) == hash(b)
est documenté dans la définition deobjet.__hash__()
:TL;DR: un dictionnaire serait briser si les touches qui ont comparé l'égalité n'a pas de carte de la même valeur.
Franchement, le contraire est dangereux!
1 == 1.0
, de sorte qu'il n'est pas invraisemblable d'imaginer que si vous aviez leur point de clés différentes et tenté d'accéder à la base sur une appréciation numéro, alors vous avez probablement un problème avec ça parce que l'ambiguïté est difficile à comprendre.Typage dynamique signifie que la valeur est plus important que ce type technique de quelque chose, puisque le type est malléable (qui est une fonctionnalité très utile) et ainsi distinguer les deux
ints
etfloats
de la même valeur que distinct est inutile sémantique qui ne fera que conduire à la confusion.Je suis d'accord avec d'autres, qu'il est logique de traiter
1
et1.0
que même dans ce contexte. Même si Python n'a le traiter différemment des autres, il serait probablement une mauvaise idée d'essayer d'utiliser1
et1.0
comme les clés distinctes pour un dictionnaire. Sur l'autre main -- j'ai du mal à penser à un cas d'utilisation pour l'utilisation de1.0
comme un alias pour1
dans le contexte de touches. Le problème est que la clé est littérale ou il est calculé. Si c'est un littéral de la clé alors pourquoi ne pas simplement utiliser1
plutôt que1.0
? Si c'est une clé calculée -- arrondi erreur pourrait muck les choses:Donc, je dirais que, de façon générale, la réponse à votre question "est-ce toujours utile pour le langage?" "Non, probablement pas."