2015-06-30 15 views
7

Guardate questo codice Python:Stringhe non indicate da dict?

from gc import get_referrers as refs 
x = 'x' 
d = {x:x} 
print(d in refs(x)) 

Esso stampa False. Questo è strano di per sé, ma diventa molto più strano se si considera quanto segue:

  • Se x è un numero (int, float, complesso, frazione, decimale) invece di una stringa, esso stampa ancora False. Anche per byte e bytearray. Ma per ogni altro tipo (lavabile se usato come chiave, come tupla o frozenset - ma molti altri, se usato solo come valore), stampa True.

  • Se d è un qualsiasi altro contenitore (set, elenco, tupla ...) contenente x, stampa True. Solo per un ditt, stampa False. Inoltre, non importa se x è una chiave o un valore, o come sopra, entrambi.

ho pensato in Python ogni oggetto è un riferimento (al contrario di Java, che ha tipi primitivi, o rubino, che valore-tipi piccole INT), ma ora sembra str e int sono tipi pò primitivi, che non sono referenziati. Ma d'altra parte, perché solo in dicts ??

So anche che gli inte da -5 a 256 vengono memorizzati nella cache in CPython (e le stringhe piccole possono essere internate), quindi ha senso non registrarli, poiché non verranno mai eliminati comunque, ma questo funziona per tutti gli interi (e stringhe lunghe) Ho provato, molto più grande di quella gamma.

Qualcuno sa cosa sta succedendo qui?

--- UPDATE ---

più curiose ... sembra datetime. {Datetime, data, ora} classi hanno lo stesso comportamento "senza riferimenti". Ora, conosco una cosa che quelli, AnyStr e Number hanno in comune: i loro hash sono randomizzati con un sale per sessione. Ma questo non rende qualsiasi senso, poiché il comportamento è osservato anche quando questi sono semplicemente valori in dict, non chiavi. E i valori non sono hash. O sono loro?

+0

Interessante, il conteggio dei riferimenti ('sys.getrefcount') aumenta di 2 dopo aver assegnato il dizionario. ma 'get_referrers' restituisce anche False se la chiave e il valore sono diversi (e uno è x), quindi non sembra essere una protezione contro i riferimenti circolari. – cdarke

+0

Sì, era una delle mie ipotesi, ma come hai detto tu non vale perché x non deve essere sia chiave che valore. E altri contenitori (elenchi) possono avere riferimenti circolari, non mostrano questo comportamento. – Veky

+0

BTW, i sintomi sono simili in Python 2.7 e 3.4, i conteggi di riferimento sono numeri totali apparentemente diversi, il che è prevedibile. – cdarke

risposta

4

Da gcmodule.c:

Alcuni tipi di contenitori non possono partecipare ad un ciclo di riferimento, e quindi non hanno bisogno di essere monitorati dal garbage collector. Untracking di questi oggetti riduce il costo delle raccolte di dati inutili. Tuttavia, la determinazione di quali oggetti possono essere non tracciati non è gratuita e i costi devono essere pari a ponderati rispetto ai vantaggi per la garbage collection.

...

Dizionari contenenti solo oggetti immutabili, inoltre, non hanno bisogno di essere monitorati . I dizionari non sono tracciati quando vengono creati. Se un elemento tracciato è inserito in un dizionario (come chiave o valore), viene localizzato il dizionario . Durante una raccolta completa dei rifiuti (tutte le generazioni), , il raccoglitore scollegherà qualsiasi dizionario il cui contenuto non sia monitorato.

Fondamentalmente, poiché l'oggetto in Python è il conteggio dei riferimenti, l'obiettivo del garbage collector è di interrompere i cicli di riferimento, altri oggetti non referenziati vengono distrutti quando l'ultimo riferimento scompare. Per l'ottimizzazione, Garbage Collector non tiene traccia di determinati oggetti che semplicemente non possono mai partecipare ai cicli di riferimento.

Quindi, le stringhe sono di riferimento. Tuttavia, il garbage collector non è interessato a questi dizionari e pertanto gc.get_referrers() non li elenca.

+0

Ho scoperto che anche nella fonte, mi hai battuto. È interessante notare che se il valore è immutabile (la chiave non può essere) il valore appare in 'get_referrers()', ma la chiave non lo fa mai, il che potrebbe contraddire la citazione (anche se dice che il * dizionario * viene tracciato, non la chiave). – cdarke

+0

Scrub per l'ultimo commento. La chiave non viene tracciata se è (credo) memorizzata nella cache. Cambiare 'x' in" Slartibartfast "e il valore in' [1,2,3,4] ', fornisce sia la chiave che il valore come tracciamento in' gc.get_referrers() '. Chiaramente c'è un po 'di magia qui. – cdarke

+0

Sembra che grepping la fonte sia davvero buono per l'anima, come diceva un certo Larry Wall. :-) Grazie. Ma non riesco ancora a vedere perché solo dicts e non sets ... è come se gli insiemi fossero oggetti di seconda classe in Python. :-) – Veky