You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: roteiros/8_closures_1_escopo.md
+43-44Lines changed: 43 additions & 44 deletions
Original file line number
Diff line number
Diff line change
@@ -1,7 +1,6 @@
1
-
2
1
# 8. Closures e contexto de variáveis
3
2
4
-
Já passamos funções como argumento, já retornamos funções e até já mudamos o comportamento das mesmas. Mas tem uma coisa que ainda não fizemos: Definir uma função no corpo de outra função:
3
+
Já passamos funções como argumento, já retornamos funções e até já mudamos o comportamento das mesmas. Mas tem uma coisa que ainda não fizemos: definir uma função no corpo de outra função:
5
4
6
5
7
6
```Python
@@ -11,13 +10,13 @@ def func_0():
11
10
pass
12
11
```
13
12
14
-
Jaber diz: `Mas isso é uma classe! eu sabia que programação funcional era legal, mas realmente tudo é orientação a objetos.`
13
+
Jaber diz: `Mas isso é uma classe! Eu sabia que programação funcional era legal, mas realmente tudo é orientação a objetos.`
15
14
16
15
Tá Jaber, eu entendo seu ponto de vista, mas vou usar uma definição muito boa do livro do Mertz:
17
16
18
-
"Uma classe são dados com operações anexadas (...) Uma Clojure são operações com dados anexados"
17
+
"Uma classe são dados com operações anexadas (...) Uma Closure são operações com dados anexados"
19
18
20
-
Viu? muda totalmente o modo de ver... Vamos nos explicar de maneira simples em breve, mas vamos entender as closures e fingir que classes não existem, só por alguns minutos.
19
+
Viu? Muda totalmente o modo de ver... Vamos nos explicar de maneira simples em breve, mas vamos entender as closures e fingir que classes não existem, só por alguns minutos.
21
20
22
21
Vamos imaginar que temos que guardar um valor dentro de um função. Essa função vai ser uma função que exclusivamente armazena um valor e uma função dentro do seu escopo:
23
22
@@ -47,9 +46,9 @@ var_func = func_externa(5)
47
46
var_func(5) # 10
48
47
```
49
48
50
-
Como dá pra notar a função externa é atribuída a uma variável e essa variável executa a função interna. Parece complicado, mas na verdade é bem simples. Vamos recapitular algumas coisas.
49
+
Como dá pra notar, a função externa é atribuída a uma variável e essa variável executa a função interna. Parece complicado, mas na verdade é bem simples. Vamos recapitular algumas coisas.
51
50
52
-
Como em python as funções podem ser definidas em qualquer contexto e armazenada em qualquer lugar imagine que a `func_externa()` está sendo atribuída a uma variável. Em um contexto totalmente normal, como fizemos com as funções anônimas até agora. A diferença é um valor, ou uma quantidade `n` de valores, vão ser passadas no momento da atribuição. Esses valores vão ficar armazenados na função de maneira imutável, vamos reaproveitar o código com alguns exemplos:
51
+
Como em python as funções podem ser definidas em qualquer contexto e armazenada em qualquer lugar imagine que a `func_externa()` está sendo atribuída a uma variável. Em um contexto totalmente normal, como fizemos com as funções anônimas até agora. A diferença é um valor, ou uma quantidade `n` de valores, vão ser passadas no momento da atribuição. Esses valores vão ficar armazenados na função de maneira imutável. Vamos reaproveitar o código com alguns exemplos:
53
52
54
53
```Python
55
54
soma_um = func_externa(1)
@@ -66,14 +65,14 @@ soma_quarto(0) # 4
66
65
soma_cinco(0) # 5
67
66
```
68
67
69
-
Bom, agora imagino que tenha ficado um pouco mais claro. Fixamos valores na `func_externa()` e armazenamos em variáveis (`soma_um()`, `soma_dois()`, `soma_tres()` ...) cada respectiva função mostra valor inicial da função.
68
+
Bom, agora imagino que tenha ficado um pouco mais claro. Fixamos valores na `func_externa()` e armazenamos em variáveis (`soma_um()`, `soma_dois()`, `soma_tres()`, ...) cada respectiva função mostra o valor inicial da função.
70
69
71
-
Quando executamos a função `soma_um(n)` qualquer valor que for usado em `n` vai ser somado a ao valor fixo na função externa `func_externa(1)`, ou seja, `1`. Vale lembrar que a soma é executada porque esse é o comportamento da função interna `func_interna()`. Vamos tentar outra vez e de uma maneira mais simples:
70
+
Quando executamos a função `soma_um(n)` qualquer valor que for usado em `n` vai ser somado ao valor fixo na função externa `func_externa(1)`, ou seja, `1`. Vale lembrar que a soma é executada porque esse é o comportamento da função interna `func_interna()`. Vamos tentar outra vez e de uma maneira mais simples:
Uma vantagem de definir esse dicionário dentro da função é que ele vai ficar isolado das variáveis globais, o que faz ele não gerar efeito colateral e isso é muito positivo:
99
+
Uma vantagem de definir esse dicionário dentro da função é que ele vai ficar isolado das variáveis globais, o que faz ele não gerar efeito colateral, e isso é muito positivo:
Você deve ter percebido que até agora as closures tem dois tipos de comportamento diferentes, porém a imutabilidade permanece:
128
+
Você deve ter percebido que até agora as closures tem dois tipos de comportamentos diferentes, porém a imutabilidade permanece:
130
129
131
130
1. Fixando parâmetros para padronização de chamadas
132
131
2. O escopo da função externa é acessível para a função interna
133
132
134
-
Aqui você deve ter sacado o esquema de operações com dados. O dado passado a função externa permanece imutável sempre e inacessível a qualquer contexto externo ao da função, ou seja, o dado foi fixado e não pode ser transformado em nenhum outro valor, a não ser que seja feita outra chamada com outro valor. O que deveria ser dito sobre as classes, e que preferó postergar, é o que toca exatamente nesse ponto. Vamos fazer uma comparação:
133
+
Aqui você deve ter sacado o esquema de operações com dados. O dado passado à função externa permanece imutável sempre e inacessível a qualquer contexto externo ao da função, ou seja, o dado foi fixado e não pode ser transformado em nenhum outro valor, a não ser que seja feita outra chamada com outro valor. O que deveria ser dito sobre as classes, e que preferi postergar, é o que toca exatamente nesse ponto. Vamos fazer uma comparação:
135
134
136
135
137
136
### classe `__call__()`
@@ -149,7 +148,7 @@ class diga_oi:
149
148
150
149
def__call__(self, nome):
151
150
"""
152
-
__call__ permite que o objeto aplique o operado (),
151
+
__call__ permite que o objeto aplique o operador (),
153
152
seja chamado como função
154
153
"""
155
154
return'{}{}'.format(self.saudacao, nome)
@@ -175,7 +174,7 @@ class diga_oi:
175
174
"""
176
175
def__init__(self, saudacao):
177
176
"""
178
-
Inicializa a instancia do objeto com saudacao sendo imutavel
177
+
Inicializa a instância do objeto com saudacao sendo imutável
179
178
"""
180
179
object.__setattr__(self, 'saudacao', saudacao)
181
180
@@ -184,7 +183,7 @@ class diga_oi:
184
183
185
184
def__call__(self, nome):
186
185
"""
187
-
__call__ permite que o objeto aplique o operado (),
186
+
__call__ permite que o objeto aplique o operador (),
188
187
seja chamado como função
189
188
"""
190
189
return'{}{}'.format(self.saudacao, nome)
@@ -195,21 +194,21 @@ class diga_oi:
195
194
196
195
## 8.2 Mutação das variáveis de uma closure
197
196
198
-
Diferente do que eu disse até agora, os valores podem ser alterados no escopo da função externa mas temos uma série de limitações. Caso o objeto passado como parâmetro, ou alocado na função externa, seja mutável (listas, dicionários, conjuntos, ...), o objeto pode receber normalmente as modificações, vamos fazer um teste:
197
+
Diferente do que eu disse até agora, os valores podem ser alterados no escopo da função externa, mas temos uma série de limitações. Caso o objeto passado como parâmetro, ou alocado na função externa, seja mutável (listas, dicionários, conjuntos, ...), o objeto pode receber normalmente as modificações, vamos fazer um teste:
199
198
200
199
201
200
```Python
202
201
defcontador():
203
202
"""
204
203
Função contadora de acessos.
205
204
206
-
Internamente mantem uma lista que é definida
205
+
Internamente mantém uma lista que é definida
207
206
vazia no momento em que é declarada
208
207
"""
209
208
lista = []
210
209
defsoma():
211
210
"""
212
-
Adiciona 1 a lista toda vez que a função é chamada.
211
+
Adiciona 1 à lista toda vez que a função é chamada.
213
212
214
213
Retorna a somatória dos valores contidos na lista
215
214
"""
@@ -225,20 +224,20 @@ count() # 4
225
224
count() # 5
226
225
```
227
226
228
-
Isso é uma forma porca de fazer um contador, mas ele funciona. O mais importante disso é que a variável `lista` não está sendo modificada. Os valores estão sendo atribuídos a lista poque ela é um objeto mutável. Mas não seria possível, e vamos tentar isso agora, mudar o conteúdo da variável lista:
227
+
Isso é uma forma porca de fazer um contador, mas ele funciona. O mais importante disso é que a variável `lista` não está sendo modificada. Os valores estão sendo atribuídos à lista porque ela é um objeto mutável. Mas não seria possível, e vamos tentar isso agora, mudar o conteúdo da variável lista:
229
228
230
229
```Python
231
230
defcontador():
232
231
"""
233
232
Função contadora de acessos.
234
233
235
-
Internamente mantem uma lista que é definida
234
+
Internamente mantém uma lista que é definida
236
235
vazia no momento em que é declarada
237
236
"""
238
237
lista =0
239
238
defsoma():
240
239
"""
241
-
Adiciona 1 a lista toda vez que a função é chamada.
240
+
Adiciona 1 à lista toda vez que a função é chamada.
242
241
243
242
Retorna a somatória dos valores contidos na lista
244
243
"""
@@ -250,11 +249,11 @@ count = contador()
250
249
count() # UnboundLocalError: local variable 'lista' referenced before assignment
251
250
```
252
251
253
-
Vamos tentar mudar o foco um pouco e dar mais importancia a esse erro, ele pode nos mostrar coisas lindas sobre Python e que podemos usar com muito empenho para fazer funcional com mais espenho e graciosidade.
252
+
Vamos tentar mudar o foco um pouco e dar mais importância a esse erro, ele pode nos mostrar coisas lindas sobre Python e que podemos usar para fazer funcional com mais empenho e graciosidade.
254
253
255
254
### UnboundLocalError e escopo de variáveis
256
255
257
-
`UnboundLocalError` é um erro muito comum em Python, e o que me deixa muito surpreso foi issso te lavado tanto tempo pra acontecer nesses nossos contextos de programação funcional. Vamos falar agora sobre escopo de variáveis, porém vale lembrar que esse não é um tópico de programação funcional e sim de comportamento específico do Python. Se você já sabe sobre tudo isso, você pode pular todos esse tópicos, porém vale a pena, para o melhor entendimento das closures ficar atento a variáveis livres.
256
+
`UnboundLocalError` é um erro muito comum em Python, e o que me deixa muito surpreso foi isso ter levado tanto tempo pra acontecer nesses nossos contextos de programação funcional. Vamos falar agora sobre escopo de variáveis, porém vale lembrar que esse não é um tópico de programação funcional, e sim de comportamento específico do Python. Se você já sabe sobre tudo isso, você pode pular todos esse tópicos, porém vale a pena, para o melhor entendimento das closures, ficar atento a variáveis livres.
258
257
259
258
260
259
#### Variáveis globais e locais
@@ -276,7 +275,7 @@ print(var_0) # 5
276
275
print(var_1) # NameError: name 'var_1' is not defined
277
276
```
278
277
279
-
var_0 está presente em toda a execução, porém var_1 é uma variável do escopo local da função `func()` e fora desse contexto ela simplismente não existe.
278
+
var_0 está presente em toda a execução, porém var_1 é uma variável do escopo local da função `func()` e fora desse contexto ela simplesmente não existe.
280
279
281
280
Outro ponto legal disso é que a variável global não pode ser modificada pela função `func()`:
282
281
@@ -290,7 +289,7 @@ func()
290
289
print(var_0) # 5
291
290
```
292
291
293
-
No momento em que tentamos atribuir a no escopo de `func()` criamos uma nova `var_0` dentro desse contexto, (vamos falar mais sobre isso em um tópico futuro chamado introspecção de funções) o que faz que a variável global permaneça a mesma.
292
+
No momento em que tentamos atribuir no escopo de `func()`, criamos uma nova `var_0` dentro desse contexto, (vamos falar mais sobre isso em um tópico futuro chamado introspecção de funções) o que faz que a variável global permaneça a mesma.
294
293
295
294
Se nós quisermos transformar o valor da variável global dentro do escopo da função, podemos usar a palavra reservada `global`:
296
295
@@ -305,27 +304,27 @@ func()
305
304
print(var_0) # 7
306
305
```
307
306
308
-
Agora, como você pode notar o comportamento é diferente, a palavra `global` disse ao Python que variável usada aqui é exatamente o do escopo global. Quando o Python procura uma variável (vamos falar mais sobre isso em um tópico futuro chamado introspecção de funções [2]) ele segue uma hierarquia:
307
+
Agora, como você pode notar, o comportamento é diferente. A palavra `global` disse ao Python que a variável usada aqui é exatamente a do escopo global. Quando o Python procura uma variável (vamos falar mais sobre isso em um tópico futuro chamado introspecção de funções [2]) ele segue uma hierarquia:
309
308
310
309
```
311
310
Existe no meu escopo local? Se sim, use. Caso não
312
-
Procure em um escopo mais amplo, se existe, use, Caso não
311
+
Procure em um escopo mais amplo, se existe, use. Caso não
313
312
Procure em um escopo mais amplo ....
314
313
....
315
314
```
316
-
Ele busca até que o contexto seja o global, a variável não existir ele vai nos retornar `NameError`. Mas isso só vale para leitura. E agora para escrerver?
315
+
Ele busca até que o contexto seja o global, se a variável não existir ele vai nos retornar `NameError`. Mas isso só vale para leitura. E agora para escrever?
317
316
318
317
```
319
318
Variável criada no escopo, fica no escopo.
320
319
```
321
320
322
-
É simples não? isso nos ajuda a previnir muitos erros e não 'assinar' variáveis fora do nosso escopo local e gerar efeitos colaterais bizarros. Exemplo?
321
+
É simples não? Isso nos ajuda a prevenir muitos erros e não 'assinar' variáveis fora do nosso escopo local e gerar efeitos colaterais bizarros. Exemplo:
Você notou o que existe de errado nessa função? Ela usar a palavra reservada `dir` que é uma função do contexto global do python `dir()`, ou seja, se os contextos locais não fosse criados durante todo o resto da execução desse programa não poderiamos fazer uso da função `dir()` pois ela foi sobreescrita.
337
+
Você notou o que existe de errado nessa função? Ela usa a palavra reservada `dir`, que é uma função do contexto global do python `dir()`, ou seja, se os contextos locais não fossem criados durante todo o resto da execução desse programa não poderíamos fazer uso da função `dir()` pois ela foi sobrescrita.
339
338
340
339
#### Variáveis livres
341
340
@@ -347,13 +346,13 @@ def contador():
347
346
"""
348
347
Função contadora de acessos.
349
348
350
-
Internamente mantem uma lista que é definida
349
+
Internamente mantém uma lista que é definida
351
350
vazia no momento em que é declarada
352
351
"""
353
352
lista =0# Variável local de contador
354
353
defsoma():
355
354
"""
356
-
Adiciona 1 a lista toda vez que a função é chamada.
355
+
Adiciona 1 à lista toda vez que a função é chamada.
357
356
358
357
Retorna a somatória dos valores contidos na lista
359
358
"""
@@ -362,33 +361,33 @@ def contador():
362
361
return soma
363
362
```
364
363
365
-
Vamos pensar um pouco, não é possível modificar `lista` pois ela está em um escopo externo ao de `soma()` só que não podemos usar `global` pois lista não é global, lembra da ordem da busca pos nomes?
364
+
Vamos pensar um pouco, não é possível modificar `lista` pois ela está em um escopo externo ao de `soma()`, só que não podemos usar `global` pois `lista` não é global. Lembra da ordem da busca por nomes?
366
365
367
366
```
368
-
faz parte de soma? Não
369
-
faz parte de contador, Sim
367
+
Faz parte de soma? Não
368
+
Faz parte de contador? Sim
370
369
```
371
370
372
-
Então ele usa, só que responde `UnboundLocalError` pois não podemos mudar seu valor. Em Python 3, e só em python 3, existe a palavra reservada `nonlocal` que dá o poder de outro escopo mutar o valor fora de seu escopo e que não está no escopo global. Ou seja, resolve o nosso problema e isso é lindo. Existe uma PEP inteira detalhando esse aspecto [PEP 3104](https://www.python.org/dev/peps/pep-3104/) pois não quero me alongar nessa explicação, pra simplificar, vou usar o contexto do [wikipedia](https://pt.wikipedia.org/wiki/Vari%C3%A1veis_livres_e_ligadas):
371
+
Então ele usa, só que responde `UnboundLocalError` pois não podemos mudar seu valor. Em Python 3, e só em Python 3, existe a palavra reservada `nonlocal`, que dá o poder de outro escopo mutar o valor fora de seu escopo e que não está no escopo global. Ou seja, resolve o nosso problema e isso é lindo. Existe uma PEP inteira detalhando esse aspecto [PEP 3104](https://www.python.org/dev/peps/pep-3104/) pois não quero me alongar nessa explicação, pra simplificar, vou usar o contexto do [wikipedia](https://pt.wikipedia.org/wiki/Vari%C3%A1veis_livres_e_ligadas):
373
372
374
373
```
375
-
uma variável livre é uma variável referenciada em uma função, que não é nem uma variável local nem um argumento daquela função.
374
+
Uma variável livre é uma variável referenciada em uma função, que não é nem uma variável local nem um argumento daquela função.
376
375
```
377
376
378
-
No caso de nossa closures, é uma variável declara na função externa, que pode ser acessada pela função interna e não pode ser modificada pela mesma. Porém podemos usar `nonlocal`. Vamos tentar?
377
+
No caso de nossas closures, é uma variável declarada na função externa, que pode ser acessada pela função interna e não pode ser modificada pela mesma. Porém podemos usar `nonlocal`. Vamos tentar?
379
378
380
379
```Python
381
380
defcontador():
382
381
"""
383
382
Função contadora de acessos.
384
383
385
-
Internamente mantem uma lista que é definida
384
+
Internamente mantém uma lista que é definida
386
385
vazia no momento em que é declarada
387
386
"""
388
387
var =0
389
388
defsoma():
390
389
"""
391
-
Adiciona 1 a lista toda vez que a função é chamada.
390
+
Adiciona 1 à lista toda vez que a função é chamada.
392
391
393
392
Retorna a somatória dos valores contidos na lista
394
393
"""
@@ -398,6 +397,6 @@ def contador():
398
397
return soma
399
398
```
400
399
401
-
Agora é possível gerar esse contador sem o uso da lista, então ele não 'enche nossa memória' com uma lista que pode ter um tamanho nada convencional. Porém isso fere o conceito de imutabilidade, mais ainda é melhor que uma classe por que faz acesso ao recurso `var` é somente a função interna, sem que isso possa ser transformado pelo escopo exterior, como é feito no caso das classes.
400
+
Agora é possível gerar esse contador sem o uso da lista, então ele não 'enche nossa memória' com uma lista que pode ter um tamanho nada convencional. Porém isso fere o conceito de imutabilidade, mas ainda é melhor que uma classe porque faz acesso ao recurso `var` é somente a função interna, sem que isso possa ser transformado pelo escopo externo, como é feito no caso das classes.
402
401
403
402
Agora vamos olhar para um lado mais avançado das closures, mas você vai conseguir dar mais vazão e usos derivados das mesmas.
0 commit comments