Словари и массивы в Python - глубокое погружение

Введение В этом руководстве мы рассмотрим две самые популярные структуры данных Python - словари и массивы. Каждый из них обеспечивает определенный способ организации ваших данных с указанием плюсов и минусов для определенных задач, а также знание того, когда использовать, что позволит вам использовать встроенные функции. Примечание. В этом руководстве предполагается, что Python 3.x, и большая его часть ориентирована на более поздние версии. Однако мы также отметим некоторые ключевые отличия Python 2.x. Руководство по массивам Python Массив

Вступление

В этом руководстве мы рассмотрим две самые популярные структуры данных 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-подобных типов:

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)

      # [] 
      
Licensed under CC BY-NC-SA 4.0
comments powered by Disqus