Занятие 6: Типы данных Python, примеры и задачи

5 (100%) 5 vote[s]

В этом занятии рассмотрим простые типы данных Python (целочисленные типы: int, long, числа с плавающей точкой: float, логический тип: bool, строки: str), неизменяемые типы, сложные типы или структуры данных (список: list, тьюпл: tuple, словарь: dict).

Числовые типы данных

Целочисленные типы: int, long

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

Чтобы хранить типы данных int в python используется 32 бита (в 32-битных системах, 64 в 64-битных; дальнейшие расчеты приводятся на примере 32-битных), то есть 4 байта (= 32/8). Вы помните, что 1 бит — это 1 двоичный разряд, который приобретает значение 0 или 1. Имея 32 бита, мы можем закодировать n = 2 ** 32 = 4294967296 различных значений. Туда входит 0, отрицательные и положительные числа, следовательно целое число типа int может принимать значения в интервале — (n-1) / 2 … + (n-1) / 2, то есть -2147483647 … 0 … 2147483647. 

Любые значения вне этого интервала не могут быть представлены типом int. Во многих языках программирования выход значений за предел int не отслеживается и происходит так называемое «переполнение» (при переносе разрядов данные начинают занимать лишние ячейки памяти, а компилятор/интерпретатор продолжает работать со старыми ячейками, теряя новые разряды числа), что приводит к неадекватным результатам: добавляя 2 очень больших числа вы получаете очень малое или отрицательное. Python отслеживает такие ситуации и когда результат вычислений выходит за пределы int, автоматически конвертирует типы данных в другой целочисленный тип long.

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

Типы данных long обозначаются интерпретатором буквой L в конце числа. Это можно наблюдать при выводе значения в интерактивном режиме без print-а (используется другой формат вывода данных). При выводе на экран print-ом L на конце будет отсутствовать. Также вы можете создать значение с L, сигнализируя интерпретатору, который должен использоваться именно long.

Числа с плавающей запятой: float

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

Для этого действительные числа хранятся в форме x * 2y. x называется мантиссой (1≤x <2, имеет ограниченное количество знаков), а в — экспоненте (-1022≤y≤1022). Таким образом, числа с плавающей запятой все равно имеют ограничения, но их диапазон чрезвычайно большой.

Недостатком такого подхода является то, что не все числа можно точно выразить через степени двойки. Поэтому некоторые значения (например, 0.1 = 1.999999 … х 2-1) сохраняются примерно, а затем округляются для вывода. Эта погрешность накапливается при вычислениях и может приводить к неожиданным результатам.

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

def user_test(a,b):
    return a * b
result_test = user_test(0.2, 6)
result_right = 1.2
if result_test == result_right:
    print 'OK'
else:
    print 'ERROR'

Данный пример выведет ERROR, хотя кажется очевидным, что 0.2 * 6 = 1.2. Потому что при умножении накапливается погрешность округления значения 0.2 и этого достаточно для получения неверного результата. С учетом этой особенности предыдущий пример должен выглядеть так:

def user_test(a,b):
    return a * b
result_test = user_test(0.2, 6)
result_right = 1.2
error = 0.00001 # это значение будет определять достаточную точность вычислений
if abs(result_test - result_right) < error:
    print 'OK'
else:
    print 'ERROR'

Интересный факт : поэтому для представления сумм в серьезных финансовых программах никогда не используются типы данных, представленных действительными числами. Все вычисления проводятся в целых числах — копейках или долях копеек, в зависимости от требуемой точности. Например, стоимость ноутбука А в правильном интернет-магазине составляет не $ 899.99, а 89999 центов. Пользователю цена будет выводиться в $ с разделителем-точкой, но во всех расчетах будет использоваться целочисленная. В противном случае суммы в счетах могут не совпасть с заявленной стоимостью товара.

Логический тип Bool

Как вы уже знаете , первичным источником логических значений в программе является сравнение <<= ==! = <> =>>. В них всех есть интересная особенность: если речь идет о числовых (int, long, float) или логических значениях, для сравнения их между собой интерпретатор автоматически конвертирует типы данных. 

Если же нам нужно точно сравнить два числовых значения (включая их типа), для этого можно воспользоваться операцией is :

Еще один интересный оператор, результатом которого являются логические значения, это in — он проверяет наличие значения в последовательности. Для словарей — наличие значение среди ключей словаря:

Кроме того, логические значения (типы данных) возникают при выполнении логических операции — тех самых, что используются для объединения простых условий между собой: and, or, not. Для иллюстрации работы логических операций в математике часто используют так называемые «таблицы истинности»:

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

if False and x&gt;0: # это условие никогда не выполнится
    print 'OK'

Строки str и операции с ними

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

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

Кроме того, для обозначения строк могут использоваться 3 пары одинарных или двойных кавычек. В таком случае все, что находится между ними, сохраняется «как есть».

Строки является одним из типов последовательностей . Это значит, что к строкам применяется ряд стандартных функций: len (), max (), min () и другие. А также, строка может перебираться в цикле for. Интересно, что при этом элементы, содержащиеся в строке, (то есть буквы, знаки и др.) также считаются строками, только единичной длины.

escape-последовательности

Строки могут содержать специальные символы, которые при выводе строки обрабатываются специальным образом — они называются escape-последовательностями. Наиболее часто используются следующие:

    • \n  — переход на новую строку;
    • \t  — вставка табуляции;
    • — двойные кавычки (полезно, если необходимо вставить кавычки в строку, окруженный двойными кавычками);
    • \’ — одинарные кавычки (полезно, если необходимо вставить одинарные кавычки в строку, окруженный одинарными кавычками).

Например:

Редактирование строк

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

s = 'Hello world'
s = s[:6] + 'Python!'

Или использования функции s1.split (separator), которая разбивает строку s1 на части, используя переданную строку separator в качестве разделителя. Результатом ее работы является список, состоящий из этих частей. Вы можете внести в этот список любые изменения, а затем объединить в новую строку с помощью другой функции — s2.join (sequence), которая объединяет последовательность sequence (например список строк), добавляя в качестве разделителя между ними строку s2:

s = 'Hello world'
s = s.split(' ') # ['Hello', 'world']
s = ', '.join(s) # 'Hello, world'

Форматирование строк

Еще одной возможностью строк является «форматирование» — возможность вставки в строку данных в требуемом формате без дополнительной конкатенации. Для этого используется оператор %, первым операндом которого является строка-шаблон, а вторым — тьюпл с данными для вставки.

Строка-шаблон содержит «заглушки», которые обозначают, в какие места должны вставляться данные и в каком формате. Например:

s = 'My name is %s, I’m %s years old.' % ('Vasia', 21)

аналогичное записи

s = 'My name is ' + 'Vasia' + ', I’m ' + str(21) + ' years old.'

Данные вставляются в строку в том порядке, в котором они находятся в тьюпле. «Заглушка» % s обозначает вставку данных с приведением их к типу строка — str ().

      • %d — целое десятичное число;
      • %x, %X — целое шестнадцатеричное число;
        %f — действительное число в десятичном представлении;
      • %e, %E — действительное число в экспоненциальном представлении;
      • %% — знак процента.

Для форматирования числовых значений внутри «заглушек» (между % и буквой) могут использоваться дополнительные параметры:

      • + — отображать знак числа;
      • Пробел — добавить пробел перед положительным числом вместо «+»;
      • m.n — вставить число как строку с m символами, оставить n знаков после запятой; при этом строка будет расширен до необходимой длины пробелами;
      • 0m.n — то же, но строка будет расширен до необходимой длины нулями.

Например:

Списки и тьюплы

О списках вы также знаете уже достаточно много. Из нового — это единственный изменяемый тип данных из тех, что мы рассматривали ранее. Это значит, что для внесения изменений в список не нужно создавать новый, а можно изменить любой отдельный его элемент.

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

Следующая программа моделирует того же пассажира, который ездит в автобусе и ждет билет со «счастливым» номером. Первая функция генерирует случайный номер билета, вторая — проверяет, является ли билет счастливым. Функция моделирует получение билета пассажиром и возвращает тьюпл с двумя значениями: номером полученного билета и логическим значением в зависимости от того, является ли номер счастливым.

from random import randint
 
def generate_ticket_number():
    return randint(0, 999999)
 
def is_ticket_lucky(num):
    num = str(num)
    num = '0' * (6 - len(num)) + num
    return int(num[0]) + int(num[1]) + int(num[2]) == int(num[3]) + int(num[4]) + int(num[5])
 
def get_ticket():
    number = generate_ticket_number()
    is_valid = is_ticket_lucky(number)
    return (number, is_valid)
 
 
my_ticket_in_bus = get_ticket()
if my_ticket_in_bus[1] == True:
    template = 'Woohoo! I\'m lucky, my ticket is %s'
else:
    template = 'My ticket is %s. Will try tomorrow.'
print template % (my_ticket_in_bus[0])

Двумерные структуры данных

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

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

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

Такую матрицу легко представить в виде вложенных списков:

matrix = [[1, 2, 3], [1, 2, 7], [4, 9, 2], [6, 2, 5]]

Далее, когда мы обратимся по индексу, например, 0, получим список, содержащий нулевую строку матрицы:

print matrix[0] # [1, 2, 3]

Указав сразу 2 индекса, мы сразу обратимся к этому вложенному списку и получим отдельный элемент матрицы:

print matrix[0][1] # 2

К сожалению, такая вложенная структура не гарантирует того, что данные являются корректными. Например, внешний список может содержать вложенные списки различной длины, которые не могут представлять прямоугольную числовую матрицу:

matrix = [[1, 2, 3, 0, 0], [1, 7], [4, 9, 2], [6, 2, 5]]

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

def is_matrix(mat):
    if not isinstance(mat, list) or not isinstance(mat[0], list):
        return False
    size = len(mat[0])
    for row in mat:
        if not isinstance(row, list) or len(row) <> size:
            return False
    return True

При необходимости мы можем обойти все элементы матрицы, используя двойной цикл. Например, вывести все элементы матрицы:

def print_elements(mat):
    for i in range(len(mat)):
        for j in range(len(mat[i])):
            print mat[i][j]

Или удобно вывести матрицу в виде прямоугольника:

def output_matrix(mat):
    for row in mat:
        string = ''
        for el in row:
            string = string + "%4d" % (el)
        print string

Напишем программу, которая транспонирует заданную матрицу, то есть меняет местами ее столбцы и строки. Для этого необходимо создать новый список (изменяя старый, мы можем потерять какие-то элементы и результат будет неверным), в нем создать необходимое количество вложенных списков и заполнить их элементами, соответственно изменяя их индексы (matrix2 [i] [j] = matrix [j] [i]). Для этого используем предварительно определенные функции is_matrix () и output_matrix ():

matrix = [[1,2,3],[1,2,7],[4,9,2],[6,2,5]]
 
def transpone(mat):
    mat2 = []
    for i in range(len(mat[0])):
        for j in range(len(mat)):
            if j==0:
                mat2.append([])
            mat2[i].append(mat[j][i])
    return mat2
 
if is_matrix(matrix):
    output(matrix)
    print "transponed:"
    output(transpone(matrix))

Словари

Принципиально новым для вас типом данных являются словари. А они являются одной из наиболее гибких готовых структур в python.

Как и списки, и тьюплы, словари могут содержать элементы с данными любых типов. Принципиальное отличие заключается в том, что эти элементы не имеют строго определенного порядка и доступ к ним предоставляется не по номерам индексах, а с помощью ключей. То есть словарь состоит из пар «ключ» — «значение», где ключи должны относиться к неизменным типов (числа, строки, тьюплы), а «значения», что им соответствуют, могут быть любыми (включая списки или другие словари).

d = {(1,2): 'tuple', 1: 'int', 2.0: 'float', '3': 'string'}
print d[(1,2)] # 'tuple'
print d[1] # 'int'
print d[1.0] # 'int',как и при сравнении в ключах не различаются разные числовые типы
print d[2.0] # 'float'
print d['3'] # 'string'

Как и списки, словари являются изменяемым типом данных. Это значит, что в любой момент мы можем добавить в словарь новые элементы:

d['qwe'] = 'asd'

Для удаления значений из словаря используется функция d.pop (key), которая удаляет из словаря d значение, соответствующее ключу key.

d.pop((1,2))
print d # {1: 'int', 2.0: 'float', '3': 'string', 'qwe': 'asd'}

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

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

people = {'Alice': {'phone': '2341', 'addr': 'Foo drive 23' },
          'Beth':  {'phone': '9102', 'addr': 'Bar street 42'}}
 
name = 'Alice'          
key = 'phone'
if name in people: 
    print "%s phone is %s" % (name, people[name][key])

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

import sys
alphabet = 'abcdefghijklmnopqrstuvwxyz'
text = sys.argv[1].lower()
shift = int(sys.argv[2])
coded_text = ''
new_letter = ''
letter_position = None
 
coder = {}
for i in range(len(alphabet)):
    coder[alphabet[i]] = alphabet[(i + shift) % len(alphabet)]
 
for letter in text:
    if letter in coder:
        coded_text = coded_text + coder[letter]
    else:
        coded_text = coded_text + letter
print coded_text

Стандартные функции для словарей

d.clear () — удаляет все значения из словаря d

d = {1:10, 2:20, 3:30}
d.clear()
print d # {}

d.copy () — возвращает копию словаря d

d1 = {1:10, 2:20, 3:30}
d2 = d1
d3 = d1.copy()
d1[1] = '!'
print d2[1] # '!'
print d3[1] # 10

d.get (key, default) — возвращает значение, соответствующее ключу key словаря d; если ключ отсутствует, возвращает значение default

d = {1:10, 2:20, 3:30}
print d.get(1, 'Nothing found') # 10
print d.get(0, 'Nothing found') # 'Nothing found'

d.keys () — возвращает список ключей словаря d

d = {1:10, 2:20, 3:30}
print d.keys() # [1, 2, 3]

d.values () — возвращает список значений словаря d

d = {1:10, 2:20, 3:30}
print d.values() # [10, 20, 30]

d.items () — возвращает список тьюплов, каждый из которых содержит пару (ключ, значение) для словаря d

d = {1:10, 2:20, 3:30}
print d.items() # [(1, 10), (2, 20), (3, 30)]

Практические задания

Задание 1. Разработать функцию count_holes (n)

Разработать функцию count_holes (n), которая принимает 1 аргумент — целое число или строку, содержащую целое число,
и возвращает целое число — количество «отверстий» в десятичной записи этого числа печатными цифрами (считать, что в «4» и в «0» по одному отверстию), или строку ERROR , если переданный аргумент не удовлетворяет требованиям: является действительным или вообще не числом.

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

Например
Вызов функции: count_holes ( ‘123’)
Возвращает: 0
Вызов функции: count_holes (906)
Возвращает: 3
Вызов функции: count_holes ( ‘001’)
Возвращает: 0
Вызов функции: count_holes (-8)
Возвращает: 2
Вызов функции: count_holes (-8.0)
Возвращает: ERROR

Смотреть ответ:

Задание 2. Разработать функцию encode_morze (text)

Разработать функцию encode_morze (text), которая принимает 1 аргумент — строка, и возвращает строку , содержащую диаграмму сигнала, соответствующую переданному тексту, закодированному международным кодом Морзе для английского алфавита. Разделительные и другие знаки, не входящие в латинского алфавита, игнорировать. Регистром букв пренебрегать.

Для передачи сообщения за единицу времени принимается длительность одной точки. Продолжительность тире равна трем точкам. Пауза между элементами одного знака — одна точка, между знаками в слове — 3 точки, между словами — 7 точек. Словом считать последовательность символов, ограниченная пробелами. Результирующая «диаграмма» демонстрирует наличие сигнала в каждый промежуток времени: на i-й позиции находится «^», если в этот момент передается сигнал, и «_», если сигнала нет. Лишние паузы в конце сообщения на «диаграмме» отсутствуют.

Например
Вызов функции: encode_morze ( ‘Morze code «)
Возвращает: ^^^ _ ^^^ ___ ^^^ _ ^^^ _ ^^^ ___ ^ _ ^^^ _ ^ ___ ^^^ _ ^^^ _ ^ _ ^ ___ ^ _______ ^^^ _ ^ _ ^^^ _ ^ ___ ^^^ _ ^^^ _ ^^^ ___ ^^^ _ ^ _ ^ ___ ^
Вызов функции: encode_morze ( ‘SOS’)
Возвращает: ^ _ ^ _ ^ ___ ^^^ _ ^^^ _ ^^^ ___ ^ _ ^ _ ^
Вызов функции: encode_morze ( ‘1’)
Возвращает:

Смотреть ответ:

Задание 3. Разработать функцию saddle_point (matrix)

Разработать функцию saddle_point (matrix), которая принимает 1 аргумент — прямоугольную матрицу целых чисел, заданную в виде списка списков, и возвращает тьюпл с координатами «седловой точки» передаваемой матрицы или логическую константу False , если такой точки не существует.

Седловой точкой считается такой элемент матрицы, который является минимальным (строго меньше других) в своей строке и максимальным (строго больше других) в своем столбце, например матрица:
1 2 3
0 1 февраля
«1» в левом верхнем углу (по координатами (0; 0)) является седловой точкой матрицы.

Считать, что передаваемые данные корректны, то есть матрица не содержит других значений кроме целых чисел, а все вложенные списки имеют одинаковую длину. Результирующий тьюпл содержит два числа — порядковые номера седловой точки в строке (индекс его списка во внешнем списке) и в столбце (индекс во внутреннем списке).

Например
1 2 3
3 2 1
Вызов функции: saddle_point ([[1,2,3], [3,2,1]])
Возвращает: False
8 3 0 1 2 3 4 8 1 2 3
3 2 1 2 3 сентябрь 7 апреля 9 февраля 3
7 июня 0 Sa 1 3 5 2 3 4 1 января
Вызов функции: saddle_point ([[8,3,0,1,2,3,4,8,1,2,3],[3,2,1,2,3,9,4,7,9,2,3], [7,6,0,1,3,5,2,3,4,1,1]])
Возвращает: (1, 2)
21
Вызов функции: saddle_point ([[21]])
Возвращает: (0, 0)

Смотреть ответ:

Задание 4. Разработать функцию find_most_frequent(text)

Разработать функцию find_most_frequent (text), которая принимает 1 аргумент — текст произвольной длины, который может содержать буквы латинского алфавита, пробелы и знаки препинания (,.:;!? —) и возвращает список слов (в нижнем регистре), которые встречаются в тексте чаще всего.

Слова, записанные через дефис, считать двумя словами (например, «hand-made»). Слова в разных падежах, числах и с другими преобразованиями (например, «page» и «pages») считаются разными словами. Регистр слов — наоборот, не имеет значения: слова «page» и «Page» считаются 1 словом.

Если слов, которые встречаются чаще всего, несколько — вывести их в алфавитном порядке.

Например
Вызов функции: find_most_frequent ( ‘Hello, Hello, my dear!’)
Возвращает: [ ‘hello’]
Вызов функции: find_most_frequent ( ‘to understand recursion you need first to understand recursion … «)
Возвращает: [‘ recursion», «to», «understand ‘]
Вызов функции: find_most_frequent (‘ Mom! Mom! Are you sleeping? !!!»)
Возвращает: [ ‘mom’]

Смотреть ответ:

Читайте больше по теме:

Подписаться
Уведомление о
guest
0 комментариев
Inline Feedbacks
View all comments
Просмотры: 2111

Популярные записи