ЛЕКЦИЯ 6
.pdf
2.5. Инструкция global.
Инструкция global и родственная ей инструкция nonlocal – единственные инструкции в языке Python, отдаленно напоминающие инструкции объявления. Однако они не объявляют тип или размер – они объявляют пространства имен.
●Глобальные имена – это имена, которые определены на верхнем уровне вмещающего модуля.
●Глобальные имена должны объявляться, только если им будут присваиваться значения внутри функций.
●Обращаться к глобальным именам внутри функций можно и без объявления их глобальными.
X = 88 # Глобальная переменная X def func():
global X # Глобальная переменная X: за пределами инструкции def X = 99
func()
print(X) # Выведет 99
–------------------------------------------------------------ y, z = 1, 2
def all_global(): global x
x = y + z print(x) # Выведет 3
●Минимизируйте количество глобальных переменных.
X = 99
def func1(): global X X = 88
def func2(): global X
X = 77
Чтобы понять этот программный код, необходимо знать путь потока выполнения всей программы.
●Минимизируйте количество изменений в соседних файлах.
# first.py
X = 99 # Этот программный код не знает о существовании second.py
# second.py import first
print(first.X) # Нет ничего плохого в том, чтобы обратиться
# к имени в другом файле
first.X = 88 # Но изменение может привести к сложностям Лучше добавить функцию доступа: # first.py
X = 99
def setX(new): global X X = new
# second.py
import first first.setX(88)
2.6.Области видимости и вложенные функции.
●При обращении к переменной (X) поиск имени X сначала производится в локальной области видимости (функции); затем в локальных областях видимости всех лексически объемлющих функций, изнутри наружу; затем в текущей глобальной области видимости (в модуле); и, наконец, во встроенной области видимости (модуль builtins).
●Поиск имен, объявленных в инструкции global, начинается сразу с глобальной (в модуле) области видимости.
●Операция присваивания (X = value) по умолчанию создает или изменяет имя X в текущей локальной области видимости.
●Если имя X объявлено глобальным внутри функции, операция присваивания создает или изменяет имя X в области видимости объемлющего модуля.
●Если имя X объявлено нелокальным внутри функции, операция присваивания создает или изменяет имя X в ближайшей области видимости объемлющей функции.
X = 99 # Имя в глобальной области: не используется def f1():
X = 88 # Локальное имя в объемлющей функции def f2():
print(X) # Обращение ̆во вложенной функции f2()
f1() # Выведет 88: локальная переменная в объемлющей функции
def f1(): X = 88
def f2():
print(X) # Сохраняет X в объемлющей области return f2 # Возвращает f2, но не вызывает ее
action = f1() # Создает и возвращает функцию action() # Вызов этой функции: выведет 88
2.7. Инструкция nonlocal.
Эта инструкция позволяет вложенным функциям изменять переменные, которые определены в областях видимости
синтаксически объемлющих функций. |
|
def tester(start): |
|
state = start # Обращение к нелокальным переменным |
>>> F = tester(0) |
def nested(label): # действует как обычно |
>>> F(‘spam’) |
print(label, state) # Извлекает значение state из области |
spam 0 |
return nested # видимости объемлющей функции |
>>> F(‘ham’) |
|
Ham 0 |
Если переменную state, локальную для функции tester, объявить в функции nested с помощью инструкции nonlocal, мы сможем изменять ее внутри функции nested:
def tester(start):
state = start # В каждом вызове сохраняется свое значение state def nested(label):
nonlocal state # Объект state находится
print(label, state) # в объемлющей области видимости
state += 1 # Изменит значение переменной, объявленной как nonlocal return nested
>>>F = tester(0)
>>>F(‘spam’) # Будет увеличивать значение state при каждом вызове spam 0
>>> F(‘ham’) |
>>> F(‘eggs’) |
ham 1 |
eggs 2 |
3.Аргументы.
●Аргументы передаются через автоматическое присваивание объектов локальным переменным.
Аргументы функции – ссылки на объекты. Сами объекты никогда не копируются автоматически.
●Операция присваивания именам аргументов внутри функции не оказывает влияния на вызывающую программу. При вызове функции имена аргументов, указанные в ее заголовке, становятся новыми локальными именами в области видимости функции.
●Неизменяемые объекты передаются «по значению». Такие объекты, как целые числа и строки, передаются в виде ссылок на объекты, но так как неизменяемые объекты невозможно изменить непосредственно, передача таких объектов очень напоминает копирование.
●Изменяемые объекты передаются «по указателю». Такие объекты, как списки и словари, также передаются в виде ссылок на объекты, что очень похоже на то, как в языке C передаются указатели на массивы, – изменяемые объекты допускают возможность непосредственного изменения внутри функции так же, как и массивы в языке C. Изменяемые объекты могут рассматриваться функциями как средство ввода, так и вывода информации.
def changer(a, b): # В аргументах передаются ссылки на объекты a= 2 # Изменяется только значение локального имени
b[0] = ‘spam’ # Изменяется непосредственно разделяемый объект
>>X = 1
>>L= [1,2] # Вызывающая программа
>>changer(X, L) # Передаются изменяемый и неизменяемый объекты
>>X,L # Переменная X – не изменилась, L — изменилась
(1, [‘spam’, 2])
4.Концепции проектирования функций.
●Взаимодействие: для передачи значений функции используйте аргументы, для возврата результатов – инструкцию return. Всегда следует стремиться сделать функцию максимально независимой от того, что происходит
за ее пределами. Аргументы и инструкция return часто являются лучшими способами ограничить внешнее воздействие небольшим числом известных мест в программном коде.
●Взаимодействие: используйте глобальные переменные, только если это действительно необходимо.
Глобальные переменные (то есть имена в объемлющем модуле) обычно далеко не самый лучший способ организации взаимодействий с функциями. Они могут порождать зависимости и проблемы согласованности, которые существенно осложняют отладку программ.
●Взаимодействие: не воздействуйте на изменяемые аргументы, если вызывающая программа не предполагает этого. Функции могут оказывать воздействие на части изменяемых объектов - аргументов, но, как и
вслучае с глобальными переменными, это предполагает слишком тесную связь между вызывающей программой и вызываемой функцией, что может сделать функцию слишком специфичной и неустойчивой.
●Связность: каждая функция должна иметь единственное назначение. Хорошо спроектированная функция должна решать одну задачу, которую можно выразить в одном повествовательном предложении. Если это предложение допускает слишком широкое толкование (например: «эта функция реализует всю программу целиком») или содержит союзы (например: «эта функция дает возможность клиентам составлять и отправлять заказ
на доставку пиццы»), то стоит подумать над тем, чтобы разбить ее на отдельные и более простые функции. В противном случае окажется невозможным повторно использовать программный код функции, в котором смешаны различные действия.
●Размер: каждая функция должна иметь относительно небольшой размер. Это условие естественным образом следует из предыдущего, однако если функция начинает занимать несколько экранов – это явный признак, что пора подумать о том, чтобы разбить ее. Особенно, если учесть краткость, присущую языку Python.
4.Концепции проектирования функций.
●Взаимодействие: избегайте непосредственного изменения переменных в другом модуле. Непосредственное
изменение переменных в других модулях устанавливает тесную зависимость между модулями, так же как тесную зависимость устанавливает изменение глобальных переменных из функций – модули становятся сложными в
понимании и малопригодными для многократного использования. Всегда, когда это возможно, для изменения переменных модуля вместо прямых инструкций присваивания используйте функции доступа.
5.Рекурсивные функциии.
●Рекурсивные функции – функции, которые могут вызывать сами себя, прямо или косвенно, образуя цикл.
●Это полезный прием, позволяющий реализовывать обход структур данных с произвольной и неизвестной заранее организацией.
●Рекурсия является альтернативой простым циклам и итерациям, хотя и не обязательно более простой или более производительной.
def mysum(L):
print(L) # Поможет отслеживать уровни рекурсии
if not L: # На каждом уровне список L будет получаться короче return 0
else:
return L[0] + mysum(L[1:]) # Вызывает себя саму
>>mysum([1, 2, 3, 4, 5])
[1,2, 3, 4, 5] [2, 3, 4, 5] [3, 4, 5] [4, 5] [5] [ ] 15
5. Рекурсивные функциии.
def mysum(L):
return 0 if not L else L[0] + mysum(L[1:]) # Трехместный оператор
def mysum(L): # Суммирует любые типы
return L[0] if len(L) == 1 else L[0] + mysum(L[1:]) # предполагает наличие хотя бы одного значения
def mysum(L): |
# Использует расширенную |
first, *rest = L |
# операцию присваивания последовательностеи ̆ |
return first if not rest else first + mysum(rest) |
|
>>>mysum([1]) # mysum([]) будет завершаться ошибкой в 2 последних функциях 1
>>>mysum([1, 2, 3, 4, 5])
15
>>>mysum((‘s’, ‘p’, ‘a’, ‘m’)) # Но они могут суммировать данные любых типов ‘spam’
>>>mysum([‘spam’, ‘ham’, ‘eggs’])
‘spamhameggs’
5.1. Применение рекурсии.
Рассмотрим задачу вычисления суммы всех чисел в структуре, состоящей из вложенных списков:
[1, [2, [3, 4], 5], 6, [7, 8]] # Произвольно вложенные списки
Простые инструкции циклов в этом случае не годятся, потому что выполнить обход такой структуры с помощью линейных итераций не удастся. Вложенные инструкции циклов также не могут использоваться, потому что списки могут иметь произвольные уровни вложенности и иметь произвольную организацию. Выполним обход вложенных списков с помощью рекурсии:
def sumtree(L): tot = 0
for x in L: # Обход элементов одного уровня if not isinstance(x, list):
tot += x # Числа суммируются непосредственно else:
tot += sumtree(x) # Списки обрабатываются рекурсивными вызовами return tot
L = [1, [2, [3, 4], 5], 6, [7, 8]] # Произвольная глубина вложения print(sumtree(L)) # Выведет 36
# Патологические случаи
print(sumtree([1, [2, [3, [4, [5]]]]])) # Выведет 15 (центр тяжести справа) print(sumtree([[[[[1], 2], 3], 4], 5])) # Выведет 15 (центр тяжести слева)
