Про кортежи и про файлы

Всем доброго времени суток, друзья. Данная статья соответствует 9 главе книги Марка Лутца «Изучаем Python». Глава завершает вторую часть книги «Типы и операции» и раскрывает основные понятия о том что такое кортеж, что такое файл, как их использовать и как применять. Знание принципов работы с файлами может помочь ответить на вопрос: «А собственно для чего мы вообще программируем?» и «Что мы хотим, изучая тот или иной язык?». Моё личное персональное мнение заключается в том, что мы программируем для того, чтобы создавались файлы. Файлы - это объект, который мы можем показать не программисту и он возможно даже и поймёт для чего вы сидели много времени над кодом и оптимизировали его работу.

Ну и как всегда по традиции.

Про кортежи и про файлы

Думаю эта картинка прям в тему сегодняшнего разговора и начнём его с вопроса.

Как Марк Лутц понимает смысл кортежей?

Кортеж - простая группа объектов, работающие в точности как и списки, НО они не могут быть модифицированы на месте.

М.Лутц

Чтобы понять, что мы имеем дело с кортежем, достаточно взглянуть на следующую простенькую программу.

1 Пример. Понимание кортежа

names = ('Misha', 'Sasha', 'Dasha') print(names) #('Misha', 'Sasha', 'Dasha') print(type(names)) # <class 'tuple'>

Как можно видеть, англоязычное название кортежа - tuple. Долгое время этот тип оставался для меня самым загадочным, потому что было сложно понять как и где их можно использовать. Лутц по крайней мере дал более широкое представление о нём.

Как кортеж работает и как проявляется его неизменяемость?

Хороший вопрос. Пожалуй, тут снова пример нужен.

names = ('Misha', 'Sasha', 'Dasha')

Допустим мне пришла задача от человека, который не понимает принцип работы кортежа. Он попросил заменить имя 'Sasha' на имя 'Pasha' и показывает такой код

names[1] = 'Pasha'

Что он получит?

Да ничего! Ошибку он получит:

Traceback (most recent call last):File "", line 1, inTypeError: 'tuple' object does not support item assignment

А всё потому, что он выбрал не тот тип данных. Вполне возможно что решением подобной ситуации является переводом кортежа например в тип данных list. Кстати говоря, эта ошибка вполне может соответствовать определению кортежей.

Хорошо, а есть что-то ещё, что можно узнать про них?

Конечно есть что-то ещё. Лутц, например, выделил 5 базовых характеристик, которые я здесь приведу:

  • Кортежи - упорядоченные коллекции произвольных элементов.
  • Они поддерживают доступ по смещению (по индексу можно получить значение)
  • Они относятся к категории «неизменяемая последовательность» ( вспоминаем пример выше
  • У них фиксированная длина (добавить ничего не получится), разнородны (могут быть как числа, так и строки) и имеют глубокое вложение (о них говорили много в предыдущих статьях)
  • Это массивы ссылок на объекты. Кортежи хранят точки доступа к другим объектам, индексация кортежа происходит быстро.

В предыдущем примере было продемонстрировано как не стоит работать с кортежами, а как тогда стоит?

Прекрасный вопрос. Примеры работы с кортежами можно найти в официальной документации в Python на сайте. Также напоминаю про встроенные справочные служебные слова вроде help() и dir()

А вот Лутц выделяет 17 методов работы с кортежами. В статье обзорно представлены только 6 наиболее распространённых.

() - определение пустого кортежа

TupleNumbers = (0, ) - Одноэлементный кортеж

ValueTuple = (0, 'Misha', 20, 30) - Четырёхэлементный кортеж

names[0] - индексация # 'Misha'

names[0:2] - нарезание # ('Misha', 'Sasha')

names[0]+' and ' +names[1] # конкатенация

Как они используются в боевом программировании?

1 Пример. Конкатинация

names = ('Masha', 'Sasha') + ('Vika', 'Nika') print(names) # ('Masha', 'Sasha', 'Vika', 'Nika')

2 Пример. Повторение.

names = ('Masha', 'Sasha') * 2 print(names) # ('Masha', 'Sasha', 'Masha', 'Sasha')

3 Пример. Индексация и нарезание

names = ('Masha', 'Sasha') * 2 print(names[0], names[1:3]) # Masha ('Sasha', 'Masha')

А что в обязательном порядке должно входить при организации кортежа?

Присмотритесь к примеру внимательней и увидите, что это скобки и запятые. Без них программный интерпретатор просто не поймёт, что вы работаете с кортежами.

Есть, однако ещё специфические случаи, которые стоит также иметь ввиду. Кортежи автоматически присваиваются объектам, когда требуется их указать через запятую в одном имени переменной.

nameAge = 'Misha', 40 print(nameAge) # ('Misha', 40) print(type(nameAge)) # <class 'tuple'>

Использовать или не использовать скобки - выбор конечно стоит за программистом, однако М. Лутц всё же рекомендует их использовать везде, особенно начинающим программистам. Так можно исключить вероятность появления ошибок.

Существует ли возможность каким-то образом преобразовывать кортежи, например в списки?

Конечно есть, и это даже нужно делать! Вспомните пример, о котором я говорил в самом начале, когда показывал случай с неизменяемостью.

names = ('Misha', 'Sasha', 'Dasha')

Давайте его преобразуем в список и всё же поменяем значение, и уж программист так желает вывести кортеж, то выведем кортеж

namesToList = list(names) # ['Misha', 'Sasha', 'Dasha'] namesToList[1] = 'Pasha' # ['Misha', 'Pasha', 'Dasha'] names = tuple(namesToList) # ('Misha', 'Pasha', 'Dasha')

Вот вам и решение вопроса!

А существуют ли дополнительные методы, которые можно применять в кортежах?

Ну пожалуй наиболее распространённые - поиск индекса index(), как в списках, count(). Если что-то необходимо посчитать.

А можно их как-то продемонстрировать на примерах?

Всё тот же пример с теми же данными:

names = ('Misha', 'Sasha', 'Dasha')

Пример 1.

Допустим мы хотим узнать индекс значения 'Misha'.

print(names.index('Misha')) # выведет 0

Не забываем, что индексация в Python начинается с 0

Пример 2.

Допустим мы удвоили кортеж.

names = ('Misha', 'Sasha', 'Dasha') *2 #('Misha', 'Sasha', 'Dasha', 'Misha', 'Sasha', 'Dasha')

И теперь надо посчитать сколько в нём, например содержится значений 'Misha'

print(names.count('Misha')) # 2

На этом всё! В кортежах методов не так много. Запомнить их не проблема, если проходить регулярно практику.

А как же делать сортировку в кортежах. Это допустимо?

Нет, кортежи это не допускают сортировку. Для сортировки кортежей надо переводить его в список. О том как это сделать я писал выше.

В самом начале вы сказали, что Лутц давал рекомендации для чего надо использовать кортежи, можете их сюда написать?

Это резонно. Тут Лутц даёт ответ, и связывает его с историей языка. Создатель Python Гвидо ван Россум по образованию был математиком. Так вот он видел «кортеж» как простого объединения объектов.

Кортеж имеет математическое происхождение и часто применяется в реляционных базах данных.

Однако, преимуществом кортежа является его целостность. Когда мы его используем то уверены, что его никто не сможет изменить (идёт отсылка к защите данных). Кортежи - это набор символов- констант для Python.

Они используется в тех местах, в которых списки нельзя применить. Например в словарных ключах. Да-да ключи в словарях - это кортежи. Они не изменяемы, как показала практика. (Смотрите предыдущую статью)

В последнем ответе было упомянуто о том, что кортежи могут представлять ключи словаря, значит по видимому словари можно тоже как-то преобразовать в кортеж?

Да, можно. Для этого даже есть специальная библиотека namedtuple. Она встроена в Python. Давайте продемонстрируем её работу на примере

dictMyData = {'name': 'Misha', 'LastName': 'Ivashkin', 'skills': ['Python', 'Analysys', 'Talking'] } print(dictMyData) # {'name': 'Misha', 'LastName': 'Ivashkin', 'skills': ['Python', 'Analysys', 'Talking']}

Замечаем, что keys () у нас tuple(). Круглые скобки.

print(dictMyData.keys()) # dict_keys(['name', 'LastName', 'skills'])

А values() типа list. Квадратные скобки.

print(dictMyData.values()) # dict_values(['Misha', 'Ivashkin', ['Python', 'Analysys', 'Talking']])

1) Чтобы словарь привести к кортежу воспользуемся её встроенной библиотекой.

from collections import namedtuple

2) Объявим класс в соответствии с использованием библиотеки

DictTuple = namedtuple('MyData', ['name', 'LastName', 'skills'])

3) Создадим экземпляр класса

misha = DictTuple('Misha', LastName='Ivashkin',skills=['Python', 'Analysys', 'Talking'] )

4) По созданному экземпляру с помощью индексации получаем значения, которые кстати говоря теперь тоже tuple. Если их связать друг с другом через запятую.

print((misha[0], misha[1])) # ('Misha', 'Ivashkin') print(type((misha[0], misha[1]))) # <class 'tuple'>

Таким образом Лутц заканчивает свой повествование о том, что такое кортежи и как они работают. На самом деле, когда я читал об этих типах данных сразу произошла систематизация в голове и уверен, что какие-то нейроны в мозгу соединились.

Ну а теперь плавненько-плавненько переходим к файлам.

Файлы

Кратенько вообще дам понять что понимает под понятием «Файл» автор книги.

Файл - именованные ячейки хранилища на компьютере, управляемые операционной системой. В Python предусматривает доступ к таким файлам внутри программного интерпретатора.

М.Лутц

Если это сложно понять, то вот другое объянение.

Файл - основной тип данных, создаваемый встроенной функцией, но отличающийся от чисел, последовательностей и отображений а также не реагирующий на операции выражений. Они используют в своей основе только методы, которые необходимы для обработки файлов

М.Лутц

Я полагаю, что эти два определения необходимо читать вместе, потому что именно в них скрывается основное назначение работы с файлами - это чтение, изменение, запись

Работа с фалами - одна из важнейших задач разработчика ПО. C их помощью мы можем создавать автоматические отчёты, записывать логи, извлекать данные, записывать данные, генерировать файлы и так далее. А теперь переходим к вопросам.

Как работать с файлами на Python?

Очень логичный вопрос, на который можно дать логичный ответ. Для работы с файлами надо смотреть документацию.

Начинается работа с файлами с вызова оператора open()

А можно привести какой-нибудь пример?

Не можно, а нужно.

MyFirstFile = open(r'myfile.txt', 'w')

Вот пример нашего первого файла. Как было сказано выше конкретно эта строчка создаёт файл myfile.txt с соответствующим флагом ( в данном случае 'w'). Однако существуют и другие.

А за что они отвечают?

По сути они отвечают за режим работы с этими файлами. Например в книге Лутца они все указаны. Здесь я тоже их все приведу

'w' - создать файл

'r' - открыть файл для текстового ввода

'a' - дополнить файл значениями с конца

Дополнительные параметры:

b - разрешение работы с двоичными данными

+ - открытие для ввода и вывода

Как понять, что скрипт на создание файла выполнился успешно?

Если говорить про IDE Pycharm, то успешное выполнение программы выведет код 0, а в проекте появится соответствующая надпись. В нашем случае myfile.txt

Process finished with exit code 0
Process finished with exit code 0
myfile.txt
myfile.txt

Если выводятся другие слова, то вероятно у Вас в коде закралась ошибка

Что ещё необходимо знать про файлы?

В книге выделяется 4 главных пункта

  • Файловые итараторы подходят для чтения строк. Здесь автор говорит, что наиболее хороший вариант работы с файлами - не читать их вовсе. А для чтения лучше всего использовать циклы типа for
  • Содержимое файла являются строками, а не объектами. Поэтому для преобразования их в объект Python существуют другие библиотеки вроде pickle и json
  • Файлы буферизируются и поддерживают позиционирование. Другими словами автор хочет донести мысль, что записываемый объект может не сразу быть передан из памяти на диск.
  • Вызов метода close() часто не обязательный. Кстати тут наверное подробнее про этот метод. Когда файл открывается, то его не может менять другая программа. Она лишь может только считывать данные, но не менять. Для того, чтобы другая программа смогла сделать какие-то действия надо прежде всего закрыть её в текущей, и открыть в другой. Так вот в Python после того, как файл записался, интерпретатор совершает автоматическое его закрытие, тем самым программисту не надо задумываться над тем: «А сможет ли другая программа внести какие-либо изменения в объект файла?» Но если вдруг я или вы захотите всё-таки написать закрытие явно, то такой формат точно не навредит

А можно какие-то примеры как записывать и как считывать файлы из файла?

Пожалуй да, как раз для этого самое время. Вот мы сделали файл.

MyFirstFile = open(r'myfile.txt', 'w')

Давайте осуществим в него запись.

MyFirstFile = open(r'myfile.txt', 'w') MyFirstFile.write('Hello, my name is Misha. And this is my first row \n') MyFirstFile.close()

И проверим, что запись выполнилась успешно.

Про кортежи и про файлы

Во-первых сразу видим, что завершился процесс без ошибок. А во вторых давайте откроем этот файл в текстовом редакторе

Про кортежи и про файлы

А во-вторых запись появилась в текстовом редакторе

Про кортежи и про файлы

Ну вот мы записали этот файл, а теперь как его считать?

И вот ответ уже на этот вопрос.

MyFirstFile = open(r'myfile.txt') MyFirstFile.readline()

Применяем метод readline(). И получаем в командном интерпретаторе ту же самую строку.

'Hello, my name is Misha. And this is my first row \n'

А как работает чтение файлов с несколькими строками?

А давай попробую и это реализовать.

MyFirstFile = open(r'myfile.txt', 'w') MyFirstFile.write('Hello, my name is Misha. And this is my first row \n') MyFirstFile.write('And this is my second row \n') MyFirstFile.close()

Выполним этот скрипт, и посмотрим как Python сможет его прочитать.

MyFirstFile = open(r'myfile.txt') MyFirstFile.readline() # 'Hello, my name is Misha. And this is my first row \n'

Вызов readline() выполнил только первую строку, для того, чтобы он смог вывести вторую строку, нужно его повторно написать.

MyFirstFile.readline() # 'And this is my second row \n'

Итого вот он текст нашей маленькой показательной программы:

MyFirstFile = open(r'myfile.txt') MyFirstFile.readline() MyFirstFile.readline() MyFirstFile.close()

А как можно вывести весь текст?

А для этого есть специальный метод read()

MyFirstFile = open(r'myfile.txt').read() print(MyFirstFile) #

И вот что он выведет:

Hello, my name is Misha. And this is my first row

And this is my second row

В качестве режимов для работы файлов указаны дополнительные параметры. За что они отвечают?

Помните, мы с вами обсуждали в статье о строках, что существует ещё специальные символы кодировки в виде Unicode и ASCII. Так вот это как раз про эту тему.

Флаг «b» означет запись данных в файл в байтовом представлении.

MyFirstFile = open('myfile1.bin', 'wb') MyFirstFile.write(b'123465') MyFirstFile.close() MyFirstFile = open(r'myfile1.bin', 'rb').read() print(MyFirstFile) # b'123465' print(bin(MyFirstFile[3])) # преобразования байтового представления в двоичный вид

С дополнительными параметрами программисты работают достаточно редко, поэтому в статье они показываются как справочный материал.

Здесь мы только рассмотрели как работать с файлами, а если например, программа состоит из множества переменных то как их можно записать?

Да собственно говоря, так же как и обычную строку через методы форматирования.

Пример 1. Допустим нам требуется вставить переменные возраста, имени и необходимых навыков в файл.

FirstName = 'Misha' Age = 26 skills = ['Python', 'Analysys', 'Talking']

Предлагаю реализовать этот сценарий по шагам:

  1. Сначала создаём файл
MyData = open('mydata.txt', 'w')

2. Применяем методы форматирования (я взял самый старый)

MyData.write('%s, %s, %s\n' % (FirstName, Age, skills))

Обратите внимание, что форматирование здесь выступает в качестве аргумента метода write()

3. Закрываем файл

MyData.close()

А теперь предлагаю его открыть и посмотреть что у нас получилось.

MyData = open('mydata.txt').read() print(MyData) # Misha, 26, ['Python', 'Analysys', 'Talking'] print(type(MyData)) # <class 'str'>

У нас получились данные в строковом типе. Давайте теперь запишем эти данные в переменные так, как это было.

  1. Преобразовал строку в список
splittedMyData = MyData.split(',')

2. Извлёк данные , так как это было

FirstNameFromFile = splittedMyData[0] AgeFromFile = int(splittedMyData[1]) ListSkillsFromFile = [splittedMyData[2], splittedMyData[3], splittedMyData[4]] print(FirstNameFromFile, AgeFromFile ,ListSkillsFromFile) #Misha 26 [" ['Python'", " 'Analysys'", " 'Talking']\n"]

Мне почти почти нравится, за исключением того, что в списке появились специальные символы, которых быть не должно. Дальше я немного начал играться с данными, поэтому читать следующий блок информации только тем, кто нарешал куча задач на Python

А теперь немного сложности. Предлагаю решить эту задачу с помощью библиотеки re

import re ListNames= re.findall(r'[a-zA-Z]+', ListSkillsFromFile)[:-1] print(ListNames) # ['Python', 'Analysys', 'Talking']

В нём, конечно есть небольшие ограничения, связанные с наличием одиночной 'n' . В данном решении я использовал костыль с использованием индексов, в идеале, конечно было бы круто просто сделать проверку с помощью условного оператора if

Хранение целых данных как в Python выглядит достаточно любознательной и интересной задачей. А существуют ли методы, которые можно было использовать для хранения данных «как есть»?

Да, такие способы существует например есть служебное слово eval()

Для демонстрации этой функции продемонстрируем его использование на других данных

  1. Генерируем данные
age1 ,age2, age3 = 21,22,34 Name = 'Misha' DictData = {'Weight': 60, 'Length': 170} skills = ['Python', 'Analysys', 'Talking']

2. Создаём новый файл

file = open('newfile.txt', 'w')

3. Записываем его в файл

file.write(Name + '\n') file.write('%s,%s,%s\n' % (age1, age2, age3)) file.write(str(skills) +'$' + str(DictData) + '\n')

4. Читаем файл

file = open('newfile.txt').read()

5. Получаем результат

Misha

21,22,34

['Python', 'Analysys', 'Talking']${'Weight': 60, 'Length': 170}

А теперь можно описывать функцию eval()

eval - это функция, которая используется как порция исполняемого кода. Предлагаю поработать над последней строкой файла

  1. Получаем последнюю строку
file = open('newfile.txt') row = file.readline() row = file.readline() row = file.readline() print(row) # ['Python', 'Analysys', 'Talking']${'Weight': 60, 'Length': 170}

2. Преобразуем последнюю строку в список

splittedRow = row.split('$') # ["['Python', 'Analysys', 'Talking']", "{'Weight': 60, 'Length': 170}\n"]

3. Преобразуем объекты в типы данных в Python. Тут следует обратить внимание на наличие квадратных или фигурных скобок. Иначе данные не правильно поймёт интерпретатор

part1 = eval(splittedRow[0]) type(part1) # <class 'list'> part2 = eval(splittedRow[1]) type(part2) # <class 'dict'>

Как видим у нас получился класс list() и dict()

Использование eval() конечно хорошо знать в программировании, однако использовать данный метод нужно весьма ограниченно.

А на что нужно обратить внимание?

В первую очередь на безопасность. Если внутри этой функции вставится вредоносный код, то в случае его выполнения он может заразить компьютер, удалить много нужных файлов и так далее. И поэтому для разрешения этой задачи был разработан другой модуль.

И какой же ?

- pickle

Как говорил Лутц

Pickle является более развитым инструментом, который позволяет сохранить почти любой объект Python в файл напрямую, не требуя со стороны программиста каких-либо преобразований в и из строки.

М.Лутц

Давайте попробуем повторить сохранить уже рассмотренные данные через использование этого модуля.

С прошлого примера у нас сохранилась такая строка.

splittedRow = row.split('$') # ["['Python', 'Analysys', 'Talking']", "{'Weight': 60, 'Length': 170}\n"]

Попробуем применить этот метод к ней.

import pickle F = open('dataone.pkl', 'wb') pickle.dump(splittedRow[0], F) F.close()

А теперь получим эту строку.

F = open('dataone.pkl', 'rb') E = pickle.load(F) print(E) # ['Python', 'Analysys', 'Talking']

Весьма предпочтительней использовать такой метод при сохранении данных типов в файл.

Кстати, в самом начале нашей темы вы упоминали ещё про модуль json. Можете рассказать о нём пару слов?

Да могу. Вообще JSON не настолько мощно поддерживает сохранение данных как модуль pickle. Вместе с тем он позволяет сохранить тип данных «словаря» в свой специфический формат. Здесь я уже не буду пошагово расписывать как в предыдущем примере, просто приведу код работы с этим модулем.

  1. Получаем данные
name = {'first': 'Misha', 'last': 'Ivashkin'} myData = {'name': name, 'job' : ['barista', 'developer'], 'age': 26} # {'name': {'first': 'Misha', 'last': 'Ivashkin'}, 'job': ['barista', 'developer'], 'age': 26}

2. Сохраняем данные в JSON

import json json.dumps(myData)

3. Загрузка из JSON:

fromJSON = json.loads(toJSON) # {'name': {'first': 'Misha', 'last': 'Ivashkin'}, 'job': ['barista', 'developer'], 'age': 26}

4. Создание JSON файла:

json.dump(myData, fp=open('mydata.json', 'w'), indent=3)

5. А теперь выводим результат выполнения программы

print(open('mydata.json').read())
Про кортежи и про файлы

А ещё существуют какие-либо файловые операции?

еликое множество. Всё зависит от возможностей модуля, который вы используете для работы с файлами. Помимо рассмотренных Лутц выделяет как минимум 5 дополнительных средств

  • Перенаправление в файлы стандартного потока данных ( модуль sys.stdout)
  • Дескрипторные файлы модуля os
  • Сокеты, контейнеры и очереди FIFO
  • Файлы с доступом по ключу с помощью модуля shelve
  • Потоки данных командной оболочки os.popen , subprocess.popen

Кто заинтересовался, рекомендую их подробно изучить самостоятельно. На этом мы заканчиваем обзор 2 части. Спасибо большое, что дочитали эту статью до конца.

До новых встреч уже в третьей части :)

11
Начать дискуссию