Vra

Ek het al baie oor sluitings gelees en ek dink ek verstaan ​​dit, maar sonder om die prentjie vir myself en ander te vertroebel, hoop ek iemand kan sluitings so bondig en duidelik moontlik verduidelik.Ek soek 'n eenvoudige verduideliking wat my kan help om te verstaan ​​waar en hoekom ek dit sou wou gebruik.

Was dit nuttig?

Oplossing

Sluiting op sluitings

Voorwerpe is data met metodes aangeheg, sluitings is funksies met data aangeheg.

def make_counter():
    i = 0
    def counter(): # counter() is a closure
        nonlocal i
        i += 1
        return i
    return counter

c1 = make_counter()
c2 = make_counter()

print (c1(), c1(), c2(), c2())
# -> 1 2 1 2

Ander wenke

Dis eenvoudig:'n Funksie wat veranderlikes van 'n bevat-omvang verwys, moontlik nadat vloei-van-beheer daardie omvang verlaat het.Die laaste bietjie is baie nuttig:

>>> def makeConstantAdder(x):
...     constant = x
...     def adder(y):
...         return y + constant
...     return adder
... 
>>> f = makeConstantAdder(12)
>>> f(3)
15
>>> g = makeConstantAdder(4)
>>> g(3)
7

Let daarop dat 12 en 4 onderskeidelik binne f en g "verdwyn" het, hierdie kenmerk is wat f en g behoorlike sluitings maak.

ek hou van hierdie growwe, bondige definisie:

'n Funksie wat kan verwys na omgewings wat nie meer aktief is nie.

Ek sou byvoeg

'n Afsluiting laat jou toe om veranderlikes in 'n funksie te bind sonder om dit as parameters deur te gee.

Versierders wat parameters aanvaar, is 'n algemene gebruik vir sluitings.Sluitings is 'n algemene implementeringsmeganisme vir daardie soort "funksiefabriek".Ek kies gereeld om sluitings te gebruik in die Strategie patroon wanneer die strategie deur data tydens looptyd gewysig word.

In 'n taal wat anonieme blokdefinisie toelaat -- bv. Ruby, C# -- kan sluitings gebruik word om (wat neerkom op) nuwe nuwe beheerstrukture te implementeer.Die gebrek aan anonieme blokke is onder die beperkings van sluitings in Python.

Om eerlik te wees, ek verstaan ​​sluitings baie goed, behalwe dat ek nog nooit duidelik was oor wat presies die ding is wat die "closure" is en wat so "closure" daaraan is nie.Ek beveel aan dat jy ophou soek na enige logika agter die keuse van term.

In elk geval, hier is my verduideliking:

def foo():
   x = 3
   def bar():
      print x
   x = 5
   return bar

bar = foo()
bar()   # print 5

'n Sleutelgedagte hier is dat die funksie-objek wat van foo af teruggestuur word, 'n haak na die plaaslike var 'x' behou, alhoewel 'x' buite die bestek gegaan het en in werking gestel behoort te wees.Hierdie haak is vir die var self, nie net die waarde wat var destyds gehad het nie, so wanneer staaf geroep word, druk dit 5, nie 3 nie.

Wees ook duidelik dat Python 2.x beperkte sluiting het:daar is geen manier waarop ek 'x' binne 'bar' kan verander nie, want die skryf van 'x = bla' sal 'n plaaslike 'x' in maat verklaar, nie toeken aan 'x' van foo nie.Dit is 'n newe-effek van Python se opdrag=verklaring.Om dit te omseil, stel Python 3.0 die nie-plaaslike sleutelwoord bekend:

def foo():
   x = 3
   def bar():
      print x
   def ack():
      nonlocal x
      x = 7
   x = 5
   return (bar, ack)

bar, ack = foo()
ack()   # modify x of the call to foo
bar()   # print 7

Ek het nog nooit gehoor van transaksies wat in dieselfde konteks gebruik word as om te verduidelik wat 'n sluiting is nie en daar is regtig geen transaksie semantiek hier nie.

Dit word 'n sluiting genoem omdat dit die buiteveranderlike (konstante) "sluit" -- dit wil sê, dit is nie net 'n funksie nie, maar 'n omhulsel van die omgewing waar die funksie geskep is.

In die volgende voorbeeld sal die oproep van die sluiting g na die verandering van x ook die waarde van x binne g verander, aangesien g oor x sluit:

x = 0

def f():
    def g(): 
        return x * 2
    return g


closure = f()
print(closure()) # 0
x = 2
print(closure()) # 4

Hier is 'n tipiese gebruiksgeval vir sluitings - terugroepe vir GUI-elemente (dit sal 'n alternatief wees vir die subklassering van die knoppieklas).Byvoorbeeld, jy kan 'n funksie konstrueer wat geroep sal word in reaksie op 'n knoppie druk, en "toemaak" oor die relevante veranderlikes in die ouer omvang wat nodig is vir die verwerking van die klik.Op hierdie manier kan u redelik ingewikkelde koppelvlakke van dieselfde inisialiseringsfunksie aanskakel, en al die afhanklikhede in die sluiting bou.

In Python is 'n sluiting 'n voorbeeld van 'n funksie wat veranderlikes onveranderlik daaraan verbind het.

Trouens, die datamodel verduidelik dit in sy beskrywing van funksies' __closure__ kenmerk:

Geen of a tupel van selle wat bindings vir die funksie se vrye veranderlikes bevat.Leesalleen

Om dit te demonstreer:

def enclosure(foo):
    def closure(bar):
        print(foo, bar)
    return closure

closure_instance = enclosure('foo')

Dit is duidelik dat ons weet dat ons nou 'n funksie het wat vanaf die veranderlike naam gewys word closure_instance.Oënskynlik, as ons dit met 'n voorwerp noem, bar, dit moet die string druk, 'foo' en wat ook al die stringvoorstelling van bar is.

Trouens, die snaar 'foo' is gebind aan die instansie van die funksie, en ons kan dit direk hier lees deur toegang tot die cell_contents eienskap van die eerste (en enigste) sel in die tupel van die __closure__ kenmerk:

>>> closure_instance.__closure__[0].cell_contents
'foo'

Ter syde word selvoorwerpe in die C API-dokumentasie beskryf:

Voorwerpe "sel" word gebruik om veranderlikes te implementeer waarna verskeie bestek verwys word

En ons kan die gebruik van ons sluiting demonstreer, en let daarop 'foo' sit vas in die funksie en verander nie:

>>> closure_instance('bar')
foo bar
>>> closure_instance('baz')
foo baz
>>> closure_instance('quux')
foo quux

En niks kan dit verander nie:

>>> closure_instance.__closure__ = None
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: readonly attribute

Gedeeltelike funksies

Die voorbeeld wat gegee word gebruik die sluiting as 'n gedeeltelike funksie, maar as dit ons enigste doel is, kan dieselfde doel bereik word met functools.partial

>>> from __future__ import print_function # use this if you're in Python 2.
>>> partial_function = functools.partial(print, 'foo')
>>> partial_function('bar')
foo bar
>>> partial_function('baz')
foo baz
>>> partial_function('quux')
foo quux

Daar is ook meer ingewikkelde sluitings wat nie by die gedeeltelike funksie-voorbeeld pas nie, en ek sal dit verder demonstreer soos die tyd dit toelaat.

Hier is 'n voorbeeld van Python3-sluitings

def closure(x):
    def counter():
        nonlocal x
        x += 1
        return x
    return counter;

counter1 = closure(100);
counter2 = closure(200);

print("i from closure 1 " + str(counter1()))
print("i from closure 1 " + str(counter1()))
print("i from closure 2 " + str(counter2()))
print("i from closure 1 " + str(counter1()))
print("i from closure 1 " + str(counter1()))
print("i from closure 1 " + str(counter1()))
print("i from closure 2 " + str(counter2()))

# result

i from closure 1 101
i from closure 1 102
i from closure 2 201
i from closure 1 103
i from closure 1 104
i from closure 1 105
i from closure 2 202
# A Closure is a function object that remembers values in enclosing scopes even if they are not present in memory.

# Defining a closure

# This is an outer function.
def outer_function(message):
    # This is an inner nested function.
    def inner_function():
        print(message)
    return inner_function

# Now lets call the outer function and return value bound to name 'temp'
temp = outer_function("Hello")
# On calling temp, 'message' will be still be remembered although we had finished executing outer_function()
temp()
# Technique by which some data('message') that remembers values in enclosing scopes 
# even if they are not present in memory is called closures

# Output: Hello

Kriteria waaraan sluitings moet voldoen, is:

  1. Ons moet geneste funksie hê.
  2. Geneste funksie moet verwys na die waarde wat in die omsluitende funksie gedefinieer is.
  3. Omsluitende funksie moet die geneste funksie terugstuur.

# Example 2
def make_multiplier_of(n): # Outer function
    def multiplier(x): # Inner nested function
        return x * n
    return multiplier
# Multiplier of 3
times3 = make_multiplier_of(3)
# Multiplier of 5
times5 = make_multiplier_of(5)
print(times5(3)) # 15
print(times3(2)) #  6

Vir my is "sluitings" funksies wat in staat is om die omgewing te onthou wat hulle geskep het.Hierdie funksionaliteit laat jou toe om veranderlikes of metodes binne die sluiting te gebruik wat jy op 'n ander manier nie sou kon gebruik nie omdat hulle nie meer bestaan ​​nie of hulle is buite bereik as gevolg van omvang.Kom ons kyk na hierdie kode in robyn:

def makefunction (x)
  def multiply (a,b)
    puts a*b
  end
  return lambda {|n| multiply(n,x)} # => returning a closure
end

func = makefunction(2) # => we capture the closure
func.call(6)    # => Result equal "12"  

dit werk selfs wanneer beide, "vermenigvuldig" metode en "x" veranderlike, nie meer bestaan ​​nie.Alles omdat die sluiting vermoë om te onthou.

ons het almal gebruik Versierders in luislang.Dit is goeie voorbeelde om te wys wat sluitfunksies in luislang is.

class Test():
    def decorator(func):
        def wrapper(*args):
            b = args[1] + 5
            return func(b)
        return wrapper

@decorator
def foo(val):
    print val + 2

obj = Test()
obj.foo(5)

hier is die finale waarde 12

Hier is die omhulselfunksie in staat om toegang tot func-objek te verkry, want omhulsel is "leksikale sluiting", dit kan toegang tot sy ouerkenmerke kry.Dit is hoekom, dit in staat is om toegang tot funk voorwerp.

Ek wil graag my voorbeeld en 'n verduideliking oor sluitings deel.Ek het 'n luislangvoorbeeld gemaak, en twee figure om stapeltoestande te demonstreer.

def maker(a, b, n):
    margin_top = 2
    padding = 4
    def message(msg):
        print('\n’ * margin_top, a * n, 
            ' ‘ * padding, msg, ' ‘ * padding, b * n)
    return message

f = maker('*', '#', 5)
g = maker('', '♥’, 3)
…
f('hello')
g(‘good bye!')

Die uitvoer van hierdie kode sal soos volg wees:

*****      hello      #####

      good bye!    ♥♥♥

Hier is twee figure om stapels en die sluiting aan die funksie-voorwerp te wys.

wanneer die funksie van maker teruggestuur word

wanneer die funksie later opgeroep word

Wanneer die funksie deur 'n parameter of 'n nie-plaaslike veranderlike geroep word, benodig die kode plaaslike veranderlike bindings soos margin_top, padding sowel as a, b, n.Om te verseker dat die funksiekode werk, moet die stapelraam van die makerfunksie wat lank gelede weg is, toeganklik wees, wat gerugsteun word in die sluiting wat ons saam met die 'boodskap se funksie-objek kan vind nie.

Die beste verduideliking wat ek ooit van 'n sluiting gesien het, was om die meganisme te verduidelik.Dit het iets soos volg gegaan:

Stel jou voor jou programstapel as 'n gedegenereerde boom waar elke nodus net een kind het en die enkelblaarnodus die konteks is van jou prosedure wat tans uitgevoer word.

Ontspan nou die beperking dat elke nodus slegs een kind kan hê.

As jy dit doen, kan jy 'n konstruk ('opbrengs') hê wat van 'n prosedure kan terugkeer sonder om die plaaslike konteks weg te gooi (d.w.s.dit stoot dit nie van die stapel af wanneer jy terugkeer nie).Die volgende keer dat die prosedure aangeroep word, tel die oproep die ou stapel (boom) raam op en gaan voort met uitvoering waar dit opgehou het.

Gelisensieer onder: CC-BY-SA met toeskrywing
Nie verbonde aan StackOverflow
scroll top