36 votes

En Python, quand deux objets sont-ils identiques?

Il semble qu' 2 is 2 et 3 is 3 sera toujours vrai en python, et en général, toute référence à un entier est le même que toute autre référence à la même entier. La même chose arrive à l' None (c'est à dire, None is None). Je sais que ce n'est pas d' arriver à des types définis par l'utilisateur, ou mutable types. Mais il échoue parfois sur immuable types de trop:

>>> () is ()
True
>>> (2,) is (2,)
False

C'est: deux indépendants des constructions de la vide de n-uplet de rendement des références pour le même objet en mémoire, mais deux constructions identiques d'un(immuable-)de l'élément de tuples à la fin de la création de deux objets identiques. J'ai testé, et frozensets travaillent dans une manière similaire à des n-uplets.

Qu'est ce qui détermine si un objet sera dupliquée dans la mémoire, ou qui aura une instance unique, avec beaucoup de références? Cela dépend-il si l'objet est "atomique" dans un certain sens? Varie-t-elle en fonction de la mise en œuvre?

38voto

mgilson Points 92954

Python a certains types qu'il garantit aura seulement un exemple. Des exemples de ces instances sont None, NotImplemented, et Ellipsis. Ce sont (par définition) des singletons et des choses comme None is None sont garantis pour revenir True car il n'y a aucun moyen de créer une nouvelle instance d' NoneType.

Il fournit également quelques doubletons 1True, False 2 -- Toutes les références à True pointent vers le même objet. Encore une fois, c'est parce qu'il n'y a aucun moyen de créer une nouvelle instance d' bool.

Ci-dessus, les choses sont garanties par le langage python. Cependant, comme vous l'avez remarqué, il y a certains types (tous immuable) qui stockent des certains cas de réutilisation. Ceci est permis par le langage, mais les différentes implémentations peuvent choisir d'utiliser cette allocation ou pas-en fonction de leurs stratégies d'optimisation. Quelques exemples qui relèvent de cette catégorie sont de petits entiers (-5 -> 255), le vide tuple et vide frozenset.

Enfin, Disponible interns certains des objets immuables au cours de l'analyse...

par exemple, si vous exécutez le script suivant avec Disponible, vous verrez qu'il renvoie True:

def foo():
    return (2,)

if __name__ == '__main__':
    print foo() is foo()

Cela semble vraiment bizarre. Le truc qui est Disponible de jeu est que chaque fois qu'il construit la fonction foo, il voit un tuple littérale qui contient d'autres simples (immuable) littéraux. Plutôt que de créer ce n-uplet (ou ses équivalents) de plus et plus, python crée juste une fois. Il n'y a pas de danger de cet objet d'être changé depuis l'accord de l'ensemble est immuable. Cela peut être une grande victoire pour la performance de la même boucle est appelée à plusieurs reprises. Des petites chaînes sont internés ainsi. La véritable victoire ici est dans le dictionnaire de recherches. Python peut faire un (très rapide) du pointeur de comparer et de retomber sur le plus lent de la chaîne des comparaisons lors de la vérification des collisions de hachage. Depuis tellement de python est construit sur dictionnaire des recherches, cela peut être une grande optimisation de la langue dans son ensemble.


1j'ai peut-être fait de ce mot ... Mais j'espère que vous avez compris l'idée...
2dans des circonstances normales, vous n'avez pas besoin de vérifier si l'objet est une référence à l' True -- Habituellement, vous n'soins si l'objet est "truthy" -- par exemple, si if some_instance: ... d'exécuter la branche. Mais, je l'ai mis ici juste pour être complet.


Notez que is peut être utilisé pour comparer des choses qui ne sont pas des singletons. Une utilisation courante est de créer une sentinelle de la valeur:

sentinel = object()
item = next(iterable, sentinel)
if items is sentinel:
   # iterable exhausted.

Ou:

_sentinel = object()
def function(a, b, none_is_ok_value_here=_sentinel):
    if none_is_ok_value_here is sentinel:
        # Treat the function as if `none_is_ok_value_here` was not provided.

La morale de cette histoire est de toujours dire ce que tu veux dire. Si vous voulez vérifier si une valeur est une autre valeur, puis utilisez l' is de l'opérateur. Si vous voulez vérifier si une valeur est égale à une autre valeur (mais peut-être distinctes), puis utilisez ==. Pour plus de détails sur la différence entre is et == (et à utiliser), consultez l'un des postes suivants:


Addendum

Nous avons parlé à propos de ces Disponible détails de mise en œuvre et nous avons affirmé qu'ils sont des optimisations. Il serait bien de tenter de mesurer tout ce que nous obtenir de tout cela, de l'optimisation (autre qu'un peu de confusion lorsque l'on travaille avec l' is opérateur).

Chaîne de "stage" et le dictionnaire des recherches.

Voici un petit script que vous pouvez exécuter pour voir comment beaucoup plus rapide dictionnaire des recherches sont si vous utilisez la même chaîne à rechercher la valeur au lieu d'une chaîne de caractère. Remarque, j'utilise le terme "internés" dans les noms de variable, Ces valeurs ne sont pas nécessairement des internés (bien qu'elles puissent être). Je suis juste en utilisant que pour indiquer que les "internés" chaîne est la chaîne dans le dictionnaire.

import timeit

interned = 'foo'
not_interned = (interned + ' ').strip()

assert interned is not not_interned


d = {interned: 'bar'}

print('Timings for short strings')
number = 100000000
print(timeit.timeit(
    'd[interned]',
    setup='from __main__ import interned, d',
    number=number))
print(timeit.timeit(
    'd[not_interned]',
    setup='from __main__ import not_interned, d',
    number=number))


####################################################

interned_long = interned * 100
not_interned_long = (interned_long + ' ').strip()

d[interned_long] = 'baz'

assert interned_long is not not_interned_long
print('Timings for long strings')
print(timeit.timeit(
    'd[interned_long]',
    setup='from __main__ import interned_long, d',
    number=number))
print(timeit.timeit(
    'd[not_interned_long]',
    setup='from __main__ import not_interned_long, d',
    number=number))

Les valeurs exactes ici ne devrait pas trop, mais sur mon ordinateur, les chaînes courtes de spectacle d'environ 1 partie en 7 plus rapide. Le long des chaînes sont presque 2x plus rapide (parce que la comparaison de chaînes de caractères prend plus de temps si la chaîne a plus de caractères à comparer). Les différences ne sont pas aussi frappant sur python3.x, mais ils sont toujours bel et bien là.

Tuple "interner"

Voici un petit script que vous pouvez jouer avec:

import timeit

def foo_tuple():
    return (2, 3, 4)

def foo_list():
    return [2, 3, 4]

assert foo_tuple() is foo_tuple()

number = 10000000
t_interned_tuple = timeit.timeit('foo_tuple()', setup='from __main__ import foo_tuple', number=number)
t_list = (timeit.timeit('foo_list()', setup='from __main__ import foo_list', number=number))

print(t_interned_tuple)
print(t_list)
print(t_interned_tuple / t_list)
print('*' * 80)


def tuple_creation(x):
    return (x,)

def list_creation(x):
    return [x]

t_create_tuple = timeit.timeit('tuple_creation(2)', setup='from __main__ import tuple_creation', number=number)
t_create_list = timeit.timeit('list_creation(2)', setup='from __main__ import list_creation', number=number)
print(t_create_tuple)
print(t_create_list)
print(t_create_tuple / t_create_list)

Celui-ci est un peu plus compliqué à l'heure (et je suis heureux de prendre toutes les meilleures idées combien de temps il est dans les commentaires). L'essentiel, c'est que, en moyenne (et sur mon ordinateur), un n-uplet prend environ 60%, tant pour créer une liste ne. Toutefois, foo_tuple() est en moyenne de 40% le temps qu' foo_list() prend. Qui montre que nous avons vraiment faire gagner un peu d'une accélération de ces stagiaires. Le gain de temps semble augmenter à mesure que le tuple devient plus grand (création d'une liste plus longue prend plus de temps -- Le tuple "création" prend de la constante de temps puisqu'elle a déjà été créé).

Notez également que j'ai appelé ce "stage". Il n'est pas vraiment (du moins pas dans le même sens que les chaînes sont internés). On peut voir la différence dans ce simple script:

def foo_tuple():
    return (2,)

def bar_tuple():
    return (2,)

def foo_string():
    return 'foo'

def bar_string():
    return 'foo'

print(foo_tuple() is foo_tuple())  # True
print(foo_tuple() is bar_tuple())  # False

print(foo_string() is bar_string())  # True

Nous voyons que les cordes sont vraiment des "internés" -- Différents appels en utilisant la même notation littérale retour le même objet. Le tuple "stage" semble être spécifique à une seule ligne.

21voto

mipadi Points 135410

Il varie en fonction de la mise en œuvre.

Disponible en cache de certaines des objets immuables dans la mémoire. Cela est vrai des "petits" nombres entiers comme 1 et 2 (-5 à 255, comme indiqué dans les commentaires ci-dessous). Disponible ce pour des raisons de performances; les petits entiers sont couramment utilisés dans la plupart des programmes, de sorte qu'il économise de la mémoire n'avoir qu'une copie créée (et est sans danger car les entiers sont immuables).

Cela est également vrai de "singleton" des objets comme None; il n'est jamais une None dans l'existence à un moment donné.

D'autres objets (tels que le tuple vide, ()) peut être mis en œuvre comme des singletons, ou ils peuvent ne pas l'être.

En général, vous ne devriez pas nécessairement supposer que des objets immuables seront mis en œuvre cette manière. Disponible pour des raisons de performances, mais d'autres implémentations ne peut pas, et Disponible peut même cesser de le faire à un certain moment dans l'avenir. (La seule exception pourrait être None, comme x is None est une commune Python idiome et est susceptible d'être mis en œuvre à travers les différents interprètes et des versions.)

Habituellement, vous voulez utiliser == au lieu de is. Python is opérateur n'est pas souvent utilisé, sauf lors de la vérification pour voir si une variable est - None.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X