До сих пор мы встречались с двумя составными типами: строки, состоящие из символов, и списки, состоящие из элементов любого типа. Одно из отмеченных нами различий между этими типами состоит в том, что элементы списка можно изменять, а символы строки - нельзя. Другими словами, строки являются неизменяемыми, а списки - изменяемыми.
Кортеж (англ.: tuple), как и список, представляет собой последовательность элементов любого типа. Но, в отличие от списков, кортежи неизменяемы. Синтаксически, кортеж - это последовательность разделенных запятыми значений:
>>> tup = 2, 4, 6, 8, 10
Хотя это не является обязательным, принято заключать кортежи в скобки:
>>> tup = (2, 4, 6, 8, 10)
Для того, чтобы создать кортеж с единственным элементом, нужно поставить запятую после единственного элемента:
>>> tup = (5,)
>>> type(tup)
<type 'tuple'>
Без запятой Python считает, что (5) - это целое число в скобках:
>>> tup = (5)
>>> type(tup)
<type 'int'>
Оставляя в стороне синтаксис, кортежи поддерживают те же самые операции для последовательностей, что строки и списки. Оператор индекс выбирает элемент кортежа.
>>> tup = ('a', 'b', 'c', 'd', 'e')
>>> tup[0]
'a'
Оператор срез выбирает диапазон элементов.
>>> tup[1:3]
('b', 'c')
Однако, если попробовать выполнить присваивание элементу кортежа, то получим ошибку:
>>> tup[0] = 'X'
TypeError: 'tuple' object does not support item assignment
И, разумеется, хотя мы и не можем изменить отдельный элемент кортежа, мы можем заменить кортеж на другой кортеж:
>>> tup = ('X',) + tup[1:]
>>> tup
('X', 'b', 'c', 'd', 'e')
Или, вместо этого, мы могли бы сначала преобразовать кортеж в список, изменить список, и затем преобразовать его обратно в кортеж:
>>> tup = ('X', 'b', 'c', 'd', 'e')
>>> tup = list(tup)
>>> tup
['X', 'b', 'c', 'd', 'e']
>>> tup[0] = 'a'
>>> tup = tuple(tup)
>>> tup
('a', 'b', 'c', 'd', 'e')
Время от времени возникает необходимость обменять значения двух переменных. Пользуясь традиционными предложениями присваивания, мы должны использовать временную переменную. Например, чтобы обменять значения переменных a и b:
temp = a
a = b
b = temp
Если нам придется делать это часто, такой подход покажется громоздким. Python предоставляет возможность присваивания кортежей, которая элегантно решает эту задачу:
a, b = b, a
В левой части у нас кортеж из двух переменных; в правой - кортеж из двух значений. Каждое значение присваивается соответствующей ему переменной. Все выражения в правой части вычисляются до того, как выполняется первое присваивание. Это свойство делает присваивание кортежа очень полезным инструментом.
Само собой разумеется, что количество переменных в левой части и количество значений в правой должны быть одинаковы:
>>> a, b, c, d = 1, 2, 3
ValueError: need more than 3 values to unpack
Функции могут возвращать кортежи. Например, мы могли бы написать такую функцию, которая обменивает значения двух параметров:
def swap(x, y):
return y, x
После чего мы сможем присвоить возвращаемое значение кортежу из двух переменных:
a, b = swap(a, b)
В данном случае мы не получаем никаких преимуществ, реализовав обмен значений с помощью функции swap. Кроме того, пытаясь реализовать обмен значений как функцию swap, есть опасность совершить ошибку:
def swap(x, y): # incorrect version
x, y = y, x
Если вызвать эту функцию так:
swap(a, b)
то a и x будут альтернативными именами для одного и того же значения. Изменение x в функции swap приводит к тому, что x указывает на другое значение, но это никак не отразится на переменной a в __main__. Аналогично, изменение y никак не повлияет на b.
Функция выполняется без сообщений об ошибке, но она не делает того, что от нее ожидается. Это пример семантической ошибки.
Ранее в разделе Чистые и модифицирующие функции мы обсуждали чистые функции и модифицирующие функции в связи со списками. Поскольку кортежи неизменяемы, нельзя написать функцию, модифицирующую кортеж.
Пример модифицирующей функции, вставляющей новое значение в середину списка:
#
# seqtools.py
#
def insert_in_middle(val, lst):
middle = len(lst)/2
lst[middle:middle] = [val]
Убедимся, что она работает:
>>> from seqtools import *
>>> my_list = ['a', 'b', 'd', 'e']
>>> insert_in_middle('c', my_list)
>>> my_list
['a', 'b', 'c', 'd', 'e']
Но если мы попробуем вызвать ее с кортежем, то получим ошибку:
>>> my_tuple = ('a', 'b', 'd', 'e')
>>> insert_in_middle('c', my_tuple)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "seqtools.py", line 7, in insert_in_middle
lst[middle:middle] = [val]
TypeError: 'tuple' object does not support item assignment
>>>
Проблема в том, что кортежи неизменяемы, и не поддерживают присваивание срезу. Простое решение заключается в том, чтобы сделать insert_in_middle чистой функцией:
def insert_in_middle(val, tup):
middle = len(tup)/2
return tup[:middle] + (val,) + tup[middle:]
Эта версия работает для кортежей, но не для списков или строк. Если нам необходима версия, работающая с любыми последовательностями встроенных типов, то нам нужен способ завернуть значение в корректный последовательностный тип. Это делает небольшая вспомогательная функция:
def encapsulate(val, seq):
if type(seq) == type(""):
return str(val)
if type(seq) == type([]):
return [val]
return (val,)
Теперь можно написать версию insert_in_middle, работающую с последовательностями всех встроенных типов:
def insert_in_middle(val, seq):
middle = len(seq)/2
return seq[:middle] + encapsulate(val, seq) + seq[middle:]
Две последние версии insert_in_middle являются чистыми функциями. У них нет побочных эффектов. Добавив encapsulate и последнюю версию insert_in_middle в модуль seqtools.py, мы сможем протестировать функцию:
>>> from seqtools import *
>>> my_string = 'abde'
>>> my_list = ['a', 'b', 'd', 'e']
>>> my_tuple = ('a', 'b', 'd', 'e')
>>> insert_in_middle('c', my_string)
'abcde'
>>> insert_in_middle('c', my_list)
['a', 'b', 'c', 'd', 'e']
>>> insert_in_middle('c', my_tuple)
('a', 'b', 'c', 'd', 'e')
>>> my_string
'abde'
Значения my_string, my_list и my_tuple не изменились. Если мы захотим использовать insert_in_middle для их изменения, нам придется присваивать возвращаемое функцией значение обратно переменной:
>>> my_string = insert_in_middle('c', my_string)
>>> my_string
'abcde'
Все типы данных Python, с которыми мы встречались, могут быть сгруппированы внутри списков и кортежей многими способами. Списки и кортежи могут быть вложенными, тем самым предоставляя огромные возможности для организации данных. С целью облегчения работы с данными их организуют в структуры данных.
Предположим, что проводятся выборы и мы помогаем подсчитывать голоса по мере их поступления. Данные о голосовании, поступающие из городских районов, муниципалитетов, краев и областей, иногда имеют форму итоговых сумм всех голосов, а иногда приходят в форме списка промежуточных сумм. Поразмыслив, как лучше хранить подсчитанные голоса, мы остановились на вложенном списке чисел, который определили следующим образом:
Вложенный список чисел - это список, элементы которого относятся к одному из двух типов:
Заметьте, что термин ‘вложенный список чисел’ использован в его собственном определении. Рекурсивные определения, подобные этому, довольно распространены в математике и программировании. Они предоставляют краткий и мощный способ описать рекурсивные структуры данных, частично состоящие из меньших и более простых экземпляров самих себя. Структура не циклична, поскольку рано или поздно мы достигаем списка, который не содержит других списков в качестве элементов.
Теперь предположим, что наша задача - написать функцию, которая просуммирует голоса во вложенном списке чисел. В Python имеется встроенная функция, которая вычисляет сумму последовательности чисел:
>>> sum([1, 2, 8])
11
>>> sum((3, 5, 8.5))
16.5
>>>
Для нашего вложенного списка чисел, однако, функция sum не работает:
>>> sum([1, 2, [11, 13], 8])
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unsupported operand type(s) for +: 'int' and 'list'
>>>
Проблема в том, что третий элемент этого списка, [11, 13], сам является списком, а список не может быть просуммирован с целыми числами 1, 2 и 8.
Для того, чтобы просуммировать все числа в нашем вложенном списке чисел, нам нужно обойти список, обработав каждый элемент его многоуровневой структуры. Числовые элементы будем складывать, а для списочных элементов повторять этот же обход.
Современные языки программирования, в основном, поддерживают рекурсию, что означает, функции могут вызывать себя внутри своего определения. Благодаря рекурсии, код на Python для суммирования всех чисел вложенного списка чисел оказывается на удивление коротким:
def recursive_sum(nested_num_list):
sum = 0
for element in nested_num_list:
if type(element) == type([]):
sum += recursive_sum(element)
else:
sum += element
return sum
Тело функции recursive_sum содержит цикл for, который реализует обход nested_num_list. Если element оказывается числом (ветка else), он прибавляется к sum. Если element - список, то вновь вызывается recursive_sum с этим элементом в качестве аргумента. Предложение внутри определения функции, которое вызывает эту же функцию, называют рекурсивным вызовом функции.
Рекурсия поистине один из самых красивых и элегантных инструментов в программировании.
Несколько более сложной задачей является нахождение самого большого числа в нашем вложенном списке чисел:
def recursive_max(nested_num_list):
"""
>>> recursive_max([2, 9, [1, 13], 8, 6])
13
>>> recursive_max([2, [[100, 7], 90], [1, 13], 8, 6])
100
>>> recursive_max([2, [[13, 7], 90], [1, 100], 8, 6])
100
>>> recursive_max([[[13, 7], 90], 2, [1, 100], 8, 6])
100
"""
largest = nested_num_list[0]
while type(largest) == type([]):
largest = largest[0]
for element in nested_num_list:
if type(element) == type([]):
max_of_elem = recursive_max(element)
if largest < max_of_elem:
largest = max_of_elem
else: # element is not a list
if largest < element:
largest = element
return largest
Приведенные доктесты демонстрируют recursive_max в работе.
Дополнительная хитрость потребовалась для нахождения числового значения для инициализации переменной largest. Мы не можем просто использовать nested_num_list[0], так как это может быть либо число, либо список. Для решения этой задачи мы воспользовались циклом while, в котором присваиваем largest первое из найденных числовых значений, и неважно, насколько глубоко оно вложено.
Два приведенных примера оба имеют базовый случай который не приводит к рекурсивному вызову: случай, когда элемент является числом, а не списком. Без базового случая мы получили бы бесконечную рекурсию, и программа бы не работала. Python бы прекратил выполнение программы, достигнув максимально разрешенной глубины рекурсии, и возвратил ошибку выполнения.
Создайте файл infinite_recursion.py со следующим кодом:
#
# infinite_recursion.py
#
def recursion_depth(number):
print "Recursion depth number %d." % number
recursion_depth(number + 1)
recursion_depth(0)
Перейдя в каталог, где вы сохранили файл, введите в командной строке:
$ python infinite_recursion.py
После того, как промелькнут и остановятся сообщения, вы увидите конец длинной последовательности сообщений об ошибках - распечатки стека вызовов:
...
File "infinite_recursion.py", line 3, in recursion_depth
recursion_depth(number + 1)
RuntimeError: maximum recursion depth exceeded
Мы, конечно же, не хотим, чтобы что-то подобное получил пользователь, запустив одну из наших программ. Поэтому, прежде чем завершить обсуждение рекурсии, давайте посмотрим, как в Python обрабатываются ошибки, подобные этой.
Каждый раз, когда происходит ошибка выполнения, создается исключение (англ.: exception). Программа прекращает выполняться и Python распечатывает стек вызовов, где в конце идет сообщение о возникшем исключении.
Например, деление на ноль создает исключение:
>>> print 55/0
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ZeroDivisionError: integer division or modulo by zero
>>>
Так же, как и попытка прочитать несуществующий элемент списка:
>>> a = []
>>> print a[5]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
IndexError: list index out of range
>>>
Или попытка присвоить значение элементу кортежа:
>>> tup = ('a', 'b', 'd', 'd')
>>> tup[2] = 'c'
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'tuple' object does not support item assignment
>>>
Во всех случаях сообщение об ошибке на последней строке состоит из двух частей: тип ошибки перед двоеточием, и конкретное описание ошибки после него.
Иногда необходимо выполнить операцию, которая может вызвать исключение, но мы не хотим, чтобы программа останавливалась. В этом случае можно организовать обработку исключений, используя предложения try и except.
Например, мы можем попросить пользователя ввести имя файла, и затем попробовать открыть его. Но мы не хотим, чтобы программа сломалась, если файл не существует; мы хотим обработать вероятное исключение:
filename = raw_input('Enter a file name: ')
try:
f = open (filename, "r")
except:
print 'There is no file named', filename
Предложение try выполняет предложения в первом блоке. Если исключения не происходит, предложение except игнорируется. Если же возникает исключение, то выполняются предложения блока except, после чего выполнение программы продолжается.
Можно встроить эту конструкцию в функцию exists (англ.: существует), которая принимает в качестве параметра имя файла и возвращает True, если файл существует, и False, если он не существует:
def exists(filename):
try:
f = open(filename)
f.close()
return True
except:
return False
Можно использовать несколько блоков except для того, чтобы обрабатывать различные исключения (см. урок Errors and Exceptions в учебнике Python Tutorial от создателя Python Гвидо ван Россума).
Если ваша программа обнаруживает ошибочную ситуацию, то она может возбудить (англ.: raise) исключение. Вот пример, в котором принимается ввод от пользователя и проверяется, что введенное число неотрицательно.
#
# learn_exceptions.py
#
def get_age():
age = input('Please enter your age: ')
if age < 0:
raise ValueError, '%s is not a valid age' % age
return age
Предложение raise принимает два аргумента: тип исключения, и конкретное описание ошибки. ValueError является встроенным типом исключения, который лучше всего подходит в данной ситуации. Полный список встроенных исключений можно найти в разделе 2.3 Справочника по библиотеке Python, также написанного создателем Python Гвидо ван Россумом.
Если функция, вызывающая get_age, обрабатывает ошибку, то программа может продолжить выполняться. Иначе Python распечатывает стек вызовов и завершается:
>>> get_age()
Please enter your age: 42
42
>>> get_age()
Please enter your age: -2
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "learn_exceptions.py", line 4, in get_age
raise ValueError, '%s is not a valid age' % age
ValueError: -2 is not a valid age
>>>
Сообщение об ошибке включает тип ошибки и дополнительную информацию, которую вы указали.
Воспользовавшись обработкой исключений, теперь мы можем изменить функцию infinite_recursion.py так, чтобы она останавливалась по достижении максимально допустимой глубины рекурсии:
#
# infinite_recursion.py
#
def recursion_depth(number):
print "Recursion depth number %d." % number
try:
recursion_depth(number + 1)
except:
print "Maximum recursion depth exceeded."
recursion_depth(0)
Запустите эту версию и посмотрите на результат.
Когда рекурсивный вызов является последним предложением в определении функции, такую рекурсию называют хвостовой рекурсией.
Вот версия функции countdown из главы 6, написанная с использованием хвостовой рекурсии:
def countdown(n):
if n == 0:
print "Go!"
else:
print n
countdown(n-1)
Все вычисления, которые делаются с использованием итераций, могут быть сделаны также с использованием рекурсии.
Рекурсивно определяются некоторые из хорошо известных математических функций. Например, факториал, для которого имеется собственный оператор, !, определяется так:
0! = 1
n! = n(n-1)
Можно легко закодировать это на Python:
def factorial(n):
if n == 0:
return 1
else:
return n * factorial(n-1)
Другой пример, хорошо известный из математики — это последовательность Фибоначчи, определяемая так:
fibonacci(0) = 1
fibonacci(1) = 1
fibonacci(n) = fibonacci(n-1) + fibonacci(n-2)
Это тоже можно с легкостью записать на языке Python:
def fibonacci (n):
if n == 0 or n == 1:
return 1
else:
return fibonacci(n-1) + fibonacci(n-2)
Обе функции, и factorial и fibonacci, демонстрируют хвостовую рекурсию.
Однако, хвостовая рекурсия считается плохой практикой в таких языках, как Python, так как она требует больше системных ресурсов, чем эквивалентное решение, использующее итерацию.
Вызов factorial(1000) превысит максимально допустимую глубину рекурсии. Также попробуйте выполнить fibonacci(35) и посмотрите, сколько времени на это понадобится (будьте терпеливы, вы получите результат).
Вам будет предложено написать итеративную версию функции factorial в качестве упражнения. А в следующей главе вы увидите более эффективный способ реализации fibonacci.
Списочное включение (англ.: list comprehension) - это синтаксическая конструкция, которая позволяет создавать списки из других списков, используя компактную математическую нотацию:
>>> numbers = [1, 2, 3, 4]
>>> [x**2 for x in numbers]
[1, 4, 9, 16]
>>> [x**2 for x in numbers if x**2 > 8]
[9, 16]
>>> [(x, x**2, x**3) for x in numbers]
[(1, 1, 1), (2, 4, 8), (3, 9, 27), (4, 16, 64)]
>>> files = ['bin', 'Data', 'Desktop', '.bashrc', '.ssh', '.vimrc']
>>> [name for name in files if name[0] != '.']
['bin', 'Data', 'Desktop']
>>> letters = ['a', 'b', 'c']
>>> [n*letter for n in numbers for letter in letters]
['a', 'b', 'c', 'aa', 'bb', 'cc', 'aaa', 'bbb', 'ccc', 'aaaa', 'bbbb', 'cccc']
>>>
Обобщенный синтаксис для выражения, генерирующего список, таков:
[expr for item1 in seq1 for item2 in seq2 ... for itemx in seqx if condition]
Выражение списочного включения имеет тот же эффект, что и следующий код:
output_sequence = []
for item1 in seq1:
for item2 in seq2:
...
for itemx in seqx:
if condition:
output_sequence.append(expr)
Как видите, код, использующий списковое включение, гораздо компактнее.
Следующий пример частично реализует поведение программы tree, имеющейся в операционных системах Unix.
#!/usr/bin/env python
import os
import sys
def getroot():
if len(sys.argv) == 1:
path = ''
else:
path = sys.argv[1]
if os.path.isabs(path):
tree_root = path
else:
tree_root = os.path.join(os.getcwd(), path)
return tree_root
def getdirlist(path):
dirlist = os.listdir(path)
dirlist = [name for name in dirlist if name[0] != '.']
dirlist.sort()
return dirlist
def traverse(path, prefix='|--', s='.\n', f=0, d=0):
dirlist = getdirlist(path)
for num, file in enumerate(dirlist):
lastprefix = prefix[:-3] + '``--'
dirsize = len(dirlist)
if num < dirsize - 1:
s += '%s %s\n' % (prefix, file)
else:
s += '%s %s\n' % (lastprefix, file)
path2file = os.path.join(path, file)
if os.path.isdir(path2file):
d += 1
if getdirlist(path2file):
s, f, d = traverse(path2file, '| ' + prefix, s, f, d)
else:
f += 1
return s, f, d
if __name__ == '__main__':
root = getroot()
tree_str, files, dirs = traverse(root)
if dirs == 1:
dirstring = 'directory'
else:
dirstring = 'directories'
if files == 1:
filestring = 'file'
else:
filestring = 'files'
print tree_str
print '%d %s, %d %s' % (dirs, dirstring, files, filestring)
Ниже вам будет предложено самостоятельно исследовать эту программу в качестве упражнений.
Выполните эту программу и объясните результаты:
def swap(x, y): # incorrect version
print "before swap statement: id(x):", id(x), "id(y):", id(y)
x, y = y, x
print "after swap statement: id(x):", id(x), "id(y):", id(y)
a, b = 0, 1
print "before swap function call: id(a):", id(a), "id(b):", id(b)
swap(a, b)
print "after swap function call: id(a):", id(a), "id(b):", id(b)
Объясните, почему эта версия swap не работает, как задумано. Какими будут значения a и b после вызова swap?
Создайте модуль с именем seqtools.py. Поместите в него функции encapsulate и insert_in_middle из этой главы. Добавьте доктесты, проверяющие, что эти функции работают правильно со всеми тремя встроенными типами последовательностей.
Добавьте следующие функции в seqtools.py:
def make_empty(seq):
"""
>>> make_empty([1, 2, 3, 4])
[]
>>> make_empty(('a', 'b', 'c'))
()
>>> make_empty("No, not me!")
''
"""
def insert_at_end(val, seq):
"""
>>> insert_at_end(5, [1, 3, 4, 6])
[1, 3, 4, 6, 5]
>>> insert_at_end('x', 'abc')
'abcx'
>>> insert_at_end(5, (1, 3, 4, 6))
(1, 3, 4, 6, 5)
"""
def insert_in_front(val, seq):
"""
>>> insert_in_front(5, [1, 3, 4, 6])
[5, 1, 3, 4, 6]
>>> insert_in_front(5, (1, 3, 4, 6))
(5, 1, 3, 4, 6)
>>> insert_in_front('x', 'abc')
'xabc'
"""
def index_of(val, seq, start=0):
"""
>>> index_of(9, [1, 7, 11, 9, 10])
3
>>> index_of(5, (1, 2, 4, 5, 6, 10, 5, 5))
3
>>> index_of(5, (1, 2, 4, 5, 6, 10, 5, 5), 4)
6
>>> index_of('y', 'happy birthday')
4
>>> index_of('banana', ['apple', 'banana', 'cherry', 'date'])
1
>>> index_of(5, [2, 3, 4])
-1
>>> index_of('b', ['apple', 'banana', 'cherry', 'date'])
-1
"""
def remove_at(index, seq):
"""
>>> remove_at(3, [1, 7, 11, 9, 10])
[1, 7, 11, 10]
>>> remove_at(5, (1, 4, 6, 7, 0, 9, 3, 5))
(1, 4, 6, 7, 0, 3, 5)
>>> remove_at(2, "Yomrktown")
'Yorktown'
"""
def remove_val(val, seq):
"""
>>> remove_val(11, [1, 7, 11, 9, 10])
[1, 7, 9, 10]
>>> remove_val(15, (1, 15, 11, 4, 9))
(1, 11, 4, 9)
>>> remove_val('what', ('who', 'what', 'when', 'where', 'why', 'how'))
('who', 'when', 'where', 'why', 'how')
"""
def remove_all(val, seq):
"""
>>> remove_all(11, [1, 7, 11, 9, 11, 10, 2, 11])
[1, 7, 9, 10, 2]
>>> remove_all('i', 'Mississippi')
'Msssspp'
"""
def count(val, seq):
"""
>>> count(5, (1, 5, 3, 7, 5, 8, 5))
3
>>> count('s', 'Mississippi')
4
>>> count((1, 2), [1, 5, (1, 2), 7, (1, 2), 8, 5])
2
"""
def reverse(seq):
"""
>>> reverse([1, 2, 3, 4, 5])
[5, 4, 3, 2, 1]
>>> reverse(('shoe', 'my', 'buckle', 2, 1))
(1, 2, 'buckle', 'my', 'shoe')
>>> reverse('Python')
'nohtyP'
"""
def sort_sequence(seq):
"""
>>> sort_sequence([3, 4, 6, 7, 8, 2])
[2, 3, 4, 6, 7, 8]
>>> sort_sequence((3, 4, 6, 7, 8, 2))
(2, 3, 4, 6, 7, 8)
>>> sort_sequence("nothappy")
'ahnoppty'
"""
if __name__ == "__main__":
import doctest
doctest.testmod()
Как и прежде, напишите тела функций, проверяя их работу с помощью доктестов по готовности каждой новой функции.
Напишите функцию recursive_min, которая возвращает наименьшее число из вложенного списка чисел:
def recursive_min(nested_num_list):
"""
>>> recursive_min([2, 9, [1, 13], 8, 6])
1
>>> recursive_min([2, [[100, 1], 90], [10, 13], 8, 6])
1
>>> recursive_min([2, [[13, -7], 90], [1, 100], 8, 6])
-7
>>> recursive_min([[[-13, 7], 90], 2, [1, 100], 8, 6])
-13
"""
Ваша функция должна успешно пройти доктесты.
Напишите функцию recursive_count, которая возвращает количество вхождений target в nested_number_list:
def recursive_count(target, nested_num_list):
"""
>>> recursive_count(2, [2, 9, [2, 1, 13, 2], 8, [2, 6]])
4
>>> recursive_count(7, [[9, [7, 1, 13, 2], 8], [7, 6]])
2
>>> recursive_count(15, [[9, [7, 1, 13, 2], 8], [2, 6]])
0
>>> recursive_count(5, [[5, [5, [1, 5], 5], 5], [5, 6]])
6
"""
Ваша функция должна успешно пройти доктесты.
Напишите функцию flatten, которая возвращает (невложенный) список чисел, содержащий все числа из nested_number_list:
def flatten(nested_num_list):
"""
>>> flatten([2, 9, [2, 1, 13, 2], 8, [2, 6]])
[2, 9, 2, 1, 13, 2, 8, 2, 6]
>>> flatten([[9, [7, 1, 13, 2], 8], [7, 6]])
[9, 7, 1, 13, 2, 8, 7, 6]
>>> flatten([[9, [7, 1, 13, 2], 8], [2, 6]])
[9, 7, 1, 13, 2, 8, 2, 6]
>>> flatten([[5, [5, [1, 5], 5], 5], [5, 6]])
[5, 5, 1, 5, 5, 5, 5, 6]
"""
Убедитесь, что ваша функция успешно проходит доктесты.
Напишите функцию readposint, которая просит пользователя ввести положительное целое число, и затем проверяет то, что ввел пользователь. Вот как можно работать с такой функцией:
>>> num = readposint()
Please enter a positive integer: yes
yes is not a positive integer. Try again.
Please enter a positive integer: 3.14
3.14 is not a positive integer. Try again.
Please enter a positive integer: -6
-6 is not a positive integer. Try again.
Please enter a positive integer: 42
>>> num
42
>>> num2 = readposint("Now enter another one: ")
Now enter another one: 31
>>> num2
31
>>>
Используйте обработку исключений при проверке введенного пользователем значения.
Предскажите, что выдаст Python в ответ на следующее:
>>> nums = [1, 2, 3, 4]
>>> [x**3 for x in nums]
>>> nums = [1, 2, 3, 4]
>>> [x**2 for x in nums if x**2 != 4]
>>> nums = [1, 2, 3, 4]
>>> [(x, y) for x in nums for y in nums]
>>> nums = [1, 2, 3, 4]
>>> [(x, y) for x in nums for y in nums if x != y]
Вы должны предвидеть результаты, прежде чем попробуйте выполнить приведенные фрагменты кода в интерактивном режиме.
Воспользовавшись pydoc или онлайновой документацией на сайте http://docs.python.org/, выясните, что делают функции sys.getrecursionlimit() и sys.setrecursionlimit(n). Проведите несколько экспериментов, подобных тому, который мы поставили с помощью infinite_recursion.py, чтобы убедиться в том, как работают эти функции.
Перепишите функцию factorial, используя итерацию вместо рекурсии. Вызовите вашу новую функцию с аргументом 1000 и заметьте, как быстро она отработает.
Напишите программу litter.py, которая создает пустой файл с именем trash.txt в каждом каталоге дерева каталогов. Корень дерева каталогов программа получает в качестве аргумента (или использует текущий каталог по умолчанию). После этого напишите программу cleanup.py, которая удаляет все эти файлы. Подсказка: используйте программу tree из учебного примера в качестве основы для разработки этих двух рекурсивных программ.