В този урок ще научите за затварянето на Python, как да дефинирате затваряне и причините, поради които трябва да го използвате.
Нелокална променлива в вложена функция
Преди да разгледаме какво е затваряне, първо трябва да разберем какво е вложена функция и нелокална променлива.
Функция, дефинирана вътре в друга функция, се нарича вложена функция. Вложените функции могат да имат достъп до променливи от обхващащия обхват.
В Python тези нелокални променливи са само за четене по подразбиране и трябва да ги декларираме изрично като нелокални (използвайки нелокална ключова дума), за да ги модифицираме.
Следва пример за вложена функция за достъп до нелокална променлива.
def print_msg(msg): # This is the outer enclosing function def printer(): # This is the nested function print(msg) printer() # We execute the function # Output: Hello print_msg("Hello")
Изход
Здравейте
Виждаме, че вложената printer()
функция е успяла да осъществи достъп до нелокалната променлива на msg на заграждащата функция.
Определяне на функция за затваряне
В горния пример, какво би се случило, ако последният ред на функцията print_msg()
върне printer()
функцията, вместо да я извика? Това означава, че функцията е дефинирана както следва:
def print_msg(msg): # This is the outer enclosing function def printer(): # This is the nested function print(msg) return printer # returns the nested function # Now let's try calling this function. # Output: Hello another = print_msg("Hello") another()
Изход
Здравейте
Това е необичайно.
Най- print_msg()
функцията се нарича с низ "Hello"
и се върна функция е свързан с името на друг. При обаждане another()
съобщението все още се помни, въпреки че вече бяхме приключили с изпълнението на print_msg()
функцията.
Тази техника, чрез която някои данни ( "Hello
в случая) се прикачват към кода, се нарича затваряне в Python .
Тази стойност в заграждащия обхват се запомня, дори когато променливата излезе извън обхвата или самата функция е премахната от текущото пространство от имена.
Опитайте да изпълните следното в обвивката на Python, за да видите изхода.
>>> del print_msg >>> another() Hello >>> print_msg("Hello") Traceback (most recent call last):… NameError: name 'print_msg' is not defined
Тук върнатата функция все още работи дори когато оригиналната функция е била изтрита.
Кога имаме затваряния?
Както се вижда от горния пример, имаме затваряне в Python, когато вложена функция се позовава на стойност в обхвата си.
Критериите, които трябва да бъдат изпълнени, за да се създаде затваряне в Python, са обобщени в следващите точки.
- Трябва да имаме вложена функция (функция вътре във функция).
- Вложената функция трябва да се отнася до стойност, дефинирана в заграждащата функция.
- Ограждащата функция трябва да върне вложената функция.
Кога да се използват затваряния?
И така, за какво са полезни затварянията?
Затварянето може да избегне използването на глобални стойности и предоставя някаква форма на скриване на данни. Той може да осигури и обектно ориентирано решение на проблема.
Когато има малко методи (един метод в повечето случаи), които да бъдат внедрени в клас, затварянията могат да осигурят алтернативно и по-елегантно решение. Но когато броят на атрибутите и методите стане по-голям, по-добре е да внедрите клас.
Ето един прост пример, при който затварянето може да е по-предпочитано от дефинирането на клас и създаването на обекти. Но предпочитанието е ваше.
def make_multiplier_of(n): def multiplier(x): return x * n return multiplier # Multiplier of 3 times3 = make_multiplier_of(3) # Multiplier of 5 times5 = make_multiplier_of(5) # Output: 27 print(times3(9)) # Output: 15 print(times5(3)) # Output: 30 print(times5(times3(2)))
Изход
27 15 30
Python Decorators също широко използват затварянията.
В заключителна бележка е добре да се отбележи, че стойностите, които се затварят във функцията за затваряне, могат да бъдат открити.
Всички функционални обекти имат __closure__
атрибут, който връща набор от клетъчни обекти, ако е функция за затваряне. Позовавайки се на горния пример, ние знаем times3
и times5
имаме функции за затваряне.
>>> make_multiplier_of.__closure__ >>> times3.__closure__ (,)
Клетъчният обект има атрибут cell_contents, който съхранява затворената стойност.
>>> times3.__closure__(0).cell_contents 3 >>> times5.__closure__(0).cell_contents 5