В этой статье мы рассмотрим некоторые встроенные структуры данных в Python. Некоторые из этих структур данных популярны и используются в каждой программе на Python, которую вы будете писать, например, списки, словари и кортежи.

Другие структуры данных менее популярны и используются в определенных случаях, например наборы, frozensets, memoryview и Namedtuples. Каждая из этих встроенных структур данных имеет свои преимущества и недостатки, и мы должны выбрать правильную для нашего конкретного случая использования.

Структура данных, описанная в этой статье:

  1. Список
  2. Кортеж
  3. Набор
  4. Замороженный
  5. Словари
  6. массив.массив
  7. Numpy-массив
  8. Дек
  9. 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'}