forked from diveintomark/diveintopython3
-
Notifications
You must be signed in to change notification settings - Fork 3
/
iterators.html
394 lines (315 loc) · 30.9 KB
/
iterators.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
<!DOCTYPE html>
<meta charset=utf-8>
<title>Classes & Iterators - Dive Into Python 3</title>
<!--[if IE]><script src=j/html5.js></script><![endif]-->
<link rel=stylesheet href=dip3.css>
<style>
body{counter-reset:h1 7}
</style>
<link rel=stylesheet media='only screen and (max-device-width: 480px)' href=mobile.css>
<link rel=stylesheet media=print href=print.css>
<meta name=viewport content='initial-scale=1.0'>
<form action=http://www.google.com/cse><div><input type=hidden name=cx value=014021643941856155761:l5eihuescdw><input type=hidden name=ie value=UTF-8> <input type=search name=q size=25 placeholder="powered by Google™"> <input type=submit name=sa value=Search></div></form>
<p>Você está aqui: <a href=index.html>Home</a> <span class=u>‣</span> <a href=table-of-contents.html#iterators>Dive Into Python 3</a> <span class=u>‣</span>
<p id=level>Nível de dificuldade: <span class=u title=intermediate>♦♦♦♢♢</span>
<h1>Classes <i class=baa>&</i> Iterators</h1>
<blockquote class=q>
<p><span class=u>❝</span> Leste é leste, oeste é oeste, e os dois nunca se encontraram.<span class=u>❞</span><br>— <a href=http://en.wikiquote.org/wiki/Rudyard_Kipling>Rudyard Kipling</a>
</blockquote>
<p id=toc>
<h2 id=divingin>Mergulhando em</h2>
<p class=f>Iterators são o “tempero secreto” do Python 3. Eles estão em todo lugar, contidos em todo lugar, sempre fora de vista. <a href=comprehensions.html>Comprehensions</a> são apenas uma forma dos <i>iterators</i>. Generators são apenas uma foram simples do <i>iterators</i>. A função que produz valores é agradável quando numa forma compacta utilizando iterators sem a construção de iterators. Deixe-me mostrar o que eu quiz dizer com isto.
<p>Lembra-se do <a href=generators.html#a-fibonacci-generator>gerador de Fibonacci</a>? Há aqui um iterator implícito a partir do zero:
<p class=d>[<a href=examples/fibonacci2.py>download <code>fibonacci2.py</code></a>]
<pre class=pp><code>class Fib:
'''iterator que produz números da sequência de Fibonacci'''
def __init__(self, max):
self.max = max
def __iter__(self):
self.a = 0
self.b = 1
return self
def __next__(self):
fib = self.a
if fib > self.max:
raise StopIteration
self.a, self.b = self.b, self.a + self.b
return fib</code></pre>
<p>Deixe-me mostrar linha por linha.
<pre class='nd pp'><code>class Fib:</code></pre>
<p><code>class</code>? O que é uma classe?
<p class=a>⁂
<h2 id=defining-classes>Definindo Classes</h2>
<p>Python totalmente orientado a objetos: você pode definir sua própria classe, herdar de si mesmo ou de suas classes built-in, e instanciar suas classes que você definiu.
<p>Definir uma classe em Python é simples. Tal como acontece com as funções, não há nenhuma definição separada da interface. Apenas definir a classe e começar a codificar. Uma classe Python começa com a palavra reservada <code>class</code>, seguida do nome da classe. Tecnicamente, isto é tudo que é necessário, uma vez que não é preciso herdar de qualquer outra classe.
<pre class=pp><code><a>class PapayaWhip: <span class=u>①</span></a>
<a> pass <span class=u>②</span></a></code></pre>
<ol>
<li>O nome da classe é <code>PapayaWhip</code>, e não é herdada de nenhuma outra classe. Nomes de classe normalmente são maiusculas, <code>CadaPalavraDessaManeira</code>, mas isto é um convenção, e não um requisito.
<li>Você provavelmente esta se perguntando sobre isto, mas tudo na classe é identado, como no código de funções, condicional <code>if</code>, laço <code>for</code>, ou qualquer outro bloco de código. A primeira linha não identada já será considerada fora da classe.
</ol>
<p>Esta classe <code>PapayaWhip</code> não define métodos ou atributos, mas sintaticamente, é necessário que haja algo na definição, assim há a declaração <code>pass</code>. Esta é uma palavra reservada do Python que significa apenas "Passe adiante, nada para fazer aqui". É uma declaração que não faz nada, é bom quando você está apagando funções ou classes.
<blockquote class='note compare java'>
<p><span class=u>☞</span>O argumento <code>pass</code> em Python é como chaves vazias(<code>{}</code>) em Java ou C.
</blockquote>
<p>Muitas classes são herdadas de outras classes, mas esta não. Muitas classes definem métodos, mas esta não o faz. Não há nada que uma classe Python deva ter, além do nome. Em particular, programadores de C++ podem achar que seja esquisito não ter explicitamente construtores e destrutores. Embora não seja necessário, classes Python <em>podem</em> ter algo semelhante a um construtor: o método <code>__init__()</code>.
<h3 id=init-method>O Método <code>__init__()</code></h3>
<p>Este exemplo mostra a inicialização da classe <code>Fib</code> usando o método <code>__init__</code>.
<pre class=pp><code>class Fib:
<a> '''iterator que produz números da sequência de Fibonacci''' <span class=u>①</span></a>
<a> def __init__(self, max): <span class=u>②</span></a></code></pre>
<ol>
<li>Classes podem (e devem) ter <code>docstring</code> também, assim como módulos e funções.
<li>O método <code>__init__()</code> é chamado imediatamente após uma instância da classe ser criada. Seria tentador — mas tecnicamente incorreto — chamá-lo de “construtor” da classe. É tentador, porque se parece com um construtor C++(pela convenção, o método <code>__init__()</code> é o primeiro a ser definido na classe), age como um(é o primeiro pedaço de código a ser executado quando uma nova instância da classe é criada), e ainda se parece com um. Incorreto, porque o objeto já foi construído quando o método <code>__init__()</code> fora chamado, e você já tem uma instância valida para a nova classe.
</ol>
<p>O primeiro argumento de todos métodos da classe, incluindo o método <code>__init__()</code>, é sempre a referência a instância da classe atual. Por convenção este argumento é chamado <var>self</var>. Este argumento faz o papel da palavra reservada <code>this</code> em <abbr>C++</abbr> ou Java, mas <var>self</var> não é uma palavra reservada em Python, meramente uma convenção de nomeclatura. No entanto, por favor, não chame nenhuma outra coisa de <var>self</var>, isto é fortemente recomendado.
<p>Em todos os métodos das classes, <var>self</var> se refere a instância da classe a qual ela foi chamada. Mas no caso específico do método <code>__init__()</code>, a instância cujo método foi chamado é também o objeto recém-criado. Embora você tenha que declarar explicitamente o </var> quando define o método, você <em>não</em> precisa especificá-lo ao chamar o método; o Python irá adicioná-lo automaticamente pra você.
<p class=a>⁂
<h2 id=instantiating-classes>Instânciando Classes</h2>
<p>Instanciar classes em Python é simples. Para instânciar uma classe, simplesmente é chamar a classe como se fosse uma função, passando os argumentos que são necessários pelo método <code>__init__()</code>. O valor de retorno será o novo objeto recém criado.
<pre class=screen>
<samp class=p>>>> </samp><kbd class=pp>import fibonacci2</kbd>
<a><samp class=p>>>> </samp><kbd class=pp>fib = fibonacci2.Fib(100)</kbd> <span class=u>①</span></a>
<a><samp class=p>>>> </samp><kbd class=pp>fib</kbd> <span class=u>②</span></a>
<samp class=pp><fibonacci2.Fib object at 0x00DB8810></samp>
<a><samp class=p>>>> </samp><kbd class=pp>fib.__class__</kbd> <span class=u>③</span></a>
<samp class=pp><class 'fibonacci2.Fib'></samp>
<a><samp class=p>>>> </samp><kbd class=pp>fib.__doc__</kbd> <span class=u>④</span></a>
<samp class=pp>'iterator que produz números da sequência de Fibonacci'</samp></pre>
<ol>
<li>Você está criando uma instância da classe <code>Fib</code>(definido no módulo <code>fibonacci2</code>) e atribuindo a nova instância a variável <var>fib</var>. Você está passando um parâmetro, <code>100</code>, que acabará como o argumento <var>max</var> no método <code>__init__()</code> da classe <code>Fib</code>.
<li><var>fib</var> é agora uma instância da classe <code>Fib</code>.
<li>Toda instância de classe tem um atríbuto built-in, <code>__class__</code>, que é a classe do objeto. Programadores de Java podem achar semelhante a classe <code>Class</code>, que contém métodos como <code>getName()</code> e <code>getSuperclass()</code> para obter informações sobre o objeto. Em Python, estes dados estão disponíveis através de atributos, mas a ideia é a mesma.
<li>Você pode acessar o <code>docstring</code> da instância assim como uma função ou módulo. Todas as instâncias da classe compartilham o mesmo <code>docstring</code>.
</ol>
<blockquote class='note compare java'>
<p><span class=u>☞</span>Em Python, basta chamar uma classe como se fosse uma função para criar uma nova instância da classe. Não há nenhum novo operador explícito como há em <abbr>C++</abbr> ou Java.
</blockquote>
<p class=a>⁂
<h2 id=instance-variables>Variáveis de Instância</h2>
<p>Na próxima linha:
<pre class=pp><code>class Fib:
def __init__(self, max):
<a> self.max = max <span class=u>①</span></a></code></pre>
<ol>
<li>O que é <var>self.max</var>? É uma variável da instância. Este é completamento separado do <var>max</var>, o qual foi passado ao método <code>__init__()</code> como argumento. <var>self.max</var> é "global" na instância. Que significa que você pode acessá-la através de outros métodos.
</ol>
<pre class=pp><code>class Fib:
def __init__(self, max):
<a> self.max = max <span class=u>①</span></a>
.
.
.
def __next__(self):
fib = self.a
<a> if fib > self.max: <span class=u>②</span></a></code></pre>
<ol>
<li><var>self.max</var> é definido no método <code>__init__()</code>…
<li>…e referênciado no método <code>__next__()</code>.
</ol>
<p>Variáveis de instância são específicas a uma instância da classe. Por exemplo, se você criar duas instâncias de <code>Fib</code> com diferentes valores máximos, cada uma irá lembrar de seus próprios valores.
<pre class='nd screen'>
<samp class=p>>>> </samp><kbd class=pp>import fibonacci2</kbd>
<samp class=p>>>> </samp><kbd class=pp>fib1 = fibonacci2.Fib(100)</kbd>
<samp class=p>>>> </samp><kbd class=pp>fib2 = fibonacci2.Fib(200)</kbd>
<samp class=p>>>> </samp><kbd class=pp>fib1.max</kbd>
<samp class=pp>100</samp>
<samp class=p>>>> </samp><kbd class=pp>fib2.max</kbd>
<samp class=pp>200</samp></pre>
<p class=a>⁂
<h2 id=a-fibonacci-iterator>Um Iterator Fibonacci</h2>
<p><em>Agora</em> você está pronto para aprender como construir um iterator. Um iterator é apenas uma classe que define o método <code>__iter__()</code>.
<aside class=ots>
Todos os três métodos da classe, <code>__init__</code>, <code>__iter__</code>, e <code>__next__</code>, começam e terminam com um par de caracteres underscore (<code>_</code>). Por que isto? Não há nada mágico sobre isto, mas geralmente indica que estes são "métodos especiais". A única coisa "especial" nestes métodos é que não são chamados diretamente. Python os chama quando você usa uma sintaxe da classe ou instância da classe. <a href=special-method-names.html>Mais sobre métodos especiais</a>.
</aside>
<p class=d>[<a href=examples/fibonacci2.py>download <code>fibonacci2.py</code></a>]
<pre class=pp><code><a>class Fib: <span class=u>①</span></a>
<a> def __init__(self, max): <span class=u>②</span></a>
self.max = max
<a> def __iter__(self): <span class=u>③</span></a>
self.a = 0
self.b = 1
return self
<a> def __next__(self): <span class=u>④</span></a>
fib = self.a
if fib > self.max:
<a> raise StopIteration <span class=u>⑤</span></a>
self.a, self.b = self.b, self.a + self.b
<a> return fib <span class=u>⑥</span></a></code></pre>
<ol>
<li>Para construir um iterator do zero, <code>Fib</code> precisa ser uma classe, não uma função.
<li>“Chamando” <code>Fib(max)</code> estará realmente criando uma instância dessa classe e chamando o método <code>__init__()</code> com o valor <var>max</var>. O método <code>__init__()</code> salva o valor máximo com uma variável da instância para que outroas métodos também possam referênciá-lo mais tarde.
<li>O método <code>__iter__()</code> é chamado sempre que chamam <code>iter(fib)</code>. (Como você verá em um minuto, o loop <code>for</code> irá chamá-lo automaticamente, mas você pode acessá-lo manualmente). Após o inicializar a iteração( neste caso, redefinindo <code>self.a</code> e <code>self.b</code>, nossos dois contadores), o método <code>__iter__()</code> pode retornar qualquer objeto que implementa o método <code>__next__()</code>. Neste caso(e na maioria deles), <code>__iter__()</code> simplesmente retorna <var>self</var>, uma vez que esta classe implemente seu próprio método<code>__next__()</code>.
<li>O método <code>__next__()</code> é chamado sempre que alguém chama <code>next()</code> em um iterator de uma instância da classe. Isto fará sentido em um minuto.
<li>Quando o método <code>__next__()</code> gera um exceção <code>StopIteration</code>, isto sinaliza para quem o chama que a iteração chegou ao fim. Ao contrário da maioria das exceçoes, esta não é um erro; é uma condição normal que significa que não mais valores a serem gerados. Se quem o chama é um loop <code>for</code>, este vai notar a exceção <code>StopIteration</code> e sairá amigavelmente do loop. (Em outras palavras, ele vai absorver a exceção). Este é um toque de mágica usado de chave para o uso do iterator em loops <code>for</code>.
<li>Para guspir o próximo valor, o método <code>__next__()</code> simplesmente retorna este valor. Não use-o aqui, este é apenas uma pitada quando está usando generators. Aqui você esta criando iterators do use, então use <code>return</code> invés.
</ol>
<p>Completamente confuso agora? Excelente. Vamos ver como chamar este iterator:
<pre class='nd screen'>
<samp class=p>>>> </samp><kbd class=pp>from fibonacci2 import Fib</kbd>
<samp class=p>>>> </samp><kbd class=pp>for n in Fib(1000):</kbd>
<samp class=p>... </samp><kbd class=pp> print(n, end=' ')</kbd>
<samp class=pp>0 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987</samp></pre>
<p>Oras, é exatamente o mesmo! Byte à byte identico a chamada do <a href=generators.html#a-fibonacci-generator>Fibonacci-como-gerador</a> (modulo com a primeira letra maiúscula). Mas como?
<p>Há uma pitada de mágica no uso do laço <code>for</code>. Aqui está o que acontece:
<ul>
<li>O laço <code>for</code> chama <code>Fib(1000)</code>, como mostrado. Este retorna uma instância da classe <code>Fib</code>. Esta chama <var>fib_inst</var>.
<li>Secretamente, e muito inteligente, o laço <code>for</code> chama <code>iter(fib_inst)</code>, que retorna um objeto iterator. Que chama <var>fib_iter</var>. Neste caso, <var>fib_iter</var> == <var>fib_inst</var>, porque o método <code>__iter__()</code> retorna <var>self</var>, mas o laço <code>for</code> não conhece(ou cuida) sobre ele.
<li>Para percorrer o iterator, o laço <code>for</code> chama <code>next(fib_iter)</code>, que chama o método <code>__next__()</code> no objeto <code>fib_iter</code>, que acha o próximo-número-Fibonacci calcula e retorna o valor. O laço <code>for</code> obtém este valor e o atribui à <var>n</var>, então executa o corpo do laço <code>for</code> para este valor de <var>n</var>.
<li>Como o laço <code>for</code> sabe quando parar? Estou feliz em lhe responder! Quando <code>next(fib_iter)</code> retorna uma exceção como <code>StopIteration</code>, o laço <code>for</code> irá engolir a exceção e sair graciosamente. (Qualquer outra exceção será tratada como de costume.) E aonde mais você havia visto uma exceção <code>StopIteration</code>? No método <code>__next__()</code>, é claro!
</ul>
<p class=a>⁂
<h2 id=a-plural-rule-iterator>Regras gerais do Iterator</h2>
<aside>iter(f) calls f.__iter__<br>next(f) calls f.__next__</aside>
<p>Now it’s time for the finale. Let’s rewrite the <a href=generators.html>plural rules generator</a> as an iterator.
<p class=d>[<a href=examples/plural6.py>download <code>plural6.py</code></a>]
<pre class=pp><code>class LazyRules:
rules_filename = 'plural6-rules.txt'
def __init__(self):
self.pattern_file = open(self.rules_filename, encoding='utf-8')
self.cache = []
def __iter__(self):
self.cache_index = 0
return self
def __next__(self):
self.cache_index += 1
if len(self.cache) >= self.cache_index:
return self.cache[self.cache_index - 1]
if self.pattern_file.closed:
raise StopIteration
line = self.pattern_file.readline()
if not line:
self.pattern_file.close()
raise StopIteration
pattern, search, replace = line.split(None, 3)
funcs = build_match_and_apply_functions(
pattern, search, replace)
self.cache.append(funcs)
return funcs
rules = LazyRules()</code></pre>
<p>Então, esta é uma classe que implementa métodos <code>__iter__()</code> e <code>__next__()</code>, assim pode ser uma com um iterator. Então, você instânciará a classe e atribuirá suas <var>rules</var>. Isto acontece apenas na uma vez, na importação.
<p>Vamos atacar a classe de uma avez.
<pre class=pp><code>class LazyRules:
rules_filename = 'plural6-rules.txt'
def __init__(self):
<a> self.pattern_file = open(self.rules_filename, encoding='utf-8') <span class=u>①</span></a>
<a> self.cache = [] <span class=u>②</span></a></code></pre>
<ol>
<li>Quando nós instanciamos a classe <code>LazyRules</code>, abrimos um arquivo padrão, mas não lemos nada dele.(Isto será mais tarde.)
<li>Depois de abrir o arquivo de padrões, inicializar o cache. Você vai usar este cache mais tarde (no <code>__next__()</code>), enquanto você lê as linhas do arquivo padrão.
</ol>
<p>Antes de contiuarmos, vamos dar uma olhada em <var>rules_filename</var>. Não está definido no método <code>__iter__()</code>. Na verdade, não esta definido em <em>nenhum</em> método. Esta definido no nível de classe. É uma <i>variável de classe</i>, e embora você você possa acessá-la como uma variável de instância(<var>self.rules_filename</var>), e compartilhada por todas as instâncias da classe <code>LazyRules</code>.
<pre class=screen>
<samp class=p>>>> </samp><kbd class=pp>import plural6</kbd>
<samp class=p>>>> </samp><kbd class=pp>r1 = plural6.LazyRules()</kbd>
<samp class=p>>>> </samp><kbd class=pp>r2 = plural6.LazyRules()</kbd>
<a><samp class=p>>>> </samp><kbd class=pp>r1.rules_filename</kbd> <span class=u>①</span></a>
<samp class=pp>'plural6-rules.txt'</samp>
<samp class=p>>>> </samp><kbd class=pp>r2.rules_filename</kbd>
<samp class=pp>'plural6-rules.txt'</samp>
<a><samp class=p>>>> </samp><kbd class=pp>r2.rules_filename = 'r2-override.txt'</kbd> <span class=u>②</span></a>
<samp class=p>>>> </samp><kbd class=pp>r2.rules_filename</kbd>
<samp class=pp>'r2-override.txt'</samp>
<samp class=p>>>> </samp><kbd class=pp>r1.rules_filename</kbd>
<samp class=pp>'plural6-rules.txt'</samp>
<a><samp class=p>>>> </samp><kbd class=pp>r2.__class__.rules_filename</kbd> <span class=u>③</span></a>
<samp class=pp>'plural6-rules.txt'</samp>
<a><samp class=p>>>> </samp><kbd class=pp>r2.__class__.rules_filename = 'papayawhip.txt'</kbd> <span class=u>④</span></a>
<samp class=p>>>> </samp><kbd class=pp>r1.rules_filename</kbd>
<samp class=pp>'papayawhip.txt'</samp>
<a><samp class=p>>>> </samp><kbd class=pp>r2.rules_filename</kbd> <span class=u>⑤</span></a>
<samp class=pp>'r2-overridetxt'</samp></pre>
<ol>
<li>Cada instância da classe herda o atributo <var>rules_filename</var> com o valor definido pela classe.
<li>Mudando o valor do atributo em uma só instância não afeta as demais instâncias…
<li>…nem alterando o atributo da classe. Você poderá acessar o atributo da classe( ao contrário de um atributo de uma instância individual) usando um atributo especial <code>__class__</code> de acesso a própria classe.
<li>Se você alterar o atributo de classe, todas as instâncias que ainda estão herdando esse valor (como <var>r1</var> aqui) serão afetados.
<li>Instâncias que tenham substituído o atributo (como <var>r2</var> aqui) não serão afetadas.
</ol>
<p>E agora de volta ao nosso show.
<pre class=pp><code><a> def __iter__(self): <span class=u>①</span></a>
self.cache_index = 0
<a> return self <span class=u>②</span></a>
</code></pre>
<ol>
<li>O método <code>__iter__()</code> será chamado toda vez que alguém — digamos o laço <code>for</code> — chame o <code>iter(rules)</code>.
<li>A única coisa que todo método <code>__iter__()</code> deva fazer é retornar um iterator. Neste caso, ele retorna <var>self</var>, que sinaliza que esta classe define um método <code>__next__()</code> que vai cuidar do retorno dos valores ao longo da iteração.
</ol>
<pre class=pp><code><a> def __next__(self): <span class=u>①</span></a>
.
.
.
pattern, search, replace = line.split(None, 3)
<a> funcs = build_match_and_apply_functions( <span class=u>②</span></a>
pattern, search, replace)
<a> self.cache.append(funcs) <span class=u>③</span></a>
return funcs</code></pre>
<ol>
<li>O método The <code>__next__()</code> sempre é chamado quando alguém — digamos, um laço <code>for</code> — chama <code>next(rules)</code>. Este método só irá fazer sentido se iniciar no fim e ir voltando. Então vamos fazer isto.
<li>A última parte desta função parece familiar, pelo menos. A função <code>build_match_and_apply_functions()</code> não mudou, é a mesma que sempre foi.
<li>A única diferença é que, antes de retornar o que foi encontrado na função(que serão armazenados em tuplas) nós iremos salvar em <code>self.cache</code>.
</ol>
<p>Movendo para trás…
<pre class=pp><code> def __next__(self):
.
.
.
<a> line = self.pattern_file.readline() <span class=u>①</span></a>
<a> if not line: <span class=u>②</span></a>
self.pattern_file.close()
<a> raise StopIteration <span class=u>③</span></a>
.
.
.</code></pre>
<ol>
<li>Uma dica avançada no uso de arquivos aqui. O método <code>readline()</code> (nota: singular, não no plural os <code>readlines()</code>) lê exatamente uma linha de um arquivo aberto. Especificamente, a próxima linha. (Objetos de arquivo são iteradores também! É iterador toda hora…</em>)
<li>Se não houver linha para o <code>readline()</code> ler, <var>line</var> não será uma string vazia. Mesmo que o arquivo contenha uma linha em branco, <var>line</var> a linha irá acabar como uma string de um caracter '\n'. Se <var>line</var> esta realmente vazia, significa que não há mais linhas para ler do arquivo.
<li>Quando chegarmos ao fim do arquivo, devemos fechar o arquivo e provocar a exceção <code>StopIteration</code>. Lembre-se, chegamos neste ponto e devemos aplicar a próxima regra(rule). A próxima regra vem da próxima linha… mas não há próxima linha! Portanto, não iremos retornar nenhum valor. A iteração acabou. (<span class=u>♫</span> A festa acabou… <span class=u>♫</span>)
</ol>
<p>Retornando por todo o caminho até o começo do método <code>__next__()</code>…
<pre class=pp><code> def __next__(self):
self.cache_index += 1
if len(self.cache) >= self.cache_index:
<a> return self.cache[self.cache_index - 1] <span class=u>①</span></a>
if self.pattern_file.closed:
<a> raise StopIteration <span class=u>②</span></a>
.
.
.</code></pre>
<ol>
<li><code>self.cache</code> será uma lista das funções que precisam corresponder e aplicar regras individuais. (Ao menos <em>isto</em> deva soar familiar!) <code>self.cache_index</code> mantém o controle de qual item armazenado deve ser o próximo a retornar. Se não tivermos esgotado o cache ainda(ou seja, se o tamanho de <code>self.cache</code> for maior que <code>self.cache_index</code>), então esta ok! Então podemos corresponder os itens aplicando a função invés de reconstruir o cache do zero.
<li>Por outro lado, se não conseguir usar o cache, <em>e</em> o arquivo estiver fechado(isto pode acontecer, ainda mais abaixo no método, como você pode ver no código anterior) então não há mais nada oque podemos fazer. Se o arquivo estiver fechado, isso significa que já esgotou — nós já lemos cada linha do arquivo, e já coletamos cada resultado e o utilizamos. O arquivo está esgotado, o cache esgotado. Espere, tem mais. Aguenta aí, estamos quase terminando.
</ol>
<p>Juntando tudo, aqui esta o que acontece:
<ul>
<li>Quando um módulo é importando, cria-se uma instância da classe <code>LazyRules</code> class, chamada <var>rules</var>, que abre o arquivo padrão mas não o lê.
<li>Quando interrogo o primeiro item a função, irá verificar o cache mas o achará vazio. Então lê a primeira linha do arquivo, constroi e aplica em cada parte, e os armazena no cache.
<li>Vamos dizer que o primeiro elemento corresponde. Se assim for, não há mais que aplicar as funções, e nenhuma outra linha será lida do arquivo.
<li>Além disso, em função do argumento, suponhamos que tenham invocado a função <code>plural()</code> <em>denovo</em> para popular uma palavra diferente. O código do laço <code>for</code> na função <code>plural()</code> chamará <code>iter(rules)</code>, que reiniciará o índice do cache, mas não redefinirá o objeto do arquivo já aberto.
<li>A primeira vez, o laço <code>for</code> irá pedir um valor de <var>rules</var>, invocando o método <code>__next__()</code>. Desta vez, entretanto, o cache esta preparado para um único par de valores, encontrados na primeira linha do arquivo padrão. Desde que foi criado o cache durante o preenchimento das palavras seguintes, são todas recuperáveis do cache. O índice do cache é incrementado e o arquivo que está aberto não é modificado.
<li>Vamos dizer que por acaso o argumento <em>não</em> bata com a <var>rule</var> desta vez. Então o código do laço <code>for</code> irá tentar novamente e pedir outro valor das <var>rules</var>. Invocando o método <code>__next__()</code> uma segunda vez. Desta vez, o cache está esgotado — só contém um item, e pedimos por um segundo — então o método <code>__next__()</code> continua. Lê outra linha do arquivo aberto, compara com o que foi encontrado pela função, e a armazena.
<li>Este processo de leitura-processamento-e-cache vai continuar ao longo da leitura das <var>rules</var> do arquivo até encontrar uma que seja a procurada. Se não for encontrado e o fim do arquivo chegar, este continuará aberto para o próximo comando <code>readline()</code> seja invocado. Enquanto isso, o cache agora tem mais itens nele, e se começarmos novamente a tentar encontrar uma nova palavra, primeiro serão testadas com as em cache para depois tentar uma re-leitura do arquivo.
</ul>
<p>Vamos popularizar o nirvana
<ol>
<li><strong>Início de baixo custo.</strong> A única coisa importante que acontece no <code>import</code> é instanciar uma simples classe e não abrir o arquivo(mas não o lê).
<li><strong>Máxima eficiência.</strong> O exemplo anterior lê o arquivo e dinamicamente através das funções toda vez que queira preencher uma palavra. Esta versão irá armazenar em cache as funções tão cedo elas são construídas, e no pior caso, irá ler do arquivo apenas uma vez, não importa quantas palavras você irá popularizar.
<li><strong>Serparação entre código e dado.</strong> Todos os dados estão separados em um arquivo. O código é código, e dados são dados, e nunca estarão juntos.
</ol>
<blockquote class=note>
<p><span class=u>☞</span>Este é realmente o nirvana? Bem, Sim e não. Aqui há algo a considerar como um exemplo <em>preguiçoso</em> de <code>LazyRules</code> : o arquivo está aberto(durante o <code>__init__()</code>), e estará aberto até chegar ao fim do arquivo. Python eventualmente irá fechar o arquivo quando terminar, ou quando a última instância da classe <code>LazyRules</code> é distribuída, mas ainda, pode persistir por um <em>longo</em> tempo. Se esta classe é parte de um processo Python rodando a muito tempo, o interpretador pode nunca terminar, e o objeto <code>LazyRules</code> pode nunca ser destruído.
<p>Há mais formas para isto. Invés de abrir o arquivo durante o <code>__init__()</code> e deixá-lo aberto enquanto lê cada linha uma por vez, você poderia ler todas elas, e imediatamente fechar o arquivo. Ou você poderia ler uma linha do arquivo, salvar sua posição com o <a href=files.html#read>método <code>tell()</code></a>, fechar o arquivo, e re-abrir e usar o <a href=files.html#read>método <code>seek()</code></a> para continuar a ler em qualquer ponto que você tenha saído. Ou você poderia apenas deixar aberto, como no exemplo. Programação é design, e design é tudo moda e contexto. Deixar o arquivo aberto por um longo tempo pode causar um problema; fazendo seu código mais complicado e trazendo problemas. Que será um grande problema dependendo de sua equipe de desenvolvimento, da sua aplicação e de seu ambiente de execusão.
</blockquote>
<p class=a>⁂
<h2 id=furtherreading>Leituras posteriores</h2>
<ul>
<li><a href=http://docs.python.org/3.1/library/stdtypes.html#iterator-types>Iterator types</a>
<li><a href=http://www.python.org/dev/peps/pep-0234/>PEP 234: Iterators</a>
<li><a href=http://www.python.org/dev/peps/pep-0255/>PEP 255: Simple Generators</a>
<li><a href=http://www.dabeaz.com/generators/>Generator Tricks for Systems Programmers</a>
</ul>
<p class=v><a href=generators.html rel=prev title='back to “Closures & Generators”'><span class=u>☜</span></a> <a href=advanced-iterators.html rel=next title='onward to “Advanced Iterators”'><span class=u>☞</span></a>
<p class=c>© 2001–11 <a href=about.html>Mark Pilgrim</a>
<script src=j/jquery.js></script>
<script src=j/prettify.js></script>
<script src=j/dip3.js></script>