Ar

Array in Python

96 esercizi

Informazioni su Array

Un list è una collezione mutabile di elementi in sequenza. Come la maggior parte delle collezioni (vedi i tipi integrati tuple, dict e set), gli array possono contenere riferimenti a qualsiasi tipo di dato (o a più tipi di dato), compresi altri array. Come qualsiasi sequenza, gli elementi si possono recuperare tramite un numero 0-based index da sinistra e -1-based index da destra. Gli array si possono copiare in tutto o in parte tramite la notazione di slicing o <list>.copy().

Gli array supportano sia le operazioni comuni sia le operazioni sulle sequenze mutabili, come min()/max(), <list>.index(), .append() e .reverse(). Gli elementi di un array si possono scorrere con il costrutto for item in <list>. Quando servono sia l'indice sia il valore dell'elemento, si può usare for index, item in enumerate(<list>).

Gli array sono implementati come array dinamici, simili al tipo Arraylist di Java, ed il loro uso più comune è memorizzare gruppi di dati simili (stringhe, numeri, set ecc.) di lunghezza sconosciuta (il numero di elementi può aumentare o diminuire a piacere).

Accedere agli elementi, verificare l'appartenenza tramite in o aggiungere elementi sul lato «destro» di un array sono tutte operazioni molto efficienti. Aggiungere in testa (cioè sul lato «sinistro») o inserire nel mezzo di un array è molto meno efficiente, perché quelle operazioni richiedono di spostare gli elementi per mantenerli in sequenza. Per una struttura dati simile che supporta appends/pops efficienti in memoria da entrambe le estremità, vedi collections.deque, che ha all'incirca le stesse prestazioni O(1) in entrambe le direzioni.

Poiché gli array sono mutabili e possono contenere riferimenti a oggetti Python arbitrari, occupano anche più spazio in memoria di un array.array o di una tuple (che è immutabile) della stessa lunghezza apparente. Nonostante questo, gli array sono una struttura dati estremamente flessibile ed utile, e molti metodi ed operazioni integrati di Python producono array come output.

Costruzione

Un list si può dichiarare come letterale con parentesi quadre [] e virgole tra gli elementi:

>>> no_elements = []

>>> no_elements
[]

>>> one_element = ["Guava"]

>>> one_element
['Guava']

>>> elements_separated_with_commas = ["Parrot", "Bird", 334782]

>>> elements_separated_with_commas
['Parrot', 'Bird', 334782]

Per una maggiore leggibilità, quando all'interno di un array ci sono molti elementi o strutture dati annidate, si possono usare le interruzioni di riga:

>>> lots_of_entries = [
...    "Rose",
...    "Sunflower",
...    "Poppy",
...    "Pansy",
...    "Tulip",
...    "Fuchsia",
...    "Cyclamen",
...    "Lavender"
... ]

>>> lots_of_entries
['Rose', 'Sunflower', 'Poppy', 'Pansy', 'Tulip', 'Fuchsia', 'Cyclamen', 'Lavender']


# Each data structure is on its own line to help clarify what they are.
>>> nested_data_structures = [
...    {"fish": "gold", "monkey": "brown", "parrot": "grey"},
...    ("fish", "mammal", "bird"),
...    ['water', 'jungle', 'sky']
... ]

>>> nested_data_structures
[{'fish': 'gold', 'monkey': 'brown', 'parrot': 'grey'}, ('fish', 'mammal', 'bird'), ['water', 'jungle', 'sky']]

Il costruttore list() si può usare vuoto oppure con un iterabile come argomento. Il costruttore scorre l'iterabile e ne aggiunge gli elementi all'array in ordine:

>>> no_elements = list()
>>> no_elements
[]

# The tuple is unpacked and each element is added.
>>> multiple_elements_from_tuple = list(("Parrot", "Bird", 334782))

>>> multiple_elements_from_tuple
['Parrot', 'Bird', 334782]

# The set is unpacked and each element is added.
>>> multiple_elements_from_set = list({2, 3, 5, 7, 11})

>>> multiple_elements_from_set
[2, 3, 5, 7, 11]

I risultati possono sorprendere quando si usa un costruttore di array con una stringa o un dict:

# String elements (Unicode code points) are iterated through and added *individually*.
>>> multiple_elements_string = list("Timbuktu")

>>> multiple_elements_string
['T', 'i', 'm', 'b', 'u', 'k', 't', 'u']

# Unicode separators and positioning code points are also added *individually*.
>>> multiple_code_points_string = list('अभ्यास')

>>> multiple_code_points_string
['अ', 'भ', '्', 'य', 'ा', 'स']

# The iteration default for dictionaries is over the keys, so only key data is inserted into the list.
>>> source_data = {"fish": "gold", "monkey": "brown"}
>>> list(source_data)
['fish', 'monkey']

Poiché il costruttore list() accetta come argomenti solo iterabili (o niente), gli oggetti che non sono iterabili sollevano un TypeError. Di conseguenza, è molto più semplice creare un array con un solo elemento usando il metodo letterale.

# Numbers are not iterable, and so attempting to create a list with a number passed to the constructor fails.
>>> one_element = list(16)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'int' object is not iterable

# Tuples *are* iterable, so passing a one-element tuple to the constructor does work, but it's awkward
>>> one_element_from_iterable = list((16,))

>>> one_element_from_iterable
[16]

Accesso agli elementi

Gli elementi dentro un array (così come gli elementi di altri tipi di sequenza, ad esempio str e tuple) si possono recuperare usando la notazione a parentesi. Gli indici possono andare da left --> right (partendo da zero) oppure da right --> left (partendo da -1).

indice da sinistra ⟹






0
👇🏾
1
👇🏾
2
👇🏾
3
👇🏾
4
👇🏾
5
👇🏾
P y t h o n
👆🏾
-6
👆🏾
-5
👆🏾
-4
👆🏾
-3
👆🏾
-2
👆🏾
-1





⟸ indice da destra
>>> breakfast_foods = ["Oatmeal", "Fruit Salad", "Eggs", "Toast"]

# Oatmeal is at index 0 or index -4.
>>> breakfast_foods[0]
'Oatmeal'

>>> breakfast_foods[-4]
'Oatmeal'

# Eggs are at index -2 or 2
>>> breakfast_foods[-2]
'Eggs'

>>> breakfast_foods[2]
'Eggs'

# Toast is at -1
>>> breakfast_foods[-1]
'Toast'

Si può accedere a una sezione di un array tramite la notazione di slicing (<list>[<start>:<stop>]). Una slice è definita come una sequenza di elementi in posizione index, tale che start <= index < stop. Lo slicing restituisce una copia degli elementi «affettati» e non modifica l'array originale.

Nello slicing si può usare anche un parametro step (<list>[<start>:<stop>:<step>]) per «saltare» o filtrare gli elementi restituiti (ad esempio, uno step di 2 seleziona un elemento sì ed uno no della sezione):

>>> colors = ["Red", "Purple", "Green", "Yellow", "Orange", "Pink", "Blue", "Grey"]

# If there is no step parameter, the step is assumed to be 1.
>>> middle_colors = colors[2:6]

>>> middle_colors
['Green', 'Yellow', 'Orange', 'Pink']

# If the start or stop parameters are omitted, the slice will
# start at index zero, and will stop at the end of the list.
>>> primary_colors = colors[::3]

>>> primary_colors
['Red', 'Yellow', 'Blue']

Lavorare con gli array

Gli array forniscono un iteratore e si possono scorrere allo stesso modo degli altri tipi di sequenza, usando for item in <list> oppure for index, item in enumerate(<list>):

# Make a list, and then loop through it to print out the elements
>>> colors = ["Orange", "Green", "Grey", "Blue"]
>>> for item in colors:
...     print(item)

Orange
Green
Grey
Blue


# Print the same list, but with the indexes of the colors included
>>> colors = ["Orange", "Green", "Grey", "Blue"]
>>> for index, item in enumerate(colors):
...     print(item, ":", index)

Orange : 0
Green : 1
Grey : 2
Blue : 3


# Start with a list of numbers and then loop through and print out their cubes.
>>> numbers_to_cube = [5, 13, 12, 16]
>>> for number in numbers_to_cube:
...     print(number**3)

125
2197
1728
4096

Un modo comune per comporre un array di valori è usare <list>.append() dentro un ciclo:

>>> cubes_to_1000 = []
>>> for number in range(11):
...    cubes_to_1000.append(number**3)

>>> cubes_to_1000
[0, 1, 8, 27, 64, 125, 216, 343, 512, 729, 1000]

Gli array si possono anche combinare con diverse tecniche:

# Using the plus + operator unpacks each list and creates a new list, but it is not efficient.
>>> new_via_concatenate = ["George", 5] + ["cat", "Tabby"]

>>> new_via_concatenate
['George', 5, 'cat', 'Tabby']

# Likewise, using the multiplication operator * is the equivalent of using + n times.
>>> first_group = ["cat", "dog", "elephant"]
>>> multiplied_group = first_group * 3

>>> multiplied_group
['cat', 'dog', 'elephant', 'cat', 'dog', 'elephant', 'cat', 'dog', 'elephant']

# Another method for combining 2 lists is to use slice assignment or a loop-append.
# This assigns the second list to index 0 in the first list.
>>> first_one = ["cat", "Tabby"]
>>> second_one = ["George", 5]
>>> first_one[0:0] = second_one

>>> first_one
['George', 5, 'cat', 'Tabby']

# This loops through the first list and appends its items to the end of the second list.
>>> first_one = ["cat", "Tabby"]
>>> second_one = ["George", 5]

>>> for item in first_one:
...      second_one.append(item)

>>> second_one
['George', 5, 'cat', 'Tabby']

Alcune avvertenze

Ricorda che in Python le variabili sono etichette che puntano a oggetti sottostanti. I lists aggiungono un ulteriore livello, quello degli oggetti contenitore: contengono riferimenti agli oggetti che raccolgono. Se non vengono gestiti correttamente, questo può causare diversi problemi quando si lavora con gli array.

Assegnare più di un nome di variabile

Assegnare un oggetto list a un nuovo nome di variabile non copia l'oggetto list né i suoi elementi. Qualsiasi modifica fatta agli elementi di list sotto il nuovo nome si ripercuote sull'originale.

Fare una shallow_copy con list.copy() o con lo slicing evita questo problema di riferimenti di primo livello. Una shallow_copy crea un nuovo oggetto list, ma non crea nuovi oggetti per gli elementi contenuti. Questo tipo di copia di solito basta per aggiungere o rimuovere elementi dai due oggetti list in modo indipendente, ottenendo di fatto due array «separati».

>>> actual_names = ["Tony", "Natasha", "Thor", "Bruce"]

# Assigning a new variable name does not make a copy of the container or its data.
>>> same_list = actual_names

#  Altering the list via the new name is the same as altering the list via the old name.
>>> same_list.append("Clarke")
["Tony", "Natasha", "Thor", "Bruce", "Clarke"]

>>> actual_names
["Tony", "Natasha", "Thor", "Bruce", "Clarke"]

#  Likewise, altering the data in the list via the original name will also alter the data under the new name.
>>> actual_names[0] = "Wanda"
['Wanda', 'Natasha', 'Thor', 'Bruce', 'Clarke']

# If you copy the list, there will be two separate list objects which can be changed independently.
>>> copied_list = actual_names.copy()
>>> copied_list[0] = "Tony"

>>> actual_names
['Wanda', 'Natasha', 'Thor', 'Bruce', 'Clarke']

>>> copied_list
["Tony", "Natasha", "Thor", "Bruce", "Clarke"]

Questa complicazione con i riferimenti peggiora quando si lavora con array annidati o moltiplicati (gli esempi seguenti provengono dall'ottimo post del 2013 di Ned Batchelder, Names and values: making a game board):

from pprint import pprint

# This will produce a game grid that is 8x8, pre-populated with zeros.
>>> game_grid = [[0]*8]*8

>>> pprint(game_grid)
[[0, 0, 0, 0, 0, 0, 0, 0],
 [0, 0, 0, 0, 0, 0, 0, 0],
 [0, 0, 0, 0, 0, 0, 0, 0],
 [0, 0, 0, 0, 0, 0, 0, 0],
 [0, 0, 0, 0, 0, 0, 0, 0],
 [0, 0, 0, 0, 0, 0, 0, 0],
 [0, 0, 0, 0, 0, 0, 0, 0],
 [0, 0, 0, 0, 0, 0, 0, 0]]

# An attempt to put a "X" in the bottom right corner.
>>> game_grid[7][7] = "X"

# This attempt doesn't work because all the rows are referencing the same underlying list object.
>>> pprint(game_grid)
[[0, 0, 0, 0, 0, 0, 0, 'X'],
 [0, 0, 0, 0, 0, 0, 0, 'X'],
 [0, 0, 0, 0, 0, 0, 0, 'X'],
 [0, 0, 0, 0, 0, 0, 0, 'X'],
 [0, 0, 0, 0, 0, 0, 0, 'X'],
 [0, 0, 0, 0, 0, 0, 0, 'X'],
 [0, 0, 0, 0, 0, 0, 0, 'X'],
 [0, 0, 0, 0, 0, 0, 0, 'X']]

Ma in questo caso una shallow_copy basta per ottenere il comportamento che vorremmo:

from pprint import pprint

# This loop will safely produce a game grid that is 8x8, pre-populated with zeros
>>> game_grid = []
>>> filled_row = [0] * 8
>>> for row in range(8):
...    game_grid.append(filled_row.copy()) # This is making a new shallow copy of the inner list object each iteration.

>>> pprint(game_grid)
[[0, 0, 0, 0, 0, 0, 0, 0],
 [0, 0, 0, 0, 0, 0, 0, 0],
 [0, 0, 0, 0, 0, 0, 0, 0],
 [0, 0, 0, 0, 0, 0, 0, 0],
 [0, 0, 0, 0, 0, 0, 0, 0],
 [0, 0, 0, 0, 0, 0, 0, 0],
 [0, 0, 0, 0, 0, 0, 0, 0],
 [0, 0, 0, 0, 0, 0, 0, 0]]

# An attempt to put a "X" in the bottom right corner.
>>> game_grid[7][7] = "X"

# The game grid now works the way we expect it to!
>>> pprint(game_grid)
[[0, 0, 0, 0, 0, 0, 0, 0],
 [0, 0, 0, 0, 0, 0, 0, 0],
 [0, 0, 0, 0, 0, 0, 0, 0],
 [0, 0, 0, 0, 0, 0, 0, 0],
 [0, 0, 0, 0, 0, 0, 0, 0],
 [0, 0, 0, 0, 0, 0, 0, 0],
 [0, 0, 0, 0, 0, 0, 0, 0],
 [0, 0, 0, 0, 0, 0, 0, 'X']]

Come accennato prima, gli array sono contenitori di riferimenti, quindi c'è un secondo livello di possibile complicazione. Se un array contiene variabili, oggetti o strutture dati annidate, quei riferimenti di secondo livello non vengono copiati da shallow_copy né dallo slicing. Modificare gli oggetti sottostanti avrà effetto su tutte quante le copie, dato che ogni oggetto list contiene solo riferimenti che puntano agli elementi contenuti.

from pprint import pprint

>>> pprint(game_grid)
[[0, 0, 0, 0, 0, 0, 0, 0],
 [0, 0, 0, 0, 0, 0, 0, 0],
 [0, 0, 0, 0, 0, 0, 0, 0],
 [0, 0, 0, 0, 0, 0, 0, 0],
 [0, 0, 0, 0, 0, 0, 0, 0],
 [0, 0, 0, 0, 0, 0, 0, 0],
 [0, 0, 0, 0, 0, 0, 0, 0],
 [0, 0, 0, 0, 0, 0, 0, 'X']]

# We'd like a new board, so we make a shallow copy.
>>> new_game_grid = game_grid.copy()

# But a shallow copy doesn't copy the contained references or objects.
>>> new_game_grid[0][0] = 'X'

# So changing the items in the copy also changes the originals items.
>>>  pprint(game_grid)
[['X', 0, 0, 0, 0, 0, 0, 0],
 [0, 0, 0, 0, 0, 0, 0, 0],
 [0, 0, 0, 0, 0, 0, 0, 0],
 [0, 0, 0, 0, 0, 0, 0, 0],
 [0, 0, 0, 0, 0, 0, 0, 0],
 [0, 0, 0, 0, 0, 0, 0, 0],
 [0, 0, 0, 0, 0, 0, 0, 0],
 [0, 0, 0, 0, 0, 0, 0, 'X']]

Tipi di dati correlati

Gli array si usano spesso come stack e code, anche se la loro implementazione sottostante rende lente l'aggiunta in testa e l'inserimento. Il modulo collections offre una variante deque ottimizzata per aggiungere e rimuovere elementi velocemente da entrambe le estremità, implementata come una lista doppiamente collegata. Gli array annidati si usano anche per rappresentare piccole matrici, anche se le librerie Numpy e Pandas sono molto più solide per manipolare in modo efficiente matrici e dati tabellari. Il modulo collections fornisce anche un tipo UserList, che si può personalizzare per rispondere a esigenze particolari di un array.

Modifica tramite GitHub Il collegamento si apre in una nuova finestra o scheda

Impara Array

La pratica è bloccata

Sblocca 5 altri esercizi per esercitarti su Array