Skip to content

Commit b22919b

Browse files
committed
closes #21, correções de @magnvmopvs
1 parent eb7bcdb commit b22919b

File tree

1 file changed

+43
-44
lines changed

1 file changed

+43
-44
lines changed

roteiros/8_closures_1_escopo.md

Lines changed: 43 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
1-
21
# 8. Closures e contexto de variáveis
32

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:
54

65

76
```Python
@@ -11,13 +10,13 @@ def func_0():
1110
pass
1211
```
1312

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.`
1514

1615
Tá Jaber, eu entendo seu ponto de vista, mas vou usar uma definição muito boa do livro do Mertz:
1716

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"
1918

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.
2120

2221
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:
2322

@@ -47,9 +46,9 @@ var_func = func_externa(5)
4746
var_func(5) # 10
4847
```
4948

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.
5150

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:
5352

5453
```Python
5554
soma_um = func_externa(1)
@@ -66,14 +65,14 @@ soma_quarto(0) # 4
6665
soma_cinco(0) # 5
6766
```
6867

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.
7069

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:
7271

7372
```Python
7473
def diga_oi(saudacao):
7574
"""
76-
A funão diga_oi armazenada a sua saudação
75+
A função diga_oi armazenada a sua saudação
7776
"""
7877
def nome_pessoa(nome):
7978
"""
@@ -82,7 +81,7 @@ def diga_oi(saudacao):
8281
return '{} {}'.format(saudacao, nome)
8382
return nome_pessoa
8483

85-
oi_pirata = diga_oi('Ahoy!') # Definição de diga_oi (função externa) fixando Arroy
84+
oi_pirata = diga_oi('Ahoy!') # Definição de diga_oi (função externa) fixando Ahoy
8685

8786
oi_pirata('Eduardo') # Ahoy! Eduardo
8887
oi_pirata('Jaber') # Ahoy! Jaber
@@ -97,7 +96,7 @@ Mas vamos tentar fixar um dicionário de idiomas pra `diga_oi()`? Por exemplo, a
9796
dic = {'pirata': 'Ahoy', 'ingles':'Hello', 'portugues': 'Olá'}
9897
```
9998

100-
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:
101100

102101
```Python
103102
def diga_oi():
@@ -126,12 +125,12 @@ saudacoes('ingles', 'Python') # 'Hello Python'
126125
## 8.1 Classes vs closures
127126

128127

129-
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:
130129

131130
1. Fixando parâmetros para padronização de chamadas
132131
2. O escopo da função externa é acessível para a função interna
133132

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:
135134

136135

137136
### classe `__call__()`
@@ -149,7 +148,7 @@ class diga_oi:
149148

150149
def __call__(self, nome):
151150
"""
152-
__call__ permite que o objeto aplique o operado (),
151+
__call__ permite que o objeto aplique o operador (),
153152
seja chamado como função
154153
"""
155154
return '{} {}'.format(self.saudacao, nome)
@@ -175,7 +174,7 @@ class diga_oi:
175174
"""
176175
def __init__(self, saudacao):
177176
"""
178-
Inicializa a instancia do objeto com saudacao sendo imutavel
177+
Inicializa a instância do objeto com saudacao sendo imutável
179178
"""
180179
object.__setattr__(self, 'saudacao', saudacao)
181180

@@ -184,7 +183,7 @@ class diga_oi:
184183

185184
def __call__(self, nome):
186185
"""
187-
__call__ permite que o objeto aplique o operado (),
186+
__call__ permite que o objeto aplique o operador (),
188187
seja chamado como função
189188
"""
190189
return '{} {}'.format(self.saudacao, nome)
@@ -195,21 +194,21 @@ class diga_oi:
195194

196195
## 8.2 Mutação das variáveis de uma closure
197196

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:
199198

200199

201200
```Python
202201
def contador():
203202
"""
204203
Função contadora de acessos.
205204
206-
Internamente mantem uma lista que é definida
205+
Internamente mantém uma lista que é definida
207206
vazia no momento em que é declarada
208207
"""
209208
lista = []
210209
def soma():
211210
"""
212-
Adiciona 1 a lista toda vez que a função é chamada.
211+
Adiciona 1 à lista toda vez que a função é chamada.
213212
214213
Retorna a somatória dos valores contidos na lista
215214
"""
@@ -225,20 +224,20 @@ count() # 4
225224
count() # 5
226225
```
227226

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:
229228

230229
```Python
231230
def contador():
232231
"""
233232
Função contadora de acessos.
234233
235-
Internamente mantem uma lista que é definida
234+
Internamente mantém uma lista que é definida
236235
vazia no momento em que é declarada
237236
"""
238237
lista = 0
239238
def soma():
240239
"""
241-
Adiciona 1 a lista toda vez que a função é chamada.
240+
Adiciona 1 à lista toda vez que a função é chamada.
242241
243242
Retorna a somatória dos valores contidos na lista
244243
"""
@@ -250,11 +249,11 @@ count = contador()
250249
count() # UnboundLocalError: local variable 'lista' referenced before assignment
251250
```
252251

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.
254253

255254
### UnboundLocalError e escopo de variáveis
256255

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.
258257

259258

260259
#### Variáveis globais e locais
@@ -276,7 +275,7 @@ print(var_0) # 5
276275
print(var_1) # NameError: name 'var_1' is not defined
277276
```
278277

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.
280279

281280
Outro ponto legal disso é que a variável global não pode ser modificada pela função `func()`:
282281

@@ -290,7 +289,7 @@ func()
290289
print(var_0) # 5
291290
```
292291

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.
294293

295294
Se nós quisermos transformar o valor da variável global dentro do escopo da função, podemos usar a palavra reservada `global`:
296295

@@ -305,27 +304,27 @@ func()
305304
print(var_0) # 7
306305
```
307306

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:
309308

310309
```
311310
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
313312
Procure em um escopo mais amplo ....
314313
....
315314
```
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?
317316

318317
```
319318
Variável criada no escopo, fica no escopo.
320319
```
321320

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:
323322

324323
```Python
325324
def dir_concat(dir:str) -> str:
326325
"""
327326
Função que recebe uma string com um diretório
328-
e contatena ele com nosso path atual:
327+
e concatena ele com nosso path atual:
329328
330329
Exemplo:
331330
>>> dir_concat('documentos')
@@ -335,7 +334,7 @@ def dir_concat(dir:str) -> str:
335334
return '{}/{}'.format(getcwd(), dir)
336335
```
337336

338-
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.
339338

340339
#### Variáveis livres
341340

@@ -347,13 +346,13 @@ def contador():
347346
"""
348347
Função contadora de acessos.
349348
350-
Internamente mantem uma lista que é definida
349+
Internamente mantém uma lista que é definida
351350
vazia no momento em que é declarada
352351
"""
353352
lista = 0 # Variável local de contador
354353
def soma():
355354
"""
356-
Adiciona 1 a lista toda vez que a função é chamada.
355+
Adiciona 1 à lista toda vez que a função é chamada.
357356
358357
Retorna a somatória dos valores contidos na lista
359358
"""
@@ -362,33 +361,33 @@ def contador():
362361
return soma
363362
```
364363

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?
366365

367366
```
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
370369
```
371370

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):
373372

374373
```
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.
376375
```
377376

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?
379378

380379
```Python
381380
def contador():
382381
"""
383382
Função contadora de acessos.
384383
385-
Internamente mantem uma lista que é definida
384+
Internamente mantém uma lista que é definida
386385
vazia no momento em que é declarada
387386
"""
388387
var = 0
389388
def soma():
390389
"""
391-
Adiciona 1 a lista toda vez que a função é chamada.
390+
Adiciona 1 à lista toda vez que a função é chamada.
392391
393392
Retorna a somatória dos valores contidos na lista
394393
"""
@@ -398,6 +397,6 @@ def contador():
398397
return soma
399398
```
400399

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.
402401

403402
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

Comments
 (0)