Основы Python
В этом разделе излагаются основные принципы использования языка программирования Python для создания моделей машинного обучения.Python представляет собой высокоуровневый язык программирования с большими возможностями, который не требует компиляции, но в то же время обеспечивает определенную эффективность, сопоставимую с языками программирования на уровне машинного кода. Этот язык зарекомендовал себя как наиболее предпочтительный инструмент для создания инструментов и приложений в сфере машинного обучения....
Ключевые идеи
- необходимое программное обеспечение
- Модули Python
- Краткая информация о Python
- Типы переменных в Python: Основы и примеры
- <class 'str'>
- <class 'float'>
Практическое задание
Возьмите небольшой список или словарь данных, преобразуйте его в NumPy-массив и посчитайте две простые статистики. Свяжите результат с главой "Основы Python".
Основы Python
2.1 необходимое программное обеспечение
В этом разделе излагаются основные принципы использования языка программирования Python для создания моделей машинного обучения.Python представляет собой высокоуровневый язык программирования с большими возможностями, который не требует компиляции, но в то же время обеспечивает определенную эффективность, сопоставимую с языками программирования на уровне машинного кода. Этот язык зарекомендовал себя как наиболее предпочтительный инструмент для создания инструментов и приложений в сфере машинного обучения. Благодаря обширной коллекции открытых и постоянно обновляемых библиотек, Python отличается своей функциональной мощью. Он идеально подходит для эффективной работы в области программирования и написания кода. Python также отличается быстродействием и обладает структурой, оптимально подходящей для работы с большими объемами данных, что является ключевым элементом в машинном обучении.
Эта глава не является формальным обучением Python, а лишь помогает читателям легче начать изучение и практическое применение материалов последующих глав. Мы сосредоточимся на некоторых полезных простых трюках, и на некоторых тонкостях поведения, которые часто влияют на наше кодирование в ML. Все коды представленные в этом учебнике и в этой главе в частности можно найти на https://sohoware.ru/SohoBook/ Читатели, знакомые с Python, могут просто пропустить эту главу.
Для установки Python, перейдите на официальный сайт https://www.python.org/downloads/, выберите версию 3.10.X и скачайте установочный файл для вашей ОС. Важно отметить опцию "Add Python to PATH" в процессе установки, чтобы обеспечить доступ к Python из командной строки. После завершения установки проверьте её, введя в командной строке python–version (или python3 –version для MacOSили Linux), что должно вывести номер версии Python.

Для установки Visual Studio Code (VS Code), посетите сайт https://code.visualstudio.com/ и загрузите версию для вашей ОС. После установки откройте VS Code и установите расширение Python через Marketplace, используя боковую панель расширений. Это позволит оптимизировать среду для разработки на Python, предоставив удобные инструменты и функции для написания и тестирования кода.

2.2Модули Python
Давайте сделаем менее традиционное вступление. В отличие от других книг по компьютерным языкам, мы начинаем обсуждение с того, как использовать наши собственные коды, которые мы можем разработать в процессе обучения.
Прежде всего, важно ознакомиться с системой модулей Python, которая позволяет расширять функциональные возможности вашего кода за счет использования предварительно разработанных библиотек. Например, модуль sys предоставляет доступ к некоторым переменным и функциям, взаимодействующим с интерпретатором Python.
importsys# Импортируем модуль sys для работы с системными параметрами
sys.path.append('grbin') # Добавляем путь к каталогу с пользовательскими модулями
# Можно раскомментировать следующую строку для просмотра текущего списка путей
# print(sys.path) # Вывод списка путей, где Python ищет модулиКомментарии в коде начинаются с символа # и предназначены для пояснения работы кода, делая его более понятным для других разработчиков.
Для более длинных пояснений или документации в коде используются многострочные комментарии, оформленные в виде строк с тройными кавычками. Это позволяет вставлять обширные описания без необходимости начинать каждую строку с #.
'''
Это многострочный комментарий, где можно описать более сложную логику или дать подробные инструкции.
Использование многострочных комментариев улучшает читаемость кода и облегчает его поддержку.
'''
Для демонстрации мы создадим простой модуль grcodes.py, который будет содержать функцию printx. Эта функция предназначена для демонстрации способа вывода имени и значения переменной.

importsys# Импортируем модуль sys для работы с системными параметрами
# Определение функции в модуле grcodes.py
defprintx(name):"""
Функция выводит имя переменной и ее значение.
Пример использования: name = 88.0; printx('name')"""
frame = sys._getframe(1)
print(name, '=', repr(eval(name, frame.f_globals, frame.f_locals)))Использование функции printx демонстрирует, как можно упростить вывод информации, минимизируя повторения и уменьшая вероятность ошибок при изменении кода.

importgrcodesasgr# Импортируем пользовательский модуль и задаем ему псевдоним для удобства
x = 1.0# Присваиваем значение переменной
print(x) # Стандартный вывод значения переменной
gr.printx('x') # Использование функции printx из модуля gr для вывода имени и значения переменной
# Получение справки по функции printx
help(gr.printx)Обратите внимание, что при использовании fromgrcodesimportprintx, ссылка gr. перед printx становится необязательной. Это делает код более лаконичным и удобным для чтения.
2.3Краткая информация о Python
Python – это высокоуровневый, интерпретируемый язык программирования, разработанный Гвидо ванРоссумом в конце 1980-х и впервые представленный общественности в 1991 году. Основная цель создания Python – облегчение процесса программирования за счет упрощения чтения кода и его написания. Python поддерживает множество парадигм программирования, включая процедурную, объектно-ориентированную и функциональную, что делает его подходящим для различных типов проектов.
Философия Python заключается в стремлении к простоте и элегантности, а также в обеспечении высокой читаемости кода. Это достигается за счет использования значительных отступов и минималистичного синтаксиса. "Дзен Python" (PEP 20) излагает ключевые принципы, лежащие в основе дизайна Python, например, "Простое лучше, чем сложное" и "Читаемость имеет значение". Эти принципы направлены на то, чтобы программисты писали лаконичный и понятный код.
Python активно развивается и поддерживается мировым сообществом разработчиков. Python Software Foundation (PSF) – некоммерческая организация, которая способствует развитию языка и поддерживает его открытую экосистему.
С момента своего создания Python претерпел значительные изменения. Python 3, представленный в 2008 году, стал серьезным обновлением, внесшим множество улучшений и изменений, несовместимых с Python 2. Это привело к долгому переходному периоду, в течение которого обе версии сосуществовали, но в конечном итоге поддержка Python 2 была прекращена в 2020 году, чтобы сосредоточить усилия сообщества на развитии Python 3.
В современной разработке на Python значительную роль играют интегрированные среды разработки (IDE), которые предоставляют разработчикам обширные инструменты для написания кода, его тестирования и отладки. Среди множества доступных IDE, Visual Studio Code (VSCode) занимает особое место благодаря своей гибкости, мощности и расширяемости. VSCode, разработанный Microsoft, предлагает широкий спектр функций для разработки на Python, включая интеграцию с Git, отладку, управление зависимостями, автоматическое дополнение кода и многое другое.
VSCode поддерживает работу с Python через расширения, такие как Python Extensionfor Visual Studio Code, которое обеспечивает поддержку интеллектуальной подсказки кода, отладки, тестирования, форматирования кода, а также управления виртуальными средами. Это делает VSCode идеальным выбором для разработчиков на Python любого уровня квалификации, от новичков до профессионалов.
Для улучшения взаимодействия с кодом и повышения производительности работы в VSCode, рекомендуется установить набор базовых расширений, начиная с Python Extension, которое облегчает многие задачи разработки, включая написание, тестирование и отладку кода. Установка расширений в VSCode происходит через встроенный магазин расширений, доступ к которому можно получить непосредственно из среды IDE.
Для активации расширения Python и начала работы с Python в VSCode, выполните следующие шаги:1. Откройте VSCode и перейдите в магазин расширений, нажав на иконку расширений в боковой панели или используя комбинацию клавиш Ctrl+Shift+X.
2. Введите в поисковую строку "Python" и выберите расширение Python от Microsoft.
3. Нажмите кнопку "Установить" для добавления расширения в вашу среду разработки.
После установки расширения,VSCode автоматически определит интерпретаторы Python, установленные в вашей системе, и предложит их для использования в ваших проектах. Вы также сможете настроить среду разработки в соответствии с вашими предпочтениями, включая выбор интерпретатора, настройку путей к исполняемым файлам, конфигурацию отладчика и многое другое.
2.4Типы переменных в Python: Основы и примеры
Python считается объектно-ориентированным языком. Каждая переменная в Python является объектом. Он "динамически типизирован": тип переменной определяется в момент ее объявления. Вам не нужно заранее объявлять переменные или определять их типы. В Python есть несколько основных типов переменных: числа и строки. Эти переменные могут существовать самостоятельно или формировать кортежи (Tuple), списки (List), словари (Dictionary), множества (Set), массивы Numpy и т.д. Над всеми этими типами можно проводить различные операции (арифметические, логические, форматирование и т.д.) в коде. Обратите внимание, что переменная определяется довольно свободно, это может быть кортеж, список, словарь и т.д. Например, список может находиться внутри списка, кортеж в списке или список в кортеже.
Python поддерживает три типа чисел - целые числа, числа с плавающей точкой и комплексные числа.Чтобы определить целочисленную переменную, можно просто присвоить ей целое число.
my_int = 48 # с этим присваиванием my_int становится целым числом
print(my_int); printx('my_int')
48 myint = 48
type(my_int) # Проверить тип переменной. print() не нужен,
# если это последняя строка в ячейкеint
my_int = 5.0 # теперь my_int становится числом с плавающей точкой
type(my_int)float
my_complex = 8.0 + 5.0j # такимобразомmy_complexявляетсякомплекснымчислом
print(my_complex)
printx('my_complex')
(8+5j) my complex = (8+5j)
my_int, my_float, my_string = 20, 10.0, "Hello!"ifmy_string == "Hello!": # операторысравнения: ==,!=, <, <=, >, >=
print("Строка: %s" % my_string) # Отступ 4 пробелаifisinstance(my_float, float) andmy_float == 10.0: # isinstance(): возвращает, являетсялиобъектэкземпляромкласса
print("Это число с плавающей точкой, и оно равно %f" % my_float)
ifisinstance(my_int, int) andmy_int == 20:
print("Это целое число, и оно равно: %d" % my_int)
Строка: Hello!Это число с плавающей точкой, и оно равно 10.000000
Это целое число, и оно равно: 20
Тип переменной может быть преобразован:
my_float = 5.0# присваиваем my_float значение 5.0, тем самым переменная становится типа float
print(my_float) # выводим значение переменной my_float
my_float = float(6) # создаем число с плавающей точкой, преобразуя целое число с помощью float()
print(my_float) # выводим значение переменной my_float
print(int(7.0)) # преобразуем float в int и выводим
print('int(7.0)') # выводим строку 'int(7.0)'5.0
6.0
int(7.0)
Чтобы проверить адрес переменной в памяти, используйте:
a = 1.0
print('a =', a, 'поадресупамяти:', id(a))
a = 1.0 по адресу памяти: 140703208811472
b = a
print('b =', b, 'по адресу памяти:', id(b))
b = 1.0 по адресу памяти: 140703208811472Обратите внимание, что у 'b' тот же адрес, что и у 'a'.
a, b = 2.0, 3.0
print('a =', a, 'поадресупамяти:', id(a))
print('b =', b, 'по адресу памяти:', id(b))
a = 2.0 по адресу памяти: 4365829648
b = 3.0 по адресу памяти: 4365829264Обратите внимание на изменение адреса при изменении значения переменной.
Нижнее подчеркивание (или символ подчеркивания) _ играет несколько уникальных ролей в Python. Давайте рассмотрим некоторые из них.
n1 = 100000000000
n_1 = 100_000_000_000# для удобства чтения
# Тернарное условное выражение
print('Да, n1 такоеже, какn_1') ifn1 == n_1elseprint('Нет')
n2 = 1_000_000_000
print('Итого=', n1 + n2)
# f-строка (Python3.6 или позже) итог=n1+n2
print('Итого=', f'{n1 + n2:,}')
# Ошибка в следующей строке: переменная 'total' не определена
# Чтобы исправить, изменим 'total' на 'n1 + n2'
print('Итого=', f'{n1 + n2:_}')Да, n1 такое же, как n_1
Итого= 101000000000
Итого= 101,000,000,000
Итого= 101_000_000_0002.4.1Строки
Строки представляют собой фрагменты текста, для создания меток и имен файлов результатов работы программы. Строки можно определить как любую последовательность символов, заключенную в кавычки. В качестве кавычек можно использовать как пару одинарных, так и пару двойных кавычек.
my_string = "Как дела? Как твое настроение?"
# строка определена, символы в ней можно индексировать
print(my_string, my_string[14],my_string[15], my_string[16], my_string[17], my_string[18:])
my_string = 'Привет,' + " привет!" + " Я здесь."
# заметьте, оператор "+" для строк выполняет конкатенацию
print(my_string)Какдела? Как твое настроение? т в о е настроение?
Привет, привет! Я здесь.
Хотя можно использовать как одинарные, так и двойные кавычки, при наличии апострофов в строке следует использовать двойные кавычки, иначе эти апострофы завершат строку, если использовать одинарные кавычки, и наоборот. Например,
my_string = "Не волнуйтесь, просто используйте двойные кавычки для 'экранирования'."
print(my_string)Не волнуйтесь, просто используйте двойные кавычки для 'экранирования'.
Можно поменять местами эти два типа кавычек:
my_string = 'Не волнуйтесь о "двойных кавычках".'
print(my_string)Не волнуйтесь о "двойных кавычках".
Обращайтесь к документации Python, когда необходимо включить такие элементы, обратные слэши и символы Unicode. Ниже приведены некоторые удобные и чистые операторы, применяемые к числам и строкам. Вы можете попробовать это на практике и получить некоторый опыт.
one, two, three = 1, 2, 3# Присваиваем значения переменным.
summation = one + two + three
print('сумма=', summation) # Выводим 'сумма'
сумма= 6
one, two, three = 1, 2, 3.0# Типы переменных могут быть разными!
summation = one + two + three
print('Суммирование=', summation) # Выводим 'Суммирование'
Суммирование= 6.0
one3 = two3 = three3 = 3# Присваиваем одинаковое значение переменным
print(one3, two3, three3)3 3 3
Большеудобныхопераций:
hello, world = "Hello,", "world!"
# Склеиваемстроки
helloworld = hello + " " + world + "!!"
print(helloworld, ' ', hello + " " + world)
# Получаем длину строки, учитывая пробелы и знаки препинания.
lhw = len(helloworld)
print('Длинастроки "helloworld" составляет', lhw)Hello, world!!! Hello, world!
Длинастроки "helloworld" составляет 17
Вы можете разбить строку на список строк, каждая из которых является словом.
hello, world = "Hello,", "world!"
helloworld = hello + " " + world + "!!"
the_words = helloworld.split(" ") # Создаетсписокстрок
# Аналогичные операции со списками позже
print("Разделяем слова строки: %s" % the_words)
print('Соединяем обратно с использованием пробела в качестве разделителя:', ' '.join(the_words))
Разделяем слова строки: ['Hello,', 'world!!!']
Соединяем обратно с использованием пробела в качестве разделителя: Hello, world!!!
Чтобы найти букву (символ) в строке, попробуйте сделать следующее:
my_string = "Helloworld!"
print('"o" находитсясразупосле', my_string.index("o"), 'йбуквы.')
print('Первая буква "l" находится сразу после', my_string.index("l"), 'й буквы.')"o" находится сразу после 4 й буквы.
Первая буква "l" находится сразу после 2 й буквы.
В этом примере мы используем оператор + для конкатенации (соединения) строк, чтобы избежать лишних пробелов между числом и суффиксом:
my_string = "Helloworld!"
# "+" выполняетконкатенацию
print('Позициябуквы "o" сразупосле ' + str(my_string.index("o")) + '-йбуквы.')
print('1-я буква "l" находится сразу после ' + str(my_string.index("l")) + '-й буквы.')
Позиция буквы "o" сразу после 4-й буквы.
1-я буква "l" находится сразу после 2-й буквы.Вам может понадобиться найти частоту встречаемости каждого элемента в списке.
fromcollectionsimportCounter# Импортмодуля Counter
my_list = ['а', 'а', 'б', 'б', 'б', 'в', 'г', 'г', 'г', 'г', 'г']
count = Counter(my_list) #Объект Counter являетсясловарем
print(count) # Вывод частоты каждого элемента в списке
# Смотрите также Словарь позже
{'а': 2, 'б': 3, 'в': 1, 'г': 5}
В этом примере буква 'а' встречается в списке 2 раза, буква 'б' - 3 раза, буква 'в' - 1 раз, а буква 'г' - 5 раз.
print('Частота встречаемости "б" равна', count['б'])
# частота элемента, индексированного его ключомЧастота встречаемости "б" равна 3
Таким образом, буква "б" встречается в предоставленном списке 3 раза.
Обратите внимание, что Python (и многие другие языки программирования) начинает считать с 0, а не с 1.Ниже мы перечислим другие операции, которые могут быть полезны.
Преобразование между заглавными и строчными буквами в строке:
my_string = "Привет мир!"# Определение строки
print(my_string.upper(), my_string.lower(), my_string.title()) # Конвертация в верхний и нижний регистр соответственно.ПРИВЕТ МИР! привет мир! Привет Мир!
Возвратстрокиспомощьюнарезки:
my_string = "АБВГДЕЖ"
reversed_string = my_string[::-1]
print(reversed_string)ЖЕДГВБА
Строка "АБВГДЕЖ" была перевёрнута, и результатом является строка "ЖЕДГВБА".
Функцияtitle() классаstring:
my_string = "Россия - страна возможностей"
new_string = my_string.title()
print(new_string)
Россия - Страна ВозможностейМетод title() применён к строке "Россия - страна возможностей", что приводит к тому, что первая буква каждого слова становится заглавной, а все остальные буквы — строчными.
Использование повторов
n = 8
my_list = [1] * n
print(my_list)
[1, 1, 1, 1, 1, 1, 1, 1]Список my_list содержит 8 элементов, каждый из которых является числом 1.
# Объявляем переменную my_string и присваиваем ей значение "абвгдеж"
my_string = "абвгдеж"
# Выводим строку my_string, повторенную два раза
print(my_string * 2)абвгдежабвгдеж
Оператор * в Python используется для умножения строк. В данном случае my_string * 2 означает, что строка my_string будет повторена два раза.
Таким образом, этот код выводит строку "абвгдеж" дважды.
# Объявить переменную lotsofhellos и присвоить ей значение "Hello ", повторённое 5 раз
lotsofhellos = "Привет " * 5
# Вывести значение переменной lotsofhellos
print(lotsofhellos)Привет ПриветПриветПриветПривет
Эта программа работает следующим образом. Первая строка объявляет переменную lotsofhellos и присвоит ей значение "Привет ", повторённое 5 раз. Оператор умножения (*) в Python используется для конкатенации строк. В этом случае он повторяет строку "Привет " 5 раз.
Длина заданного аргумента с помощью функции len():
# Длина строки "АБВГД"
print("Длина строки «АБВГД» равна", len("АБВГД"))Длина строки «АБВГД» равна 5
В этом коде мы используем функцию len() для получения длины строки. Функция len() принимает строку в качестве аргумента и возвращает целое число, равное количеству символов в строке.
# Программа для вывода длины строки
# Инициализируем переменную my_string
my_string = "Hello, world!"
# Выводимнаэкрандлинустрокиmy_string
print("Длинастроки", my_string, "равна", len(my_string))Длина строки "Hello, world!" равна 13
# Объявляем две переменные, содержащие списки
even_numbers, odd_numbers = [2, 4, 6, 8], [1, 3, 5, 7]
# Получаем длину списка even_numbers с помощью функции len()
length = len(even_numbers)
# Объединяем два списка с помощью оператора +
all_numbers = odd_numbers + even_numbers
# Выводим на экран объединенный список, а также исходную и новую длину
print(all_numbers, ' Исходнаядлинаравна', length, '. Новая длина равна', len(all_numbers))
[1, 3, 5, 7, 2, 4, 6, 8] Исходная длина равна 4. Новая длина равна 8Отсортируйтеэлементыспомощьюфункцииsorted():
# Создание списка
all_numbers= [5, 3, 9, 1, 4]
# Вывод на экран отсортированных в алфавитном порядке
#символов русского языка 'ДГВБА' и 'АБВГД' в обратном порядке
print(sorted('ДГВБА'), sorted('АБВГД', reverse=True))
# Вывод на экран отсортированного списка all_numbers
# ввозрастающемиубывающемпорядке
print(sorted(all_numbers), sorted(all_numbers, reverse=True))
['А', 'Б', 'В', 'Г', 'Д'] ['Д', 'Г', 'В', 'Б', 'А']
[1, 3, 4, 5, 9] [9, 5, 4, 3, 1]
Умножение каждого элемента списка на одно и то же число:
# исходный список и переменная
original_list, n = [1, 2, 3, 4], 2
# создание нового списка с помощью генератора списков,
# где каждый элемент исходного списка умножается на n
new_list = [n * xforxinoriginal_list]
# вывод нового списка
print(new_list)
[2, 4, 6, 8]
Создание индекса для списка с помощью функции enumerate():
my_list= ['a', 'b', 'c'] # Создаем список с элементами 'a', 'b', 'c'
# Перебираем список, получая индекс и значение каждого элемента
forindex, valueinenumerate(my_list):
# Выводим индекс (увеличенный на 1) и значение каждого элемента списка
print('{0}: {1}'.format(index+1, value))
1: a
2: b
3: c
my_list = ['a', 'b', 'c'] #Создаемсписоксэлементами 'a', 'b', 'c'forindex, valueinenumerate(my_list): # Генерируеминдексыизначения
print(f'{index+1}: {value}') #Выводиминдекс (увеличенныйна 1) изначениекаждогоэлементасписка, используя f-строку
1: a
2: b
3: c
Исключение ошибкиtry- except- finally отслеживает код, избегая остановки выполнения:
a, b = 1, 2# Присваиваем значения переменным a и b
try:
print(a / b) # Пытаемся выполнить деление
exceptZeroDivisionError:
print("деление на ноль") # Обрабатываем ошибку деления на ноль
else:
# Блок else выполняется, если в блоке try не было исключений
print("исключений не возникло")
finally:
# Блок finally выполняется в любом случае
print("Независимо от того, что произошло, всегда выполняем это")0.5
исключений не возникло
Независимо от того, что произошло, всегда выполняем это
Получить размер памяти в байтах:importsys# Импортируем модуль sys
num = "AAA"
print('Размер памяти составляет %d'%sys.getsizeof(num),'байт')
# Размер памяти строкиРазмер памяти составляет 52 байт
Это интересный результат, так как он демонстрирует, что размер памяти, занимаемой строкой в Python, не только зависит от количества символов в строке, но и включает в себя дополнительные метаданные, такие как длина строки и тип данных. Строка из трех символов занимает значительно больше памяти, чем могло бы показаться на первый взгляд. Это важно учитывать при работе с большими объемами текстовых данных в Python.
num = 12345
print('Размер памяти составляет %d'%sys.getsizeof(num),'байт')
# Размер памяти целого числаРазмер памяти составляет 28 байт
Этот результат демонстрирует, что в Python целые числа также занимают больше памяти, чем просто количество байт, необходимое для хранения самих чисел. Дополнительные байтытакже используются для хранения метаданных о переменной, таких как её тип
Проверьте, начинается ли строка с чего-то или заканчивается чем-то:
# Определение строки на русском языке
astring = "Привет мир!"
# Проверяем начинается ли строка с "Привет", и заканчивается ли она на "абвгд" или "!"
starts_with_privet = astring.startswith("Привет")
ends_with_abvgd = astring.endswith("абвгд")
ends_with_exclamation = astring.endswith("!")
print(starts_with_privet, ends_with_abvgd, ends_with_exclamation)TrueFalseTrue
Мы проверили, начинается ли строка astring, содержащая русский текст "Привет мир!", с подстроки "Привет", и заканчивается ли она подстроками "абвгд" или "!".
Результаты выполнения кода следующие:
Строка astring начинается с подстроки "Привет": True.
Строка astring заканчивается подстрокой "абвгд": False.
Строка astring заканчивается символом "!": True.Большеподобныхфункций:
# Проверяем, состоят ли все символы из чисел
# Проверяем, состоят ли все символы из букв
# Проверяем, написана ли строка в верхнем регистре
my_string = "Приветмир!"
my_string1 = "ПриветМир"
my_string2 = "ПРИВЕТ МИР!"
print(my_string.isalnum()) # все ли символы буквы или цифры
print(my_string1.isalpha()) #вселисимволыбуквы
print(my_string2.isupper()) # написано ли все заглавными буквамиFalse
True
True
Проверка типа и других атрибутов переменных:
n, x, s = 1234, 1234.0, 'это строка'
print(type(n), type(x), type(s)) #проверкатипаобъекта
print(len(s), len(str(n)), len(str(x))) # определение длины строки и строковых представлений чисел
<class 'int'><class 'float'><class 'str'>10 4 6
Результаты выполнения кода следующие:
Типы переменных:n имеет тип int, что означает целое число.
x имеет тип float, что означает число с плавающей точкой (вещественное число).s имеет тип str, что означает строку.
Длины строк:
Длина строки s ("это строка") равна 10 символам.
Длина строкового представления числа n ("1234") равна 4 символам.
Длина строкового представления числа x ("1234.0") равна 6 символам, так как включает в себя цифры, точку и ноль после точки.Эти результаты корректно отражают типы данных и длину строк для заданных переменных.
2.4.2Конвертация типов данных в Python
Когда одна из переменных является числом с плавающей точкой в операции с другими целыми числами, переменная становится числом с плавающей точкой.
a = 2# Инициализация переменной a значением 2
print('a=', a, ' type of a:', type(a)) # Выводзначенияитипапеременной a
b = 3.0# Инициализация переменной b значением 3.0
a = a + b# Суммирование переменных a и b, результат сохраняется в a
# Вывод значений и типов переменных a и b
print('a=', a, ' type of a:', type(a))
print('b=', b, ' type of b:', type(b))
a= 2 type of a: <class 'int'>
a= 5.0 type of a: <class 'float'>
b= 3.0 type of b: <class 'float'>Тип переменной может быть преобразован в другие типы.
n, x, s = 1234, 1234.5, 'это строка'# Инициализация переменных n, x и s
sfn = str(n) # Преобразование целого числа n в строку
print(sfn, type(sfn)) # Вывод значения и типа переменной sfn
sfx = str(x) # Преобразование числа с плавающей точкой x в строку
print(sfx, type(sfx)) # Вывод значения и типа переменной sfx
1234 <class 'str'>1234.5 <class 'str'>
n, x, s = 1234, 1234.5, 'это строка'# Инициализация переменных n, x и s
xfn = float(n) # Преобразование целого числа n в число с плавающей точкой
print(xfn, type(xfn)) # Вывод значения и типа переменной xfn
nfx = int(x) # Преобразование числа с плавающей точкой x в целое число
print(nfx, type(nfx)) # Вывод значения и типа переменной nfx1234.0 <class 'float'>
1234 <class 'int'>
# a = int('Привет') # Преобразование строки 'Привет' в целое число, вызовет ошибку
# ValueError
# a = int('2.5') # Преобразование строки '2.5' в целое число, также вызовет ошибку
# ValueError
a = int('25') # Преобразование строки '25' в целое число, работает корректно
print(a, type(a)) # Вывод значения и типа переменной a после преобразования
25 <class 'int'>Комментарии в коде также объясняют, что попытка преобразовать строки 'Привет' или '2.5' в целое число приведёт к ошибке ValueError, поскольку эти строки не могут быть корректно интерпретированы как целые числа.
# Выполнение операции сложения
result = 8.0 + float("8.0") # Сложение числа с плавающей точкой 8.0 и преобразованного в число с плавающей точкой строки "8.0"
print(result) # Вывод результата операции16.0
Выполнена операция сложения числа 8.0 и строки '8.0', преобразованной в число с плавающей точкой. Результатом этой операции является 16.0.
a = int(False) # Преобразование булевого значения False в целое число
print(a, type(a)) # Вывод значения и типа переменной a после преобразования
0 <class 'int'>
Однако операторы со смешанными числами и строками недопустимы, и это вызывает ошибку TypeError:
# Определение числа с плавающей точкой и строки
my_float = 3.14
my_string = "Этострока-пример"
# Попытка сложить их вместе
try:
my_mix = my_float + my_string# Попытка сложения числа с плавающей точкой и строки
exceptTypeErrorase:
print(f"Ошибка: {e}") # Выводошибки
Ошибка: unsupported operand type(s) for +: 'float' and 'str'2.4.3Искусство Форматирования Данных в Python
Форматирование чрезвычайно полезно при выводе переменных. Python использует строковое форматирование в стиле языка C для создания новых форматированных строк. Оператор "%" используется для форматирования набора переменных, заключенных в "кортеж", который представляет собой список фиксированного размера (это будет обсуждено позже). Это дает в результате обычный текст в месте, указанном одним из "спецификаторов аргументов", таких как "%s", "%d", "%f" и т.д. Лучший способ понять это - через примеры:
name = "Иван"
print("Привет, %s! Как ты?" % name) # %s используется для строк
# для двух или более аргументов используйте кортеж:
name, age = "Алексей", 23
print("%s%dлет."% (name, age)) # %d используется для чиселПривет, Иван! Как ты?
Алексей 23 лет.
Следующий пример демонстрирует использование f-строки в Python, доступное начиная с версии 3.6. F-строки позволяют вставлять значения переменных прямо в строку, что делает код более читаемым и удобным.
# Определяем переменные
name = "Иван"# Имя
age = 23# Возраст
# Выводим приветствие и информацию о возрасте
# Используем f-строку для вставки переменных прямо в строку
print(f"{name}{age}лет.") # f-строка доступна с Python 3.6 и более поздних версийИван 23 лет.
Любой объект, который не является строкой (например, список), также может быть отформатирован с использованием оператора %s. Оператор %s форматирует объект в виде строки с использованием метода "repr" и возвращает его. Например:
list1, list2, x = [1, 2, 3], ['Аня', 'Маша'], 12.5# множественное присваивание
print("Список1:%s; Список2:%s\nx=%s, x=%f, x=%.3f, x=%e" % (list1, list2, x, x, x, x))
x=12.5, x=12.500000, x=12.500, x=1.250000e+01
Во второй строке вывода отображаются значения переменной x в различных форматах:
x=%s - строковый формат.
x=%f - формат с плавающей запятой с двумя десятичными знаками.
x=%.3f - формат с плавающей запятой с тремя десятичными знаками.
x=%e - формат экспоненциальный.
list1, list2, x = [1, 2, 3], ['Аня', 'Маша'], 12.5# множественное присваивание
print(f"List1:{list1};List2:{list2};x={x},x={x:.2f}, x={x:.3e}") #В этой строке выполняется печать значений переменных list1, list2 и x. Форматирование вывода производится с помощью f-строки.
List1:[1, 2, 3];List2:['Аня', 'Маша'];x=12.5,x=12.50, x=1.250e+01
Часто используемые спецификаторы аргументов форматирования (если не используется f-строка):В Python существует специальный способ вставить значения переменных в строку, делая ее более динамичной и информативной. Этот способ называется форматированием строк.
Символы форматирования:
"%s" - Строка или объект с возможностью преобразования в строку (например, числа).
"%d" - Целые числа.
"%f" - Числа с плавающей точкой.
"%.f" - Числа с плавающей точкой с заданным количеством знаков после запятой. Например, "%.2f" выведет число с двумя знаками после запятой.
"%e" - Научная нотация: число с плавающей точкой, умноженное на указанную степень 10.
"%x/%X" - Целые числа в шестнадцатеричной системе счисления (маленькие/большие буквы).
Пример использования:
имя = "Иван"
возраст = 30
рост = 1.82
print(f"Привет, {имя}! Твой возраст - {возраст} лет, а рост - {рост:.2f} метра.")
Привет, Иван! Твой возраст - 30 лет, а рост - 1.82 метра.
В этом примере:Символ f перед строкой указывает, что используется строчное форматирование.
Символы %s используются для вставки значений переменных имя и возраст.
Символ %.2f используется для форматирования значения рост с двумя знаками после запятой.
Дополнительные возможности:
Можно задавать ширину поля для вывода значения: %10d выведет целое число в поле шириной 10 символов.
Можно указывать знаки заполнения пустых позиций: %05d выведет целое число с заполнением ведущих нулей до ширины 5 символов.
Можно указывать выравнивание значения: %<5d выровняет целое число влево в поле шириной 5 символов.2.5Арифметические операторы
Операторы сложения, вычитания, умножения и деления можно использовать с числами.
2.4.1 Сложение, вычитание, умножение, деление и возведение в степень
+ - сложение
- - вычитание
* - умножение
/ - деление
// - целочисленное деление (с отбрасыванием дробной части)
% - остаток от целочисленного деления (остаток или модуль)
** - возведение в степень
Вот несколько примеров использования этих операторов:
# Сложение
print(1 + 2) # 3
# Вычитание
print(5 - 3) # 2
# Умножение
print(2 * 3) # 6
# Деление
print(10 / 2) # 5
# Целочисленное деление
print(10 // 2) # 5
# Остаток от целочисленного деления
print(10 % 2) # 0
# Возведение в степень
print(2 ** 3) # 85.0
# Объявляем переменную number и присваиваем ей значение 1 + 2 * 3 / 4.0
number = 1 + 2 * 3 / 4.0
# Выводим значение переменной number на экран
print(number)2.5
В Python оператор модуля (%) возвращает целочисленный остаток от деления: делимое % делитель = остаток.
# Объявляем две переменные:
# numerator - числитель
# denominator - знаменатель
numerator, denominator = 11, 2
# Вычисляем частное от деления по модулю
# floor division
floor = numerator // denominator
# Выводимрезультатнаэкран
print(str(numerator)+'//'+str(denominator)+ '=', floor)
# Вычисляемостатокотделения
remainder = numerator % denominator
# Выводимрезультатнаэкран
print(str(numerator) +'%'+ str(denominator) +'=', remainder)
# Вычисляемсуммучастногоиостатка
print(floor*denominator + remainder)
11//2= 5
11%2= 111
В Python использование двух символов умножения обозначает возведение в степень.
# Объявляем две переменные:
# squared - квадрат числа 7
# cubed - кубчисла 2
squared, cubed = 7 ** 2, 2 ** 3
# Выводим значения переменных в консоль
print('7 ** 2 =', squared, ', and 2 ** 3 =',cubed)
7 ** 2 = 49,and 2 ** 3 = 8
# Побитовое исключающее ИЛИ (XOR) между числами 7 и 2
test72 = 7 ^ 2
# Вывод результата в консоль
print(test72)Оператор ^ выполняет побитовую операцию XOR над операндами.
Он сравнивает соответствующие биты двух чисел и возвращает 1, если биты различны, и 0, если они совпадают.
Результат операции XOR (5 в данном случае) присваивается переменной test72.Обратите внимание, что оператор ^ в Python используется для побитового XOR, а не для возведения в степень.
В Python допускается простая операция обмена между двумя переменными.
# Объявляем переменные a и b и присваиваем им значения 100 и 200 соответственно
a, b = 100, 200
# Выводим значения переменных a и b
print('a=', a, 'b=', b)
# Меняем значения переменных a и b местами, используя операторы присваивания
a, b = b, a
# Выводим значения переменных a и b
print('a=', a, 'b=', b)
a= 100 b= 200
a= 200 b= 100Как видно из вывода, после выполнения кода значения переменных a и b поменялись местами.
Python предоставляет ряд встроенных функций и типов, которые всегда доступны. Для быстрого ознакомления обратитесь к следующей таблице или найдите более подробную информацию по адресу https://docs.python.org/3/library/functions.html.

2.6 Булевы значения и операторы
Булевы значения - это два постоянных объекта: True и False. При использовании в качестве аргумента арифметического оператора они ведут себя как целые числа 0 и 1 соответственно. Встроенная функция bool() может использоваться для приведения любого значения к булеву.
Определения приведены ниже:
# Выводит True для всех значений, кроме 0 и пустого значения
print(bool(5), bool(-5), bool(0.2), bool(-0.1), bool(str('a')), bool(str('0')))
# True TrueTrueTrueTrueTrue
# Выводит False для 0 и -0.0
print(bool(0), bool(-0.0))
# FalseFalse
# Выводит False дляпустыхзначений
print(bool(''), bool([]), bool({}), bool(()))
# FalseFalseFalseFalseФункция bool() возвращает False, только если значение равно нулю или контейнер пуст. В противном случае - True. Обратите внимание, что str('0') не является ни нулем, ни пустым.
К булевым операторам относятся "и" и "или".
# Первая строка кода выводит три значения:
# - TrueandTrue - логическое И двух истинных значений, результат - True
# - FalseorTrue - логическое ИЛИ двух значений, одно из которых истинно, результат - True
# - TrueorTrue - логическое ИЛИ двух истинных значений, результат - True
print(TrueandTrue, FalseorTrue, TrueorTrue)
# Вторая строка кода выводит два значения:
# - FalseandFalse - логическое И двух ложных значений, результат - False
# - FalseandTrue - логическое И двух значений, одно из которых ложно, результат - False
print(FalseandFalse, FalseandTrue)TrueTrueTrue
FalseFalse
2.7Списки: контейнеры переменного типа
Мы уже несколько раз сталкивались со списками. В этом разделе мы рассмотрим их более подробно.
Список - это набор переменных, и он очень похож на массивы. Список может содержать переменные любого типа и сколько угодно переменных. Эти переменные хранятся в паре квадратных скобок [].
При необходимости по спискам можно выполнять итерации для проведения операций. Это один из "итерируемых" объектов.
2.7.1 Создание, добавление, конкатенация и обновление списков
# Объявляем переменную x_list типа list
x_list = []
# Выводим содержимое переменной x_list на экран
print(f"x_list={x_list}")
# Выводим адрес переменной x_list в шестнадцатеричном формате
print(hex(id(x_list)))
x_list=[]0x104983300
x_list = []
x_list.append(1) # Добавляет число 1 в конец списка x_list
x_list.append(2) # Добавляет число 2 в конец списка x_list
x_list.append(3.) # Добавляет число 3.0 (с плавающей точкой) в конец списка x_list
print(x_list[0]) # Выводит первый элемент списка (индекс 0), который равен 1
print(x_list[1]) # Выводит второй элемент списка (индекс 1), который равен 2
print(x_list[2]) # Выводит третий элемент списка (индекс 2), который равен 3.0
print(x_list) # Выводит весь список целиком3.0
[1, 2, 3.0]
forxinx_list: # Цикл for для перебора элементов списка x_list
print(x, end=',') # Печать каждого элемента без переноса строки
print('\n') # Печать новой строки после цикла
x_list2 = x_list * 2# Создание нового списка x_list2 путем объединения x_list с самим собой
print(x_list2) # Печать нового списка1,2,3.0,
[1, 2, 3.0, 1, 2, 3.0]
print(id(x_list),id(x_list2))4367086336 4367678592
Объяснение:
print(id(x_list), id(x_list2)): Эта строка кода выводит на экран уникальные идентификаторы (адреса в памяти) двух списков: x_list и x_list2.Разные адреса подтверждают, что списки x_list и x_list2 являются разными объектами в памяти, даже несмотря на то, что они содержат одинаковые элементы. Это происходит потому, что операция x_list2 = x_list * 2 создала новый список, а не изменила исходный список x_list.
Важные моменты:В Python каждая переменная ссылается на объект в памяти.
Идентификатор объекта (его адрес в памяти) можно получить с помощью функции id().Когда вы присваиваете один список другому, вы создаете новую ссылку на тот же объект в памяти.
Когда вы выполняете операции над списками, которые создают новые списки (например, умножение списков или конкатенация списков), вы создаете новые объекты в памяти, которые имеют разные идентификаторы.
# Возвращает идентификатор (адрес в памяти) второго элемента списка x_list
id(x_list[1])1594536160
id(x_list[1]): Эта строка кода возвращает идентификатор (адрес в памяти) второго элемента списка x_list.
x_list3 = x_list# Присваивание списка x_list переменной x_list3
print(x_list, ' ', x_list3) # Вывод значений обеих переменных
[1, 2, 3.0] [1, 2, 3.0]
Пояснение:
x_list3 = x_list: В этой строке не создается новый список. Вместо этого переменная x_list3 становится просто еще одним именем для того же списка, на который указывает x_list. Это означает, что обе переменные ссылаются на один и тот же объект в памяти.
print(x_list, ' ', x_list3): Эта строка выводит значения обеих переменных, которые оказываются одинаковыми, поскольку они ссылаются на один и тот же список.
Важные моменты:Присваивание списков в Python не создает копию списка. Вместо этого оно создает еще одну ссылку на тот же список в памяти.
Это означает, что любые изменения, внесенные в один список, будут отражены и в другом списке, поскольку они оба ссылаются на один и тот же объект.
Чтобы создать независимую копию списка, необходимо использовать методы копирования, такие как list.copy() или срезы списков.
print(id(x_list),id(x_list3))1847992120648 1847992120648
Объяснение:
print(id(x_list), id(x_list3)): Эта строка кода выводит уникальные идентификаторы (адреса в памяти) переменных x_list и x_list3.Одинаковые адреса подтверждают, что обе переменные ссылаются на один и тот же объект в памяти. Это происходит потому, что присваивание x_list3 = x_list не создало новый список, а просто создало еще одну ссылку на тот же список.
x_list4 = x_list.copy() # Создание независимой копии списка x_list с помощью метода copy()
print(x_list, ' ', x_list4) # Вывод значений обоих списков
[1, 2, 3.0] [1, 2, 3.0]
Пояснение:
x_list4 = x_list.copy(): Метод copy() создает новую независимую копию списка x_list и присваивает ее переменной x_list4. Это означает, что x_list4 будет иметь те же элементы, что и x_list, но будет находиться в отдельной области памяти.
print(x_list, ' ', x_list4): Эта строка выводит значения обоих списков, которые оказываются одинаковыми по содержанию, но являются независимыми друг от друга.
print(id(x_list),id(x_list4))1847992120648 1847993186760
Разные адреса подтверждают, что x_list и x_list4 являются разными объектами в памяти, даже несмотря на то, что они содержат одинаковые элементы. Это происходит потому, что метод copy() создал новую независимую копию списка x_list.
# Присваиваем значению первого элемента списка x_list значение 4.0
x_list[0] = 4.0
# Выводим список на экран
print(x_list)
[4.0, 2, 3.0]
# Создаемсписокx_list
x_list = [1, 2, 3.0]
# Создаем список x_list2, который является копией списка x_list
x_list2 = x_list.copy()
# Изменяем значение первого элемента списка x_list
x_list[0] = 4.0
# Выводим список x_list2 на экран
print(x_list2)
[1, 2, 3.0]В данном случае мы видим, что изменение значения элемента списка x_list не влияет на значение элемента списка x_list2. Это происходит потому, что x_list2 является отдельной копией списка x_list.
Создание списка путем распаковки строки цифр:
num = 19345678
# Метод 1: Использование функции map() и преобразования в список
list_of_digits = list(map(int, str(num)))
print(list_of_digits) #Вывод: [1, 9, 3, 4, 5, 6, 7, 8]
# Метод 2: Использование list comprehension
list_of_digits = [int(x) forxinstr(num)]
print(list_of_digits) #Вывод: [1, 9, 3, 4, 5, 6, 7, 8]
Пояснения:
Метод 1: Использование map() и преобразования в списокmap(int, str(num)): Применяет функцию int() к каждому символу в строке str(num), преобразуя их в целые числа. map() возвращает итератор, который нужно преобразовать в список с помощью list().
Метод 2: Использование listcomprehension[int(x) for x instr(num)]: Создает новый список, в котором каждый элемент является результатом применения функции int() к соответствующему символу в строке str(num). Это более компактный и часто более читаемый способ создания списков на основе существующих итераций.
Оба метода дают одинаковый результат:
list_of_digits: Список, содержащий цифры числа num как отдельные целые числа: [1, 9, 3, 4, 5, 6, 7, 8].
Выбор между методами зависит от предпочтений в стиле кодирования и читаемости:
map() может быть более лаконичным для простых преобразований.List comprehension может быть более выразительным и гибким для сложных преобразований и фильтраций.
Поэлементное сложение списков требует небольшой хитрости. Лучшие способы, включая использование массивов numpy, будут рассмотрены в разделе о понимании списков.
# Объявляем две переменные, содержащие списки
list1, list2 = [20, 30, 40], [5, 6, 8]
# Выводим на экран исходные списки
print(list1, " ", list2, " ", list1+list2)
# Выводим на экран исходные списки в виде строк
print("Исходная строка 1: " + str(list1))
print("Исходная строка 2: " + str(list2))
# Выводим на экран сообщение о том, что "+" не является сложением
print('"+" не является сложением, а конкатенацией:',list1+list2)
[20, 30, 40] [5, 6, 8] [20, 30, 40, 5, 6, 8]
Исходная строка 1: [20, 30, 40]
Исходная строка 2: [5, 6, 8]
"+" не является сложением, а конкатенацией: [20, 30, 40, 5, 6, 8]
# Объявляем два списка
list1 = [1, 2, 3, 4, 5]
list2 = [6, 7, 8, 9, 10]
# Объявляем пустой список для хранения суммы элементов
add_list = []
# Используем цикл for для поэлементного сложения списков
foriinrange(0, len(list1)):
add_list.append(list1[i] + list2[i])
# Выводим на экран результат
print ("Элементное сложение двух списков: " + str(add_list))
Элементное сложение двух списков: [7, 9, 11, 13, 15]
# Выведем на экран адрес первого элемента списка add_list
print("Адрес первого элемента списка:", id(add_list[0]))
Адрес первого элемента списка: 4363141552
# Выведем на экран адрес первого элемента списка list1
print("Адрес первого элемента списка:", id(list1[0]))
Адрес первого элемента списка: 4377526512
# Объявляем два списка
list1 = [1, 2, 3, 4, 5]
list2 = [6, 7, 8, 9, 10]
# Объявляем пустой список для хранения результатов
add_list = []
# Цикл for, который перебирает элементы двух списков одновременно
fori1, i2inzip(list1, list2):
# Добавляемкэлементу i1 элемент i2
add_list.append(i1 + i2)
# Выводим на экран результаты
print("Сумма элементов двух списков поэлементно: ", add_list)
Сумма элементов двух списков поэлементно: [7, 9, 11, 13, 15]
Пояснение кода:
add_list = []: Объявляем пустой список для хранения результатов.
for i1, i2 inzip(list1, list2):: Цикл for, который перебирает элементы двух списков одновременно.zip(): встроенная функция, которая возвращает итератор, содержащий кортеж из элементов двух списков, расположенных в одном и том же индексе.
i1: первый элемент кортежа, возвращаемого zip().
i2: второй элемент кортежа, возвращаемого zip().
add_list.append(i1 + i2):: Добавляем к элементу i1 элемент i2.
print("Сумма элементов двух списков поэлементно: ", add_list):: Выводим на экран результаты.2.7.2Нарезка строк и списков
Нарезка — это полезная и эффективная операция для работы с частями строк, списков или массивов (об массивах мы поговорим позже). Начнем с нарезки строк, а затем перейдем к спискам.
Срез строки — это получение части строки, заданной индексами или диапазоном индексов.
Индексы в Python начинаются с нуля, поэтому первый символ в строке имеет индекс 0, второй символ — индекс 1 и так далее.
Двоеточие (:) используется для обозначения диапазона индексов в срезе.Отрицательные индексы используются для отсчёта с конца строки.
Параметр шага позволяет задавать интервал между выбираемыми символами в срезе.
Исключение IndexError возникает, когда вы пытаетесь получить доступ к элементу по индексу, который выходит за пределы длины строки или списка.
my_string = "У каждой избушки свои погремушки"
# Выводим исходную строку
print(my_string)
# Выводим пятый символ строки
print('5й=',my_string[4])
# Выводим седьмой по одиннадцатый символы строки
print('с7 по 11й =',my_string[6:11])У каждой избушки свои погремушки
5й= ж
с7 по 11й = ой из
# Создаем строковую переменную
my_string = "Привет, мир!"# Вы можете изменить строку на любую другую
# Выводим срезы строки с пояснениями:
print('[6:-1]=', my_string[6:-1]) # Срез от 7-го элемента (включительно) до последнего, не включая последний
print('[:]=', my_string[:]) # Все символы строки
print('[6:]=', my_string[6:]) # Срез от 7-го элемента (включительно) до конца строки
print('[:-1]=', my_string[:-1]) # Все символы строки, кроме последнего
[6:-1]=, мир
[:]= Привет, мир!
[6:]=, мир!
[:-1]= Привет, мир
# Объявляем переменную my_string и присваиваем ей значение "Helloworld!"
my_string = "Helloworld!"
# Выводим на экран текст "Символы с 4-го по 9-й с шагом 2"
print("Символы с 4-го по 9-й с шагом 2")
# Используем срез для получения подстроки из my_string, начиная с 4-го символа, заканчивая 9-м, с шагом 2
# Результатом будет строка "llo w"
print("[3:9:2]=", my_string[3:9:2])
# Объявляем переменную my_string и присваиваем ей значение "Helloworld!"
# Строка "Helloworld!" состоит из 10 символов:
# H - 1-й символ
# e - 2-й символ
# l - 3-й символ
# l - 4-й символ
# o - 5-й символ
# w - 6-й символ
# o - 7-й символ
# r - 8-й символ
# l - 9-й символ
# d - 10-й символ
# Используем срез для получения подстроки из my_string, начиная с 4-го символа, заканчивая 9-м, с шагом 2
# Результатомбудетстрока "llo w"
# Синтаксиссреза: [start:stop:step]
# start - индекс первого символа в подстроке
# stop - индекс последнего символа в подстроке
# step - шаг последовательности символов в подстроке
Символы с 4-го по 9-й с шагом 2
[3:9:2]= l o
Используя отрицательный шаг, мы можем легко перевернуть строку, как мы видели ранее:
my_string = "Тепленькая пошла! (с)"
# Выводимисходнуюстроку
print("string:", my_string)
# Выводим строку в обратном порядке
print("[::-1]=", my_string[::-1])
string: Тепленькаяпошла! (с)
[::-1]=)с(!алшопяакьнелпеТВкратце, если в квадратных скобках указано одно число, то срез берёт символ, расположенный на позиции (число + 1). Это происходит потому, что Python начинает отсчёт с нуля. Двоеточие означает "все доступные". Если оно используется отдельно, то срез захватывает всю строку. Если слева от него находится число, то срез берётся от этого числа до конца строки, и наоборот. Отрицательное число означает, что отсчёт ведётся с конца строки: −3 означает "третий символ с конца". Также можно использовать параметр шага для пропуска символов.
Обратите внимание, что при обращении к символу строки с индексом, который не существует, генерируется исключение IndexError.
# Объявим переменную my_string без присвоения значения,
# чтобы продемонстрировать ошибку
my_string = ""
# Попытка вывести элемент с индексом 14, который выходит за пределы строки
print('[14]=', my_string[14]) # ВыведетошибкуIndexError
# Создаем список my_list, содержащий смешанные типы переменных:
my_list = [0, 1, 2, 3, "4E", 5, 6, 7, [8, 8], 9]
# Выводим список my_list с помощью оператора print()
# В диапазоне от 0 до 9 с шагом 1
print(my_list[0:10:1])
# Выводим список my_list с помощью оператора print()
# В диапазоне от начала списка до конца
print(my_list[:])
[0, 1, 2, 3, '4E', 5, 6, 7, [8, 8], 9]
[0, 1, 2, 3, '4E', 5, 6, 7, [8, 8], 9]2.7.3Использование подстановочных знаков "_" в списках
# Создаем список со смешанными значениями
nlist = [10, 20, 30, 40, 50, 6.0, '7H']
# Распаковываем элементы списка, игнорируя первые два и пятый элемент
_, _, n3, _, *nn = nlist
# Печатаем третий элемент и остальные элементы после пятого с переводом текста
print("n3=", n3, "остальные числа", *nn)
# Создаем еще один список с числовыми значениями
nlist = [10, 20, 30, 40, 50, 60, 70]
# Распаковываем элементы, оставляя третий, последний и остальные в отдельный список nn
_, _, n3, *nn, nlast = nlist
# Печатаем третий элемент, последний элемент и остальные элементы с переводом текста
print("n3=", n3, ", последний=", nlast, ", и все остальные числа", *nn)
n3= 30 остальные числа 50 6.0 7H
n3= 30, последний= 70, и все остальные числа 40 50 602.7.4Вложенный список (списки в списках)
# Создаем вложенный список смешанных типов переменных
nested_list = [[11, 12], ['2B', 22], [31, [32, 3.2]]]
# Выводим вложенный список на экран
print('Вложенныйсписок:')
print(nested_list)
# Выводим количество вложенных списков
print('Количество вложенных списков:', len(nested_list))
Вложенный список:
[[11, 12], ['2B', 22], [31, [32, 3.2]]]
Количество вложенных списков: 3
# Выводим первый вложенный список
# nested_list[0] - первыйэлементспискаnested_list
# Выводим второй вложенный список
# nested_list[1] - второй элемент списка nested_list
# Выводим третий вложенный список
# nested_list[2] - третийэлементспискаnested_list
# Выводим весь вложенный список целиком
# print(nested_list) - функция вывода данных на экран
print(nested_list[0])
print(nested_list[1])
print(nested_list[2])
print(nested_list)
[11, 12]
['2B', 22]
[31, [32, 3.2]]
[[11, 12], ['2B', 22], [31, [32, 3.2]]]
print(nested_list[0][0]) #выводит первый элемент (с индексом 0) в первом вложенном списке (также с индексом 0). Таким образом, он обращается к числу 11.
print(nested_list[0][1]) #выводит второй элемент (с индексом 1) в первом вложенном списке (с индексом 0). Таким образом, он обращается к числу 12.11
12
print(nested_list) #выводит весь вложенный список целиком
print(nested_list[1][0]) #выводит первый элемент (с индексом 0) во втором вложенном списке (с индексом 1). Таким образом, он выводит строку '2B'.
print(nested_list[2][1]) #выводит второй элемент (с индексом 1) в третьем вложенном списке (с индексом 2). Этот второй элемент сам является списком: [32, 3.2].
print(nested_list[2][1][0]) #выводит первый элемент (с индексом 0) в списке, который находится во втором элементе (с индексом 1) третьего вложенного списка (с индексом 2). Таким образом, он выводит число 32.
[[11, 12], ['2B', 22], [31, [32, 3.2]]]2B
[32, 3.2]32
2.8 Кортежи
После обсуждения списков (списков) рассмотрение кортежей становится простым. Это связано с тем, что они по сути одинаковы, и главное отличие заключается в следующем:
Кортеж обычно заключается в круглые скобки (), а список - в квадратные [].Кортеж неизменяемый, а список мутируемый. Это означает, что кортежи нельзя изменить после их создания. Значения в кортежах сохраняются.
2.8.1 сохранение значений
Поскольку кортеж неизменяемый, мы используем его для хранения данных, которые необходимо сохранить. Таким образом, его использование очень ограничено. Он используется для хранения констант, предотвращая их изменение. Кроме того, работа с кортежами происходит быстрее.
За исключением этих различий, кортеж ведет себя как список. К нему можно обращаться по индексу, выполнять итерацию и присваивать другим переменным. Ниже приведены несколько примеров.
# Создаём кортеж
ttuple = (10, 20, 30, 40, 50, 6.0, '7H')
# Печатаем кортеж
print('ttuple =', ttuple)
# Присваиваем переменной `aa` первое значение кортежа
aa = ttuple[0]
# Печатаем значение переменной `aa`
print('aa =', aa)
# Печатаем значения элементов кортежа с индексами 1, 6 и -1
print(ttuple[1], ' ', ttuple[6], ' ', ttuple[-1])
ttuple = (10, 20, 30, 40, 50, 6.0, '7H')
aa = 1020 7H 7H
# Объявляемкортеж
ttuple = ('a', 'b', 'c', 'd', 'e')
# Цикл for с использованием функции enumerate()
# Функция enumerate() возвращает кортеж из индекса и значения элемента
fori, datainenumerate(ttuple):
# Проверяем, меньше ли индекс 3
ifi<3:
# Выводим индекс и значение элемента
print(i, ':', data)
0: a
1: b
2: c
# Создаем кортеж
ttuple = (1, 2, 3)
# Выводим кортеж
print("Терминальный вывод:", ttuple)
# Пробуем изменить значение элемента кортежа
try:
ttuple[2] = 300
exceptTypeError:
print("Ошибка: нельзя изменить значение элемента кортежа")Это, пожалуй, все, что нам нужно знать о кортежах.
2.9 Словари: Индексируемые по ключам
Словари представляют собой тип данных, предназначенный для хранения и организации информации в виде пар «ключ-значение». Они предлагают гибкий и эффективный способ работы с данными, что делает их незаменимым инструментом во многих программных задачах.
2.9.1 Основные характеристики словарей
Пары «ключ-значение»: В словарях данные хранятся в виде пар, где каждому ключу соответствует определённое значение. Ключи выступают в качестве уникальных идентификаторов, используемых для доступа к соответствующим значениям.
Индексация с помощью ключей: Вместо числовых индексов, как в списках, словари используют уникальные ключи для обращения к значениям. Это позволяет напрямую получать доступ к нужным элементам без перебора всей коллекции.
Разнообразные типы значений: Значениями в словарях могут быть объекты любых типов данных, включая строки, числа, списки, другие словари и даже функции. Это обеспечивает широкую вариативность использования словарей для различных целей.
Отсутствие дубликатов ключей: Каждый ключ в словаре должен быть уникальным. Не допускается наличие двух или более пар с одинаковыми ключами. Это гарантирует однозначность ссылок и предотвращает конфликты при доступе к значениям.
Возможность дублирования значений: В то время как ключи должны быть уникальными, значения в словаре могут повторяться. Это позволяет использовать один и тот же ключ для хранения нескольких значений, если это необходимо.
2.9.2Примеры использования словарей
Хранение контактной информации: Словари идеально подходят для хранения данных, где необходимо связать уникальное имя или идентификатор с набором свойств. Например, можно использовать словарь для хранения имени, телефона, адреса и email человека.
Представление структур данных: Словари позволяют моделировать различные структуры данных, такие как деревья, графы и множества, где элементы связаны между собой различными отношениями.
Подсчёт частоты слов в тексте: Словари незаменимы для подсчёта количества вхождений слов в тексте. Каждое слово можно использовать в качестве ключа, а его значение будет представлять собой количество его упоминаний в тексте.
Настройки и конфигурации: Словари часто используются для хранения параметров и настроек программ, позволяя легко изменять их значения без необходимости переписывать код.
Словари являются мощным и универсальным инструментом в Python. Их способность эффективно хранить и управлять данными в виде пар «ключ-значение» делает их незаменимыми во многих областях программирования.
2.9.3Присвоение данных словарю
Например, телефонные номера могут быть назначены словарю в следующем формате:
# Создаем словарь phonebook1
phonebook1 = {}
# Добавляем в словарь записи
phonebook1["Маша"] = 513476565
phonebook1["Леша"] = 513387234
phonebook1["Настя"] = 513682762
phonebook1["Дед мороз"] = 513387234
# Выводим словарь на экран
print(phonebook1)
{'Маша': 513476565, 'Леша': 513387234, 'Настя': 513682762, 'Дед мороз': 513387234}
Инициализировать словарь можно также следующим образом:
# Создаем словарь phonebook2
phonebook2 = {'Лера': 656477456, 'Настя': 656377243, 'Тимофей': 656662798}
# Выводим словарь на экран
print(phonebook2)
{'Лера': 656477456, 'Настя': 656377243, 'Тимофей': 656662798}
# Объявляем словарь phonebook0
phonebook0 = {
"Костя": [788567837, 788347278], # Костя имеет два номера телефона
# это список
"Инна": 513683222,
"Владик": 656477456
}
# Выводим словарь phonebook0 на экран
print("Телефоннаякнига:")
print(phonebook0)
Телефонная книга:
{'Костя': [788567837, 788347278], 'Инна': 513683222, 'Владик': 656477456}2.9.4Перебор словарей
Подобно спискам, словари также можно перебирать. Поскольку ключи и значения хранятся в виде пар, мы можем использовать цикл for для их обработки.
# Создаем словарь телефонной книги
phonebook1 = {
"Иванов Иван": 1234567,
"Петров Петр": 89012345678,
"Сидоров Сидор": 7890123456
}
# Перебираемсловарь
forname, numberinphonebook1.items():
# Выводим имя абонента и его телефонный номер
print("Телефонный номер %s равен %d" % (name, number))Телефонный номер Иванов Иван равен 1234567
Телефонный номер Петров Петр равен 89012345678
Телефонный номер Сидоров Сидор равен 7890123456
# Создаем словарь телефонной книги
phonebook1 = {
"Иванов Иван": 1234567,
"Петров Петр": 89012345678,
"Сидоров Сидор": 7890123456
}
# Перебираемсловарь
forkey, valueinphonebook1.items():
print(key, value)ИвановИван 1234567
Петров Петр 89012345678
Сидоров Сидор 7890123456
# Создаем словарь телефонной книги
phonebook1 = {
"Иванов Иван": 1234567,
"Петров Петр": 89012345678,
"Сидоров Сидор": 7890123456
}
# Перебираемсловарь
forkeyinphonebook1.keys():
print(key)Иванов Иван
Петров Петр
Сидоров Сидор
# Создаем словарь телефонной книги
phonebook1 = {
"Иванов Иван": 1234567,
"Петров Петр": 89012345678,
"СидоровСидор": 7890123456
}
# Перебираемсловарь
forvalueinphonebook1.values():
print(value)1234567
89012345678
7890123456
2.9.5Удаление значения
Чтобы удалить пару записей, мы используем встроенную функцию del или pop, используя ключи.
Оператор del удаляет элемент из словаря по указанному ключу. Например, следующий код удаляет элемент с ключом "Иванов Иван" из словаря phonebook:
phonebook = {
"Иванов Иван": 1234567,
"Петров Петр": 89012345678,
"Сидоров Сидор": 7890123456
}
delphonebook["Иванов Иван"]
print(phonebook)
{'Петров Петр': 89012345678, 'Сидоров Сидор': 7890123456}Оператор pop удаляет элемент из словаря по указанному ключу и возвращает значение этого элемента. Например, следующий код удаляет элемент с ключом "Иванов Иван" из словаря phonebook и сохраняет значение этого элемента в переменной number:
phonebook = {
"Иванов Иван": 1234567,
"Петров Петр": 89012345678,
"Сидоров Сидор": 7890123456
}
number = phonebook.pop("ИвановИван")
print(phonebook)
print(number)
{'ПетровПетр': 89012345678, 'СидоровСидор': 7890123456}1234567
2.9.6 Слияние двух словарей
Во-первых, используйте метод update().
# Создаем две телефонные книги
phonebook1 = {
"Иванов Иван": 1234567,
"Петров Петр": 89012345678,
"Сидоров Сидор": 7890123456
}
phonebook2 = {
"Васильев Василий": 9123456789,
"Пупкин Пупкин": 4567890123
}
# Обновляем телефонную книгу 1
# Используем метод `update()` для объединения двух словарей
phonebook1.update(phonebook2)
# Выводим обновленную телефонную книгу 1
print(phonebook1){'Иванов Иван': 1234567, 'Петров Петр': 89012345678, 'Сидоров Сидор': 7890123456, 'Васильев Василий': 9123456789, 'Пупкин Пупкин': 4567890123}
Теперь воспользуйтесь более простым средством, которое называется double-star. Оно позволяет создать третий новый словарь, который является комбинацией двух словарей, не затрагивая два исходных.
# Создаем две телефонные книги
phonebook1 = {
"Иванов Иван": 1234567,
"Петров Петр": 89012345678,
"Сидоров Сидор": 7890123456
}
phonebook0 = {
"Васильев Василий": 9123456789,
"Пупкин Пупкин": 4567890123
}
# Создаем новую телефонную книгу 3
# Используем оператор `**` для объединения двух словарей
phonebook3 = {**phonebook1, **phonebook0}
# Выводим новую телефонную книгу 3
print(phonebook3){'Иванов Иван': 1234567, 'Петров Петр': 89012345678, 'Сидоров Сидор': 7890123456, 'Васильев Василий': 9123456789, 'Пупкин Пупкин': 4567890123}
Дублирующиеся ключи (если таковые имеются) удаляются из нового словаря:
# Создаем два словаря
dict_1 = {
"Яблоко": 7,
"Банан": 5
}
dict_2 = {
"Банан": 3,
"Апельсин": 4
}
# Объединяем два словаря
# Используемоператор `**`
combined_dict = {**dict_1, **dict_2}
# Выводим объединенный словарь
print(combined_dict)
{'Яблоко': 7, 'Банан': 3, 'Апельсин': 4}В результате выполнения кода будет создан новый словарь combined_dict, который будет содержать все элементы из словарей dict_1 и dict_2.
Оператор ** используется для объединения двух словарей. Он объединяет элементы двух словарей, основываясь на их ключах. Если ключ в двух словарях совпадает, то в результате объединения будет использовано значение из второго словаря.
В данном случае ключ "Банан" присутствует в обоих словарях. В результате объединения значение для этого ключа будет взято из второго словаря, то есть 3.
2.10 Множества
В Python множество — это коллекция, которая подобна списку, но каждый элемент в множестве уникален и не может повторяться.
2.10.1 Избегаем Дублирования
Приведем пример:
# Создаем переменную sentence1 и присваиваем ей строку с повторяющимися словами
sentence1 = "Его имя Семен и Семен это его имя"
# Используем метод split() для преобразования строки в список слов
# Разделитель по умолчанию - пробел, так что он разделяет строку по словам
words1 = sentence1.split()
# Выводим список слов для наглядности
# Вывод должен показать все слова, включая дубликаты
print(words1)
# Конвертируем список слов в множество, что автоматически удаляет все дубликаты
word_set1 = set(words1)
# Выводим множество слов, чтобы увидеть, что дубликаты были удалены
print(word_set1)
['Его', 'имя', 'Семен', 'и', 'Семен', 'это', 'его', 'имя']
{'Его', 'его', 'и', 'имя', 'это', 'Семен'}Как вы видите, после преобразования списка в множество, все дублирующиеся слова исчезли. В множестве осталось только уникальные элементы.
Использование множества позволяет нам легко убирать повторения без необходимости писать дополнительный код для проверки наличия дубликатов. Это очень удобно, например, при обработке данных, когда нужно избавиться от повторов в списке.
2.10.2 Пересечение двух множеств
# Определяем первое множество слов
word_set1 = {'hello', 'world', 'name', 'is', 'first', 'python', 'and'}
# Определяемвтороемножествослов
word_set2 = {'this', 'is', 'a', 'test', 'name', 'first', 'and'}
# Используем метод intersection() для нахождения пересечения между двумя множествами.
# Пересечение множеств возвращает новое множество, которое содержит все элементы,
# которые есть в обоих исходных множествах.
common_elements = word_set1.intersection(word_set2)
# Выводим на экран общие элементы множеств
print(common_elements) # Выведет множество с общими элементами
{'name', 'is', 'and', 'first'}2.10.3 Разность двух множеств
В Python множества — это коллекции, которые содержат неповторяющиеся элементы в неупорядоченном виде. Одной из уникальных операций для множеств является возможность находить их разность, что позволяет выявить элементы, присутствующие в одном множестве и отсутствующие в другом.
# Определяем два множества с различными словами
word_set1 = {'hello', 'his', 'Her', 'His', 'Настя'}
word_set2 = {'hello', 'Her', 'her', 'Аня'}
# Получаем и печатаем слова, которые есть в word_set1, но нет в word_set2
# Ожидаемый вывод: {'his', 'His', 'Настя'}
print(word_set1.difference(word_set2))
{'his', 'His', 'Настя'}В примере выше, слова 'his', 'His' и 'Настя' являются уникальными для word_set1 по сравнению с word_set2.
Теперь, если мы захотим найти элементы, которые уникальны для каждого из множеств (т.е., присутствуют только в одном из множеств), мы можем использовать операцию симметричной разности.
# Выводим симметричную разность между word_set1 и word_set2
# Ожидаемый вывод: {'his', 'His', 'her', 'Аня', 'Настя'}
print(word_set1.symmetric_difference(word_set2))
{'his', 'His', 'her', 'Аня', 'Настя'}В этом случае симметричная разность возвращает слова 'his', 'His', 'her', 'Аня' и 'Настя', которые являются уникальными для обоих множеств, но не находятся в их пересечении.
Часто возникает вопрос: возможно ли применять операции множеств к спискам? Списки в Python не поддерживают операции множеств напрямую, так как они являются упорядоченными коллекциями с возможностью включать дублирующие элементы.
# Создаем два списка слов
words1 = ['hello', 'his', 'Her', 'His', 'Настя']
words2 = ['hello', 'Her', 'her', 'Аня']
# Попытка использовать метод intersection вызовет ошибку, так как списки не имеют такого метода
# Этот код, если его раскомментировать, вызовет AttributeError:
# print(words1.intersection(words2)) # AttributeError: объект 'list' неимеетатрибута 'intersection'Чтобы использовать операции множеств на списках, сначала необходимо преобразовать списки в множества с помощью функции set(), провести нужную операцию, а затем результат можно преобразовать обратно в список с помощью функции list(), если это необходимо.
# Создаем два множества с уникальными словами
word_set1 = {'hello', 'his', 'Her', 'His', 'Настя'}
word_set2 = {'hello', 'Her', 'her', 'Аня'}
# Выводим разность между множествами word_set1 и word_set2.
# Метод difference возвращает множество, содержащее элементы, которые есть в word_set1, но нет в word_set2.
# Ожидаемый вывод: {'his', 'His', 'Настя'}
print('Разность word_set1 и word_set2:', word_set1.difference(word_set2))
# Выводим симметричную разность между множествами word_set1 и word_set2.
# Метод symmetric_difference возвращает множество, содержащее элементы, которые есть только в одном из множеств.
# Ожидаемый вывод: {'his', 'His', 'her', 'Аня', 'Настя'}
print('Симметричнаяразность word_set1 и word_set2:', word_set1.symmetric_difference(word_set2))
# Списки в Python не поддерживают операции множеств напрямую, поэтому мы не можем использовать методы likeintersection.
# Однако, мы можем преобразовать списки в множества, выполнить операцию, а затем преобразовать результат обратно в список, если нужно.
# Создаем два списка слов
words1 = ['hello', 'his', 'Her', 'His', 'Настя']
words2 = ['hello', 'Her', 'her', 'Аня']
# Преобразуем списки в множества и находим их симметричную разность
# Затем преобразуем результат обратно в список
sym_diff_list = list(set(words1).symmetric_difference(set(words2)))
# Выводим результат симметричной разности списков
# Ожидаемый вывод: ['his', 'His', 'her', 'Аня', 'Настя']
print('Симметричная разность списков words1 и words2:', sym_diff_list)
Разность word_set1 и word_set2: {'His', 'his', 'Настя'}Симметричнаяразность word_set1 и word_set2: {'Настя', 'Аня', 'His', 'his', 'her'}
Симметричная разность списков words1 и words2: ['Настя', 'Аня', 'His', 'his', 'her']В этом коде мы создаем два множества и два списка. Мы демонстрируем операции разности и симметричной разности на множествах и объясняем, как можно адаптировать списки для выполнения подобных операций с помощью преобразования их в множества. Комментарии в коде подробно описывают каждый шаг и предполагаемый вывод операций.
2.11 Условия и циклы
Условия: Это основа для принятия решений в программировании. Вы используете условия для определения, следует ли выполнить определенный блок кода. Например, "если сегодня понедельник, то выполнить задачу А".
Циклы 'for' и 'while': Эти конструкции используются для повторения блока кода несколько раз. Цикл 'for' обычно используется, когда известно количество повторений, а 'while' - когда повторения зависят от выполнения определенного условия. Например, 'for' используется для итерации по элементам списка, а 'while' может использоваться для выполнения блока кода до тех пор, пока не будет достигнуто определенное условие.
2.11.1 Операторы
Операторы сравнения в Python включают:
== (равно)
!= (не равно)
< (меньше)
<= (меньше или равно)
> (больше)
>= (больше или равно)Когда мы используем эти операторы, Python сравнивает значения по обе стороны от оператора и возвращает булево значение True или False.
Эти операторы являются основой для создания условных выражений, которые позволяют вашей программе реагировать на различные данные и сценарии. Их можно использовать для проверки ввода пользователя, сравнения результатов вычислений и управления поведением программы в зависимости от внутренних состояний и данных.
Пример:
# Присваиваем переменной x значение 2
x = 2
# Сравниваем x с 2, ожидаем получить True, потому что x действительно равно 2
print(x == 2) # Результат: True
# Сравниваем x с 3, ожидаем получить False, так как x не равно 3
print(x == 3) # Результат: False
# Проверяем, меньше ли x чем 3, ожидаем получить True, потому что 2 меньше 3
print(x<3) # Результат: TrueТеперь давайте использовать оператор сравнения в условной конструкции. Условные конструкции позволяют выполнить определенный блок кода, если условие истинно (True).
Пример использования в условной конструкции:
# Присваиваем переменной x значение 2
x = 2
# Проверяем, равно ли x значению 2
ifx == 2:
# Если условие истинно, выводим сообщение "x равен 2!"
print("x равен 2!") # Предположительный вывод: x равен 2!
else:
# Если условие ложно, выводим сообщение "x не равен 2."
print("x не равен 2.") # Этот код не будет выполнен, так как условие истинноВ этом коде мы видим использование оператора == в условии if. Если условие истинно, то есть значение переменной x действительно равно 2, тогда Python выполняет код в блоке if. Если бы переменная x была равна любому другому значению, кроме 2, тогда выполнялся бы код в блоке else.
Основное важное замечание: когда мы пишем x == 2, мы не присваиваем x значение 2, а проверяем, равно ли текущее значение x числу 2. Это сравнение, а не присваивание.
Оператор “in” используется для проверки наличия заданного объекта в контейнере итерируемых объектов, таких как список. Этот оператор возвращает True, если элемент присутствует в контейнере, и False в противном случае.
Рассмотрим следующий пример кода, где мы определяем две переменные name1 и name2, содержащие имена, а также список groupA, состоящий из нескольких имен:
# Объявляем две строковые переменные
name1, name2 = "Саша", "Леша"
# Создаем список groupA с именами
groupA = ["Саша", "Маша"]
# Проверяем, находится ли name1 (Саша) в списке groupA
ifname1ingroupA:
print(name1, "найден(а) вгруппеA")
# Проверяем, находится ли name2 (Леша) в списке groupA
ifname2ingroupA:
print(name2, "найден(а) вгруппеA")
else:
# Ветка else выполнится, если name2 (Леша) не найден в groupA
print(name2, "не найден(а) в группе A")
# Добавляем еще одно имя для демонстрации
name3 = "Лена"
# Проверяем, находится ли name3 (Лена) в списке groupA
ifname3ingroupA:
print(name3, "найден(а) вгруппеA")
else:
# Ветка else выполнится, если name3 (Лена) не найден в groupA
print(name3, "не найден(а) в группе A")
Саша найден(а) в группе A
Леша не найден(а) в группе A
Лена не найден(а) в группе AЭтот пример демонстрирует, как оператор in может быть эффективно использован для проверки принадлежности элементов списку.
Оператор "is" в отличие от оператора равенства “==”, не проверяет значения операндов. Он проверяет, ссылаются ли оба операнда на один и тот же объект. Например:
x, y = ['a', 'b'], ['a', 'b']
z = y# Присваиваем z ссылку на тот же объект, что и y
print(x == y, ' x=', x, 'y=', y, hex(id(x)), hex(id(y))) # Выводит True, потому что значения в x и y равны
print(xisy, 'x=', x, 'y=', y, hex(id(x)), hex(id(y))) # Выводит False, потому что x и y - разные объекты (разные ID)
print(yisz, ' y=', y, 'z=', z, hex(id(y)), hex(id(z))) # Выводит True, y и z ссылаются на один и тот же объект
# Меняем второй элемент в списке y
y[1] = 'x'
print('После изменения одного значения в y')
print(x == y, 'x=', x, 'y=', y, hex(id(x)), hex(id(y))) # Выводит False, теперь их значения не равны
print(xisy, 'x=', x, 'y=', y, hex(id(x)), hex(id(y))) # Выводит False, x и y - разные объекты
print(yisz, ' y=', y, 'z=', z, hex(id(y)), hex(id(z))) # Выводит True, y и z по-прежнему ссылаются на один объект
# Добавляем список x в список y
y.append(x)
print('После добавления списка x в список y')
print(x == y, 'x=', x, 'y=', y, hex(id(x)), hex(id(y))) # Выводит False, значения x и y различаются
print(xisy, 'x=', x, 'y=', y, hex(id(x)), hex(id(y))) # Выводит False, x и y - разные объекты
print(yisz, ' y=', y, 'z=', z, hex(id(y)), hex(id(z))) # Выводит True, y и z по-прежнему ссылаются на один объект
True x= ['a', 'b'] y= ['a', 'b'] 0x100a77300 0x100b07c80
False x= ['a', 'b'] y= ['a', 'b'] 0x100a77300 0x100b07c80
True y= ['a', 'b'] z= ['a', 'b'] 0x100b07c80 0x100b07c80После изменения одного значения в y
False x= ['a', 'b'] y= ['a', 'x'] 0x100a77300 0x100b07c80
False x= ['a', 'b'] y= ['a', 'x'] 0x100a77300 0x100b07c80
True y= ['a', 'x'] z= ['a', 'x'] 0x100b07c80 0x100b07c80После добавления списка x в список y
False x= ['a', 'b'] y= ['a', 'x', ['a', 'b']] 0x100a77300 0x100b07c80
False x= ['a', 'b'] y= ['a', 'x', ['a', 'b']] 0x100a77300 0x100b07c80
True y= ['a', 'x', ['a', 'b']] z= ['a', 'x', ['a', 'b']] 0x100b07c80 0x100b07c80В этом примере мы видим, как операторы == и is работают по-разному. == сравнивает значения в списках, в то время как is проверяет, ссылаются ли переменные на один и тот же объект в памяти. Это демонстрируется использованием функции id(), которая возвращает уникальный идентификатор объекта в памяти. В начале x и y имеют одинаковые значения, но ссылаются на разные объекты, как видно из разных ID. Когда мы присваиваем z значение y, z начинает ссылаться на тот же объект, что и y.
Изменение элемента в списке y не влияет на x, так как они являются разными объектами. Однако, поскольку z по-прежнему ссылается на тот же объект, что и y, изменения в y отражаются и в z. Добавление x в список y делает список y длиннее и изменяет его содержание, но не влияет на x и не меняет факт того, что y и z ссылаются на один и тот же объект.
Оператор not является логическим оператором, который инвертирует значение булевого выражения: если выражение истинно (True), результатом будет False, и наоборот.
Оператор not особенно полезен при формировании сложных логических условий. Он может быть использован в сочетании с другими операторами, такими как is (для проверки идентичности объектов) и in (для проверки наличия элемента в последовательности).
Рассмотрим несколько примеров:
# Пример 1: Простое использование `not`
print(notFalse) # Выводит True, так как `not` инвертирует False в True
# Пример 2: Сравнение с использованием `not`
print(notFalse == False) # Выводит False, эквивалентно выражению True == False
# Пример 3: Работа с операторами == и is в сочетании с not
x, y = [1, 2, 3], [1, 2, 3]
z = y# Присваиваем z ссылку на тот же объект, что и y
# Сравнение значений списка
# Используем оператор `==` для проверки равенства значений в списках
print(x == y, 'x=', x, 'y=', y, hex(id(x)), hex(id(y)))
# Выводит True, поскольку значения в списках x и y одинаковы, несмотря на то, что это разные объекты
# Сравнение идентичности объектов с `isnot`
# Используем `isnot` для проверки, указывают ли переменные на разные объекты
print(xisnoty, 'x=', x, 'y=', y, hex(id(x)), hex(id(y)))
# Выводит True, потому что x и y, хоть и имеют одинаковые значения, являются разными объектами
# Использование `is` для проверки идентичности объектов
print(yisz, 'y=', y, 'z=', z, hex(id(y)), hex(id(z)))
# Выводит True, поскольку y и z указывают на один и тот же объектTrue
False
True x= [1, 2, 3] y= [1, 2, 3] 0x104b5b300 0x104bebc80
True x= [1, 2, 3] y= [1, 2, 3] 0x104b5b300 0x104bebc80
True y= [1, 2, 3] z= [1, 2, 3] 0x104bebc80 0x104bebc802.11.1Использование условных конструкций "if"
Как и в других языках программирования, конструкция "if" является основным инструментом для выполнения условной логики. Мы уже видели несколько примеров ранее. Ниже приведены дополнительные примеры использования условий в Python в конструкции "if", демонстрирующие работу с блоками кода:
# Задаем комфортную температуру в градусах Цельсия.
temp_good = 22.0
# Текущая температура, значение которой можно изменить для эксперимента.
temp_now1 = 48.0
# Определяем условия для разных состояний температуры.
statement1 = (temp_now1<= (temp_good + 10.0)) & (temp_now1>= (temp_good - 10.0)) #комфортно
statement2 = temp_now1< (temp_good - 10.0) #холодно
statement3 = temp_now1> (temp_good + 10.0) #жарко
# Применяем условные операторы для вывода соответствующих сообщений.
ifstatement1: # Не забудьте про двоеточие.
print("Окей, это", temp_now1, "градусов Цельсия. Комфортно. Пойдем.")pass# Здесь можно добавить дополнительные действия.
elifstatement2: # Не забудьте про двоеточие.
print("Нет, это", temp_now1, "градусов Цельсия. Слишком холодно. Мы не можем идти.")pass# Здесь можно добавить дополнительные действия.
elifstatement3:
print("Нет, это", temp_now1, "градусовЦельсия. Слишком жарко. Мы не можем идти.")pass# Здесь можно добавить дополнительные действия.
else:
print("Давайте проверим температуру.") # Выполнить другое действиеpass
Нет, это 48.0 градусов Цельсия. Слишком жарко. Мы не можем идти.
Обратите внимание, что в Python нет ограничений на количество блоков, которые можно использовать в одном условном операторе "if".
2.11.3Циклы “for”
Цикл for- это структура управления в программировании, которая позволяет повторять определенный блок кода заданное количество раз. Это особенно полезно, когда нужно выполнять операции с каждым элементом в коллекции (например, списке или массиве) или повторять задачу фиксированное количество раз.
В Python синтаксис цикла for выглядит следующим образом:forпеременнаяin последовательность:
# блок кода для выполненияЗдесь переменная принимает значение каждого элемента из последовательности по очереди, и блок кода внутри цикла выполняется для каждого из этих элементов.
Перебор Элементов Списка:
forэлементin [1, 2, 3, 4, 5]:
print(элемент)Выводит каждый элемент списка.
Использование с Range:
forчислоinrange(10):
print(число)Выводит числа от 0 до 9.
Циклы for играют важную роль в программировании искусственного интеллекта (ИИ), особенно в обработке данных, обучении моделей и автоматизации задач:
Обработка Данных:Циклы часто используются для предварительной обработки данных, например, для нормализации или стандартизации наборов данных перед их использованием в моделях машинного обучения.
Обучение Моделей:В некоторых алгоритмах, таких как градиентный спуск, циклы используются для многократного обновления весов модели в процессе обучения.
Автоматизация и Тестирование:Циклы for полезны для автоматизации повторяющихся задач, таких как тестирование моделей на различных наборах данных или с разными параметрами.
Особые Алгоритмы в ИИ:В области глубокого обучения, циклы for используются в рекуррентных нейронных сетях (RNN) для итерации через последовательности данных (например, текст).
primes = [2, 3, 5] # Определение списка простых чисел
forprimeinprimes: # Цикл по списку простых чисел
print(prime, end=' ') # Вывод каждого простого числа в строку
print('\nЭлементы в range(10) это')
forninrange(10): # Создание диапазона от 0 до 9
print(n, end=',') # Вывод каждого числа в строку с разделением запятой
print('\nЭлементы в range(3, 8) это')
forninrange(3, 8): # Создание диапазона от 3 до 7
print(n, end=',') # Вывод каждого числа в строку
print('\nЭлементы в range(3, 10, 2) это')
forninrange(-3, 10, 2): # Создание диапазона от -3 до 9 с шагом 2
print(n, end=',') # Вывод каждого числа в строку
print('\nЭлементы в range(10, -3, -2) это')
forninrange(10, -3, -2): # Создание обратного диапазона от 10 до -2 с шагом -2
print(n, end=',') # Вывод каждого числа в строку2 3 5
Элементы в range(10) это0,1,2,3,4,5,6,7,8,9,
Элементы в range(3, 8) это3,4,5,6,7,
Элементы в range(3, 10, 2) это
-3,-1,1,3,5,7,9,
Элементы в range(10, -3, -2) это
10,8,6,4,2,0,-2,%2.11.4 Циклы "while"
Цикл while- это структура управления в программировании, которая позволяет выполнить блок кода, пока выполняется определенное условие. Этот цикл полезен, когда количество итераций заранее неизвестно или зависит от динамических факторов во время выполнения программы.
В Python синтаксис цикла while следующий:
while условие:
# блок кода для выполненияЗдесь блок кода внутри цикла while будет выполняться до тех пор, пока условие остается истинным. Как только условие становится ложным, выполнение цикла прекращается.
Ожидание Изменения Состояния:
whileне_достигнут_предел:
# выполнить некоторые операцииЦикл продолжается до тех пор, пока не будет достигнут определенный предел.
Бесконечный Цикл с Условием Выхода:
whileTrue:
# выполнить операции
ifусловие_выхода:break
Цикл выполняется бесконечно, пока не будет выполнено специфическое условие для выхода.
Циклы while повторяются до тех пор, пока выполняется определенное булево условие. Это условие управляет выполнением операций внутри цикла. Рассмотрим пример:
# Инициализируем счетчик
count = 0
# Цикл продолжается, пока count меньше 10
whilecount<10:
print(count, end=',')
count += 1# Это эквивалентно записи count = count + 10,1,2,3,4,5,6,7,8,9,
Использование инструкций "break" и "continue"
break используется для выхода из цикла for или while.
continue пропускает текущий блок кода и возвращает выполнение к условию цикла.
Пример с break:
# Выводим все числа до заданного предела
count = 0
whileTrue:
print(count, end=',')
count += 1
ifcount>=10: # Если достигнут предел, прерываем циклbreak
print('\n') # Переходим на новую строку после завершения цикла0,1,2,3,4,5,6,7,8,9,
Пример с continue:
# Выводим только четные числа: 0, 2, 4, 6, 8
forninrange(10):
ifn % 2!= 0: # Если число нечетное, пропускаем егоcontinue
print(n, end=',')0,2,4,6,8,
Циклы с блоком "else"
Когда условие цикла не выполняется, исполняется код в блоке else. Однако, если внутри цикла была выполнена инструкция break, блок else пропускается. Важно отметить, что блок else будет выполнен даже если перед ним была инструкция continue.
Пример с else:
# Выводим числа от 0 до 4, а затем сообщение о достижении предела
count = 0
nlimit = 5
whilecount<nlimit:
print(count, end=',')
count += 1
else:
print("значение счетчика достигло %d" % (nlimit))0,1,2,3,4,значение счетчика достигло 5
2.11.8 Тернарные условные операторы
Тернарный условный оператор в Python представляет собой компактный способ выполнения операций на основе условия. В отличие от традиционных многострочных условных конструкций if-else, тернарный оператор позволяет инкапсулировать всю логику в одну строку. Это делает код более чистым, лаконичным и в соответствии с принципом DRY (Don'tRepeatYourself — «Не повторяйся»).
В качестве примера рассмотрим четырехстрочную конструкцию условного оператора:
if условие:
переменная = значение_1
else:
переменная = значение_2Эту конструкцию можно эффективно сократить до одной строки с использованием тернарного условного оператора, который следует общему синтаксису:
переменная = значение_1 if условие else значение_2Такой подход не только сокращает количество кода, но и облегчает его чтение и понимание, сохраняя при этом полную функциональность условной логики. Тернарные операторы особенно полезны в ситуациях, когда необходимо присвоить значение переменной в зависимости от некоторого условия.
# Определение переменных для демонстрации
значение_1 = "Значение, когда условие истинно"
значение_2 = "Значение, когда условие ложно"
# Определение условия для проверки
# В этом случае условие проверяет, является ли число четным
число = 10# Можно изменить для проверки разных условий
# Использование тернарного условного оператора для присваивания значения переменной 'результат'
# Если 'число' четное (число % 2 равно 0), присвоить 'значение_1', иначе 'значение_2'
результат = значение_1ifчисло % 2 == 0elseзначение_2
# Вывод результата
# Выведет 'значение_1', если число четное, и 'значение_2', если нечетное
print(результат)Значение, когда условие истинно
Тернарный оператор значение_1 if число % 2 == 0 else значение_2 используется для присваивания одного из этих значений переменной результат, в зависимости от того, четное число или нет. В конце результат выводится на экран.
2.12 Функции (методы)
Функции представляют собой удобный способ структурировать код на блоки, которые можно вызывать неограниченное количество раз по мере необходимости. Это значительно уменьшает повторение кода, делает код более чистым, читаемым и удобным для поддержки. Кроме того, функции являются отличным способом определения интерфейсов для удобного обмена кодами между программистами.
2.12.1 Блочная структура для определения функций
Функции в Python определяются с использованием ключевого слова блока "def", за которым следует имя функции, которое также является именем блока. Функция вызывается с использованием имени функции, за которым следуют круглые скобки (), внутри которых находятся аргументы, если они есть. Попробуйте самую простую функцию:
Defhi():
# Выводит приветственное сообщение на экран
print("Привет, добро пожаловать в эту простую функцию!")
# Вызов функции приветствие
hi()Привет, добро пожаловать в эту простую функцию!
В этом примере функция hiне принимает никаких аргументов и при её вызове выводит на экран сообщение "Привет, добро пожаловать в эту простую функцию!". Это демонстрирует базовый принцип создания и использования функций в Python.
2.12.2 Функции с аргументами
В простейшем случае, который мы рассмотрели ранее, аргументы функции не требовались. Однако часто функции создаются с обязательными аргументами, которые представляют собой переменные, передаваемые от вызывающего кода к функции. Это позволяет функции работать с различными входными данными и делает ее поведение более гибким.
# Определение функции с двумя параметрами: username и greeting
defgreeting_student(username, greeting):
# Вывод приветственного сообщения с использованием f-строки для вставки переменных
print(f"Привет, {username}, поздравляем! Желаем тебе {greeting}")
# Вызов функции с передачей двух строковых аргументов
greeting_student("Степа", "увлекательного путешествия в использовании функций!")Привет, Степа, поздравляем! Желаем тебе увлекательного путешествия в использовании функций!
Функции могут также возвращать значения вызывающему коду с использованием ключевого слова return.
2.12.3 Пример функции с возвращаемым значением:
# Определение функции для суммирования двух чисел
defsum_two_numbers(a, b):
returna + b# Возврат суммы двух аргументов функции
# Присваивание значений двум переменным и вызов функции для их суммирования
x, y = 2.0, 8.0
apb = sum_two_numbers(x, y)
print(f'{x} + {y} = {apb}') #Выводрезультата: "2.0 + 8.0 = 10.0"
# Изменение значений переменных и повторный вызов функции
x, y = 20, 80
apb = sum_two_numbers(x, y)
print(f'{x} + {y} = {apb}, отлично!') # Вывод результата: "20 + 80 = 100, отлично!"2.0 + 8.0 = 10.0
20 + 80 = 100, отлично!2.12.4Область видимости переменных
В Python существует правило LEGB (Local, Enclosing, Global, Built-in), определяющее порядок, в котором интерпретатор ищет переменную. Поиск завершается, когда переменная найдена.
# Определение функции с локальным изменением значения аргумента
defsum_two_numbers(a, b):
a += 1# Локальное увеличение значения аргумента a на 1
print('Внутри функции, a=',a) # Вывод локального значения a
returna + b# Возврат суммы измененного а и b
x, y = 2.0, 8.0
print('Перед вызовом функции, x=',x,'y=',y) # Вывод значений x и y перед вызовом функции
apb = sum_two_numbers(x, y) # Вызов функции и сохранение результата
print('После вызова функции, %f + %f = %f' % (x, y, apb)) # Вывод результатов после вызова функции
Перед вызовом функции, x= 2.0 y= 8.0
Внутри функции, a= 3.0
После вызова функции, 2.000000 + 8.000000 = 11.0000002.12.5 Лямбда-функции (Анонимные функции)
Анонимная функция — это функция без имени. В большинстве языков программирования, когда вы определяете функцию, вы также задаете ей имя, чтобы вы могли вызывать ее по этому имени позже в вашем коде. Однако анонимные функции отличаются тем, что они определяются без имени и обычно используются на месте, где они были созданы.
В Python анонимные функции создаются с помощью ключевого слова lambda, отсюда и другое их название — лямбда-функции. Лямбда-функции могут принимать любое количество аргументов, но могут содержать только одно выражение. Их синтаксис упрощен и предназначен для создания небольших функций, и эти функции не требуют явного return для возвращения значения — результат выражения автоматически возвращается.
Пример использования анонимной функции в Python:
result = (lambdax, y: x + y)(10, 20)
print(result)30
В этом примере лямбда-функция, которая складывает два числа, определена и немедленно вызвана с аргументами 10 и 20. Функция не имеет имени и используется единожды, прямо в месте своего определения.
Анонимные функции особенно полезны в случаях, когда требуется передать простую функцию в качестве аргумента другой функции. Это часто встречается в функциональном программировании, например, при использовании функций высшего порядка, таких как map(), filter() и reduce(), где лямбда-функции могут служить компактными и эффективными обработчиками.
Лямбда-функция — это компактное представление функции в одной строке. Она является одной из самых простых и в то же время полезных форм функций. Благодаря своей сжатости,она идеально подходят для создания небольших анонимных функций прямо в месте использования.
Лямбда-функции полезны для определения математических выражений. Например, линейная функция может быть определена как f(x)=ax+b, где a и b — константы. В контексте Python, это может быть выражено через лямбда-функцию следующим образом:
linear_function = lambdax, a, b: a * x + b
print("Линейнаяфункция: f(x) = 2x + 3 при x=5 дает", linear_function(5, 2, 3))
Линейная функция: f(x) = 2x + 3 при x=5 дает 13
Аналогично, квадратичная функция, которая выражается как f(x)=ax2+bx+cf(x)=ax2+bx+c, может быть определена через лямбда-функцию так:
quadratic_function = lambdax, a, b, c: a * x**2 + b * x + c
print("Квадратичнаяфункция: f(x) = 2x^2 + 3x + 1 при x=2 дает", quadratic_function(2, 2, 3, 1))
Квадратичная функция: f(x) = 2x^2 + 3x + 1 при x=2 дает 15Лямбда-функции часто используются в сочетании с обычными функциями, особенно когда требуется возвращать значение из компактной функции. Например, они могут использоваться в качестве аргументов для функций высшего порядка, таких как map(), filter() и reduce().
Пример использования лямбда-функции вместе с функцией map():
numbers = [1, 2, 3, 4, 5]
squared_numbers = list(map(lambdax: x**2, numbers))
print("Квадратычисел:", squared_numbers) # Вывод: [1, 4, 9, 16, 25]
Квадраты чисел: [1, 4, 9, 16, 25]2.13 Классы и объекты
Класс является основной конструкцией в объектно-ориентированном программировании, служащей шаблоном для создания объектов. Класс инкапсулирует переменные и функции (или методы), обеспечивая структуру и поведение для своих объектов. От класса можно создать неограниченное количество объектов, каждый из которых наследует структуру, переменные (атрибуты) и функции (методы) класса.
2.13.1 Простейший класс
В этом примере представлен простейший класс с именем "C". В классе определен атрибут ca, который является атрибутом класса. Комментарии внутри класса, заключенные в тройные кавычки, предназначены для предоставления документации класса или объяснения его назначения.
classC:''' Простейший возможный класс с именем "C" '''
ca = "атрибут класса"# Определение атрибута класса
# Использование функции help() для получения информации о классе C
help(C)
# Создание двух экземпляров класса C
i1 = C() # Создается экземпляр i1 класса C
i2 = C() # Создается экземпляр i2 класса C
# Демонстрация доступа к атрибуту класса через экземпляры и сам класс
print('i1.ca =', i1.ca) # Доступ к атрибуту класса через экземпляр i1
print('i2.ca =', i2.ca) # Доступ к атрибуту класса через экземпляр i2
print('C.ca =', C.ca) # Доступ к атрибуту класса напрямую через сам класс
# Изменение атрибута класса и наблюдение за его влиянием на экземпляры
C.ca = "Это измененный атрибут класса 'ca'"
print('После изменения:')
print('C.ca =', C.ca) # Показывает измененное значение атрибута класса
print('i1.ca =', i1.ca) # Экземпляр i1 отражает изменение атрибута класса
print('i2.ca =', i2.ca) # Экземпляр i2 отражает изменение атрибута классаВ этом коде показано, как создаются экземпляры класса и как они связаны с атрибутами класса. Также демонстрируется, что изменение атрибута класса отражается на всех его экземплярах, что подчеркивает свойство разделяемых атрибутов в объектно-ориентированном программировании.
Атрибуты классов и атрибуты экземпляров объектов хранятся в отдельных словарях. Для класса C словарь C.__dict__ содержит атрибуты класса, в то время как i1.__dict__ и i2.__dict__ хранят атрибуты соответствующих экземпляров объектов. Когда мы вносим изменения в атрибут экземпляра, такие как i1.ca, создается новый словарь для этого экземпляра, отражающий измененные атрибуты. Если же изменения не производятся, как в случае с i2 до присвоения значения i2.ca, словарь экземпляра остается пустым, и атрибуты по-прежнему наследуются от класса. После того как к i2.ca присваивается значение, для i2 также создается словарь атрибутов, отражающий изменения на уровне экземпляра. Важно отметить, что любые будущие изменения атрибутов на уровне класса не повлияют на атрибуты экземпляров, у которых уже созданы собственные словари атрибутов.
classC:
ca = "Исходное значение атрибута класса 'ca'"
# Создаем экземпляры класса C
i1 = C()
i2 = C()
# Изменяем атрибут класса 'ca'
C.ca = "2-я модификация атрибута класса 'ca'"
# Печатаем словарь класса C
print("Словарь класса C:", C.__dict__)
# Изменяем атрибут экземпляра 'ca' для i1
i1.ca = "Это измененный атрибут экземпляра 'ca'"
# Печатаем словарь экземпляра i1
print("Словарьэкземпляраi1:", i1.__dict__)
# Печатаем словарь экземпляра i2 до изменений
print("Словарь экземпляра i2 до изменений:", i2.__dict__)
# Изменяем атрибут экземпляра 'ca' для i2
i2.ca = "Теперь изменяем атрибут экземпляра 'ca'"
# Печатаем словарь экземпляра i2 после изменений
print("Словарь экземпляра i2 после изменений:", i2.__dict__)Словарькласса C: {'__module__': '__main__', 'ca': "2-ямодификацияатрибутакласса 'ca'", '__dict__': <attribute '__dict__' of 'C' objects>, '__weakref__': <attribute '__weakref__' of 'C' objects>, '__doc__': None}
Словарь экземпляра i1: {'ca': "Это измененный атрибут экземпляра 'ca'"}
Словарь экземпляра i2 до изменений: {}
Словарь экземпляра i2 после изменений: {'ca': "Теперь изменяем атрибут экземпляра 'ca'"}2.13.2 Класс для научных вычислений
classCircle:"""
Класс Circle предназначен для выполнения вычислений, связанных с кругом.
В частности, позволяет вычислить площадь круга по заданному радиусу.
"""
pi=3.14159# Классовый атрибут, содержащий значение числа Пи, используемое во всех экземплярах класса
def__init__(self, radius):"""
Конструктор класса, инициализирующий новый экземпляр класса Circle с заданным радиусом.
:paramradius: радиус круга"""
self.radius=radius# Экземплярный атрибут, содержащий радиус круга
defcircle_area(self):"""
Метод для вычисления площади круга.
:return: площадь круга"""
returnself.pi*self.radius**2# Возвращает площадь круга, используя формулу πr²
# Создание экземпляра класса Circle с радиусом 10
r=10
c10=Circle(r)
# Вывод начальных значений
print('Circle.pi до изменения:', Circle.pi)
print('Радиус:', c10.radius)
print('c10.pi до изменения:', c10.pi)
# Изменение значения pi только для экземпляра c10
c10.pi=3.14
# Вывод значений после изменения
print('c10.pi после изменения через c10.pi:', c10.pi)
print('Circle.pi после изменения через c10.pi:', Circle.pi)
print('Площадькруга c10 =', c10.circle_area())
print('Площадь круга с радиусом 100 =', Circle(100).circle_area())
Circle.pi до изменения: 3.14159
Радиус: 10
c10.pi до изменения: 3.14159
c10.pi после изменения через c10.pi: 3.14
Circle.pi после изменения через c10.pi: 3.14159
Площадь круга c10 = 314.0
Площадь круга с радиусом 100 = 31415.899999999998
В этом коде:Класс Circle описывает круг с возможностью вычисления его площади.
Атрибут pi является классовым и общим для всех экземпляров, представляя константу числа Пи.
Метод __init__ инициализирует экземпляр класса, задавая его радиус.
Метод circle_area вычисляет площадь круга, используя атрибуты экземпляра.
Демонстрируется, как изменение атрибута pi у конкретного экземпляра (c10) не влияет на классовый атрибут pi, подчеркивая разницу между классовыми и экземплярными атрибутами.
Этот пример также иллюстрирует основные концепции ООП, такие как инкапсуляция (сокрытие данных внутри класса) и наследование (при создании подклассов).
2.13.3 Подкласс (наследование класса)
Подклассы часто используются для реализации наследования в Python, что позволяет создавать новые классы, полностью используя структуру существующего класса (атрибуты и функции), не влияя на текущее использование этого класса. Это также полезно для модернизации существующих программ за счет уменьшения дублирования кода.
Предположим, что код для класса Circle, созданный ранее, уже распространен и используется многими. Теперь мы решаем создать другой класс для вычисления площади сектора круга, исходя из значения доли круга. Для этого мы можем создать подкласс, названный PartialCircle, который не повлияет на использование уже распространенного класса Circle.
classPartialCircle(Circle):
''' Подкласс "PartialCircle", основанный на классе "Circle": Вычисляет площадь сектора круга. '''
def__init__(self, radius, portion):''' Инициализациясатрибутами: radiusиportion. '''
super().__init__(radius) # Наследует атрибуты из базового класса "Circle".
self.portion = portion# Атрибут подкласса, определяющий долю круга.
defsector_area(self):''' Вычисляет площадь сектора круга. '''
returnself.portion * self.circle_area() # Использует метод базового класса для вычисления площади круга.Читатели могут, выполнить этот код и уделить время на изучение информации о структуре подкласса, его связи с базовым классом, использовании self для подготовки связей с будущими объектами, а также о том, какие атрибуты и функции были унаследованы от базового класса и созданы в подклассе.
pc10 = PartialCircle(10., 0.5) # Создание экземпляра объекта подкласса с радиусом 10 и долей 50%.
print(pc10.pi) # Используем атрибут из базового класса
print(pc10.radius) # Также атрибут из базового класса
print(pc10.sector_area()) # Вычисляем площадь 50% сектора круга
Теперь изменим значение константы pi через экземпляр подкласса:
pc10.pi = 3.14
print(pc10.pi) # Значение изменилось на 3.14
Проверим значение pi через базовый класс Circle:
print(c10.pi) # Значение остается неизменным. Изменения в подклассе не влияют на базовый класс.Давайте составим полный код-листинг с подробными комментариями, демонстрирующий наследование классов в Python, используя пример с классами Circle и PartialCircle.
# Определение базового класса Circle
classCircle:''' Класс "Circle" для представления круга и вычисления его площади. '''
def__init__(self, radius):
''' Инициализация экземпляра класса с одним атрибутом: radius (радиус). '''
self.radius = radius# Атрибут экземпляра, хранящий радиус круга.
self.pi = 3.14159# Атрибут, хранящий значение числа π.
defcircle_area(self):''' Метод для вычисления площади круга. '''
returnself.pi * (self.radius ** 2) # Возвращает площадь круга по формуле πr^2.
# Определение подкласса PartialCircle, наследующего Circle
classPartialCircle(Circle):
''' Подкласс "PartialCircle", основанный на классе "Circle": Вычисляет площадь сектора круга. '''
def__init__(self, radius, portion):
''' Инициализация с атрибутами: radius и portion (доля круга). '''
super().__init__(radius) # Вызов инициализатора базового класса для установки радиуса.
self.portion = portion# Атрибут подкласса, определяющий долю круга, для которой нужно вычислить площадь.
defsector_area(self):''' Вычисляет площадь сектора круга. '''
returnself.portion * super().circle_area() # Использует метод базового класса для вычисления площади круга и умножает на долю.
# Создание экземпляра базового класса Circle
c10 = Circle(10) # Экземпляр класса Circle с радиусом 10
print("Площадь круга:", c10.circle_area()) # Вычисление и вывод площади круга
# Создание экземпляра подкласса PartialCircle
pc10 = PartialCircle(10, 0.5) # Экземпляр класса PartialCircle с радиусом 10 и долей 50%
print("Площадь 50% сектора круга:", pc10.sector_area()) # Вычисление и вывод площади сектора круга
# Изменение значения π через экземпляр подкласса
pc10.pi = 3.14
print("Измененное значение pi в подклассе:", pc10.pi)
# Проверка, изменилось ли значение π в базовом классе
print("Значение pi в базовом классе:", c10.pi) # Значение π в базовом классе остается неизменным
Площадь круга: 314.159
Площадь 50% сектора круга: 157.0795
Измененное значение pi в подклассе: 3.14
Значение pi в базовом классе: 3.141592.14 Массивы Numpy: Удобство для научных вычислений
Массивы Numpy похожи на списки, и с ними гораздо проще работать для научных вычислений. Операции с массивами Numpy обычно выполняются гораздо быстрее при работе с объемными данными.
2.14.1 Списки и массивы Numpyи их отличие от классического списка
В Python списки и массивы NumPy являются структурами данных, предназначенными для хранения наборов элементов. Несмотря на поверхностные сходства, они обладают существенными различиями, которые необходимо учитывать при выборе подходящего инструмента для решения конкретных задач. В данной главе мы детально рассмотрим общие черты и отличительные особенности списков и массивов NumPy, уделяя особое внимание их применимости в различных вычислительных контекстах.
Общие характеристики
Изменяемость: Оба типа структур допускают модификацию после создания, позволяя добавлять, удалять или изменять элементы посредством так называемых деструктивных операций.
Индексация: Обращение к отдельным элементам осуществляется посредством их числового индекса, отражающего позицию в структуре.
Срезы: Создание новых структур, содержащих определенные фрагменты исходных данных, обеспечивается механизмом срезов.Ключевые различия
Происхождение: Массивы NumPy доступны в составе внешней библиотеки NumPy, требующей импорта, в то время как списки являются встроенным типом данных языка Python.
Поэлементные операции: Массивы NumPy поддерживают прямое выполнение математических и логических операций над всеми элементами без использования циклов, что недоступно для списков.
Однородность типов: Массивы NumPy обязывают к использованию однотипных данных, в то время как списки допускают хранение элементов различных типов.
Многомерность: Массивы NumPy способны представлять многомерные структуры, включая матрицы и тензоры, что выходит за рамки возможностей одномерных списков.
Производительность: Операции с массивами NumPy, как правило, значительно превосходят по скорости аналогичные действия над списками благодаря оптимизации вычислений и использованию низкоуровневого кода на языке C.
Эффективность памяти: Массивы NumPy характеризуются более компактным хранением данных в сравнении со списками, что особенно актуально при работе с большими объемами информации.
Применимость в областях: Массивы NumPy находят широкое применение в математических вычислениях, обработке массивов данных и алгоритмах машинного обучения благодаря своим преимуществам в производительности и поддержке многомерных операций.
Осознанный выбор подходящей структуры данных позволяет оптимизировать производительность, память и удобство разработки в различных вычислительных задачах.
2.14.2 Структура массива NumPy
Сначала мы кратко рассмотрим структуру массива numpy в сравнении со списком, который мы обсуждали ранее. Чтобы начать обсуждение, мы импортируем пакет numpy.
importnumpyasnp# импортируем модуль numpy и даем ему псевдоним np
x1 = np.array([28, 3, 28, 0]) # создаем одномерный массив numpy
print('x1=', x1) # выводим массив x1
# массив numpy похож на список
x1= [28 3 28 0]
Как показано выше, массив numpy "обрамляется" в пару квадратных скобок (как и список).importnumpyasnp
# Импортируем модуль numpy и даем ему псевдоним np
x2 = np.array([[51, 22.0], [0, 0], (18 + 9j, 3.)]) # смешанные типы
# Создаем массив x2 из трех строк и двух столбцов. Первые две строки содержат целые числа, а третья строка содержит комплексное число и вещественное число.
print('x2=', x2) # Все становятся комплексными числами
# Выводим массив x2. Поскольку в массиве присутствуют комплексные числа, все числа в массиве становятся комплексными числами.
x2= [[51.+0.j 22.+0.j]
[0.+0.j 0.+0.j]
[18.+9.j 3.+0.j]]Это двумерный массив NumPy. Он обрамляется двойной парой квадратных скобок. Список не имеет многомерности, за исключением формы вложенности: списков в списках.
Мы также можем создавать массивы NumPy с помощью списков. В следующем примере мы сначала создаем два списка, а затем создаем из них массивы NumPy:
# список веса людей (в кг)
list_w = [57.5, 64.3, 71.6, 68.2]
# список высота людей (в м)
list_h = [1.5, 1.6, 1.7, 1.65]
# выводим на экран список весов
print('Веслюдей:', list_w)
# выводим на экран список высот
print('Высота людей:', list_h)
Вес людей: [57.5, 64.3, 71.6, 68.2]
Высота людей: [1.5, 1.6, 1.7, 1.65]
# ИмпортируембиблиотекуNumPyimportnumpyasnp
# Объявляем списки весов и высот людей
list_w = [57.5, 64.3, 71.6, 68.2]
list_h = [1.5, 1.6, 1.7, 1.65]
# Преобразуем списки в NumPy-массивы
narray_w = np.array(list_w)
narray_h = np.array(list_h)
# Выводим на экран массивы
print('Массив весов:', narray_w)
print('Массив высот:', narray_h)
Массив весов: [57.5 64.3 71.6 68.2]
Массив высот: [1.5 1.6 1.7 1.65]Давайте создадим функцию, которая выводит информацию о заданном массиве NumPy.
Сначала мы создадим функцию с именем getArrayInfo(). Эта функция принимает в качестве аргумента массив NumPy и печатает следующие сведения о нем:
Значение первого элемента массива
Тип массива
Количество измерений массива
Форму массива
Количество элементов в массиве
Тип данных массива
Адрес памяти массива
Вот код функции getArrayInfo():
defgetArrayInfo(a):
"""Получение информации о заданном массиве: getArrayInfo(array)"""
# Печать элементов первой оси массива
print('Элементы первой оси массива:', a[0])
# Печать типа массива
print('Тип:', type(a))
# Печать количества измерений массива
print('Количество измерений, a.ndim:', a.ndim)
# Печать формы массива (размеры по каждой оси)
print('Форма массива, a.shape:', a.shape)
# Печать общего количества элементов в массиве
print('Количество элементов, a.size:', a.size)
# Печать типа данных элементов массива
print('Тип данных элементов, a.dtype:', a.dtype)
# Печать адреса памяти, где хранится массив (информация для отладки)
print('Адрес памяти:', a.data)Теперь мы можем использовать функцию getArrayInfo() для получения информации о любом массиве NumPy. Например, вот как мы можем получить информацию о массиве narray_w:
narray_w = np.array([57.5, 64.3, 71.6, 78.9])
getArrayInfo(narray_w)
Элементы первой оси массива: 57.5
Тип: <class 'numpy.ndarray'>
Количество измерений, a.ndim: 1
Форма массива, a.shape: (4,)
Количество элементов, a.size: 4
Тип данных элементов, a.dtype: float64
Адреспамяти: <memoryat 0x102f5a680>2.14.3 Срезыдля массивов NumPy
Срезы также работают для массивов NumPy, аналогично спискам. Давайтевозьмемсрезмассива.
importnumpyasnp
list_w = [57.5, 64.3, 71.6, 68.2]
list_h = [1.5, 1.6, 1.7, 1.65]
narray_w = np.array([57.5, 64.3, 71.6, 78.9])
print(list_w[1:3]) # срез между вторым и третьим элементами
print(narray_w[1:3]) # срез между вторым и третьим элементами
[64.3, 71.6]
[64.3 71.6]Давайте теперь добавим элемент как к списку, так и к массиву numpy.
list_w.append(59.8)
print(list_w)
# Для массива numpy мы будем использовать:
print(np.append(narray_w,59.8))57.5, 64.3, 71.6, 68.2, 59.8
57.5 64.3 71.6 68.2 59.8
print(list_w, ', ', narray_w)
print(type(list_w), ', ', type(narray_w))
print(len(list_w), ', ', narray_w.ndim) # Используйтеlen() дляполучениядлины57.5, 64.3, 71.6, 68.2, 59.8 57.5 64.3 71.6 68.2
<class 'list'><class 'numpy.ndarray'>5 1
nwh = (narray_w,narray_h) # Это формирует кортеж из массивов np
print(nwh)
# (array([57.5, 64.3, 71.6, 68.2]), array([1.5, 1.6, 1.7, 1.65]))
# Чтобы сформировать многомерный массив, мы можем использовать следующее (подробнее об этом позже):
arr = np.array([narray_w,narray_h])
# array([[57.5, 64.3, 71.6, 68.2],
# [1.5, 1.6, 1.7, 1.65]])В NumPy, основной структурой данных является многомерный массив. Рассмотрим массив arr с двумя измерениями. Размеры массива задаются в виде кортежа (2, 4), где первое число - количество строк (записей вдоль оси 0), а второе - количество столбцов (записей вдоль оси 1).
Транспонирование - это операция изменения порядка осей массива. Для выполнения транспонирования массива в NumPy используется метод.T. Эта операция заменяет строки на столбцы и наоборот, что приводит к изменению формы массива.
importnumpyasnp
# Исходныймассив
arr = np.array([[57.5, 64.3, 71.6, 68.2],
[1.5, 1.6, 1.7, 1.65]])
# Транспонирование массива
arrT = arr.T
# Вывод транспонированного массива
print(arrT)
[[57.5 1.5]
[64.3 1.6]
[71.6 1.7]
[68.2 1.65]]В результате выполнения этого кода, форма массива изменится с исходной (2, 4) на (4, 2). Это означает, что массив, первоначально содержащий 2 строки и 4 столбца, после транспонирования будет содержать 4 строки и 2 столбца.
Как видно из примера, первый столбец исходного массива стал первой строкой транспонированного массива, и так далее для остальных элементов. Этот процесс широко используется в математических вычислениях, обработке данных и машинном обучении для подготовки и преобразования данных.
2.14.4 Ссылка и копия массиваNumPy
Рассмотрим, как изменения в массиве NumPy влияют на другие переменные, которые ссылаются на этот же массив.
importnumpyasnp
# Создаем массив NumPy с двумя строками и четырьмя столбцами
arr = np.array([[57.5, 64.3, 71.6, 68.2],
[1.5, 1.6, 1.7, 1.65]])
# Создаем вторую переменную, которая ссылается на тот же массив
arrb = arr
# Выводим содержимое arrb
print(arrb)
# Изменяем первый элемент arr
arr[0, 0] = 888.0
# Снова выводим содержимое arrb и видим, что оно изменилось
print(arrb)
[[57.5 64.3 71.6 68.2]
[1.5 1.6 1.7 1.65]]
[[888. 64.3 71.6 68.2]
[1.5 1.6 1.7 1.65]]Как видно из результата, после изменения элемента в arr, содержимое arrb также изменилось. Это происходит потому, что arrb не является копией arr, а ссылается на тот же объект данных. Любые изменения, сделанные через одну переменную, будут видны и через другую.
Замечание: Для создания независимой копии массива в NumPy следует использовать метод copy() следующим образом:
arrc = arr.copy()
Используя copy(), вы создаете новый объект данных, который может быть изменен независимо от оригинала.
При работе с массивами в NumPy важно понимать различие между присваиванием и созданием копии. Рассмотрим пример:importnumpyasnp
# Создаем двумерный массив
arr = np.array([[57.5, 64.3, 71.6, 68.2],
[1.5,1.6,1.7, 1.65]])
# Пытаемся создать "копию" через присваивание
arrc = arrВ данном случае arrc не является независимой копией arr. Это всего лишь новая ссылка на тот же самый массив, и изменения в arrc окажут влияние на arr. Чтобы создать настоящую копию, необходимо использовать метод copy():
# Создаем независимую копию массива
arrc = arr.copy()Теперь arrc является полностью независимой копией arr, и изменения в одном не затронут другой. Однако стоит помнить, что создание копий может быть ресурсоемкой операцией, поэтому следует использовать его с умом:
# Изменяем элемент в исходном массиве
arr[0, 0] = 77.0
# Выводим оба массива для сравнения
print('arr:', arr)
print('arrc:', arrc)Выполнив этот код, мы увидим, что arr изменился, но arrc остался прежним.
Замечание: Используйте метод copy() только тогда, когда это действительно необходимо, чтобы избежать ненужных затрат памяти и времени выполнения.
importnumpyasnp# Импортируем библиотеку NumPy под псевдонимом np
# Создаем двумерный массив arr с помощью функции np.array
arr = np.array([[57.5, 64.3, 71.6, 68.2],
[1.5,1.6, 1.7, 1.65]])
# Создаем независимую копию массива arr, используя метод copy
arrc = arr.copy()
# Выводим содержимое массива arrc
print('arrc', arrc)
# Изменяем элемент в позиции [0, 0] массива arr на 77.0
arr[0, 0] = 77.0
# Выводим измененный массив arr
print('arr', arr)
# Выводим массив arrc, чтобы показать, что он не изменился
print('arrc', arrc)
arrc [[57.5 64.3 71.6 68.2]
[1.5 1.6 1.7 1.65]]
arr [[77. 64.3 71.6 68.2]
[1.5 1.6 1.7 1.65]]
arrc [[57.5 64.3 71.6 68.2]
[1.5 1.6 1.7 1.65]]2.14.5 Ось в массиве NumPy
Ось (axis) - это одно из фундаментальных понятий, используемых при работе с массивами в библиотеке NumPy. Ось указывает направление в массиве. Так, одномерный массив обладает одной осью (axis 0), двумерный — двумя осями (axis 0 и axis 1), и так далее.
Многомерные массивы могут быть сконструированы из одномерных путем их объединения. Для этого используется метод np.stack(), который позволяет объединять последовательности массивов вдоль указанной оси. Синтаксис метода следующий:
np.stack(последовательность_массивов, axis=номер_оси)где последовательность_массивов- это список или кортеж массивов NumPy, а номер_оси - это индекс оси, вдоль которой необходимо провести объединение.
Предположим, у нас есть два одномерных массива, которые мы хотим объединить в двумерный массив, располагая их друг над другом:importnumpyasnp# Импортируем библиотеку NumPy под псевдонимом np
# Предполагаемые одномерные массивы для объединения
narray_w = np.array([57.5, 64.3, 71.6, 68.2])
narray_h = np.array([1.5, 1.6, 1.7, 1.65])
# Объединение массивов вдоль оси 0 (по вертикали)
arr = np.stack([narray_w, narray_h], axis=0)
# Вывод результата
print(arr)
[[57.5 64.3 71.6 68.2]
[1.5 1.6 1.7 1.65]]
Часто возникает необходимость преобразовать многомерный массив в одномерный. Для этого в NumPy существует метод np.ravel(), который 'выпрямляет' многомерный массив в одномерную форму.importnumpyasnp# Импортируем библиотеку NumPy под псевдонимом np
# Предполагаемые одномерные массивы для объединения
narray_w = np.array([57.5, 64.3, 71.6, 68.2])
narray_h = np.array([1.5, 1.6, 1.7, 1.65])
# Объединение массивов вдоль оси 0 (по вертикали)
arr = np.stack([narray_w, narray_h], axis=0)
# Выводим исходный двумерный массив
print(arr)
# Преобразование двумерного массива в одномерный
rarr = np.ravel(arr)
# Вывод результата
print(rarr)
[[57.5 64.3 71.6 68.2]
[1.5 1.6 1.7 1.65]]
[57.5 64.3 71.6 68.2 1.5 1.6 1.7 1.65]
Рисунок: Введение в численные вычисления с NumPyВ процессе работы с массивами в NumPy часто встречаются ситуации, когда необходимо изменить размерность массива. Это может быть преобразование двумерного массива с формой (2, 4) в одномерный массив длиной 8 элементов.
В области машинного обучения часто требуется выполнить суммирование элементов массива по определенной оси. В NumPy для этих целей предусмотрена функция np.sum, которая позволяет эффективно суммировать элементы вдоль заданной оси.importnumpyasnp# Импортируем библиотеку NumPy под псевдонимом np
# Предположим, у нас есть следующий двумерный массив:
arr = np.array([[57.5, 64.3, 71.6, 68.2],
[1.5, 1.6, 1.7, 1.65]])
# Выводим исходный массив для наглядности
print(arr)
# Суммирование элементов массива по столбцам (axis=0)
column_sum = np.sum(arr, axis=0)
print('Суммапостолбцам:', column_sum, column_sum.shape)
# Суммирование элементов массива по строкам (axis=1)
row_sum = np.sum(arr, axis=1)
print('Суммапострокам:', row_sum, row_sum.shape)
[[57.5 64.3 71.6 68.2]
[1.5 1.6 1.7 1.65]]
Сумма по столбцам: [59. 65.9 73.3 69.85] (4,)
Сумма по строкам: [261.6 6.45] (2,)Сумма по столбцам: результатом является одномерный массив, каждый элемент которого представляет собой сумму чисел в соответствующем столбце исходного массива. Размерность этого массива — (4,).
Сумма по строкам: результатом также является одномерный массив, каждый элемент которого — сумма чисел в соответствующей строке исходного массива. Размерность этого массива — (2,).
Суммирование вдоль разных осей массива приводит к уменьшению его размерности и изменению его формы.
2.14.7Вычисления по элементам
В Python существуют разные типы структур данных, которые поддерживают различные операции. Два из наиболее часто используемых типов для хранения коллекций чисел - это списки (list) и массивы NumPy (numpy.array). Они ведут себя по-разному при выполнении математических операций.
Списки в Python можно соединять вместе, используя оператор +. Это операция называется конкатенацией. Конкатенация - это процесс прикрепления одного списка в конец другого, что приводит к созданию нового списка, содержащего элементы обоих исходных списков.
Если у нас есть два списка list_w и list_h, то выполнение list_w + list_h создаст новый список, в котором сначала идут все элементы из list_w, а затем все элементы из list_h.
list_w = [57.5, 64.3, 71.6, 68.2, 59.8]
list_h = [1.5, 1.6, 1.7, 1.65]
listwh = list_w + list_h
print('Сконкатенированный список:', listwh)Сконкатенированный список: [57.5, 64.3, 71.6, 68.2, 59.8, 1.5, 1.6, 1.7, 1.65]
В отличие от списков, массивы NumPy поддерживают поэлементные операции. Это означает, что при использовании оператора + между двумя массивами NumPy, Python складывает соответствующие элементы этих массивов.
Если narray_w и narray_h являются массивами NumPy одинаковой длины, операция narray_w + narray_h создаст новый массив NumPy, в котором каждый элемент является суммой соответствующих элементов исходных массивов.
importnumpyasnp# Импортируем библиотеку NumPy под псевдонимом np
narray_w = np.array([57.5, 64.3, 71.6, 68.2])
narray_h = np.array([1.5, 1.6, 1.7, 1.65])
narraywh = narray_w + narray_h
print('Поэлементносложенныймассив:', narraywh)
Поэлементно сложенный массив: [59. 65.9 73.3 69.85]Массивы NumPy особенно удобны при необходимости выполнения поэлементного умножения. Это часто используется для преобразования единиц измерения, например, при переводе веса из килограммов в фунты.
Чтобы перевести вес из килограммов в фунты, мы можем умножить массив весов в килограммах на число 2.20462 (поскольку 1 килограмм равен 2.20462 фунтам).
importnumpyasnp# Импортируем библиотеку NumPy под псевдонимом np
weights_kg = np.array([57.5, 64.3, 71.6, 68.2])
weights_lbs = weights_kg * 2.20462
print('Весвфунтах:', weights_lbs)
Вес в фунтах: [126.76565 141.757066 157.850792 150.355084]Таким образом, массивы NumPy предоставляют мощные и гибкие средства для выполнения математических операций над большими данными.
Прежде мы обсуждали использование обычных списков и функций, таких как zip(), для поэлементных операций, но это может быть неэффективно, особенно для больших объемов данных. Вместо этого мы можем использовать возможности NumPy, которые значительно ускоряют вычисления.
importnumpyasnp
# Пример использования обычных списков для вычисления суммы элементов двух списков
list1 = [20, 30, 40, 50, 60]
list2 = [4, 5, 6, 2, 8]
sum_lists = [x + yforx, yinzip(list1, list2)]
print('Сумма списков:', sum_lists)
# Теперь выполним ту же операцию с использованием массивов NumPy
sum_arrays = (np.array(list1) + np.array(list2)).tolist()
print('Суммамассивов NumPy:', sum_arrays)
Сумма списков: [24, 35, 46, 52, 68]
Сумма массивов NumPy: [24, 35, 46, 52, 68]В примере с использованием NumPy мы преобразуем списки в массивы NumPy, выполняем поэлементное сложение, а затем преобразуем результат обратно в список. Это гораздо более эффективно, особенно когда мы работаем с большими наборами данных.
2.14.8Практические методы создания многомерных массивов
В областях, таких как машинное обучение и математические вычисления, многомерные массивы являются неотъемлемым инструментом. Это связано с тем, что при работе с большими объемами данных, необходимы эффективные средства для их генерации, манипуляции и обработки. Библиотека Numpy предоставляет ряд функций для работы с массивами данных различной размерности.
Функция np.arange(start, stop, step, dtype) создает одномерный массив с последовательностью чисел, начиная с start и заканчивая stop (не включая stop), с шагом step. Параметр dtype определяет тип данных элементов массива. Если он не указан, тип данных будет определен исходя из типа входных данных.
np.arange(2, 8, 0.5, dtype=np.float)
array([2., 2.5, 3., 3.5, 4., 4.5, 5., 5.5, 6., 6.5, 7., 7.5])Этот код вернет массив вещественных чисел с интервалом 0.5, начиная с 2 и заканчивая числом меньше 8.
В отличие от np.arange, функция np.linspace(start, stop, num) возвращает массив, который содержит num равномерно распределенных чисел в интервале от start до stop, включая оба конца интервала. Это идеально подходит для случаев, когда необходимо получить точное количество элементов.
np.linspace(1., 4., 6)
array([1., 1.6, 2.2, 2.8, 3.4, 4.])Этот код сгенерирует массив из 6 чисел, равномерно распределенных между 1 и 4 включительно.
Использование этих функций упрощает процесс создания массивов для тестирования алгоритмов, визуализации данных и других вычислительных задач. Они помогают избежать ручного ввода значений и автоматизируют процессы предварительной обработки данных.
Инициализация массивов с заданным значением.
importnumpyasnp
a = np.array([1.,2.,3.])
a.fill(9.9) # Заполнить все элементы одним и тем же значением
print(a)
[9.9 9.9 9.9]Первоначально создается одномерный массив a с тремя элементами [1., 2., 3.]. Затем используется метод.fill(9.9), который заменяет все элементы массива на значение 9.9. В результате получается массив, состоящий из одинаковых значений 9.9.
Создание массива без инициализации значений
importnumpyasnp
x = np.empty((3,4)) # Указана форма (размерность) (3,4), без инициализации значений
print(x)
[[0. 0. 0. 0.]
[0. 0. 0. 0.]
[0. 0. 0. 0.]]
Функция np.empty((3,4)) создает массив размером 3x4 (3 строки и 4 столбца), но не инициализирует его значения, что означает, что в нем будут находиться произвольные числа, которые уже были в этой области памяти.Инициализация массива нулями.
importnumpyasnp
x = np.zeros((6,6)) # Инициализированнулями
print(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.]]
Функция np.zeros((6,6)) создает массив размером 6x6, где каждый элемент инициализирован значением 0.Создание массива и инициализация его единицами и последующее умножение.
importnumpyasnp
y = np.ones((2,2))*2# Массив 2x2 с 1.0 во всех элементах
print(y)
[[2. 2.]
[2. 2.]]
Функция np.ones((2,2)) создает массив 2x2, где каждый элемент инициализирован значением 1.0. После этого массив умножается на 2, и в результате получается массив, где каждый элемент равен 2.Присваивание одного массива срезу другого массива.
importnumpyasnp# ИмпортируембиблиотекуNumPy
# Создаем первый массив размером 6x6, инициализированный нулями
x = np.zeros((6, 6))
print("Исходный массив x:")
print(x)
# Создаем второй массив размером 2x2, инициализированный единицами
y = np.ones((2, 2))
print("\nМассив y:")
print(y)
# Умножаем каждый элемент массива y на 2
y *= 2
print("\nМассив y после умножения на 2:")
print(y)
# Присваиваем массив y срезу массива x.
# Здесь мы берем срез от 3-й до 5-й строки (не включая 5) и от 3-й до 5-й колонки (не включая 5)
# и присваиваем ему значения массива y
x[3:5, 3:5] = y
print("\nМассив x после присваивания срезу значения массива y:")
print(x)
Исходный массив 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.]]
Массив y:
[[1. 1.]
[1. 1.]]
Массив y после умножения на 2:
[[2. 2.]
[2. 2.]]
Массив x после присваивания срезу значения массива y:
[[0. 0. 0. 0. 0. 0.]
[0. 0. 0. 0. 0. 0.]
[0. 0. 0. 0. 0. 0.]
[0. 0. 0. 2. 2. 0.]
[0. 0. 0. 2. 2. 0.]
[0. 0. 0. 0. 0. 0.]]Эти примеры демонстрируют различные функции библиотеки NumPy для создания и манипуляции массивами данных в Python.
2.14.9 Работа с внешним пакетом: TensorFlow
В арсенале инструментов современного специалиста по машинному обучению присутствует множество библиотек и пакетов, специально разработанных для облегчения и ускорения процесса построения масштабируемых моделей машинного обучения. Одним из таких мощных инструментов является TensorFlow, который мы будем использовать в рамках данного учебного курса.
TensorFlow — это внешняя библиотека, не входящая в стандартную поставку Python, поэтому перед началом работы она должна быть установлена. Это можно сделать с помощью системы управления пакетами pip, выполнив следующую команду в командной строке или терминале:
pipinstalltensorflow
После выполнения этой команды система pip автоматически загрузит и установит последнюю версию TensorFlow и все необходимые зависимости.
В процессе обучения и работы с кодом вы можете столкнуться с ошибками, связанными с отсутствием определенных модулей. В таком случае необходимо выполнить установку недостающего модуля аналогичным образом, используя pip.
После успешной установки TensorFlow его можно легко импортировать в ваш Python-скрипт с помощью стандартной команды импорта:importtensorflowastf# Импорт TensorFlow под псевдонимом tf для удобства
Таким образом, вы получаете доступ ко всем функциям и классам TensorFlow, которые можно использовать для построения и обучения моделей машинного обучения.
TensorFlow также предоставляет модуль tensorflow.numpy (tfnp) для работы с массивами, аналогично numpy. Он обеспечивает совместимость с numpy и позволяет использовать многие из тех же функций и методов. Пример создания массива:
importtensorflow.experimental.numpyastfnp# Импорт модуля для работы с массивами
# Создаем массив размером 3 на 4, заполненный нулями
x = tfnp.zeros((3, 4)) # Используем функцию zeros для создания массива, заполненного нулями
# Конвертируем массив TensorFlownumpy в стандартный массив NumPy для вывода
x_np = x.numpy() # Конвертация в стандартный массив NumPy
# Выводим массив, используя стандартные опции форматирования вывода NumPy
print(x_np)
# Выводим размер массива (должно быть 12, так как 3 строки на 4 столбца)
print(tfnp.size(x))
[[0. 0. 0. 0.]
[0. 0. 0. 0.]
[0. 0. 0. 0.]]12
В этом примере с помощью tfnp.zeros создается массив размером 3 строки на 4 столбца, заполненный нулями. Затем мы выводим его на экран и показываем общее количество элементов в массиве.
Тензор — это математический объект, который является обобщением понятий скаляра, вектора и матрицы на более высокие измерения. Тензоры широко используются в физике, инженерии и, конечно же, в области машинного обучения и глубокого обучения. Они представляют собой многомерные массивы данных, которые могут иметь любое количество измерений, также называемых рангами.
Ранг тензора: Ранг тензора (или его порядок) определяет количество измерений тензора. Например, скаляр (одиночное число) является тензором ранга 0, вектор (список чисел) — тензором ранга 1, матрица (таблица чисел) — тензором ранга 2. Тензоры с рангом выше 2 часто называют многомерными тензорами.
Форма тензора: Форма тензора описывает его размерность в каждом измерении. Например, матрица размером 3x4 имеет форму [3, 4], что означает, что матрица содержит 3 строки и 4 столбца.
Элементы тензора: Каждый элемент тензора — это число, которое может быть целым, вещественным, и даже более сложным типом данных, в зависимости от контекста использования. Элементы тензора обычно обрабатываются совместно с помощью математических операций.
Тензоры могут быть использованы для представления разнообразных данных:
Скалярные данные (температура, цена, и т.д.) могут быть представлены тензорами ранга 0.
Векторные данные (силы, скорости, и т.д.) или временные ряды могут быть представлены тензорами ранга 1.Изображения, которые можно представить в виде 2D матриц пикселей, могут быть представлены тензорами ранга 2 (или ранга 3, если учитывать цветовые каналы).
Видеоданные, где каждый кадр — это изображение, могут быть представлены тензорами ранга 4 (время, высота, ширина, цветовые каналы).importtensorflowastf
y = tf.random.normal([3, 4], mean=0.0, stddev=1.0)
Функция tf.random.normal генерирует тензор заданной формы ([3, 4] означает 3 строки и 4 столбца) с элементами, взятыми из нормального (гауссовского) распределения. Параметры mean и stddev определяют среднее значение и стандартное отклонение распределения соответственно. В данном случае они установлены в 0.0 и 1.0, что соответствует стандартному нормальному распределению.
x = tf.exp(y)
Функция tf.exp применяется к каждому элементу тензора y. Экспоненциальная функция exp(x) вычисляется как e^x, где e — основание натуральных логарифмов, приблизительно равное 2.71828. Это преобразование часто используется в науке и инженерии.
y_transposed = tf.transpose(y)
tf.transpose функция меняет местами размерности тензора. В случае матрицы (2D тензора), это приводит к замене строк на столбцы и наоборот. Транспонирование часто используется для подготовки тензоров к математическим операциям, требующим определенного выравнивания измерений, таким как умножение матриц.
result_matrix = tf.matmul(x, y_transposed)
Функция tf.matmul выполняет умножение двух тензоров, интерпретируя их как матрицы. Умножение матриц не является поэлементным (как умножение в алгебре), а скорее включает вычисление каждого элемента результирующей матрицы как скалярного произведения соответствующей строки первой матрицы на столбец второй. Для того, чтобы умножение было возможно, количество столбцов в первой матрице должно совпадать с количеством строк во второй. Результатом является новая матрица, размер которой определяется количеством строк первой матрицы и количеством столбцов второй.importtensorflowastf
# Инициализируем тензор размером 3x4 значениями из стандартного нормального распределения
y = tf.random.normal([3, 4], mean=0.0, stddev=1.0)
# Выводим значение тензора y
print('y')
print(y.numpy())
# Экспоненцируем каждый элемент тензора y
x = tf.exp(y) # Вычисляем e в степени каждого элемента тензора y
print(x.numpy())
# Транспонируем тензор y
y_transposed = tf.transpose(y) # Транспонируемтензор y
print(y_transposed.numpy())
# Выполняем умножение тензора x на транспонированный тензор y
result_matrix = tf.matmul(x, y_transposed) # Вычисляем произведение матриц
print(result_matrix.numpy())[[0.71528995 2.1142166 -1.1875231 -0.16214192]
[-0.1809633 -0.21809964 1.1487513 -0.02445018]
[1.6564487 0.6891605 1.2565229 1.3516059]]
[[2.0447795 8.283093 0.30497572 0.8503205]
[0.834466 0.8040453 3.1542516 0.9758463]
[5.240667 1.9920425 3.5131845 3.863625]]
[[0.71528995 -0.1809633 1.6564487]
[2.1142166 -0.21809964 0.6891605]
[-1.1875231 1.1487513 1.2565229]
[-0.16214192 -0.02445018 1.3516059]]
[[18.474825 -1.847019 10.62796]
[-1.6071612 3.273221 7.218715]
[3.161762 2.5584767 19.690228]]Тензоры в TensorFlow, являются основными объектами для работы с данными и предназначены для эффективного выполнения вычислений, особенно на графических процессорах (GPU) и в распределенных системах. Операции с тензорами в TensorFlow позволяют выполнять различные манипуляции с данными, включая математические операции, изменения формы и многое другое.
np-массивы по-прежнему остаются основными объектами в NumPy, центральной библиотеке для научных вычислений в Python. NumPy и TensorFlow часто используются вместе в проектах машинного обучения, так как NumPy предоставляет множество полезных операций для предварительной обработки данных, которые затем могут быть использованы в TensorFlow.
Для взаимодействия между тензорами TensorFlow и np-массивами NumPy можно использовать методы tf.convert_to_tensor() и.numpy(). Эти методы позволяют легко преобразовывать данные из np-массивов в тензоры TensorFlow и обратно, что обеспечивает удобную интеграцию и совместимость данных.
Давайте рассмотрим пример кода, где мы используем TensorFlow для работы с тензорами и NumPy для выполнения скалярного произведения:
# Импорт необходимых библиотекimporttensorflowastf# Импорт TensorFlow для работы с тензорами
importnumpyasnp# Импорт NumPy для работы с np-массивами
# Создание тензоров в TensorFlow
x = tf.constant([[1, 2], [3, 4]], dtype=tf.float32) # Создание тензора x размером 2x2
y = tf.constant([[5, 6], [7, 8]], dtype=tf.float32) # Создание тензора y размером 2x2
# Преобразование тензоров TensorFlow в np-массивы
x_np = x.numpy() # Преобразование тензора x в np-массив
y_np_transposed = tf.transpose(y).numpy() # Транспонирование тензора y и преобразование в np-массив
# Выполнение скалярного произведения между np-массивами
result = np.dot(x_np, y_np_transposed) # Вычисление скалярного произведения между x_np и y_np_transposed
# Вывод результата скалярного произведения
print("Скалярное произведение x и транспонированного y:")
print(result)Скалярное произведение x и транспонированного y:
[[17. 23.]
[39. 53.]]Следущий код демонстрирует основные функции работы с TensorFlow, включая создание и манипуляцию тензорами, поэлементные операции, транспонирование, матричное умножение, а также взаимодействие с NumPy. Обратите внимание, что в TensorFlow для получения значений тензора в виде NumPy массива используется метод.numpy():
# Шаг 1: Импорт необходимых библиотек
# Импортируем TensorFlow и NumPyimporttensorflowastf
importnumpyasnp
# Шаг 2: Работа с тензорами в TensorFlow
# Тензоры - это основной тип данных в TensorFlow для хранения и обработки числовых данных
# Создаем тензор с неинициализированными значениями размером 3x4
x = tf.Variable(tf.zeros((3, 4)))
print("Неинициализированный тензор X:\n", x.numpy())
# Инициализируем другой тензор случайными значениями из нормального распределения
y = tf.random.normal((3, 4))
print("Тензор Y с случайными значениями:\n", y.numpy())
# Шаг 3: Поэлементные операции
# Выполним экспоненцирование всех элементов тензора y
x_exp = tf.exp(y)
print("x_exp = e^y:\n", x_exp.numpy())
# Шаг 4: Транспонирование и матричное умножение
# Транспонируем тензор y
y_transposed = tf.transpose(y)
print("Транспонированный Y:\n", y_transposed.numpy())
# Выполняем матричное умножение x_exp на транспонированный y
result_matrix = tf.matmul(x_exp, y_transposed)
print("Результат матричного умножения x_exp на транспонированный Y:\n", result_matrix.numpy())
# Шаг 5: Взаимодействие с NumPy
# Преобразуем тензоры в np-массивы для выполнения операций, совместимых с NumPy
x_np = x_exp.numpy()
y_np_transposed = y_transposed.numpy()
# Выполним скалярное произведение с использованием NumPy
np_result = np.dot(x_np, y_np_transposed)
print("Результат скалярного произведения с NumPy:\n", np_result)Неинициализированный тензор X:
[[0. 0. 0. 0.]
[0. 0. 0. 0.]
[0. 0. 0. 0.]]
Тензор Y с случайными значениями:
[[0.4931968 -0.31107786 1.0522738 -0.91028893]
[0.7813833 -0.3026102 -1.2939718 0.28406715]
[0.6851111 -1.1481444 -0.64575064 -0.14548281]]
x_exp = e^y:
[[1.6375427 0.73265684 2.864156 0.40240794]
[2.1844919 0.7388871 0.27417964 1.3285221]
[1.9839922 0.3172249 0.52426887 0.8646048]]
Транспонированный Y:
[[0.4931968 0.7813833 0.6851111]
[-0.31107786 -0.3026102 -1.1481444]
[1.0522738 -1.2939718 -0.64575064]
[-0.91028893 0.28406715 -0.14548281]]
Результат матричного умножения x_exp на транспонированный Y:
[[3.227286 -2.5339868 -1.6273712]
[-0.07329397 1.5059394 0.27794188]
[0.6444511 1.0214796 0.53070307]]
Результат скалярного произведения с NumPy:
[[3.227286 -2.5339868 -1.6273712]
[-0.07329397 1.5059394 0.27794188]
[0.6444511 1.0214796 0.53070307]]2.14.10 Операции на месте
В области машинного обучения мы часто сталкиваемся с необходимостью обработки больших объемов данных. Чтобы обеспечить эффективность такой работы, мы стараемся использовать операции, которые не приводят к излишнему перемещению данных в памяти компьютера. Такие операции называются операциями на месте.
importtensorflowastf
# Создаемдвепеременныев TensorFlow
x = tf.Variable([1, 2, 3], dtype=tf.float32)
y = tf.Variable([4, 5, 6], dtype=tf.float32)
# Печатаем идентификаторы объектов до операции
print('id(x) до операции:', id(x))
print('id(y) до операции:', id(y))
# Стандартная операция сложения, создающая новый тензор
temp_y = y + x# В TensorFlow это создаст новый тензор
# Если вы хотите продолжить использовать y как переменную, создайте новую переменную с результатом
y = tf.Variable(temp_y)
print('id(y) после стандартной операции сложения:', id(y))
# Операция на месте с использованием метода assign_add
y.assign_add(x) # Это добавляет x к y на месте, не изменяя идентификатор y
print('id(y) после операции на месте с использованием assign_add:', id(y))
# Операция на месте с использованием оператора +=
x.assign_add(y) # Это добавляет y к x на месте, используя оператор +=
print('id(x) после операции на месте с использованием оператора +=:', id(x))
id(x) до операции: 6079315920
id(y) до операции: 6069364304
id(y) после стандартной операции сложения: 6069363968
id(y) после операции на месте с использованием assign_add: 6069363968
id(x) после операции на месте с использованием оператора +=: 60793159202.14.11Работа со срезами в многомерных массивах вNumPy
Чтение определенных строк и столбцов из многомерного массива можно выполнить с помощью срезов. Срезы позволяют нам выбирать подмножества массива для чтения или модификации.
importnumpyasnp#ИмпортируембиблиотекуNumPyдляработысмассивами
# Создаемдвумерныймассив (матрицу) сплавающейточкойдляпримера
x = np.array([[1.343, 3.358, 6.210, 6.438],
[7.653, -2.504, 12.811, 1.550],
[26.785, 0.124, 4.275, -2.182]])
# Выводимисходныймассив
print("Исходныймассив x:")
print(x)
# Чтениевторойитретьейстрокиизмассива x
print("\nВтораяитретьястрокимассива x:")
second_and_third_rows = x[1:3] # Срезотвторой (индекс 1) дотретьейстроки (индекс 2)
print(second_and_third_rows)
# Чтениеэлементоввторойстрокиотвторогодотретьегостолбца
print("\nЭлементывторойстрокиотвторогодотретьегостолбца:")
second_row_second_to_third_column = x[1:2, 1:3] # Срездлявторойстрокиистолбцовсиндексами 1 и 2
print(second_row_second_to_third_column)
# Изменяемзначениевовторойстроке, третьемстолбцена 88.0x[1, 2] = 88.0# Присваиваемэлементусиндексом [1,2] значение 88.0
# Выводиммассивпослеизменения
print("\nМассив x послеизмененияодногозначения:")
print(x)
# Изменяемзначениявовторойстрокеотвторогодотретьегостолбцана 88.0x[1, 1:3] = 88.0# Срездляизменениязначенийвовторойстрокеистолбцовсиндексами 1 и 2
# Выводиммассивпослеизменениясрезазначений
print("\nМассив x послеизменениясрезазначений:")
print(x)В этом листинге кода используется массив x в качестве примера для демонстрации операций среза и изменения значений в массиве NumPy.
Чтобы извлечь вторую и третью строки массива x, используем следующий код:
print(x) # Выведем на экран весь массив для наглядности
x[1:3] # Получим срез для второй и третьей строкВ этом примере x[1:3] относится к строкам массива x с индексами 1 и 2. Важно помнить, что индексация в Python начинается с нуля, и крайняя граница среза не включается. То есть x[1:3] возвращает строки с индексами 1 и 2, но не 3.
Если мы хотим прочитать элементы второй строки с второго по третий столбцы, мы можем сделать это так:
x[1:2,1:3] # Выбираем вторую строку и срез столбцов с индексами от 1 до 2
Здесь x[1:2,1:3] создает срез, который выбирает элементы второй строки (индекс 1) и столбцы с индексами 1 и 2.
Для изменения определенных значений массива, например, значения во второй строке и третьем столбце на 88.0, выполним следующее:
x[1,2] = 88.0# Присваиваем новое значение элементу
print(x) # Выводим измененный массивТакое присваивание изменит конкретный элемент массива без изменения других элементов.
И наконец, если мы хотим изменить несколько значений сразу, например все значения во второй строке с второго по третий столбец, используем:
x[1:2,1:3] = 88.0# Присваиваем новое значение сразу нескольким элементам
print(x) # Выводим массив с обновленными значениямиЭто приведет к тому, что во второй строке элементы со второго по третий столбец теперь будут иметь значение 88.0.
2.14.12Широковещательные операции (broadcasting)
Давайте подробно объясним концепт широковещательных операций (broadcasting) на Python, используя библиотеку NumPy. Этот процесс позволяет выполнять арифметические операции над массивами, которые изначально не совместимы по размеру.
Массивы в NumPy имеют атрибут, называемый формой (shape), который представляет собой кортеж, показывающий размерность каждого измерения массива. Например, форма массива y, созданного функцией np.arange(6), будет (6,), что означает, что это одномерный массив с шестью элементами.
При работе с массивами разной формы, перед выполнением операций над ними, мы часто должны привести их к совместимой форме. Для этого используется метод reshape. В примере массив x сначала создается с помощью np.arange(24) и имеет форму (24,), затем он преобразуется в двумерный массив (4, 6) с помощью x.reshape((4,6)).Теперь, когда формы массивов x и y совместимы (x имеет форму (4,6) и y имеет форму (6,)), можно выполнить широковещательные операции. NumPy автоматически "растягивает" массив y вдоль первого измерения (измерение с индексом 0) так, чтобы его форма соответствовала форме x. В итоге y повторяется четыре раза, чтобы соответствовать четырем строкам в x.
После применения широковещательных операций, операция сложения x + y выполняется поэлементно: каждый элемент y складывается с соответствующим элементом каждой строки x.
importnumpyasnp
# Создаем одномерный массив y
y = np.arange(6)
print('y = ', y, 'Форма y:', y.shape)
# Создаем одномерный массив x и изменяем его форму
x = np.arange(24).reshape((4,6))
print('X = \n', x, 'Форма X:', x.shape)
# Выполняем операцию сложения, используя широковещательные операции
result = x + y
print('X + y = \n', result)Результат result будет двумерным массивом, где каждый столбец увеличен на соответствующий элемент массива y.
# Импортируем библиотеку NumPy под псевдонимом npimportnumpyasnp
# Создаем одномерный массив y с помощью функции arange, которая генерирует последовательность чисел от 0 до n-1
y = np.arange(6)
# Выводим массив y и его форму
print('y = ', y, 'Форма y:', y.shape)
# Создаем одномерный массив x той же длины, что и y, но с числами от 0 до 23
x = np.arange(24)
# Изменяем форму массива x с помощью метода reshape, чтобы получился двумерный массив
# размерностью 4 строки на 6 столбцов, что соответствует форме массива y для выполнения широковещательной операции
x = x.reshape((4,6))
# Выводим массив x и его новую форму
print('X = \n', x, 'Форма X:', x.shape)
# Выполняем операцию сложения x и y. Благодаря широковещательным операциям, y автоматически
# расширяется до формы x и сложение происходит поэлементно.
result = x + y
# Выводим результат сложения
print('X + y = \n', result)Если вы выполните этот код в Python, он продемонстрирует, как NumPy обрабатывает широковещательные операции, автоматически "расширяя" меньший массив для совместимости с большим массивом при выполнении поэлементных операций.
Умножение массивов и изменение их формы:
# ИмпортбиблиотекиNumPyimportnumpyasnp
# Допустим, массивы X и y были определены ранее
# Вывод форм массивов X и y
print('Форма X:', X.shape, 'Форма y:', y.shape)
# Вычисление скалярного произведения массивов X и y
# Функция np.dot() возвращает сумму произведений соответствующих элементов массивов
print(np.dot(X, y))
# Изменение формы массива X на трехмерный массив Z с использованием метода np.reshape()
# Параметры (2,3,4) определяют новую форму массива
Z = np.reshape(X, (2,3,4))
# Вывод нового трехмерного массива Z
print(Z)
Скалярное произведение (np.dot()): Операция np.dot(X, y) используется для вычисления скалярного произведения между X и y, которое в математике и линейной алгебре означает суммирование произведений соответствующих элементов двух последовательностей чисел.Изменение формы массива (reshape): Метод reshape позволяет изменить форму массива без изменения его данных. В этом примере массив X преобразуется из его исходной формы в трехмерный массив Z с формой (2,3,4).
Операции заполнения массива одним значением и широковещательные операции сложения массивов различной формы.
# Создание двумерного массива a с использованием функции arange и reshape
# Массив заполняется последовательными числами от 0 до 11 и изменяется его форма на (3,4)
a = np.arange(12).reshape(3,4)
# Заполнение всего массива a значением 100 с помощью метода fill
a.fill(100)
# Вывод массива a после заполнения
print(a)
# Предполагается, что массив Z был определен ранее как трехмерный массив
# Сложение массива a и Z демонстрирует широковещательные операции
# Массив a расширяется до формы массива Z и происходит поэлементное сложение
print(Z + a)
# Создание и заполнение одномерного массива a
a = np.arange(4)
a.fill(100)
# Вывод одномерного массива a
print(a)
# Сложение одномерного массива a с трехмерным массивом Z
# Показана способность NumPy к широковещательным операциям, когда массивы разной формы
# могут быть сложены за счет автоматического расширения меньших массивов
print(Z + a)Создадим полный код-листинг на Python с подробными комментариями, который демонстрирует операции с массивами с использованием библиотеки NumPy:
# ИмпортбиблиотекиNumPyimportnumpyasnp
# Создание одномерного массива y с последовательными числами от 0 до 5
y = np.arange(6)
# Вывод информации о массиве y
print('y = ', y, 'Форма y:', y.shape)
# Создание одномерного массива x с последовательными числами от 0 до 23
x = np.arange(24)
# Изменение формы массива x в двумерный массив (4, 6)
x = x.reshape((4,6))
# Вывод информации о массиве x
print('x = ', x, 'Форма x:', x.shape)
# Вычисление скалярного произведения массивов x и y с использованием функции np.dot
# Важно отметить, что массивы должны быть согласованы по размерам для такой операции
dot_product = np.dot(x, y)
print('Скалярное произведение x и y:', dot_product)
# Изменение формы массива x в трехмерный массив z (2, 3, 4)
z = x.reshape((2, 3, 4))
# Вывод трехмерного массива z
print('z =\n', z)
# Создание двумерного массива a с помощью функции arange и изменение его формы на (3, 4)
a = np.arange(12).reshape((3,4))
# Заполнение массива a значением 100
a.fill(100)
# Вывод двумерного массива a после заполнения
print('a =\n', a)
# Демонстрация широковещательных операций при сложении массива a и трехмерного массива z
# Массив a автоматически "расширяется" до формы массива z для выполнения поэлементного сложения
broadcasted_sum = z + a
# Вывод результата сложения
print('Результат сложения z и a:\n', broadcasted_sum)
# Создание и заполнение одномерного массива a значением 100
a = np.arange(4)
a.fill(100)
# Вывод одномерного массива a
print('a =', a)
# Сложение одномерного массива a с трехмерным массивом z
# NumPy автоматически "расширяет" массив a для выполнения операции сложения
broadcasted_sum = z + a
# Вывод результата сложения
print('Результат сложения z и a:\n', broadcasted_sum)2.14.13Правила совместимости размерностей
Когда вы планируете выполнить операцию между двумя массивами, NumPy необходимо сравнить их формы. Это сравнение начинается с последнего измерения (наиболее правого) и продвигается к первому (наиболее левому) измерению. Для того чтобы два измерения считались совместимыми, должно быть выполнено одно из следующих условий:
Размерности равны.
Размерность одного из массивов в данном измерении равна 1.
Если хотя бы одно из этих условий не выполняется, NumPy сгенерирует ошибку ValueError, указывая на несовместимость форм.
Для иллюстрации широковещательных операций представим несколько примеров:Если мы складываем одномерный массив с двумерным, NumPy "расширит" одномерный массив, повторив его по каждой строке двумерного массива.
При сложении двух одномерных массивов разной длины, если длина одного из массивов равна 1, NumPy "растянет" его по длине другого массива.
Если к двумерному массиву добавить одномерный массив, состоящий из одного столбца, NumPy "расширит" одномерный массив, повторив его по каждому столбцу двумерного массива.
На рисунке наглядно показано, как NumPy реализует широковещательные операции, демонстрируя исходные массивы, как массивы "расширяются" и конечный результат операции сложения.
Применение этих правил позволяет упростить многие типы вычислений, особенно когда нам нужно провести операцию над массивами различной формы. Это особенно актуально, где массивы могут иметь различные размеры и формы.
Используя широковещательные операции, мы можем сократить необходимость написания избыточного кода и циклов, что делает программы более эффективными и легкими для понимания.
В следующем коде мы рассмотрим три примера, которые показывают различные аспекты широковещательных операций в NumPy:importnumpyasnp
# Пример 1: Сложение одномерного и двумерного массивов
# Создание одномерного массива
one_d_array = np.array([1, 2, 3])
# Создание двумерного массива
two_d_array = np.array([[0, 0, 0], [10, 10, 10], [20, 20, 20], [30, 30, 30]])
# Широковещательные операции позволяют сложить эти два массива, несмотря на различие их форм
broadcasted_sum_1 = one_d_array + two_d_array
print("Пример 1: Сложение одномерного и двумерного массивов")
print(broadcasted_sum_1)
# Пример 2: Сложение двух одномерных массивов разной длины
# Создание одномерного массива с одним элементом
single_element_array = np.array([100])
# Широковещательная операция "расширит" меньший массив, чтобы его можно было сложить с one_d_array
broadcasted_sum_2 = single_element_array + one_d_array
print("\nПример 2: Сложение двух одномерных массивов разной длины")
print(broadcasted_sum_2)
# Пример 3: Сложение одномерного массива, состоящего из одного столбца, с двумерным массивом
# Создание одномерного массива, представляющего столбец
column_array = np.array([[0], [10], [20], [30]])
# Широковещательная операция "расширит" столбец по горизонтали, чтобы можно было выполнить сложение
broadcasted_sum_3 = column_array + one_d_array
print("\nПример 3: Сложение одномерного массива, состоящего из одного столбца, с двумерным массивом")
print(broadcasted_sum_3)
Рисунок: Сложение массивов.2.14.14 Преобразование между массивами TensorFlowи NumPy
# Импорт библиотеки NumPy под псевдонимом np для работы с массивамиimportnumpyasnp
# Импорт TensorFlow для работы с тензорами и глубоким обучениемimporttensorflowastf
# Создание одномерного массива из 24 чисел, начиная с 0 и заканчивая 23
x = np.arange(24)
# Преобразование одномерного массива в двумерный массив (матрицу) размером 4x6
x = x.reshape(4, 6)
# Выводим на экран матрицу x для проверки
print("Матрица NumPy x:")
print(x)
# Преобразование массива NumPy x в тензор TensorFlow
tfx = tf.convert_to_tensor(x, dtype=tf.float32)
# Выводим на экран тензор tfx для проверки
print("\nTensorFlowтензорtfx:")
print(tfx)
# Преобразование тензора TensorFlow обратно в массив NumPy
npa = tfx.numpy()
# Проверка, что npa действительно является массивом NumPy
print("\nПреобразованный обратно в NumPy массив npa:")
print(npa)
print("Типnpa:", type(npa))
Матрица NumPy x:
[[0 1 2 3 4 5]
[6 7 8 9 10 11]
[12 13 14 15 16 17]
[18 19 20 21 22 23]]
TensorFlow тензорtfx:
tf.Tensor(
[[0. 1. 2. 3. 4. 5.]
[6. 7. 8. 9. 10. 11.]
[12. 13. 14. 15. 16. 17.]
[18. 19. 20. 21. 22. 23.]], shape=(4, 6), dtype=float32)
Преобразованный обратно в NumPy массив npa:
[[0. 1. 2. 3. 4. 5.]
[6. 7. 8. 9. 10. 11.]
[12. 13. 14. 15. 16. 17.]
[18. 19. 20. 21. 22. 23.]]
Типnpa: <class 'numpy.ndarray'>Этот код-листинг демонстрирует процесс создания массива в NumPy, его преобразование в тензор TensorFlow и обратное преобразование в массив NumPy.
# Импортируем необходимые библиотекиimportnumpyasnp
importtensorflowastf
# Создаем массив NumPy с последовательными числами от 0 до 23
npa = np.arange(24).reshape(4, 6) # Изменяем форму массива на 4x6
# Выводим исходный массив NumPy
print("Исходный массив NumPy:")
print(npa)
# Преобразуем массив NumPy в тензор TensorFlow
tfy = tf.convert_to_tensor(npa, dtype=tf.float32)
# Выводимтензор TensorFlow
print("\nПреобразованныйвтензор TensorFlow:")
print(tfy)
# Преобразуемтензор TensorFlow обратновмассив NumPy
npa_from_tfy = tfy.numpy()
# Выводим массив NumPy, полученный из тензора TensorFlow
print("\nПреобразованный обратно в массив NumPy:")
print(npa_from_tfy)
# Для проверки совпадения исходного и полученного массивов NumPy
# можно использовать функцию np.array_equal()
print("\nМассивы NumPy идентичны:", np.array_equal(npa, npa_from_tfy))
Исходный массив NumPy:
[[0 1 2 3 4 5]
[6 7 8 9 10 11]
[12 13 14 15 16 17]
[18 19 20 21 22 23]]
Преобразованный в тензор TensorFlow:
tf.Tensor(
[[0. 1. 2. 3. 4. 5.]
[6. 7. 8. 9. 10. 11.]
[12. 13. 14. 15. 16. 17.]
[18. 19. 20. 21. 22. 23.]], shape=(4, 6), dtype=float32)
Преобразованный обратно в массив NumPy:
[[0. 1. 2. 3. 4. 5.]
[6. 7. 8. 9. 10. 11.]
[12. 13. 14. 15. 16. 17.]
[18. 19. 20. 21. 22. 23.]]
Массивы NumPy идентичны: TrueЭтот код демонстрирует, как можно преобразовать массив NumPy в тензор TensorFlow, выполнить с ним операции в контексте TensorFlow и затем преобразовать тензор обратно в массив NumPy. Это полезно, когда нужно сочетать операции NumPy и TensorFlow в одном рабочем процессе.
2.14.15Выделение подмассивов в NumPy
Еще одна особенность массивов NumPy заключается в том, что их легко делить на подмассивы.
print(bmi[bmi>25]) # Вывести только те значения, у которых BMI больше 25
[25.556 25.117 25.051]Данный код демонстрирует возможность использования булевой индексации для фильтрации элементов массива. Здесь bmi- это предположительный массив NumPy, содержащий значения индекса массы тела различных индивидуумов. Выражение bmi> 25 создает новый массив, который содержит булевы значения (True или False) для каждого элемента исходного массива bmi, где True соответствует элементам, значение которых больше 25. Когда это булево выражение используется внутри квадратных скобок, NumPy возвращает только те элементы исходного массива bmi, для которых соответствующее значение в булевом массиве является True, т.е. все значения bmi, превышающие 25.
Importnumpyasnp#ИмпортируембиблиотекуNumPy
# Создаем массив bmi с тестовыми данными
# Для демонстрации будем использовать случайные числа от 20 до 30
bmi = np.random.uniform(20, 30, 10)
# Выводим исходный массив bmi
print("Исходный массив BMI:", bmi)
# Применяем булеву индексацию для фильтрации значений больше 25
# Это работает путем создания булева массива, где каждый элемент проверяется на условие > 25
# и затем этот булев массив используется для выбора элементов из исходного массива.
filtered_bmi = bmi[bmi>25]
# Выводим отфильтрованные значения
print("Отфильтрованные значения BMI > 25:", filtered_bmi)Этот код сначала импортирует NumPy, создает массив bmi с помощью случайных значений для примера, а затем использует булеву индексацию для фильтрации и вывода значений массива, которые больше 25.
Importnumpyasnp#ИмпортируембиблиотекуNumPy
# Создаем массив bmi с тестовыми данными
# Для демонстрации используем массив с фиксированными значениями от 20 до 30
bmi = np.array([21.3, 24.5, 27.9, 30.1, 23.4, 25.6, 27.4, 22.0, 28.5, 26.1])
# Выводим исходный массив bmi
print("Исходный массив BMI:", bmi)
# Применяем булеву индексацию для фильтрации значений больше 25
# Создается булев массив, где каждый элемент проверяется на условие > 25
# Этот булев массив используется для выбора элементов из исходного массива
filtered_bmi = bmi[bmi>25]
# Выводим отфильтрованные значения
print("Отфильтрованные значения BMI > 25:", filtered_bmi)
Исходный массив BMI: [21.3 24.5 27.9 30.1 23.4 25.6 27.4 22. 28.5 26.1]
Отфильтрованные значения BMI > 25: [27.9 30.1 25.6 27.4 28.5 26.1]2.14.16 Numpy и универсальные функции (ufunc)
Универсальные функции, или ufunc, являются элементом библиотеки NumPy, предназначенным для выполнения операций над элементами многомерных массивов поэлементно. Эти функции обладают несколькими важными характеристиками:
Элементная обработка:Ufunc применяются к каждому элементу массива отдельно, что позволяет эффективно выполнять векторизованные операции без явного использования циклов.
Поддержка трансляции (broadcasting): Это означает, что ufunc могут корректно обрабатывать массивы разного размера, расширяя меньшие массивы для соответствия форме большего массива.
Приведение типов:Ufunc поддерживают автоматическое приведение типов данных элементов массива для выполнения операций.
Стандартные функции: Как правило, ufunc реализуют стандартные математические и логические операции.В NumPy, универсальные функции являются экземплярами класса numpy.ufunc. Многие встроенные функции NumPy реализованы как ufunc и написаны на скомпилированном языке C, что обеспечивает высокую производительность.
Типы ufunc
Базовые ufunc: Работают с скалярными значениями.
Обобщенные ufunc: Работают с подмассивами (векторами, матрицами и т.д.), применяя операции в разных измерениях.Примеры ufunc
exp(x, /[, out, where, casting, order,...]): Вычисляет экспоненту всех элементов входного массива.
log(x, /[, out, where, casting, order,...]): Вычисляет натуральный логарифм каждого элемента массива поэлементно.NumPy предоставляет возможность создавать пользовательские ufunc с помощью функции frompyfunc. Это позволяет обернуть существующие функции Python в универсальные функции, расширяя таким образом возможности библиотеки.
Для более глубокого изучения универсальных функций и их применения в NumPy рекомендуется ознакомиться с документацией NumPy.
2.14.17 Массив Numpy и векторы/матрицы
Массивы NumPy являются основным строительным блоком библиотеки NumPy и широко используются в расчетах. Понимание различий между традиционными концепциями векторов и матриц в линейной алгебре и поведением массивов NumPyдает возможность для эффективного программирования и отладки кода.
Особенности Массивов NumPy
Гибкость формы: Массивы NumPy могут иметь любое количество измерений, в отличие от строгих одномерных векторов и двумерных матриц в линейной алгебре. Это позволяет представлять более сложные структуры данных.
Однородность типов: В массиве NumPy все элементы должны быть одного типа. Это отличается от структур данных в других языках программирования, где элементы могут иметь разные типы.
Векторизация операций: Операции над массивами NumPy выполняются поэлементно и могут быть векторизованы для увеличения производительности, что отличается от математических операций над векторами и матрицами.
Размерность и форма: Размерность массива NumPy определяет его форму. Например, одномерный массив похож на вектор, а двумерный - на матрицу. Однако в NumPy эти структуры обрабатываются одинаково как ndarray.
Numpy в основном использует структуру массивов. Поведение массива Numpy схоже, но в то же время сильно отличается от традиционных представлений вектора и матрицы, которые мы изучаем на линейной алгебре. Разберем это подробнее.
В отличие от простых одно- и двумерных структур, таких как векторы и матрицы, массивы Numpy могут иметь до 32 измерений по умолчанию, и это число может быть изменено. Это позволяет использовать их для сложных задач, требующих обработки данных с большим числом измерений.
Благодаря своей многомерности и гибкости, массивы Numpy идеально подходят для обработки данных в сложных моделях машинного обучения, которые требуют работы с многомерными наборами данных.
Одномерный массив Numpy похож на обычное понятие вектора в линейной алгебре. Разница заключается в том, что одномерный массив не различает вектор-строку или вектор-столбец. Это просто одномерный массив с формой (n,), где n - длина массива. В большинстве случаев он ведет себя как вектор-строка, но не совсем. Например, функция transpose() не оказывает на него воздействия. Это потому что функция transpose() меняет местами две оси массива с двумя или более осями.
Вектор-столбец в линейной алгебре следует рассматривать в numpy как специальный случай двумерного массива только с одним столбцом. Можно создать массив, похожий на вектор-столбец в линейной алгебре, добавив к одномерному массиву дополнительную ось. Смотрите следующие примеры.
Для начала, рассмотрим создание одномерного массива, его транспонирование и добавление к нему новой оси для преобразования в двумерный массив в NumPy:
importnumpyasnp#ИмпортируембиблиотекуNumPy
# Создаем одномерный массив с тремя элементами
a = np.array([1, 2, 3])
# Печатаем массив, его форму, транспонированный массив и его форму
# Для одномерного массива транспонирование не меняет форму массива
print("Исходный массив:", a)
print("Форма исходного массива:", a.shape)
print("Транспонированный массив:", a.T)
print("Форма транспонированного массива:", a.T.shape)
# Добавляем новую ось, чтобы преобразовать массив в двумерный
# Это делается для создания столбцового вектора
an = a[:,np.newaxis]
# Печатаем новый двумерный массив и его форму
print("\nДвумерный колонный вектор:", an)
print("Форма колонного вектора:", an.shape)
# Теперь мы можем транспонировать двумерный массив
# Транспонирование превращает колонный вектор в строковый вектор
print("\nТранспонированный двумерный массив (строковый вектор):", an.T)
print("Форма строкового вектора:", an.T.shape)
Исходный массив: [1 2 3]
Форма исходного массива: (3,)
Транспонированный массив: [1 2 3]Форма транспонированного массива: (3,)
Двумерный колонный вектор: [[1]
[2]
[3]]
Форма колонного вектора: (3, 1)
Транспонированный двумерный массив (строковый вектор): [[1 2 3]]
Форма строкового вектора: (1, 3)
В NumPy массив создается с помощью функции np.array(). Одномерный массив, это по сути список элементов. В нашем примере создается массив a со значениями [1, 2, 3].
a = np.array([1, 2, 3])
Когда мы печатаем массив a, его форму (shape) и форму его транспонированной версии a.T, мы видим следующее:
print(a, a.T, a.shape, a.T.shape)В выводе [1 2 3]- это наш исходный массив, (3,) - его форма, показывающая, что в массиве 3 элемента и он одномерный. В NumPyодномерные массивы не имеют различия между строками и колонками, поэтому транспонирование не влияет на них, и форма a.T также остается (3,).
Теперь давайте попробуем превратить одномерный массив в двумерный, добавив новую ось. Это делается с помощью среза и np.newaxis. Срез: означает, что мы берем все элементы в этом измерении, а np.newaxis добавляет новую ось, делая массив двумерным.
an = a[:,np.newaxis]
Затем мы печатаем новый массив an и его форму:
print(an, an.shape)В выводе [[1] [2] [3]] показан наш новый двумерный массив, а (3, 1) - его форма, показывающая, что массив имеет 3 строки и 1 колонку, что делает его колонным вектором.
Теперь, когда у нас есть двумерный массив, мы можем транспонировать его с помощью.T. Транспонирование превращает строки в колонки и наоборот.
print(an.T, an.T.shape)В выводе [[1 2 3]]- это транспонированный массив, где исходные строки стали колонками. Форма (1, 3) указывает на то, что теперь у нас есть 1 строка и 3 колонки, что делает его строковым вектором.
Для начала рассмотрим, как создать двумерный массив из одномерного. Предположим, у нас есть одномерный массив a со значениями [1, 2, 3]. Мы хотим преобразовать его в двумерный массив, чтобы использовать операции, предназначенные для матриц.
importnumpyasnp
# Исходный одномерный массив
a = np.array([1, 2, 3])
# Добавляем новое измерение в конец массива `a`
a1 = a.reshape(a.shape+(1,))
print(a1, a1.shape) # Выведет двумерный массив и его форму (3, 1)
[[1]
[2]
[3]] (3, 1)В приведенном выше коде метод reshape изменяет форму массива, добавляя одно измерение. Мы используем a.shape+(1,), чтобы к текущей форме массива (3,) добавить еще одно измерение.
Теперь рассмотрим другой способ добавления измерения, используя объект None.
# Добавляем новое измерение без указания элементов
aN = a[:,None]
print(aN, aN.shape) # Аналогично предыдущему выводу: двумерный массив и его форма (3, 1)
Здесь a[:,None] использует синтаксис срезов и None, чтобы указать NumPy добавить новое измерение. Это эквивалентно использованию np.newaxis, который мы рассмотрим далее.Также можно добавлять измерения не только в конец массива, но и в его начало.
importnumpyasnp
# Добавляем новую ось в начало массива `a`
a0 = a[np.newaxis,:]
print(a0, a0.shape) # Выводит двумерный массив в форме вектор-строки (1, 3)
Мы использовали np.newaxis, чтобы добавить новое измерение. В этом случае мы добавляем измерение в начале, поэтому форма массива становится (1, 3), что представляет собой двумерный массив с одной строкой и тремя столбцами - вектор-строку.
# Импортируем библиотеку NumPy под псевдонимом npimportnumpyasnp
# Создаем одномерный массив из трех элементов
a = np.array([1, 2, 3])
# Добавляем новое измерение в конец массива `a` для превращения его в двумерный массив
# Это делается путем изменения формы массива с помощью метода reshape
a1 = a.reshape(a.shape+(1,))
# Выводим результат и форму полученного массива
# Ожидаемый вывод: двумерный массив с тремя строками и одним столбцом
print("Добавление измерения в конец массива:")
print(a1, a1.shape)
# Добавляем новое измерение, используя объект None
# Это эквивалентно добавлению новой оси в конец массива
aN = a[:,None]
# Выводим результат и форму полученного массива
# Ожидаемый вывод: аналогично предыдущему, двумерный массив с тремя строками и одним столбцом
print("\nДобавление измерения с использованием None:")
print(aN, aN.shape)
# Добавляем новую ось в начало массива `a` с помощью np.newaxis
# Это создает вектор-строку
a0 = a[np.newaxis,:]
# Выводим результат и форму полученного массива
# Ожидаемый вывод: двумерный массив в виде вектор-строки с одной строкой и тремя столбцами
print("\nДобавление оси в начало массива:")
print(a0, a0.shape)
Добавление измерения в конец массива:
[[1]
[2]
[3]] (3, 1)
Добавление измерения с использованием None:
[[1]
[2]
[3]] (3, 1)
Добавление оси в начало массива:
[[1 2 3]] (1, 3)2.14.18 Матрица Ханкеля
Матрица Ханкеля — это матрица, элементы которой на побочных диагоналях (идущих справа налево) являются постоянными. В данном контексте сложение массива-столбца и массива-строки дает матрицу, где каждый элемент является суммой соответствующих элементов исходного одномерного массива a.
# Импортируем библиотеку NumPy под псевдонимом npimportnumpyasnp
# Создаем одномерный массив из трех элементов
a = np.array([1, 2, 3])
# Добавляем новое измерение, используя объект None
aN = a[:,None]
print("\nДобавление измерения с использованием None:")
print(aN, aN.shape)
# Добавляем новую ось в начало массива `a` с помощью np.newaxis
a0 = a[np.newaxis,:]
print("\nДобавление оси в начало массива:")
print(a0, a0.shape)
# Применяем трюк для создания матрицы Ханкеля путем сложения aN и a0
# Здесь используется broadcasting - механизм NumPy, позволяющий выполнять арифметические операции
# на массивах с различными формами. В результате получается матрица Ханкеля.
print("\nИнтересный трюк для создания матрицы Ханкеля:")
hankel_matrix = aN + a0
print(hankel_matrix)
Добавление измерения с использованием None:
[[1]
[2]
[3]] (3, 1)
Добавление оси в начало массива:
[[1 2 3]] (1, 3)
Интересный трюк для создания матрицы Ханкеля:
[[2 3 4]
[3 4 5]
[4 5 6]]
Хотя двумерные массивы Numpy схожи с матрицами в линейной алгебре, существуют ключевые различия, важные для понимания их применения. Особенно важны различия в операциях умножения: скалярное произведение выполняется с помощью np.dot() или оператора “@”, в то время как оператор “*” используется для поэлементного умножения. Некоторые операции могут изменять размерность массивов, что требует особой осторожности и понимания влияния на анализ данных. Так же существует класс матрицы в Numpy, который имитирует поведение матриц линейной алгебры, но его использование не рекомендуется из-за его потенциального устаревания.2.15 Генерация графиков
Python обладает возможностями для создания графиков, благодаря доступности различных модулей визуализации. В качестве примера рассмотрим создание демонстрационного графика с разбросанными кругами различного размера и цвета.
Для начала импортируем необходимые модули:importnumpyasnp# Модуль для работы с массивами
importmatplotlib.pyplotasplt# Модуль для создания графиков
Следующим шагом генерируем набор данных:
n = 80# Количество точек
x = np.random.rand(n) # Случайно сгенерированные координаты по оси X
y = np.random.rand(n) # Случайно сгенерированные координаты по оси Y
colors = np.random.rand(n) # Случайно сгенерированные цвета для каждой точки
areas = np.pi * (18 * np.random.rand(n))**2# Площади кругов, случайно генерируемые в диапазоне радиусов от 0 до 20
Теперь, используя сгенерированные данные, создаем график:
plt.scatter(x, y, s=areas, c=colors, alpha=0.8) # Создание графика с кругами
plt.show() # Отображение графикаНа этом графике каждая точка представляет собой круг, размер и цвет которого генерируются случайным образом. Параметр alphaконтролирует прозрачность кругов, что добавляет визуальной глубины при наложении.
# Импорт необходимых библиотекimportnumpyasnp# Для работы с массивами и выполнения математических операций
importmatplotlib.pyplotasplt# Для визуализации данных
# Генерация данных для графика
n = 80# Количество кругов
x = np.random.rand(n) # Случайные координаты по оси X для центров кругов
y = np.random.rand(n) # Случайные координаты по оси Y для центров кругов
colors = np.random.rand(n) # Случайные цвета для кругов
areas = np.pi * (18 * np.random.rand(n))**2# Случайные радиусы для кругов, преобразованные в площади
# Создание графика
plt.scatter(x, y, s=areas, c=colors, alpha=0.8) # Использование функции scatter для рисования кругов
# где x и y - координаты центров кругов, s - размеры кругов (заданные через площади),
# c - цвета кругов, alpha - прозрачность кругов.
plt.show() # Отображение графика
Рисунок: Случайно сгенерированные круглые области, заполненные разными цветами.importnumpyasnp
importmatplotlib.pyplotasplt
# Импортируем библиотеку numpy для работы с массивами и matplotlib.pyplot для визуализации данных.
n = 80# Количество точек для генерации
# Генерируем случайные координаты для n точек.
x = np.random.rand(n) # Случайные координаты по оси X
y = np.random.rand(n) # Случайные координаты по оси Y
# Генерируем случайные цвета и размеры для точек.
colors = np.random.rand(n) # Случайные цвета для каждой точки
areas = np.pi * (18 * np.random.rand(n))**2# Случайные размеры точек, представляющие собой площадь кругов
# Создаем диаграмму рассеяния с использованием сгенерированных данных.
plt.scatter(x, y, s=areas, c=colors, alpha=0.8) # 's' обозначает размер, 'c' - цвет, 'alpha' - прозрачность точек
plt.show() # Отображаем диаграмму рассеяния
# Генерируем данные для построения кривой.
x = range(1000) # Генерируем последовательность чисел от 0 до 999
y = [i ** 2foriinx] # Создаем список значений y, где каждое значение является квадратом соответствующего значения x
# Строим график функции y = x^2
plt.plot(x, y) # 'plot' используется для построения линейного графика
plt.show() # Отображаем график
# Генерируем данные для гистограммы.
x = np.linspace(0, 1, 1000)**1.5# Создаем 1000 точек между 0 и 1, возводим значения в степень 1.5 для искажения распределения
# Строим гистограмму распределения значений x.
plt.hist(x) # 'hist' используется для построения гистограммы
plt.show() # Отображаем гистограмму

2.16 Оценка эффективности кода
Оценить производительность кода можно двумя способами. Ниже приведены типичные примеры кодов, которые читатели могут использовать для анализа вычислительной эффективности своих программ. В этих примерах используются модули time и numpy для демонстрации различий в производительности между стандартными операциями Python и оптимизированными операциями, реализованными в библиотеке NumPy.
importtime
importnumpyasnp
# Создание большого списка целых чисел
g = list(range(10_000_000))
# Преобразование списка в массив NumPy с типом данных float64
q = np.array(g, 'float64')
# Измерение времени выполнения суммирования с использованием встроенной функции sum Python
start = time.process_time() # Засекаем начальное время
sg = sum(g) # Суммирование элементов списка
t_elapsed = (time.process_time() - start) # Вычисление затраченного времени
print(f'Сумма: {sg}, Затраченное время: {t_elapsed}')
# Измерение времени выполнения суммирования с использованием функции np.sum для массива NumPy
start = time.process_time() # Засекаем начальное время снова
sq = np.sum(q) # Суммирование элементов массива NumPy
t_elapsed = (time.process_time() - start) # Вычисление затраченного времени
print(f'Сумма: {sq}, Затраченное время: {t_elapsed}')
# Использование магической команды %%timeit для автоматизированного измерения времени выполнения кода в ячейке
# %%timeit
# sg = sum(g) # Эта строка демонстрирует, как использовать timeit в интерактивных средах, таких как Jupyter
Сумма: 49999995000000, Затраченное время: 0.030974000000000057
Сумма: 49999995000000.0, Затраченное время: 0.006296999999999997
В этом примере кода мы сначала импортируем необходимые модули, затем создаем большой список целых чисел и преобразуем его в массив NumPy. Далее мы измеряем время, необходимое для суммирования элементов списка и массива, используя стандартную функцию sum и функцию np.sum соответственно. Результаты демонстрируют различия в производительности между стандартными операциями Python и операциями, оптимизированными в NumPy. Комментарии в коде помогают понять, что делает каждый шаг и как интерпретировать вывод.2.17 Заключение
Овладев базовыми знаниями языка программирования Python, его модулями и основными техниками программирования, мы подошли к этапу, когда готовы применять эти знания для решения задач вычислений и освоения техник машинного обучения. Этот процесс не только позволит реализовывать сложные вычислительные задачи, но и будет способствовать постепенному совершенствованию навыков программирования на Python.
Важно подчеркнуть, что практический опыт и постоянное применение полученных знаний в реальных проектах являются ключевыми факторами для глубокого освоения языка и его возможностей. Регулярное решение задач различной сложности поможет не только закрепить базовые знания, но и познакомиться с продвинутыми техниками и подходами в программировании.
Проверь себя
Какая идея лучше всего описывает фокус главы "Основы Python"?
В машинном обучении теоретические определения полезно проверять на численных примерах и визуализациях.
Какие действия помогают закрепить материал главы?
Пройти тест