Как вызвать функцию, находящуюся в другой функции?
Какое то время, у меня была проблема, YouTube Guides оп питону не помогали. Суть вопроса: Как вызвать функцию, находящуюся в другой функции? Добавлю код для более простого понимания:
def function1(): #Первая функция print("Hello World!") def function2(): #Функция которую нужно вызвать из первой функции. print("World is answer: Hello!")
Я задал этот вопрос, не просто так. Я заметил, что ни на форумах, не на GitHab-е, даже здесь, на ruSO, нету ясного, и понятного ответа, как так сделать. Я много чего пробовал, пытаясь даже костылями вызвать(Код кривой, в силу что был написан очень давно, когда только познал базовые знания питона):
def func1(): prit("Hello world!") def func2(): print("Call func from funck is successfully!") func(fucn2())
Вот неясно, как и что, ведь выдаёт ошибку:
D:\Terminalius\TermNet\$cash>func2.py Traceback (most recent call last): File "D:\Terminalius\TermNet\$cash\func2.py", line 6, in func(fucn2()) NameError: name 'func' is not defined

Что бы вы не подумали что код неправильный, вот скриншот из редактора:
Как передать функцию как параметр в Python?
Поскольку функция в Python является объектом, ее можно передавать в другую функцию в качестве аргумента. При этом функция в аргументе указывается без круглых скобок, то есть передается только ссылка на функцию.
def calculate(a, b, operation): result = operation(a, b) return result def sum(a, b): return a + b def multiply(a, b) return a * b calculate(2, 3, sum) # 5 calculate(3, 4, multiply) # 12
Функции, которые могут принимать другие функции в качестве аргументов, также называются функциями высшего порядка. Яркий пример функции высшего порядка — функция map() , которая принимает аргументом функцию и итерируемую последовательность, и применяет функцию-параметр к каждому элементу последовательности.
Функции в Python для начинающих
В этой части мы изучим функции — составные инструкции, которые могут принимать данные ввода, выполнять указания и возвращать данные вывода. Функции позволяют определять и повторно использовать определенную функциональность в компактной форме.
Вызвать функцию — значит передать ей входные данные, необходимые для выполнения и возвращения результата. Когда вы передаете функции входные данные, это называется передача параметра функции.
Функции в Python похожи на математические функции из алгебры. Например, в алгебре функция определяется как-то так:
f(x) = x * 2
Левая часть определяет функцию f , принимающую один параметр, x . А правая часть — это определение функции, которое использует переданный параметр x , чтобы произвести вычисление и вернуть результат. В этом случае значением функции является ее параметр, умноженный на два.
Как в Python функция записывается следующим образом: имя_функции(параметры_через_запятую) . Чтобы вызвать функцию, после ее имени нужно указать круглые скобки и поместить внутрь параметры, отделив каждый из них запятой. Для создания функций в Python выберите ее имя, определите параметры, укажите, что функция должна делать и какое значение возвращать.
def имя_функции(параметры): определениие_функции
Математическая функция f(x) = x * 2 в Python будет выглядеть вот так:
def f(x): return x * 2
Ключевое слово def сообщает Python, что вы определяете функцию. После def вы указываете имя функции; оно должно отвечать тем же правилам, что и имена переменных. Согласно конвенции, в имени функции нельзя использовать заглавные буквы, а слова должны быть разделены подчеркиванием вот_так .
Как только вы присвоили своей функции имя, укажите после него круглые скобки. Внутри скобок должен содержаться один или несколько параметров.
После скобок ставится двоеточие, а новая строка начинается с отступа в четыре пробела. Любой код с отступом в четыре пробела после двоеточия является телом функции. В этом случае тело нашей функции состоит только из одной строки:
return x * 2
Ключевое слово return используется для определения значения, которое функция возвращает при вызове.
Чтобы вызвать функцию в Python, мы используем синтаксис имя_функции(параметры, через, запятую) .
Ниже описан вызов функции f из предыдущего примера с параметром 2 .
Консоль ничего не вывела. Можно сохранить вывод вашей функции в переменной и передать ее функции print .
# Продолжение# предыдущего примераdef f(x): return x * 2result = f(2)print(result) # 4
Вы можете сохранить результат, возвращаемый вашей функцией, в переменной и использовать это значение в программе позднее.
def f(x): return x + 1 z = f(4)if z == 5: print("z равно 5")else: print ("z не равно 5")
У функции может быть один параметр, несколько параметров или вообще их не быть. Чтобы определить функцию, не требующую параметров, оставьте круглые скобки пустыми.
def f(): return 1 + 1 result = f()print(result) # 2
Если хотите, чтобы функция принимала больше одного параметра, отделите каждый параметр в скобках запятой.
def f(x, y, z): return x + y + z result = f(1, 2, 3)print(result) # 6
Наконец, функция не обязана содержать инструкцию return. Если функции нечего возвращать, она возвращает значение None .
def f(): z = 1 + 1result = f()print(result) # None
Обязательные и необязательные параметры ¶
Функция может принимать параметры двух типов. Те, что встречались вам до этого, называются обязательными параметрами. Когда пользователь вызывает функцию, он должен передать в нее все обязательные параметры, иначе Python сгенерирует исключение.
В Python есть и другой вид параметров — опциональные. Опциональные параметры определяются с помощью следующего синтаксиса: имя_функции(имя_параметра = значение_параметра) . Как и обязательные, опциональные параметры нужно отделять запятыми. Ниже приведен пример функции, в коде которой используется опциональный параметр.
def f(x=2): return x**x print (f()) # 4print (f(4)) # 16
Сначала функция вызывается без передачи параметра. Так как параметр необязательный, x автоматически становится равен 2 , и функция возвращает 4 .
Затем та же функция вызывается с параметром 4 . То есть x будет равен 4 и функция вернет 16 . Вы можете определить функцию, которая принимает как обязательные, так и опциональные параметры, но обязательные нужно определять в первую очередь.
def add(x, y=10): return x + yresult = add(2)print(result)
Python с нуля. Освойте с Виртуальным ИИ-помощником!
Основы применения вложенных функций в Python
Распространенный вариант использования внутренних функций – когда нужно защитить или скрыть функцию от всего, что происходит за ее пределами, то есть полностью скрыть функцию из глобальной области видимости. Такое поведение обычно называют инкапсуляцией.
Начнем с поясняющего примера:
def increment(number): def inner_increment(): return number + 1 return inner_increment()
>>> print(increment(10)) 11 # Вызовем вложенную функцию inner_increment() >>> print(inner_increment()) NameError: name 'inner_increment' is not defined
В этом примере у нас нет прямого доступа к inner_increment() . Попытавшись обратиться к вложенной функции, мы получаем NameError . Функция increment() полностью скрывает функцию inner_increment() , предотвращая доступ из глобальной области.
Создание внутренних вспомогательных функций
Иногда нам нужна функция, выполняющая один и тот же фрагмент кода в нескольких местах своего тела. Например, мы хотим написать функцию для обработки файла CSV, содержащего информацию о точках доступа Wi-Fi в Нью-Йорке. Чтобы узнать общее количество точек доступа, а также информацию о компании, которая их предоставляет, мы создали следующий скрипт:
import csv from collections import Counter def process_hotspots(file): def most_common_provider(file_obj): hotspots = [] with file_obj as csv_file: content = csv.DictReader(csv_file) for row in content: hotspots.append(row["Provider"]) counter = Counter(hotspots) print( f"В Нью-Йорке точек Wi-Fi.\n" f" из них предоставляет" f"." ) if isinstance(file, str): # Получаем путь к файлу file_obj = open(file, "r") most_common_provider(file_obj) else: # Забираем объект файла most_common_provider(file)
Здесь process_hotspots() принимает аргумент file и проверяет, является ли файл строковым путем к физическому файлу или файловым объектом. Затем функция вызывает вспомогательную внутреннюю функцию most_common_provider() , которая принимает файловый объект и выполняет следующие операции:
- Считывает содержимое файла в генератор, который создает словари с помощью csv.DictReader .
- Составляет список провайдеров Wi-Fi.
- Подсчитывает количество точек доступа Wi-Fi для каждого поставщика с помощью объекта collections.Counter .
- Печатает сообщение с полученной информацией.
Запустив функцию, мы получим следующий результат:
>>> from hotspots import process_hotspots >>> file_obj = open("./NYC_Wi-Fi_Hotspot_Locations.csv", "r") >>> process_hotspots(file_obj) В Нью-Йорке 3319 точек Wi-Fi. 1868 из них предоставляет LinkNYC - Citybridge. >>> process_hotspots("./NYC_Wi-Fi_Hotspot_Locations.csv") В Нью-Йорке 3319 точек Wi-Fi. 1868 из них предоставляет LinkNYC - Citybridge.
Независимо от того, вызываем ли мы process_hotspots() со строковым путем к файлу или с файловым объектом, вы получите один и тот же результат.
Использование внутренних и приватных вспомогательных функций
Обычно мы создаем вспомогательные внутренние функции, такие как most_common_provider() , когда хотим обеспечить инкапсуляцию либо если не собираемся вызывать их где-либо еще, кроме включающей функции.
Хотя написание внутренних вспомогательных функций дает желаемый результат, обычно лучше вынести их, как функции верхнего уровня. В этом случае можно использовать префикс из знака подчеркивания (_) в имени функции, чтобы указать, что она является приватной для текущего модуля или класса. Это позволит получить доступ к вспомогательным функциям из любого места в текущем модуле или классе и повторно использовать их по мере необходимости.
Извлечение внутренних функций в приватные функции верхнего уровня может сделать код чище и читаемее. Такая практика соответствует принципу единственной ответственности.
Сохранение состояния с помощью вложенных функций: замыкания в Python
Функции Python в своих правах равны любым другим объектам, таким как числа, строки, списки, кортежи, модули и т. д. То есть их можно динамически создавать или уничтожать, сохранять в структурах данных, передавать в качестве аргументов другим функциям, использовать как возвращаемые значения.
В Python также можно создавать функции высшего порядка, которые принимают и возвращают другие функции.
Примеры внутренних функций, которые мы видели до сих пор, были обычными функциями, которые волей случая оказались вложенными внутрь других функций. Если нам не нужно скрывать эти функции от внешнего мира, то и нет особых причин для вложения.
В этом разделе мы поговорим о другом роде вложенных функций – замыканиях. Это динамически создаваемые функции, возвращаемые другими функциями. Главная особенность состоит в том, что замыкания имеют полный доступ к переменным и именам, определенным в локальном пространстве имен, в котором было создано замыкание, даже если включающая функция завершила свое выполнение.
Чтобы определить замыкание, нужно выполнить три шага:
- Создать вложенную функцию.
- Сослаться на переменные из включающей функции.
- Вернуть вложенную функцию.
Сейчас разберемся на примерах.
Сохранение состояния в замыкании
Итак, замыкание заставляет вложенную функцию при вызове сохранять состояние своего окружения. То есть замыкание – это не только сама внутренняя функция, но и окружающая среда.
Рассмотрим следующий пример:
def generate_power(exponent): def power(base): return base ** exponent return power
Здесь мы определяем функцию generate_power() , которая представляет собой фабрику для создания замыканий. То есть эта функция при каждом вызове создает и возвращает новую функцию-замыкание. В следующей строке определяется функция power() , которая является внутренней функцией, и принимает единственный аргумент base и возвращает результат выражения base ** exponent . Последняя строка возвращает power как функциональный объект, не вызывая его.
Откуда power() получает значение показателя степени exponent ? Вот где в игру вступает замыкание. В этом примере power() получает значение экспоненты из внешней функции generate_power() . Вот что делает Python, когда мы вызываем generate_power() :
- Определяет новый экземпляр power() , который принимает аргумент base .
- Делает «снимок» окружения power() . Он включает exponent с текущим значением.
- Возвращает power() вместе с состоянием.
Таким образом, когда мы вызываем экземпляр power() , возвращаемый функцией generate_power() , мы видим, что функция запоминает значение степени exponent :
>>> raise_two = generate_power(2) >>> raise_three = generate_power(3) >>> raise_two(4) 16 >>> raise_two(5) 25 >>> raise_three(4) 64 >>> raise_three(5) 125
Обратите внимание, что оба замыкания запоминают соответствующий показатель степени между вызовами. В этих примерах raise_two() запоминает, что exponent = 2 , а raise_three() запоминает, что exponent = 3 .
Рассмотрим другой пример:
def has_permission(page): def permission(username): if username.lower() == "admin": return f"'' имеет доступ к ." else: return f"'' не имеет доступа к ." return permission check_admin_page_permision = has_permission("Admin Page")
>>> print(check_admin_page_permision("admin")) 'admin' имеет доступ к Admin Page. >>> print(check_admin_page_permision("john")) 'john' не имеет доступа к Admin Page.
Вложенная функция проверяет, имеет ли данный пользователь необходимы права доступа к странице. Вместо того чтобы проверять, равен ли пользователь ‘admin’ , можно сделать запрос к базе данных.
Обычно замыкания не изменяют состояние, которое они получили «при рождении», как было показано в приведенных выше примерах. Но можно создавать и динамические замыкания, используя изменяемые объекты – словари, множества или списки.
Предположим, нужно вычислить среднее значение для набора данных. Данные поступают в виде потока последовательных измерений анализируемого параметра, и нужно, чтобы функция сохраняла между вызовами предшествующие измерения. В этом случае можно код фабрики для создания замыканий может выглядеть следующим образом:
def mean(): sample = [] def inner_mean(number): sample.append(number) return sum(sample) / len(sample) return inner_mean sample_mean = mean()
>>> sample_mean(100) 100.0 >>> sample_mean(105) 102.5 >>> sample_mean(101) 102.0 >>> sample_mean(98) 101.0
Замыкание, присвоенное sample_mean , сохраняет состояние выборки между вызовами. Хотя мы определяем список sample внутри mean() , он также доступен и в замыкании.
Изменение состояния замыкания
Обычно переменные замыкания полностью скрыты от внешнего мира. Однако мы можем задать для них функции геттера и сеттера:
def make_point(x, y): def point(): print(f"Point(, )") def get_x(): return x def get_y(): return y def set_x(value): nonlocal x x = value def set_y(value): nonlocal y y = value # Добавляем геттеры и сеттеры point.get_x = get_x point.set_x = set_x point.get_y = get_y point.set_y = set_y return point point = make_point(1, 2)
>>> point.get_x() 1 >>> point.get_y() 2 >>> point() Point(1, 2) >>> point.set_x(42) >>> point.set_y(7) >>> point() Point(42, 7)
Здесь make_point() возвращает замыкание, представляющее объект point . К этому объекту прикреплены функции, которые мы можем использовать для получения доступа к чтению и записи переменных x и y .
Такая фабрика может работать даже быстрее, чем эквивалентный класс, но подход не предоставляет наследование, дескрипторы и прочие возможности классов Python.
Изменение поведения с помощью вложенных функций: декораторы
Декораторы Python – еще один популярный и удобный вариант использования внутренних функций, особенно для замыканий. Декораторы – это функции высшего порядка, которые принимают в качестве аргумента вызываемый объект (функцию, метод, класс) и возвращают другой вызываемый объект.
Примечание
Если концепция декораторов вызывает у вас трудности, обратите внимание на нашу публикацию Всё, что нужно знать о декораторах Python.
Обычно декораторы применяются для динамического добавления свойств к существующему вызываемому объекту и прозрачного расширения его поведения, не затрагивая и не изменяя вызываемого объекта. Функцию-декоратор можно применить к любому вызываемому объекту. Для этого в предваряющей его строке ставится символ @ и имя декоратора:
@decorator def decorated_func(): # Function body. pass
Этот синтаксис заставляет decorator() автоматически принимать decorator_func() в качестве аргумента и обрабатывать его в своем теле. Эта операция является сокращением для инструкции следующего вида:
decorated_func = decorator(decorated_func)
Вот пример того, как можно создать функцию-декоратор для изменения поведения уже существующей функции:
def add_messages(func): def _add_messages(): print("Это мой первый декоратор.") func() print("Пока!") return _add_messages @add_messages def greet(): print("Привет, мир!") greet()
Это мой первый декоратор. Привет, мир! Пока!
В этом примере мы используем @add_messages для декорирования функции greet() . В результате функция приобретает новые функциональные возможности. Теперь, когда мы вызываем greet() , вместо того, чтобы просто напечатать Привет, мир! , она выводит два дополнительных сообщения.
Простейшей практикой отладки кода на Python является вставка вызовов print() для проверки значений переменных. Однако, добавляя и удаляя вызовы print() , мы рискуем забыть о некоторых из них. Чтобы предотвратить эту ситуацию, мы можем написать следующий декоратор:
def debug(func): def _debug(*args, **kwargs): result = func(*args, **kwargs) print( f"(args: , kwargs: ) -> " ) return result return _debug @debug def add(a, b): return a + b
>>> add(5, 6) add(args: (5, 6), kwargs: <>) -> 11 11
В этом примере функция-декоратор debug () печатает имя декорируемой функции, текущие значения каждого аргумента и возвращаемый результат. Такой декоратор можно использовать для простейшей отладки функций. Как только мы получаем желаемый результат, достаточно удалить вызов декоратора @debug , и отлаженная функция будет работать как обычно.
Приведем последний пример и заново реализуем generate_power() в виде функции-декоратора:
def generate_power(exponent): def power(func): def inner_power(*args): base = func(*args) return base ** exponent return inner_power return power @generate_power(2) def raise_two(n): return n @generate_power(3) def raise_three(n): return n
>>> raise_two(7) 49 >>> raise_three(5) 125
Эта версия generate_power() дает те же результаты, что и в исходной реализации. В этом случае мы используем для запоминания показателя степени и замыкание, и декоратор, который возвращает измененную версию функции func() .
Здесь декоратор должен принимать аргумент (показатель степени), поэтому нам потребовалось два уровня вложенность. Первый уровень представлен функцией power() , которая принимает в качестве аргумента декорируемую функцию. Второй уровень представлен функцией inner_power() , которая упаковывает показатель степени в args , выполняет окончательный расчет и возвращает результат.
Заключение
Итак, в Python вложенные функции имеют прямой доступ к переменным и именам, которые вы определяете во включающей функции. Это предоставляет механизм для инкапсуляции функций, создания вспомогательных решений, реализации замыканий и декораторов.