Вступление
В этом руководстве мы рассмотрим две самые популярные структуры данных Python - словари и массивы . Каждый из них обеспечивает определенный способ организации ваших данных с указанием плюсов и минусов для определенных задач, а также знание того, когда использовать, что позволит вам использовать встроенные функции.
Примечание: это руководство предполагает Python 3.x , и большая его часть ориентирована на версии после этого. Однако мы также отметим некоторые ключевые отличия для Python 2.x.
Руководство по массивам Python
Массив - одна из фундаментальных структур данных в информатике - последовательность из 0..n элементов, где каждый элемент имеет индекс.
Большинство массивов имеют фиксированный размер, поэтому они занимают часть памяти каждый раз, когда создается новый:
{.ezlazyload}
Здесь у нас есть простой массив, состоящий из 7 элементов. Индексирование обычно начинается с 0 , и каждый элемент имеет позиционный индекс, который мы можем использовать для доступа к нему. Это делает временную сложность доступа к массиву равной O (1) .
Большинство массивов Python динамически типизируются , что означает, что объекты массива имеют тип, но сам массив не ограничен только одним типом - у вас может быть массив, состоящий из целого числа, строки и объекта или даже другого массива, который также неоднородно смешан.
Есть 6 важных типов массивов в Python: list
, tuple
, str
,
bytes
, bytearray
и array.array
.
Говоря о каждом из них, мы будем учитывать несколько ключевых свойств:
- Независимо от того, динамичны они или нет
- Типизируются ли они статически или динамически
- Независимо от того , что они изменяемые или неизменяемых
Списки Python
Список в Python является динамическим (нефиксированный размер), динамически типизированным (элементы не ограничиваются одним типом) и изменяемым (элементы можно изменять на месте).
В Python список определяется объявлением его элементов в квадратных
скобках []
. Давайте продолжим и определим список:
myList = [1, 2, 3, "Mark", "John", "Emma"]
print(myList)
Он содержит несколько целых чисел и несколько строк, обозначающих имена. Поскольку списки динамически типизируются , это разрешено:
[1, 2, 3, 'Mark', 'John', 'Emma']
Поскольку списки динамические , мы можем изменить количество элементов, добавив новый, например:
myList.append(4)
myList.append("Peter")
print(myList)
В результате в нашем списке 8 элементов вместо 6, которые мы определили в начале:
[1, 2, 3, 'Mark', 'John', 'Emma', 4, 'Peter']
Теперь попробуем заменить элемент и добавить новый. Мы проверим идентификатор списка (ссылка в памяти), чтобы убедиться, что он не отключен под капотом новой копией, содержащей либо добавленные элементы, либо замененные:
myList = [1, 2, 3, "Mark", "John", "Emma", 4, "Peter"]
# Print original list and its ID
print('Original list: ', myList)
print('ID of object in memory: ', id(myList))
# Modify existing element and add a new one
myList[4] = "Anna"
myList.append("Dan")
# Print changed list and its ID
print('Changed list: ', myList)
print('ID of object in memory: ', id(myList))
Выполнение этого кода приводит к:
Original list: [1, 2, 3, 'Mark', 'John', 'Emma', 4, 'Peter']
ID of object in memory: 140024176315840
Changed list: [1, 2, 3, 'Mark', 'Anna', 'Emma', 4, 'Peter', 'Dan']
ID of object in memory: 140024176315840
Тот факт, что myList
указывает на один и тот же объект в памяти (
140024176315840
), дополнительно показывает, как списки изменяемы
.
Примечание: списки Python могут даже хранить функции в последовательности:
def f1():
return "Function one"
def f2():
return "Function two"
def f3():
return "Function three"
listOfFunctions = [f1, f2, f3]
print(listOfFunctions)
Что приведет к:
[<function f1 at 0x0000016531807488>, <function f2 at 0x00000165318072F0>, <function f3 at 0x0000016531807400>]
Наш вывод состоит из функций по заданным адресам. Теперь попробуем получить доступ к функции и запустить ее:
print(listOfFunctions[0]())
Поскольку первым элементом этого списка является f1()
, мы ожидаем,
что будет запущен print()
Function one
Списки - это наиболее часто используемый тип массивов в Python. Они просты в использовании и интуитивно понятны. Кроме того, их временная сложность для доступа к элементам составляет O (1) .
Кортежи Python
Кортеж в Python не является динамическим (фиксированный размер), динамически типизированным (элементы не ограничиваются одним типом) и неизменяемым (элементы не могут быть изменены на месте).
Кроме того, при их определении мы используем обычные скобки ()
myTuple = (1, 2, 3, "Mark", "John", "Emma")
print(myTuple)
Поскольку кортежи динамически типизируются , в них могут присутствовать элементы разных типов:
(1, 2, 3, 'Mark', 'John', 'Emma')
Поскольку кортежи не являются динамическими , они имеют фиксированный
размер, и мы не можем append()
на месте, поскольку это меняет их
размер. Таким образом, кортежи не имеют метода append()
Однако мы можем создать новый кортеж, состоящий из меньших кортежей, который снова имеет фиксированный размер:
myTuple = (1, 2, 3)
anotherTuple = ("Mark", "John", "Emma")
print('Original tuple: ', myTuple)
print('ID of object in memory: ', id(myTuple))
myTuple = myTuple + anotherTuple
print('New tuple: ', myTuple)
print('ID of object in memory: ', id(myTuple))
Мы назначили одну и ту же ссылку на переменную новому объекту, созданному, чтобы содержать оба этих кортежа вместе - хотя ссылочная переменная одинакова, она указывает на совершенно другой объект в памяти:
Original tuple: (1, 2, 3)
ID of object in memory: 139960147395136
New tuple: (1, 2, 3, 'Mark', 'John', 'Emma')
ID of object in memory: 139960147855776
Временная сложность доступа к элементам в кортеже также составляет O (1) .
Строки Python
В Python 3 str
(сокращение от String ) переработан с Python 2. В
Python 2 он использовался для представления как текста, так и байтов, но
начиная с Python 3 - это два совершенно разных типа данных.
Строка в Python не является динамической (фиксированный размер), статически типизированной (элементы ограничены одним типом) и неизменяемой (элементы не могут быть изменены на месте).
Последовательность байтов (в удобочитаемых символах), заключенная в
круглые скобки ""
, используется для определения строки:
myStr = "qwerty"
print(myStr)
Это приведет к:
qwerty
Мы можем получить доступ к элементам через стандартную индексацию массива, но не можем их изменить:
print(myStr[0])
myStr[0] = "p"
Это приведет к:
q
TypeError: 'str' object does not support item assignment
Фактически - строки рекурсивны . Когда мы объявляем строку с использованием символов - формируется строка для каждого символа , которая затем добавляется к списку строк, составляющих другую строку .
myStr
имеет длину 5 и состоит из пяти отдельных строк длиной 1:
myStr = "abcde"
print(len(myStr)) # Check the length of our str
print(type(myStr)) # Check the type of our str
print(myStr[0]) # Letter 'a'
print(len(myStr[0])) # Check the length of our letter
print(type(myStr[0])) # Check the type of our letter 'a'
Это приводит к:
5
<class 'str'>
a
1
<class 'str'>
И наш «символ», и строка относятся к одному классу - str
.
Подобно кортежам, мы можем объединять строки, в результате чего получается новая строка, состоящая из двух меньших:
myStr = "qwerty"
myStr2 = "123"
result = myStr + myStr2
print(result)
И вот результат:
qwerty123
Опять же, строки поддерживают только символы, и мы не можем смешивать их с другими типами:
myStr = "qwerty"
myStr2 = 123
result = myStr + myStr2
print(result)
Что приведет к:
TypeError: can only concatenate str (not "int") to str
Однако, int
, а также любой другой тип может быть преобразован
(преобразованы) в строковое представление:
myStr = "qwerty"
myStr2 = str(123) # int 123 is now casted to str
result = myStr + myStr2
print(result)
Это приведет к:
qwerty123
С помощью этого метода вы можете уйти с печатью, например, int
s и
string
s в одной строке:
myStr = "qwerty"
print("myStr's length is: " + len(myStr)) # TypeError
print("myStr's length is: " + str(len(myStr))) # String concatenation resulting in 'myStr's length is: 6'
Байты Python
Байты в Python являются нединамическими (фиксированный размер), статически типизированными (элементы ограничены одним типом) и неизменяемыми (элементы не могут быть изменены на месте).
Объект bytes
состоит из нескольких одиночных байтов или целых чисел в
диапазоне от 0
до 255
(8 бит).
Определение bytes
немного отличается от других массивов, поскольку мы
явно должны преобразовать кортеж bytes
:
myBytes = bytes((0, 1, 2))
print(myBytes)
Это приведет к:
b'\x00\x01\x02'
Если кортеж содержит элементы разных типов, TypeError
:
myBytes = bytes((0, 1, 2, 'string'))
TypeError: 'str' object cannot be interpreted as an integer
При работе с str
массив bytes
должен быть закодирован с помощью
кодировки, иначе будет неоднозначно, что они представляют:
myStr = "This is a string"
myBytes = bytes(myStr) # this will result in an error TypeError: string argument without an encoding
myBytes = bytes(myStr, 'utf-8')
print(myBytes) # this will print out myStr normally
Если вы не знакомы с тем, как bytes
прочтите наше руководство по
преобразованию байтов в строку в
Python .
Кроме того, bytes
массив целых чисел может быть изменен при
преобразовании в другой тип массива, называемый bytearray
.
Python Bytearray
Bytearray в Python является динамическим (нефиксированный размер), статически типизированным (элементы ограничены одним типом) и изменяемым (элементы можно изменять на месте).
myByteArray = bytearray((0, 1, 2))
Теперь мы можем попробовать добавить элементы в этот массив, а также изменить элемент:
myByteArray = bytearray((0, 1, 2))
print(myByteArray)
print("ByteArray ID: ", id(myByteArray))
myByteArray.append(3)
print(myByteArray)
print("ByteArray ID: ", id(myByteArray))
myByteArray[3] = 50
print(myByteArray)
print("ByteArray ID: ", id(myByteArray))
Это приводит к:
bytearray(b'\x00\x01\x02')
ByteArray ID: 140235112668272
bytearray(b'\x00\x01\x02\x03')
ByteArray ID: 140235112668272
bytearray(b'\x00\x01\x022')
ByteArray ID: 140235112668272
Все они имеют один и тот же идентификатор объекта - указывающий на один и тот же изменяемый объект в памяти.
bytearray
bytes
может быть преобразован обратно в массив байтов;
однако имейте в виду, что это дорогостоящая операция, которая занимает
O (n) времени.
Python array.array
До сих пор мы работали со встроенными типами. Однако в модуле массива
существует другой тип array
Этот array
является динамическим (нефиксированного размера),
статически типизированным (элементы ограничены одним типом) и
изменяемым (можно изменять на месте). Нам нужно явно указать тип,
который мы будем использовать в array
и эти типы относятся к типам
C-стиля: 32-битные целые числа, числа с плавающей запятой, числа типа
double и т. Д.
У каждого из них есть маркер - i
для целых чисел, f
для чисел с
плавающей запятой и d
для чисел типа double. Создадим целочисленный
массив через модуль array
import array
myArray = array.array("i", (1, 2, 3, 4))
Некоторые из наиболее часто используемых C-подобных типов:
{.ezlazyload}
Руководство по словарям Python
Словарь - это центральная структура данных в Python. Он хранит данные в парах ключ-значение.
Благодаря этому ее также можно назвать картой , хеш-картой или таблицей поиска .
Есть несколько разных вариантов словаря:
dict
collections.defaultdict
collections.OrderedDict
collections.ChainMap
Словари полагаются на хеш- значения, которые определяют ключи для операции поиска. Хеш- таблица содержит множество хеш-значений, которые никогда не меняются в течение всего времени существования хеш-таблицы .
Хешируемый тип и хеш-значения
Каждый объект имеет хэш- значение, и для его получения можно
использовать метод hash()
Это значение не является постоянным и
рассчитывается во время выполнения, хотя, если a == b
, hash(a)
всегда будет равен hash(b)
:
randomString = "This is a random string"
a = 23
b = 23.5
print(hash(randomString))
print(hash(a))
print(hash(b))
Этот код приведет к чему-то вроде:
4400833007061176223
23
1152921504606846999
Примечание: равные числовые значения имеют одно и то же хеш-значение, независимо от их типа:
a = 23
b = 23.0
print(hash(a))
print(hash(b))
Результаты в:
23
23
Именно этот механизм делает словари в Python невероятно быстрыми - уникальные идентификаторы для каждого элемента, что дает им время поиска O (1) .
Словарь Python
Содержимое словаря ( dict
) определяется в фигурных скобках {}
.
Синтаксис похож на JSON, учитывая пары ключ-значение:
myDict = {
"name": "Mike James",
"age": 32,
"country": "United Kingdom"
}
Словарь может иметь произвольное количество пар, и ключи должны быть хешируемыми без каких-либо повторяющихся ключей (повторяющиеся ключи приводят к одному и тому же хешу). В таких случаях первый ключ будет отклонен, и словарь фактически будет содержать только второй ключ.
Поскольку словари изменяемы, мы можем добавить новую пару ключ-значение, просто «обратившись» к несуществующему ключу и установив его значение:
myDict["countries_visited"] = ["Spain", "Portugal", "Russia"]
print(myDict)
Это приведет к:
{'name': 'Mike James', 'age': 34, 'country': 'United Kingdom', 'countries_visited': ['Spain', 'Portugal', 'Russia']}
dict
Python, вероятно, решит большинство ваших проблем, но если нет,
есть несколько типов словарей, которые можно импортировать из
библиотеки, называемой collections
.
Python DefaultDict
Проблема, с которой вы можете столкнуться при использовании dict
- это
попытка получить доступ к значению несуществующего ключа.
Например, в нашей предыдущей демонстрации, если бы мы получили доступ к
print(myDict["zip_code"])
, мы бы получили KeyError: zip_code
поскольку zip_code
не существует.
Это когда defaultdict
вступает в игру, поскольку он запрашивает
default_factory
- функцию, которая возвращает значение по умолчанию,
если ключ отсутствует. Таким образом, defaultdict
никогда не может
вызвать KeyError
:
from collections import defaultdict
def safe_function(): # default_factory
return "Value not defined"
myDict = defaultdict(safe_function)
myDict["name"] = "Mark James"
myDict["age"] = 32
print(myDict["country"]) # This will output Value not defined and not raise a KeyError
Это, как и ожидалось, приводит к:
Value not defined
Определение defaultdict
отличается от базового dict
потому что
каждая пара ключ-значение должна определяться «вручную», что более
утомительно, чем синтаксис, подобный JSON.
Python ChainMap
Этот тип словарей позволяет нам объединить несколько словарей в один - объединить их в цепочку. При доступе к данным он будет искать ключ один за другим, пока не найдет первый правильный:
from collections import ChainMap
myDict1 = {
"name": "Mike James",
"age": 32
}
myDict2 = {
"name": "James Mike",
"country": "United Kingdom",
"countries_visited": ["Spain", "Portugal", "Russia"]
}
myDictResult = ChainMap(myDict1, myDict2)
print(myDictResult)
В результате получается ChainMap
:
ChainMap({'name': 'Mike James', 'age': 32}, {'name': 'James Mike', 'country': 'United Kingdom', 'countries_visited': ['Spain', 'Portugal', 'Russia']})
Примечание: мы можем определить повторяющиеся ключи. 'name'
присутствует в обоих словарях. Хотя, когда мы пытаемся получить доступ к
ключу 'name'
print(myDictResult['name'])
Он находит первый совпадающий ключ:
Mike James
Также имейте в виду, что они все еще могут вызывать KeyError
,
поскольку сейчас мы работаем с основным dict
.
Python OrderedDict
Примечание. Начиная с Python 3.6, словари по умолчанию упорядочены по вставке.
OrderedDict
используется, когда вы хотите сохранить порядок вставки
пар ключ-значение в словарь. dict
не гарантирует этого, и вы можете
получить другой порядок вставки, отличный от хронологического.
Если это не главное - можете спокойно пользоваться словарем. Однако,
если это важно, например, при работе с датами , вы захотите вместо
этого OrderedDict
from collections import OrderedDict
orderedDict = OrderedDict()
orderedDict['a'] = 1
orderedDict['b'] = 2
orderedDict['c'] = 3
orderedDict['d'] = 4
print(orderedDict)
Это приводит к:
OrderedDict([('a', 1), ('b', 2), ('c', 3), ('d', 4)])
Примечание. Несмотря на то, что dict
сохраняют порядок вставки в
Python 3.6, используйте OrderedDict
если требуется порядок
вставки. Ваш код не гарантирует порядок вставки в других версиях
Python (более ранних), если вы используете обычный dict
.
Методы словаря против методов массива
Теперь, когда мы разобрались с вещами, мы должны рассмотреть все методы, реализованные в этих двух типах. С данными можно выполнять четыре основных операции: доступ (получение) , обновление , добавление , удаление .
Давайте определим массив и словарь, с которыми мы будем экспериментировать:
exampleDict = {
"id": 101,
"name": "Marc Evans",
"date_of_birth": "13.02.1993.",
"city": "Chicago",
"height": 185,
}
exampleArray = [1, 2, 3, "red", "green", "yellow", "blue", 4]
Получение данных
Словарь: есть несколько способов получить доступ к данным в словаре:
-
Ссылаясь на имя ключа -
myDict["key_name"]
:- print(exampleDict[“name”]) # Output: Marc Evans
-
Вызов метода
get()
myDict.get("key_name")
:- print(exampleDict.get(“city”)) # Output: Chicago
-
Доступ ко всем ключам в словаре -
myDict.keys()
- возвращает список ключей:- print(exampleDict.keys()) # Output: dict_keys([‘id’, ’name’, ‘date_of_birth’, ‘city’, ‘height’])
-
Доступ ко всем значениям в словаре -
myDict.values()
- возвращает список значений:- print(exampleDict.values()) # Output: dict_values([101, ‘Marc Evans’, ‘13.02.1993.’, ‘Chicago’, 185])
-
Доступ ко всем парам ключ-значение:
myDict.items()
- возвращает кортеж пар ключ-значение:- print(exampleDict.items()) # Output: dict_items([(‘id’, 101), (’name’, ‘Marc Evans’), (‘date_of_birth’, ‘13.02.1993.’), (‘city’, ‘Chicago’), (‘height’, 185)]
Массив: есть только один способ получить данные из массива:
-
Ссылаясь на индекс элемента -
myArray[index_number]
:- print(exampleArray[3]) # Output: red
Обновление данных
Словарь: есть 2 способа обновить данные в словаре:
-
Непосредственная установка нового значения для определенного ключа -
myDict["key"] = new_value
:- exampleDict[“height”] = 190 print(exampleDict[“height”]) # Output: 190
-
Вызов метода
update()
myDict.update({"key": new_value})
- аргументы метода должны быть словарем:- exampleDict.update({“height”: 190}) print(exampleDict[“height”]) # Output: 190
Массив: если массив изменяемый, его можно изменить аналогично получению данных:
-
Ссылаясь на индекс элемента и устанавливая другое значение:
myArray[index_number] = new_value
- exampleArray[3] = “purple” print(exampleArray) # Output: [1, 2, 3, ‘purple’, ‘green’, ‘yellow’, 4, ‘blue’]
Добавить данные
Словарь: Есть 2 способа добавить данные в словарь:
-
Установка значения для нового ключа, который автоматически создаст пару ключ-значение и добавит ее:
myDict["new_key"] = value
:- exampleDict[“age”] = 45 print(exampleDict) # Output: {‘id’: 101, ’name’: ‘Marc Evans’, ‘date_of_birth’: ‘13.02.1993.’, ‘city’: ‘Chicago’, ‘height’: 185, ‘age’: 45}
-
Вызов метода
update()
myDict.update({"new_key": value})
:- exampleDict.update({“age”: 45})
Массив: есть несколько способов добавить данные в массив (хотя массив должен быть изменяемым):
-
Вызов метода
append()
myArray.append(new_element)
- добавляетnew_element
в конецmyArray
:- exampleArray.append(“grey”) print(exampleArray) # Output: [1, 2, 3, “purple”, “green”, “yellow”, “blue”, 4, “grey”]
-
Вызов метода
insert()
-myArray.insert(index_number, new_element)
- вставляетnew_element
в позициюindex_number
:- exampleArray.insert(0, 0) print(exampleArray) # Output: [0, 1, 2, 3, “purple”, “green”, “yellow”, “blue”, 4, “grey”]
-
Вызов метода
extend()
myArray.extend(myArray2)
- вставляет элементыmyArray2
в конецmyArray
:- exampleArray2 = [5, 6] exampleArray.extend(exampleArray2) print(exampleArray) # Output: [0, 1, 2, 3, “purple”, “green”, “yellow”, “blue”, 4, “grey”, 5, 6]
Удаление данных
Словарь: есть несколько способов удалить данные из словаря:
-
Вызов метода
pop()
-myDict.pop("key_name")
- берет имя удаляемого ключа-
exampleDict.pop(“name”) print(exampleDict)
# {'id': 101, 'date_of_birth': '13.02.1993.', 'city': 'Chicago', 'height': 185}
-
-
Вызов
popitem()
-myDict.popitem()
- в Python 3.7+ удаляет последнюю добавленную пару ключ-значение, а в версиях Python ниже 3.7 удаляет случайную пару ключ-значение:-
exampleDict.popitem() print(exampleDict)
#{'id': 101, 'name': 'Marc Evans', 'date_of_birth': '13.02.1993.', 'city': 'Chicago'}
-
-
Использование ключевого слова
del
del myDict["key_name"]
-
del exampleDict[’name’] print(exampleDict)
# {'id': 101, 'date_of_birth': '13.02.1993.', 'city': 'Chicago', 'height': 185} # del dict deletes the entire dictionary del exampleDict print(exampleDict) # NameError: name 'exampleDict' is not defined
-
-
Вызов метода
clear()
myDict.clear()
- очищает словарь, но он все равно будет существовать как пустой{}
-
exampleDict.clear() print(exampleDict)
# {}
-
Массив: удалить данные из массива можно несколькими способами:
-
Вызов метода
pop()
-myArray.pop(index_number)
- удаляет элемент с указаннымindex_number
:-
exampleArray.pop(2) print(exampleArray)
# [1, 2, 'red', 'green', 'yellow', 'blue', 4]
-
-
Вызов метода
remove()
myArray.remove(value)
- удаляет первый элемент с указаннымvalue
:-
exampleArray.remove(2) print(exampleArray)
# [1, 3, 'red', 'green', 'yellow', 'blue', 4]
-
-
Вызов метода
clear()
-myArray.clear()
- как и в словаре, он удаляет все элементы из массива, оставляя пустой[]
:-
exampleArray.clear() print(exampleArray)
# []
-