Еще о строках и списках
Вы уже знакомы с синтаксисом [] для доступа к части строки:
my_string = "exemplary"
print(my_string[3:7])mpla
Тот же синтаксис работает со списками. Списки можно нарезать срезами так же, как строки:
my_list = [3,4,2,4,6,1,2,4,2]
print(my_list[3:7])[4, 6, 1, 2]
Больше срезов
На самом деле синтаксис [] работает очень похоже на функцию range, а значит, мы такж е можем задать ему шаг:
my_string = "exemplary"
print(my_string[0:7:2])
my_list = [1,2,3,4,5,6,7,8]
print(my_list[6:2:-1])eepa [7, 6, 5, 4]
Если мы пропуска ем любой из индексов, оператор по умолчанию включает все. Среди прочего, это позволяет написать очень короткую программу для разворота строки:
my_string = input("Please type in a string: ")
print(my_string[::-1])Please type in a string: exemplary yralpmexe
Предупреждение: использование глобальных переменных внутри функций
Мы знаем, что внутри определений функций можно присваивать новые переменные, но функция также может видеть переменные, присвоенные вне нее, в главной функции. Такие переменные называются глобальными переменными.
Использовать глобальные переменные изнутри функций обычно плохая идея. Среди прочих проблем это может вызвать ошибки, которые трудно отследить.
Ниже пример функции, которая использует глобальную переменную "по ошибке":
def print_reversed(names: list):
# using the global variable instead of the parameter by accident
i = len(name_list) - 1
while i >= 0:
print(name_list[i])
i -= 1
# here the global variable is assigned
name_list = ["Steve", "Jean", "Katherine", "Paul"]
print_reversed(name_list)
print()
print_reversed(["Huey", "Dewey", "Louie"])Paul Katherine Jean Steve
Paul Katherine Jean Steve
Несмотря на то что оба вызова функции имеют аргумент правильного типа, функция всегда выводит то, что сохранено в глобальной переменной name_list.
Чтобы еще больше все запутать, помните, что весь код для тестирования ваших функций должен быть помещен внутрь блока if __name__ == "__main__": для автоматических тестов. Предыдущий пример следует изменить:
def print_reversed(names: list):
# using the global variable instead of the parameter by accident
i = len(name_list) - 1
while i>=0:
print(name_list[i])
i -= 1
# All the code for testing the function should be within this block
if __name__ == "__main__":
# here the global variable is assigned
name_list = ["Steve", "Jean", "Katherine", "Paul"]
print_reversed(name_list)
print()
print_reversed(["Huey", "Dewey", "Louie"])Обратите внимание, что теперь глобальная переменная присваивается внутри блока if.
Автоматические тесты в системе TMC выполняются без запуска какого-либо кода из блока if. Поэтому в этом последнем примере функция даже теоретически не сработала бы, поскольку она обращается к переменной name_list, которой вообще не существует при выполнении тестов.
Строки неизменяемы
У строк и списков много общего, особенно в том, как они ведут себя с разными операторами. Главное отличие в том, что строки неизменяемы. Это значит, что их нельзя изменить.
my_string = "exemplary"
my_string[0] = "a"Строки нельзя менять, поэтому выполнение этой программы вызывает ошибку:
TypeError: 'str' object does not support item assignment
Похожая ошибка возникнет, если попытаться отсортировать строку методом sort.
Сами строки неизменяемы, но переменные, которые их хранят, нет. Строку можно заменить другой строкой.
Поэтому следующие два примера фундаментально отличаются:
my_list = [1,2,3]
my_list[0] = 10
my_string = "Hey"
my_string = my_string + "!"
Первый пример изменяет содержимое списка, на который есть ссылка. Второй пример заменяет ссылку на исходную строку ссылкой на другую строку. Исходная строка все еще где-то в памяти компьютера, но ссылки на нее нет, и ее больше нельзя использовать в программе.
Мы вернемся к этой теме в следующей части, где ссылки на списки будут рассмотрены подробнее.
Больше методов для списков и строк
Метод count подсчитывает, сколько раз указанный элемент или подстрока встречается в цели. Метод работает похожим образом и со строками, и со списками:
my_string = "How much wood would a woodchuck chuck if a woodchuck could chuck wood"
print(my_string.count("ch"))
my_list = [1,2,3,1,4,5,1,6]
print(my_list.count(1))5 3
Метод не считает перекрывающиеся появления. Например, в строке aaaa метод считает только два появления подстроки aa, хотя на самом деле их было бы три, если бы перекрывающиеся появления были разрешены.
Метод replace создает новую строку, где указанная подстрока заменяется другой строкой:
my_string = "Hi there"
new_string = my_string.replace("Hi", "Hey")
print(new_string)Hey there
Метод заменит все появления подстроки:
sentence = "sheila sells seashells on the seashore"
print(sentence.replace("she", "SHE"))SHEila sells seaSHElls on the seashore
При использовании метода replace типичная ошибка - забыть, что строки неизменяемы:
my_string = "Python is fun"
# Replaces the substring but doesn't store the result...
my_string.replace("Python", "Java")
print(my_string)Python is fun
Если старая строка больше не нужна, новую строку можно присвоить той же переменной:
my_string = "Python is fun"
# Replaces the substring and stores the result in the same variable
my_string = my_string.replace("Python", "Java")
print(my_string)Java is fun
Разработка более крупного программного проекта
Эта четвертая часть завершается немного более крупным программным проектом, где вы сможете применить многие из изученных до сих пор техник.
Правило N 1 при работе над любым программным проектом - не пытаться решить все сразу. Программа должна строиться из меньших частей, например вспомогательных функций. Нужно проверять работу каждой части, прежде чем переходить к следующей. Если пытаться охватить слишком многое сразу, скорее всего, получится только хаос.
Для этого вам понадобится способ тестировать функции вне главной функции. Этого можно добиться, явно определив главную функцию и вызвав ее вне любой другой функции программы. Тогда один вызов функции легко закомментировать для тестирования. Первые шаги построения следующего программного проекта могли бы выглядеть так:
def main():
points = []
# your program code goes here
main()Теперь вспомогательные функции можно тестировать без запуска главной функции:
# helper function for determining the grade based on the amount of points
def grade(points):
# more code
def main():
all_points = []
# your program code goes here
# comment out the main function
#main()
# test the helper function
student_points = 35
result = grade(student_points)
print(result)Передача данных из одной функции в другую
Когда программа содержит несколько функций, возникает вопрос: как передавать данные из одной функции в другую?
Следующий пример спрашивает у пользователя несколько целочисленных значений. Затем программа выводит эти значения и выполняет их "анализ". Программа разделена на три отдельные функции:
def input_from_user(how_many: int):
print(f"Please type in {how_many} numbers:")
numbers = []
for i in range(how_many):
number = int(input(f"Number {i+1}: "))
numbers.append(number)
return numbers
def print_result(numbers: list):
print("The numbers are: ")
for number in numbers:
print(number)
def analyze(numbers: list):
mean = sum(numbers) / len(numbers)
return f"There are altogether {len(numbers)} numbers, the mean is {mean}, the smallest is {min(numbers)} and the greatest is {max(numbers)}"
# the "main function" using these functions
inputs = input_from_user(5)
print_result(inputs)
analysis_result = analyze(inputs)
print(analysis_result)При выполнении программа могла бы работать так:
Please type in 5 numbers: Number 1: 10 Number 2: 34 Number 3: -32 Number 4: 99 Number 5: -53 The numbers are: 10 34 -32 99 -53 There are altogether 5 numbers, the mean is 11.6, the smallest is -53 and the greatest is 99
Идея здесь в том, что главная функция "сохраняет" все данные, обработанные программой. В этом случае все, что нужно, - это ввод пользователя в переменной inputs.
Если ввод нужен в функции, он передается как аргумент. Это происходит с функциями print_result и analyze. Если функция создает данные, которые нужны где-то еще в программе, функция возвращает их с помощью команды return, и они сохраняются в переменной в главной функции. Это происходит с функциями input_from_user и analyze.
Можно было бы использовать гло бальную переменную inputs из главной функции напрямую во вспомогательных функциях. Мы уже разбирали, почему это плохая идея, но вот еще одно объяснение. Если функции могут менять глобальную переменную, в программе могут начать происходить неожиданные вещи, особенно когда количество функций становится большим.
Передачу данных в функции и из функций лучше всего обрабатывать с помощью аргументов и возвращаемых значений.
Также можно выделить неявную главную функцию из примера выше в собственную функцию. Тогда переменная inputs больше не была бы глобальной переменной, а стала бы локальной переменной внутри функции main:
# your main function goes here
def main():
inputs = input_from_user(5)
print_result(inputs)
analysis_result = analyze(inputs)
print(analysis_result)
# run the main function
main()Ответьте, пожалуйста, на короткий опрос по материалам этой недели.
Текущие баллы можно посмотреть в синем индикаторе в правом нижнем углу страницы.