В этой статье мы рассмотрим некоторые встроенные структуры данных в Python. Некоторые из этих структур данных популярны и используются в каждой программе на Python, которую вы будете писать, например, списки, словари и кортежи.
Другие структуры данных менее популярны и используются в определенных случаях, например наборы, frozensets, memoryview и Namedtuples. Каждая из этих встроенных структур данных имеет свои преимущества и недостатки, и мы должны выбрать правильную для нашего конкретного случая использования.
Структура данных, описанная в этой статье:
- Список
- Кортеж
- Набор
- Замороженный
- Словари
- массив.массив
- Numpy-массив
- Дек
- NamedTuple
1. Список
В Python списки используются для хранения нескольких значений в одной переменной. Они похожи на массивы в C/C++, но с дополнительными функциями. Списки могут хранить различные типы данных в списке. Кроме того, списки изменяемы, то есть значение элементов списка может быть изменено.
Списки поддерживают индексацию, нарезку, вставку, извлечение и многое другое. Это самая универсальная структура данных, доступная в Python, и одна из самых популярных.
Списки обычно занимают много памяти по сравнению с arrays.array или numpy.array, поскольку каждый элемент представляет собой полноценный объект Python с указателями и счетчиком ссылок.
Большие затраты в списках возникают, когда мы создаем список, превышающий выделенное в настоящее время пространство, которое составляет 26 элементов. В этот момент Python должен выделить новое место для списка, скопировать содержимое и освободить старое пространство. Это операция O(n).
Кроме того, когда мы вставляем или удаляем элементы в начале списка, Python должен переместить все элементы, чтобы освободить место или заполнить пробел соответственно. Это тоже операция O(n).
Мы должны использовать списки, когда вам нужна простая структура данных общего назначения, и вы не слишком заботитесь о потреблении памяти или производительности, используйте списки. Или, когда нам нужна структура данных, которую мы можем динамически изменять, используйте списки.
Основные операции
# Creating a list lst1 = [1,2,3,4,5] lst2 = list([1,2,3,4,5]) # Adding an item to the list lst1.append(6) # Removing an item from the list lst1.remove(6) # Accessing an item in the list print(lst1[0]) # Slicing a list print(lst1[0:2]) # Changing values in a list lst1[0] = 0 # Deleting a list del lst1 # Sorting a list sorted_list = sorted(lst2) # Iterating over a list for item in lst2: print(item)
Выход:
1 [1, 2] 1 2 3 4 5
Кортеж
Мы можем думать о кортеже как о неизменном списке.
Кортежи также могут хранить несколько типов данных, но как только мы создадим кортеж, мы не сможем изменить его значения. Также кортежи поддерживают многие операции, которые делают списки, такие как индексирование, нарезка и len().
Кортежи считаются более быстрыми и эффективными, чем списки, когда в коллекции большое количество элементов.
Мы должны использовать кортежи, когда нам нужно хранить большое количество элементов, и нам не нужно изменять список, используйте кортежи. Или, когда нам нужна структура данных, поддерживающая индексирование, нарезку и len(), используйте кортежи.
Основные операции
# Creating a tuple tup1 = (1,2,3,4,5) tup2 = tuple((1,2,3,4,5)) # Accessing an item in the tuple print(tup1[0]) # Slicing a tuple print(tup1[0:2]) # Counting the number of occurrences of an item in a tuple tup1.count(1) # Finding the index of an item in a tuple tup1.index(1) # Deleting a tuple del tup1 # Iterating over a tuple for item in tup2: print(item) # Modifying a tuple is not possible tup2[0] = 0 # --> TypeError: 'tuple' object does not support item assignment
Выход:
1 (1, 2) 1 2 3 4 5 --------------------------------------------------------------------------- TypeError Traceback (most recent call last) /projects/Cheatsheet/My Python Cheatsheet.ipynb Cell 1597 in 1 16 print(item) 18 # Modifying a tuple is not possible ---> 19 tup2[0] = 0 TypeError: 'tuple' object does not support item assignment
Наборы
Наборы — это неупорядоченные наборы уникальных элементов. Они используются, когда мы хотим хранить коллекцию уникальных элементов и не заботимся о порядке. Когда мы добавляем повторяющиеся элементы в набор, только один из них будет добавлен в набор.
Элементы наборов неизменны, но мы можем добавлять и удалять элементы из них. Поскольку наборы неизменяемы, они также неиндексированы, то есть мы не можем получить доступ к элементам в наборе, ссылаясь на индекс.
Однако наборы очень эффективны для проверки того, входит ли элемент в набор или нет. Они поддерживают различные математические операции, такие как объединение, пересечение, разность и симметричная разность.
Наборы следует использовать, когда мы хотим хранить коллекцию уникальных элементов и не заботимся о порядке. Или, когда мы постоянно хотим проверить, есть предмет в наборе или нет.
Кроме того, множества можно использовать, когда мы хотим выполнять групповые операции над несколькими множествами, такими как объединение, пересечение, разность и симметричная разность.
Основные операции
# Creating a set set1 = {1,2,3,4,5} set2 = set([1,2,3,4,5]) # Adding an item to a set set1.add(6) # Removing an item from a set set1.remove(6) # iterating over a set for item in set1: print(item) # Remove duplicates from a list lst = [1,2,3,4,5,5,5,5,5,5,5,5,5,5,5,5] set(lst) lst = list(set(lst)) # Sets do not support item assignment set1[1] = 2 # TypeError: 'set' object does not support item assignment # Concatenating two sets set3 = set1.union(set2) # Finding the intersection of two sets set3 = set1.intersection(set2) # Finding the difference between two sets set3 = set1.difference(set2) # Finding the symmetric difference between two sets set3 = set1.symmetric_difference(set2) # We can use operators to perform the same operations set3 = set1 | set2 # union set3 = set1 & set2 # intersection set3 = set1 - set2 # difference set3 = set1 ^ set2 # symmetric difference # Note, using operators is more efficient than using the methods
Выход:
1 2 3 4 5 --------------------------------------------------------------------------- TypeError Traceback (most recent call last) /projects/Cheatsheet/My Python Cheatsheet.ipynb Cell 1599 in 1 13 print(item) 15 # Sets are immutable ---> 16 set1[1] = 2 # TypeError: 'set' object does not support item assignment 18 # Concatenating two sets 19 set3 = set1.union(set2) TypeError: 'set' object does not support item assignment
Наборы не хэшируются и не могут использоваться в качестве ключей словаря или элементов других наборов. Например:
# Creating a set set1 = {1,2,3,4,5} set2 = set([1,2,3,4,5]) # Creating a dictionary dict1 = {set1: 1, set2: 2} # TypeError: unhashable type: 'set'
Выход:
TypeError Traceback (most recent call last) /projects/Cheatsheet/My Python Cheatsheet.ipynb Cell 1601 in 2 1 # Creating a dictionary ----> 2 dict1 = {set1: 1, set2: 2} TypeError: unhashable type: 'set'
Хешируемость означает, что объект имеет хеш-значение, которое не меняется в течение его жизни. Если объект имеет хеш-значение, которое не меняется, его можно использовать в качестве ключа в словаре.
Замороженный
Замороженный набор — это встроенный тип, который имеет характеристики набора, но его элементы нельзя изменить после назначения, и мы не можем добавлять или удалять элементы. Они представляют собой неупорядоченный и неиндексированный набор уникальных элементов.
Они поддерживают операции над множествами, такие как объединение, пересечение, разность и симметричная разность. Кроме того, их можно хешировать, поэтому мы можем использовать их как ключи в словаре.
# Creating a frozenset frozenset1 = frozenset([1,2,3,4,5]) frozenset2 = frozenset({1,2,3,4,5}) # We can use frozensets as keys in a dictionary dict1 = {frozenset1: 1} # They support all the set operations frozenset3 = frozenset1.union(frozenset2) print(frozenset3) frozenset3 = frozenset1.intersection(frozenset2) print(frozenset3) frozenset3 = frozenset1.difference(frozenset2) print(frozenset3) frozenset3 = frozenset1.symmetric_difference(frozenset2) print(frozenset3) # Again, we can use operators to perform the same operations frozenset3 = frozenset1 | frozenset2 # union frozenset3 = frozenset1 & frozenset2 # intersection frozenset3 = frozenset1 - frozenset2 # difference frozenset3 = frozenset1 ^ frozenset2 # symmetric difference # We cannot add or remove items from a frozenset frozenset1.add(6) # AttributeError: 'frozenset' object has no attribute 'add' frozenset1.remove(6) # AttributeError: 'frozenset' object has no attribute 'remove'
Выход:
frozenset({1, 2, 3, 4, 5}) frozenset({1, 2, 3, 4, 5}) frozenset() frozenset() --------------------------------------------------------------------------- AttributeError Traceback (most recent call last) /projects/Cheatsheet/My Python Cheatsheet.ipynb Cell 1605 in 2 26 frozenset3 = frozenset1 ^ frozenset2 # symmetric difference 28 # We cannot add or remove items from a frozenset ---> 29 frozenset1.add(6) # AttributeError: 'frozenset' object has no attribute 'add' 30 frozenset1.remove(6) AttributeError: 'frozenset' object has no attribute 'add'
Словари
Словарь — это фундаментальная структура данных в Python и основной компонент спецификации языка Python.
Словари Python сильно оптимизированы и продолжают улучшаться. Хеш-таблицы — это двигатель высокопроизводительных словарей Python. Другими встроенными типами, основанными на хэш-картах, являются наборы и замороженные наборы.
Словари представляют собой упорядоченный и изменяемый набор пар ключ-значение. Кроме того, поскольку они построены как хеш-таблицы, они очень эффективны для поиска и вставки. Однако цена заключается в том, что они ожидают, что ключи будут хешируемыми объектами.
Они основаны на предположении, что ключи уникальны в пределах словаря. Если бы мы использовали один и тот же ключ для двух разных значений или изменили бы ключ, это сведет на нет цель использования хэш-таблицы и приведет к неожиданным результатам.
Вот почему мы можем использовать строки, целые числа и кортежи в качестве ключей словаря, но мы не можем использовать списки или наборы в качестве ключей, потому что они изменяемы.
Словари используются, когда мы хотим хранить набор пар ключ-значение и эффективно извлекать их.
Основные операции
# Create a dictionary dict1 = {'key1': 1, 'key2': 2, 'key3': 3} dict2 = {'key4': 4, 'key5': 5, 'key6': 6} # Accessing a value in a dictionary print(dict1['key1']) # Adding a new key-value pair to a dictionary dict1['key4'] = 4 # Changing the value of a key in a dictionary dict1['key1'] = 0 # Deleting a key-value pair from a dictionary del dict1['key1'] # Iterating over a dictionary keys for key in dict1.keys(): print(key) # Iterating over a dictionary values for value in dict1.values(): print(value) # Iterating over a dictionary items for key, value in dict1.items(): print(key, value) # They also support the union operator like sets and frozensets dict3 = dict1 | dict2 # union
Выход:
1 key2 key3 key4 2 3 4 key2 2 key3 3 key4 4
массив.массив
Модуль массива предоставляет класс массива, который можно использовать для создания эффективных массивов других структур данных.
array.array похожи на списки, они намного эффективнее, но ограничены хранением только одного типа данных. Они обеспечивают непрерывный блок памяти для эффективного хранения и операций.
Они изменяемы, поэтому мы можем добавлять и удалять элементы из них. Они также очень эффективны для хранения больших объемов данных и доступа к ним.
Мы должны использовать array.array, когда нам нужно хранить большое количество данных подобного типа и эффективно обращаться к ним.
Основные операции
from array import array # Creating an array of ints arr1 = array('i', [1,2,3,4,5]) # Creating an array of floats arr2 = array('f', [1.0,2.0,3.0,4.0,5.0]) # Creating an array of chars arr3 = array('u', ['a','b','c','d','e']) # Accessing an item in an array print(arr1[0]) # Slicing an array print(arr1[0:2]) # Changing an item in an array arr1[0] = 0 # Insert and remove items from an array arr1.insert(0, 1) arr1.remove(1) # We can check the size of an array in bytes print(arr1.itemsize) # 4 bytes # We can check the memory address of an array and the number of elements in it print(arr1.buffer_info()) # Iterating over an array for item in arr1: print(item) # Deleting an array del arr1 # We can also use the built-in list data structure to create arrays arr1 = list([1,2,3,4,5]) arr1 = array('i', arr1)
Выход:
1 array('i', [1, 2]) 4 (140590972114224, 5) 0 2 3 4 5
пустые массивы
Массивы Numpy похожи на улучшенную версию структуры данных массива. Они более гибкие и эффективные, чем массивы. Они также более удобны в использовании, чем массивы.
Они обеспечивают поддержку больших многомерных массивов и матриц, а также большую коллекцию высокоуровневых математических функций для работы с этими массивами.
Они построены на основе языка программирования C, что делает их очень быстрыми и эффективными. Они могут хранить элементы разных типов, в отличие от массивов, которые могут хранить элементы только одного типа.
Они изменяемы, в отличие от кортежей, наборов и замороженных наборов.
Они упорядочены, в отличие от наборов и замороженных наборов.
Они повторяемы
И самое главное, они очень эффективны.
Мы должны использовать массивы numpy, когда нам нужно хранить большое количество данных и выполнять над ними математические, линейные алгебраические или статистические операции.
Основные операции
import numpy as np # Creating an array from a list arr1 = np.array([1,2,3,4,5]) # Creating an array of zeros arr1 = np.zeros(5) # Creating an array of ones arr1 = np.ones(5) # Creating an array of a specific value arr1 = np.full(5, 5) # Creating a matrix arr1 = np.array([[1,2,3],[4,5,6],[7,8,9]]) # math operations arr1 = np.array([1,2,3,4,5]) arr2 = np.array([1,2,3,4,5]) arr3 = arr1 + arr2 arr3 = arr1 - arr2 arr3 = arr1 * arr2 arr3 = arr1 / arr2 arr3 = arr1 ** arr2 arr3 = arr1 % arr2 arr3 = arr1 // arr2 arr3 = np.abs(arr1) # absolute value arr3 = np.sin(arr1) # sine arr3 = np.cos(arr1) # cosine arr3 = np.tan(arr1) # tangent arr3 = np.exp(arr1) # e^x arr3 = np.log(arr1) # natural log arr3 = np.log10(arr1) # log base 10 arr3 = np.log2(arr1) # log base 2 arr3 = np.sqrt(arr1) # square root arr3 = np.cbrt(arr1) # cube root arr3 = np.square(arr1) # square arr3 = np.power(arr1, 2) # power arr3 = np.power(arr1, arr2) # power arr3 = np.add(arr1, arr2) # addition arr3 = np.subtract(arr1, arr2) # subtraction arr3 = np.multiply(arr1, arr2) # multiplication arr3 = np.divide(arr1, arr2) # division arr3 = np.remainder(arr1, arr2) # remainder arr3 = np.floor_divide(arr1, arr2) # floor division arr3 = np.negative(arr1) # negative # Idexing and slicing arr1[0:2] # Iterating over an array for item in arr1: print(item) # Deleting an array del arr1
Дек
Deque — это двусторонняя очередь. Его можно использовать для добавления или удаления элементов с обоих концов.
Двухсторонние очереди поддерживают потокобезопасные, эффективно использующие память операции добавления и извлечения с любой стороны двухсторонней очереди с примерно одинаковой производительностью O(1) в любом направлении.
Оказывается, мы можем использовать списки в качестве стеков и очередей. Методы append и pop могут превратить список в полезную очередь FIFO.
Append вставит элемент в конец списка, а pop удалит последний элемент. Списки на самом деле довольно эффективны для этого. Однако вставка и удаление из начала списка требует больших затрат, поскольку весь список должен перемещаться в памяти.
collections.deque предназначен для быстрой вставки и удаления с обоих концов. Когда удаление из очереди заполнено и мы добавляем элемент в очередь, он автоматически отбрасывает элемент с противоположного конца.
Единственным недостатком деков является то, что они медленны при удалении элементов из середины. Мы должны использовать deque, когда нам нужно добавить или удалить элементы с обоих концов коллекции.
Основные операции
from collections import deque # Creating a deque deque1 = deque([1,2,3,4,5]) # Adding an item to the right of a deque deque1.append(6) # Adding an item to the left of a deque deque1.appendleft(0) # Removing an item from the right of a deque deque1.pop() # Removing an item from the left of a deque deque1.popleft() # Accessing an item in a deque print(deque1[0]) # Iterating over a deque for item in deque1: print(item) # Deleting a deque del deque1 # Reverse a deque deque1.reverse() # Rotate a deque deque1.rotate(1) # rotate right # Clear a deque deque1.clear()
Именованные кортежи
Именованные кортежи похожи на смесь словарей и кортежей. Это кортежи, у которых есть имя для каждой возможной позиции. Они создаются с помощью функции namedtuple() из модуля collections.
Они неизменяемы, и доступ к ним можно получить с помощью точечной нотации, что делает их более удобочитаемыми и самодокументируемыми.
Именованные кортежи используются, когда мы хотим получить доступ к элементам кортежа, используя имена вместо индексов. На самом деле они могут быть отличной альтернативой ручному определению класса.
Мы должны использовать именованные кортежи, когда хотим получить доступ к элементам кортежа, используя имена вместо индексов. Или когда мы хотим определить класс с несколькими атрибутами и не хотим определять полный класс.
from collections import namedtuple # Creating a namedtuple Point = namedtuple('Point', ['x', 'y']) # Creating a Point point1 = Point(1, 2) # Accessing a value in a namedtuple print(point1.x) print(point1.y) # Unpacking a namedtuple x, y = point1 # Iterating over a namedtuple for value in point1: print(value) # Deleting a namedtuple del point1
У NamedTuples также есть некоторые дополнительные функции, которых нет у обычных кортежей.
- Мы можем вернуть кортеж имен полей namedtuple, используя атрибут _fields.
- Мы можем вернуть еще один namedtuple с другим значением, используя метод _replace.
- Мы также можем заменить значения namedtuple, используя метод _replace.
- Мы можем вернуть OrderedDict имен полей и значений namedtuple, используя метод _asdict.
Например:
from collections import namedtuple # Creating a namedtuple Person = namedtuple('Person', ['First_Name', 'Last_Name']) # Creating a Person person1 = Person('John', 'Doe') # Return a tuple of the fields print(person1._fields) # Return a new Person object with the fields replaced person1 = person1._replace(First_Name='Jane') # Return a new Person object with the fields replaced person1 = person1._replace(Last_Name='Smith') # Return an OrderedDict built from Person print(person1._asdict())
Выход:
('First_Name', 'Last_Name') {'First_Name': 'Jane', 'Last_Name': 'Smith'}