__getitem__ - string python



Perché '() is()' restituisce True quando '[] è[]' e '{} is{}' restituisce False? (1)

In breve:

Python crea internamente un elenco C di oggetti tuple il cui primo elemento contiene la tupla vuota. Ogni volta che si utilizza tuple() o () , Python restituirà l'oggetto esistente contenuto nell'elenco C citato e non ne creerà uno nuovo.

Tale meccanismo non esiste per oggetti dict o list che, al contrario, vengono ricreati da zero ogni volta .

Questo è probabilmente correlato al fatto che oggetti immutabili (come le tuple) non possono essere alterati e, come tali, è garantito che non cambino durante l'esecuzione. Ciò si solidifica ulteriormente se si considera che frozenset() is frozenset() restituisce True ; like () un frozenset vuoto è considerato un singleton nell'implementazione di CPython . Con gli oggetti mutabili, tali garanzie non sono disponibili e, come tali, non vi è alcun incentivo a memorizzare nella cache le istanze degli elementi zero (ovvero il loro contenuto potrebbe cambiare con l'identità che rimane invariata).

Prendi nota: questo non è qualcosa su cui si dovrebbe fare affidamento, cioè non si dovrebbe considerare le tuple vuote come single. Nessuna garanzia di questo tipo è esplicitamente presentata nella documentazione, quindi si dovrebbe presupporre che dipenda dall'implementazione.

Come è fatto:

Nel caso più comune, l'implementazione di CPython viene compilata con due macro PyTuple_MAXFREELIST e PyTuple_MAXSAVESIZE impostate su interi positivi. Il valore positivo per queste macro risulta nella creazione di una matrice di oggetti tuple con dimensioni PyTuple_MAXSAVESIZE .

Quando PyTuple_New viene chiamato con la size == 0 del parametro size == 0 si assicura di aggiungere una nuova tupla vuota all'elenco se non esiste già:

if (size == 0) {
    free_list[0] = op;
    ++numfree[0];
    Py_INCREF(op);          /* extra INCREF so that this is never freed */
}

Quindi, se viene richiesta una nuova tupla vuota, quella che si trova nella prima posizione di questa lista verrà restituita invece di una nuova istanza:

if (size == 0 && free_list[0]) {
    op = free_list[0];
    Py_INCREF(op);
    /* rest snipped for brevity.. */

Un motivo in più che provoca un incentivo a farlo è il fatto che le chiamate di funzione costruiscono una tupla per contenere gli argomenti posizionali che verranno utilizzati. Questo può essere visto nella funzione ceval.c in ceval.c :

static PyObject *
load_args(PyObject ***pp_stack, int na)
{
    PyObject *args = PyTuple_New(na);
    /* rest snipped for brevity.. */

che viene chiamato tramite do_call nello stesso file. Se il numero di argomenti na è zero, verrà restituita una tupla vuota.

In sostanza, potrebbe trattarsi di un'operazione eseguita frequentemente, quindi ha senso non ricostruire una tupla vuota ogni volta.

Ulteriori letture:

Un paio di altre risposte fanno luce sul comportamento del caching di CPython con immutable:

  • Per i numeri interi, un'altra risposta che scava nella fonte può essere trovata here .
  • Per gli archi, una manciata di risposte possono essere trovate here , here e here .

Da quanto mi è stato detto, usando [], {} o () per istanziare oggetti si restituisce una nuova istanza di list, dict o tuple rispettivamente; un nuovo oggetto istanza con una nuova identità .

Questo è stato abbastanza chiaro per me finché non l'ho provato e ho notato che () is () restituisce True invece del False atteso:

>>> () is (), [] is [], {} is {}
(True, False, False)

come previsto, questo comportamento si manifesta anche durante la creazione di oggetti con list() , dict() e tuple() rispettivamente:

>>> tuple() is tuple(), list() is list(), dict() is dict()
(True, False, False)

L'unica informazione rilevante che ho trovato nei documenti per tuple() afferma:

[...] Ad esempio, tuple('abc') restituisce ('a', 'b', 'c') e tuple([1, 2, 3]) restituisce (1, 2, 3) . Se non viene fornito alcun argomento, il costruttore crea una nuova tupla vuota, () .

Basti dire, questo non è sufficiente per rispondere alla mia domanda.

Quindi, perché le tuple vuote hanno la stessa identità, mentre altre come liste o dizionari non lo fanno?





python-internals