tag:blogger.com,1999:blog-13879967204364506492024-03-19T01:07:54.341-03:00KodumaroAs sombras da programaçãoℭacilhας, ℒa ℬatalemahttp://www.blogger.com/profile/14265747724618147106noreply@blogger.comBlogger50125tag:blogger.com,1999:blog-1387996720436450649.post-32504749279613678722015-11-28T09:30:00.002-03:002015-11-28T09:43:52.469-03:00A outra porta<div style="align: justify;">
<img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhO7xDBoefSuSpFEUCpulBL0xDa4aX6c0KTplrSvO5AcvfuPUeiT8Rb0y6bGrdPhmjnuGzFATXWqHdIO7XuOuxq1M1lP8VixMoC_zi94zQmwbmjLs_AWkv3evQc3BHFiK1QNe9X9dFBuGH4/s320/monty-hall.jpg" style="border-bottom-style: none; cursor: crosshair; float: left; margin-bottom: 10px; margin-left: 0pt; margin-right: 10px; margin-top: 0pt; text-align: justify;" />
O <a href="https://pt.wikipedia.org/wiki/Problema_de_Monty_Hall">Paradoxo de Monty Hall</a> consiste no fato de que, em se descartando algumas escolhas erradas, as que sobram desconhecidas combinam as probabilidades de estarem certas das descartadas.<br/>
<br/>
Por exemplo, em três portas, atrás de uma há um prêmio. Você escolhe uma das três e o mestre de cerimônias abre uma das outras duas, mostrando que o carro <strong>não</strong> está lá. A porta que você escolheu continua com ⅓ de chance de ser a correta, mas a outra porta não escolhida ganha a possibilidade de conter o prêmio da que foi aberta, passando a ser de ⅔.<br/>
<br/>
Isso acontece porque a porta com o prêmio <strong>e</strong> a porta escolhida (independente de ser a do prêmio ou não) influenciaram juntas na escolha da porta a ser aberta, mesmo que você não saiba qual a porta do prêmio.<br/>
<br/>
Muita gente discorda disso, diz que se sobraram duas portas, as chances passa a ser ½+½ e não ⅓+⅔. Pode-se ficar horas (ou dias) discutindo isso. A melhor forma de se verificar isso é testando!<br/>
<br/>
Vamos escrever uma classe que <code>MasterOfCeremonies</code> que oferece três portas, sendo uma premiada:
</div>
<pre><code class="prettyprint">from random import choice
class MasterOfCeremonies:
__slots__ = '__doors __prize __choice'.split()
def __init__(self) -> None:
self.__doors = 'ABC'
self.__prize = choice(self__doors)
self.__choice = None
@property
def doors(self) -> str:
return self.__doors
@property
def wins(self) -> bool:
if not self.__choice:
raise TypeError
return self.__choice == self.__prize
def choose(self, door: str) -> None:
if door not in self.__doors:
raise ValueError
self.__choice = door</code></pre>
<br/>
<div style="align: justify;">
Nossa classe guarda três portas (<code>A</code>, <code>B</code> e <code>C</code>), apenas uma premiada. Para testar, podemos jogar mil vezes e vermos em quantas vezes ganhamos.<br/>
<br/>
Para facilitar, vamos escolher sempre a porta do meio:
</div>
<pre><code class="prettyprint">if __name__ == '__main__':
wins = 0
for _ in range(1000):
wc = MasterOfCeremonies()
door = wc.doors[1] # sempre a porta do meio
wc.choose(door)
wins += 1 if wc.wins else 0
print('Wins:', wins)</code></pre>
<br/>
<div style="align: justify;">
Não importa quantas vezes você execute esse programa, vai sempre retornar um valor próximo de 333 vitórias – o que faz todo sentido! São mil lances, ⅓ dá 333.<br/>
<br/>
Agora vamos adicionar o paradoxo: um método que abre aleatoriamente uma porta sem prêmio e outro que permite ao jogador trocar para a porta fechada restante.<br/>
<br/>
Dentro da classe <code>MasterOfCeremonies</code> adicione os seguintes métodos:
</div>
<pre><code class="prettyprint"> def remove(self) -> None:
if not self.__choice or len(self.__doors) == 2:
raise TypeError
doors = set(self.__doors)
others = doors - {self.__choices, self.__prize}
other = choice(list(others))
doors -= {other}
self.__doors = ''.join(list(doors))
def change(self) -> str:
if not (self.__choice and len(self.__doors) == 2):
raise TypeError
doors = set(self.__doors)
doors -= {self.__choice}
self.__choice = list(doors)[0]
return self.__choice</code></pre>
<br/>
<div style="align: justify;">
Agora vamos mudar nosso procedimento principal para trocar de porta após a abertura (<code>remove</code>) da porta sem prêmio:
</div>
<pre><code class="prettyprint">if __name__ == '__main__':
wins = 0
for _ in range(1000):
wc = MasterOfCeremonies()
door = wc.doors[1] # sempre a porta do meio
mc.choose(door)
mc.remove() # descarta uma das portas sem prêmio
door = mc.change() # troca a porta escolhida
wins += 1 if wc.wins else 0
print('Wins:', wins)</code></pre>
<br/>
<div style="align: justify;">
Agora vem o <em>mindfuck</em>: não importa quantas vezes você execute esse programa, o número de vitórias em mil será sempre por volta de 667, ⅔ das tentativas!<br/>
<br/>
Ou seja, o paradoxo realmente <strong>funciona</strong>. Volte lá na <a href="https://pt.wikipedia.org/wiki/Problema_de_Monty_Hall">Wikipédia</a>, porque a explicação é realmente muito boa. ;-)
</div><br/>
[]’s<br/>
<br/>
<div style="align: justify; font-size: x-small;">
PS: Eu ia escrever os códigos em <a href="http://www.swi-prolog.org/">Prolog</a>, pois sua abordagem de lidar com domínio de verdades é muito conveniente para este problema, no entanto <a href="https://www.python.org/">Python</a> é muito mais didática: já basta a dificuldade em entender o Paradoxo de Monty Hall, não precisamos de mais complicação com uma implementação complexa.
</div>ℭacilhας, ℒa ℬatalemahttp://www.blogger.com/profile/14265747724618147106noreply@blogger.com0tag:blogger.com,1999:blog-1387996720436450649.post-77945385622986443212015-04-18T11:36:00.002-03:002015-11-28T21:36:34.147-03:00Tipos em Cython<div style="text-align: justify;">
<img alt="" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhPaKA7jLAUrF0pnHVfaRtLGtoeTnIVipVEAqCMrgj-BwfwAJ8RMlzqzYSxyemSHfGNLTeKXnXkjUHyqUHNDh1-Sdsf7Fij2OOmYkyArv3UGG6_257q_ShEFIjV7SiaAR8i1KXk4_2U0MMR/s1600/cython.jpg" style="border: none; cursor: crosshair; float: left; margin: 0pt 10px 10px 0pt;" /> <a href="http://kodumaro.blogspot.com.br/2015/04/cython.html">Ontem</a> fiz uma pequena introdução ao <a href="http://cython.org/">Cython</a>.<br/>
<br/>
Cython é uma plataforma que traduz código <a href="https://www.python.org/">Python</a> para C e o compila em biblioteca compartilhada importável no próprio Python. A linguagem em si é um <em>superset</em> de Python, com tipagem estática ou dinâmica (<em>duck-typing</em>) e suporte a código especial que pode ser traduzido diretamente para C.<br/>
<br/>
Uma parte importante de Cython é sua tipagem estática, mas os tipos pode ser um pouco diferentes de Python.<br/>
<br/>
Há tipos específicos, como <code>struct</code> e <code>enum</code>, que são traduzidos diretamente para o equivalente C, e tipo de extensão (<code>cdef class</code>). Entre eles:<br/>
</div><br/>
<table>
<thead>
<tr><th>Tipo Cython</th><th>Tipo C</th><th>Coerção para Python 3</th></tr>
</thead>
<tbody>
<tr>
<td><code>bool</code></td>
<td><code>PyLongObject *</code></td>
<td><code>bool</code></td>
</tr>
<tr>
<td><code>bint</code></td>
<td><code>int</code></td>
<td><code>bool</code></td>
</tr>
<tr>
<td><code>size_t</code></td>
<td><code>size_t</code></td>
<td><code>int</code></td>
</tr>
<tr>
<td><code>char</code></td>
<td><code>char</code></td>
<td><code>int</code></td>
</tr>
<tr>
<td><code>unsigned char</code></td>
<td><code>unsigned char</code></td>
<td><code>int</code></td>
</tr>
<tr>
<td><code>int</code></td>
<td><code>int</code></td>
<td><code>int</code></td>
</tr>
<tr>
<td><code>long</code></td>
<td><code>long</code></td>
<td><code>int</code></td>
</tr>
<tr>
<td><code>long long</code></td>
<td><code>long long</code></td>
<td><code>int</code></td>
</tr>
<tr>
<td><code>float</code></td>
<td><code>float</code></td>
<td><code>float</code></td>
</tr>
<tr>
<td><code>double</code></td>
<td><code>double</code></td>
<td><code>float</code></td>
</tr>
<tr>
<td><code>const char *</code></td>
<td><code>const char *</code></td>
<td><code>bytes</code></td>
</tr>
<tr>
<td><code>bytes</code></td>
<td><code>PyBytesObject *</code></td>
<td><code>bytes</code></td>
</tr>
<tr>
<td><code>const Py_UNICODE *</code></td>
<td><code>const Py_UNICODE *</code></td>
<td><code>str</code></td>
</tr>
<tr>
<td><code>unicode</code></td>
<td><code>struct PyUnicodeObject</code></td>
<td><code>str</code></td>
</tr>
<tr>
<td><code>object</code></td>
<td><code>PyObject *</code></td>
<td><code>object</code></td>
</tr>
<tr>
<td><code>list</code></td>
<td><code>PyListObject *</code></td>
<td><code>list</code></td>
</tr>
<tr>
<td><code>dict</code></td>
<td><code>PyDictObject *</code></td>
<td><code>dict</code></td>
</tr>
<tr>
<td><code>set</code></td>
<td><code>PySetObject *</code></td>
<td><code>set</code></td>
</tr>
<tr>
<td><code>tuple</code></td>
<td><code>PyTupleObject *</code></td>
<td><code>tuple</code></td>
</tr>
<tr>
<td><code>void *</code></td>
<td><code>void *</code></td>
<td>sem equivalência</td>
</tr>
<tr>
<td><code>struct <em>S</em></code></td>
<td><code>struct <em>S</em></code></td>
<td><code>dict</code></td>
</tr>
<tr>
<td><code>enum <em>E</em></code></td>
<td><code>enum <em>E</em></code></td>
<td><code>int</code></td>
</tr>
</tbody>
</table><br/>
<div style="text-align: justify;">
Todos os modificadores C (<code>unsigned</code>, <code>const</code>, <code>long</code>, <code>*</code>…) são aceitos. Na coerção de tipos, também é aceito <code>&</code>. Se um valor numérico for recebido por uma variável do tipo <code>object</code>, será usado <code>PyLongObject *</code> (inteiro de tamanho arbitrário) ou <code>PyFloatObject *</code> (ponto flutuante, equivalente a <code>double</code> de C).
</div>
<br/>
[]’s<br />
ℭacilhας, La Batalemaℭacilhας, ℒa ℬatalemahttp://www.blogger.com/profile/14265747724618147106noreply@blogger.com0tag:blogger.com,1999:blog-1387996720436450649.post-42626865911107642262015-04-17T21:36:00.001-03:002015-06-19T13:10:11.383-03:00Cython<div style="text-align: justify;">
<img alt="" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhPaKA7jLAUrF0pnHVfaRtLGtoeTnIVipVEAqCMrgj-BwfwAJ8RMlzqzYSxyemSHfGNLTeKXnXkjUHyqUHNDh1-Sdsf7Fij2OOmYkyArv3UGG6_257q_ShEFIjV7SiaAR8i1KXk4_2U0MMR/s1600/cython.jpg" style="border: none; cursor: crosshair; float: left; margin: 0pt 10px 10px 0pt;" /> Nas últimas semanas tenho desenvolvido uma aplicação usando <a href="http://cython.org">Cython</a> e me surpreendi com o resultado.<br/>
<br/>
Comecei usando o Cython apenas para compilar código Python – o que de fato rendeu o aumento de desempenho prometido –, mas então resolvi ir mais longe pra ver o quanto poderia extrair dessa plataforma: comecei a usar tipagem estática, funções C (<code>cdef</code>) e depois acabei migrando minhas classes para tipos de extensão (<code>cdef class</code>es).<br/>
<br/>
A cada novo passo era perceptível o crescimento do desempenho e da coerência geral do código. Minha única crítica é quanto aos testes: o código fica muito difícil de ser testado, já que não é possível fazer <em>monkey-patch</em> para colocar os <em>mocks</em>.<br/>
<br/>
Segundo a documentação, ao compilar código Python com Cython, você tem um aumento de 35% no desempenho do código. Usando a tipagem estática, o aumento é de 300% e usando funções C e tipos de extensão o aumento é de aproximadamente 15.000%.<br/>
<br/>
Não posso confirmar esses números, pois não fiz medições exatas, mas uma fila do RabbitMQ que enchia com 6, 7 mil mensagens, encheu com 64 mil no mesmo período de tempo (eu estava fazendo apenas carga, sem consumir a fila).<br/>
<br/>
Uma coisa que gostei muito no Cython é o <em>feeling</em>: tem uma pegada bastante parecida com a do <a href="https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/ProgrammingWithObjectiveC/Introduction/Introduction.html">Objective C</a>, o que faz sentido.<br/>
<br/>
Vou dar um exemplo da própria <a href="http://docs.cython.org/src/quickstart/cythonize.html">documentação</a> do Cython:<br/>
<br/>
Dado o seguinte código Python puro:
<pre><code class="prettyprint">def f(x):
return x**2-x
def integrate_f(a, b, N):
s = 0
dx = (b-a)/N
for i in range(N):
s += f(a+i*dx)
return s * dx</code></pre>
Você pode salvar esse código em <code>integrate.pyx</code> e compilá-lo assim:
<pre><code>bash$ <strong>cythonize -ib integrate.pyx</strong></code></pre>
Isso irá gerar o código equivalente em C (<code>integrate.c</code>) e compilá-lo na biblioteca <code>integrate.so</code>, que pode ser diretamente importada em Python:
<pre><code>bash$ <strong>python</strong>
>>> <strong>from integrate import integrate_f</strong>
>>></code></pre>
Se você tiver o <a href="http://ipython.org">IPython</a>, pode usar o Cython no <em>prompt</em>:
<pre><code>bash$ <strong>ipython</strong>
Python 2.7.9 (default, Jan 7 2015, 11:49:12)
[GCC 4.2.1 Compatible Apple LLVM 6.0 (clang-600.0.56)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> <strong>%load_ext Cython</strong>
>>> <strong>%%cython</strong>
<strong>from integrate cimport integrate_f</strong></code></pre>
De qualquer forma, só usando a biblioteca compilada em vez do código Python importado, Cython já promete um aumento de 35% de <em>performance</em> – o que não me frustrou.<br/>
<br/>
Além disso, podemos adicionar tipagem ao código: Cython é um <em>superset</em> em Python, ou seja, é uma linguagem em si e um código Python é também código válido Cython.
<pre><code class="prettyprint">def f(double x):
return x**2-x
def integrate_f(double a, double b, int N):
cdef:
int i
double s, dx
s = 0
dx = (b-a)/N
for i in range(N):
s += f(a+i*dx)
return s * dx</code></pre>
Segundo a documentação, isso garante um desempenho 4 vezes superior ao de Python.<br/>
<br/>
No entanto ainda é possível otimizar mais ainda o código! Cython tem uma sintaxe específica que gera código C diretamente, <code>cdef</code>:
<pre><code class="prettyprint">cdef double f(double x) except? -2:
return x**2-x
def integrate_f(double a, double b, int N):
cdef:
int i
double s, dx
s = 0
dx = (b-a)/N
for i in range(N):
s += f(a+i*dx)
return s * dx</code></pre>
O desempenho dessa função <code>f</code> em C promete ser 150 vezes melhor do que a mesma função em Python puro.<br/>
<br/>
O <code>except? -2</code> na função é usado para passar exceções para o código C. Em um outro momento posso entrar em mais detalhes.<br/>
<br/>
<h3>Tipo de extensão</h3>
<a href="http://docs.cython.org/src/tutorial/cdef_classes.html">Tipos de extensão</a> são equivalentes às classes de Python, porém mais restritos e muito mais eficientes.<br/>
<br/>
Por exemplo, da a seguinte classe:
<pre><code class="prettyprint">class Consumer(object):
def __init__(self, backend):
self.backend = backend
def run(self, handler):
resp = self.backend.get()
return handler.handle(resp)</code></pre>
Considerando que as instâncias só serão executadas em Cython, o código equivalente estaria em dois arquivos, o primeiro <code>consumer.pxd</code>:
<pre><code class="prettyprint">from backends.base cimport Backend
from handlers.base cimport Handler
cdef class Consumer:
cdef:
Backend backend
int run(self, Handler handler) except? -1</code></pre>
Esse código <code>.pxd</code> equivale ao cabeçalho <code>.h</code> de C. Agora, o arquivo de implementação deve ser chamado <code>consumer.pyx</code>:
<pre><code class="prettyprint">from backends.base cimport Backend
from handlers.base cimport Handler
cdef class Consumer:
def __cinit__(self, Backend backend):
self.backend = backend
cdef int run(self, Handler handler) except? -1:
cdef dict resp = self.backend.get()
return handler.handle(resp)</code></pre>
Esse código promete ser muito mais eficiente que sua versão em Python, infelizmente métodos declarados como <code>cdef</code> são acessíveis apenas em C (e em Cython). Para que o método seja acessível em Python, ele deve ser criado como um método Python comum (<code>def</code>) ou pode ser usado <code>cpdef</code>.<br/>
<br/>
O comando <code>cpdef</code> (C/Python <code>def</code>) cria a função C (como <code>cdef</code>) e um <em>wrapper</em> em Python para torná-la acessível. Se o módulo for importando com <code>cimport</code>, será usada a função C original; se for importado com o clássico <code>import</code>, será usado o <em>wrapper</em>.<br/>
<br/>
<h3>Conclusão</h3>
Até agora não tive motivos para me arrepender de usar Cython, recomendo.
</div>
<br/>
[]’s<br />
ℭacilhας, La Batalemaℭacilhας, ℒa ℬatalemahttp://www.blogger.com/profile/14265747724618147106noreply@blogger.com0tag:blogger.com,1999:blog-1387996720436450649.post-91216434924656432642015-04-15T00:12:00.000-03:002015-11-28T22:07:30.949-03:00Uma brincadeira rápida<div style="text-align: justify;">
<img alt="" src="http://photos1.blogger.com/blogger/6505/3295/200/python.png" style="border: none; cursor: crosshair; float: left; margin: 0pt 10px 10px 0pt;" /><br /><br />
Um jeito divertido de calcular <a href="http://pt.wikipedia.org/wiki/Sequência_de_Fibonacci">Fibonacci</a> usando <a href="http://www.numpy.org">NumPy</a>:<br /><br />
</div>
<pre><code class="prettyprint">from collections.abc import Callable
from numpy import matrix, long
def fib() -> Callable:
m = matrix('1, 1; 1, 0', dtype=long)
def fib(n: int) -> long:
return (m ** n)[0, 0]
return fib
fib = fib()
</code></pre>
<br />
<h3>Cython</h3>
<pre><code class="prettyprint">from libc.stdint cimport int64_t
from numpy cimport ndarray
from numpy import matrix, long
cdef:
ndarray m = matrix('1, 1; 1, 0', dtype=long)
cpdef int64_t fib(int n):
return (m ** n)[0, 0]
</code></pre>
<br/>
[]’s<br />
<br />
<blockquote><tt>[update 2015-11-28]</tt><br/>
Código atualizado para Python 3 e versão Cython.<br/>
<tt>[/update]</tt></blockquote>ℭacilhας, ℒa ℬatalemahttp://www.blogger.com/profile/14265747724618147106noreply@blogger.com0tag:blogger.com,1999:blog-1387996720436450649.post-53516003884348457212014-08-24T21:55:00.000-03:002014-09-24T17:22:17.379-03:00Combinador de ponto fixo<div style="text-align: justify;">
<img alt="Glider" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgjbk9FiJ0AyG1etlZdJdzcq0D0pGTaz_N6qtMS0U6GNgVG3K9QEO2rYvQRO-wy1DsTOWP78J03gmtb0Fd8LcaUZG5cL5yt4bA1BTFUarOaLyrnHNU7t85bS775VqVBp5Wh6kc-k38f05A/s200/glider.png" style="border: none; cursor: crosshair; float: left; height: 55px; margin: 0 10px 10px 0; width: 55px;" />
A beleza da matemática e do cálculo é que algo não precisa ter uma aplicação prática imediata para ser estudado em profundidade. A aplicabilidade pode surgir 150, 200 anos após sua concepção.<br />
<br />
Um dos tópicos mais magníficos do <a href="http://pt.wikipedia.org/wiki/C%C3%A1lculo_lambda">cálculo-λ</a> é o combinador de ponto fixo, apesar de ser de interesse mais acadêmico do que diretamente prático.<br />
<br />
Ainda assim, seu entendimento engrandece em tamanha proporção o conhecimento do programador, que vale a pela dedicar-lhe algum tempo.<br />
<br />
<h3>
Conceitos básicos</h3>
Há alguns pré-requisitos que você precisa conhecer para começar nosso estudo:<br />
<br />
<b>Cálculo-λ</b><br />
<br />
Cálculo-λ é uma parte da lógica matemática criada da década de 1930 como parte da investigação dos fundamentos da matemática. No cálculo-λ, todos os elementos são funções de primeira classe, inclusive os números.<br />
<br />
Por exemplo, o número 2 é uma função que aplica uma outra função duas vezes, e é representado como <code>λsz.s(sz)</code>.<br />
<br />
O sinal + (ou <code>S</code> em alguns textos) significa incremento, e é descrito como <code>λwyx.y(wyx)</code>. Ou seja, +3 é 3 incrementado, que é igual a 4. 2+3 é a função 2 recebendo como parâmetros + e 3, ou seja, incrementa duas vezes o número 3, +(+3), o que resulta em 5.<br />
<br />
A multiplicação é prefixal, não mesoclítica, ou seja, 2x escreve-se como <code>*2x</code> (ou <code>M2x</code>) e sua definição é <code>λab.a(b+)0</code> ou <code>λxyz.x(yz)</code>.<br />
<br />
O cálculo-λ é o princípio de onde deriva a <a href="http://pt.wikipedia.org/wiki/Programa%C3%A7%C3%A3o_funcional">programação funcional</a>.<br />
<br />
<blockquote>
<tt>[update 2014-08-25]</tt><br />
A definição de decremento é (caso eu não tenha copiado errado):
<br />
<pre><code>DEC = λnfx.n (λgh.h(gf)) (λu.x) I</code></pre>
<tt>[/update]</tt></blockquote>
<br />
<b>Ponto fixo</b><br />
<br />
Impossível entender combinador de ponto fixo sem saber o que significa ponto fixo. Ponto fixo é o ponto de uma função onde o valor retornado é igual ao valor fornecido.<br />
<br />
Por exemplo, dada a função f(x) = 2x-1, o que em notação cálculo-λ fica <code>f = λx.DEC(*2x)</code>, o ponto fixo é aquele onde <code>fx = x</code>, ou seja, o resultado é igual ao parâmetro.<br />
<br />
Usando aritmética básica:<br />
<pre>f(x) = 2x - 1
x = 2x - 1
2x - 1 = x
2x - 1 - x = 0
x - 1 = 0
x = 1</pre>
<br />
Portanto o ponto fixo de <code>λx.DEC(*2x)</code> é 1.<br />
<br />
Repare que a recursão sobre o ponto fixo resulta sempre no mesmo resultado. Se:<br />
<pre><code>x = fx</code></pre>
<br />
Podemos substituir <code>x</code> por <code>fx</code>:<br />
<pre><code>x = fx = f(fx) = f(f(fx))) = ...</code></pre>
<br />
<br />
<b>Variáveis livres e vinculadas</b><br />
<br />
Em cálculo-λ, no corpo de uma função, todas as variáveis que vêm de sua assinatura são vinculadas e as que vêm de fora são livres.<br />
<br />
Por exemplo, na função:<br />
<pre><code>λfx.Δxf</code></pre>
<br />
<br />
As variáveis <code>x</code> e <code>f</code> são vinculadas e a variável <code>Δ</code> é livre.<br />
<br />
<b>Combinadores</b><br />
<br />
Combinadores são funções que não possuem variáveis livres. Por exemplo:<br />
<pre><code>λx.xx
λx.y</code></pre>
<br />
A primeira linha é um combinador, já a segunda <b>não</b>.<br />
<br />
Veja que, a rigor, números e operadores aritméticos são variáveis livres, porém, como são valores constantes e bem definidos, tratam-se de exceções aceitáveis em um combinador.<br />
<br />
<b>Construção <i>let</i></b><br />
<br />
A construção <i>let</i> é uma construção clássica em cálculo-λ e em linguagens funcionais de programação.<br />
<br />
Darei um exemplo: seja <code>fx</code> igual a <code>*2x</code> em <code>f4</code>, o resultado é 8 – <code>fx</code> é o dobro e <code>x</code> e o caso é <code>f4</code>. Isso é descrito assim:<br />
<pre><code>let fx = *2x in f4</code></pre>
<br />
Podemos construir uma função assim, por exemplo, dado o parâmetro <code>x</code>, sendo <code>fa</code> igual a <code>*2a</code> em <code>fx-1</code>:<br />
<pre><code>λx.let fa = *2a in DEC(fx)</code></pre>
<br />
É o mesmo que:<br />
<pre><code>λx.DEC(*2x)</code></pre>
<br />
Em <a href="http://www.haskell.org/">Haskell</a> isso pode ser escrito assim:<br />
<pre><code class="prettyprint">func x = let f a = 2 * a in f x - 1</code></pre>
<br />
Em <a href="http://racket-lang.org/">Racket</a> fica assim:<br />
<pre><code>(describe func
(λ (x)
(let ((f (λ (a) (* 2 a)))
(- (f x) 1))))</code></pre>
<br />
A forma genérica da construção <i>let</i> é:<br />
<pre><code>let fx = y in z</code></pre>
<br />
E sua abertura é:<br />
<pre><code>(λf.z)(λx.y)</code></pre>
<br />
<br />
<h3>
Finalmente: o combinador de ponto fixo</h3>
Combinador de ponto fixo é uma função que retorna o ponto fixo da função fornecida como parâmetro. Em cálculo e mesmo em programação isso é muito útil na implementação de funções recursivas.<br />
<br />
Função recursiva é aquela que faz chamada a ela mesma, por exemplo, fatorial:<br />
<pre><code>FACT = λn.(ISZERO n) 1 (*n (FACT (DEC n)))</code></pre>
<br />
Olhando para o corpo da função, mesmo desconsiderando números e operadores (<code>ISZERO</code>, <code>1</code>, <code>*</code> e <code>DEC</code>), ainda há a variável livre <code>FACT</code>, que não é definida na assinatura da função.<br />
<br />
<blockquote>
<tt>[update 2014-08-25]</tt><br />
A definição de <code>ISZERO</code> é:
<br />
<pre><code>TRUE = λxy.x
FALSE = λxy.y
ISZERO = λn.n(λx.FALSE)TRUE</code></pre>
<tt>[/update]</tt></blockquote>
<br />
A forma de resolver isso é criar uma função que recebe a si mesma na assinatura:<br />
<pre><code>ALMOST-FACT = λfn.(ISZERO n) 1 (*n (f (DEC n)))</code></pre>
<br />
Quando esse combinador recebe como parâmetro o fatorial, ele retorna o próprio fatorial, assim a função fatorial é o <b>ponto fixo</b> dele!<br />
<br />
O combinador de ponto fixo é aquele que, ao receber a função como parâmetro, retorna seu ponto fixo – e quando a função recebe o ponto fixo, retorna o próprio ponto fixo, segundo o seguinte comportamento:<br />
<pre><code>yf = f(yf)</code></pre>
<br />
Só aqui já seria possível implementar uma função que calcule o ponto fixo. Em Haskell:<br />
<pre><code class="prettyprint">fix f = f (fix f)</code></pre>
<br />
E em <a href="http://docs.racket-lang.org/lazy/">Lazy Racket</a>:<br />
<pre><code>(define fix
(λ (f)
(f (fix f))))</code></pre>
<br />
Porém seria sem sentido, pois essa mesma função que resolve o problema, é parte do problema, já que ela própria possui variável livre.<br />
<br />
Resolver este problema não é difícil: basta pegarmos a definição: dada uma função <code>f</code>, queremos o ponto <code>x</code> onde <code>fx</code> seja igual a <code>x</code>… é uma construção <i>let</i>!<br />
<br />
Data uma função que recebe <code>f</code> (<code>λf.</code>), seja <code>x</code> igual a <code>fx</code> (<code>let x = fx</code>) em <code>x</code> (<code>in x</code>):<br />
<pre><code>λf.let x = fx in x</code></pre>
<br />
De fato isso já funciona em Haskell:<br />
<pre><code class="prettyprint">fix :: (a -> a) -> a
fix f = let x = f x in x</code></pre>
<br />
<br />
<h3>
Combinador Y</h3>
Para Scheme (Lazy Racket), precisamos destrinchar um pouco mais, abrindo o <i>let</i>.<br />
<br />
Precisamos de dois parâmetros na primeira posição, portanto dobraremos o <code>x</code>:<br />
<pre><code>λf.let x = fx in x
λf.let xx = f(xx) in xx
λf.(λx.xx)(λx.f(xx))</code></pre>
<br />
Mais um passo e chegamos ao famoso <a href="https://mvanier.livejournal.com/2897.html">combinador Y</a> de <a href="http://pt.wikipedia.org/wiki/Haskell_Curry">Curry</a>!<br />
<br />
Aplicando o parâmetro <code>λx.f(xx)</code> à função <code>λx.xx</code>, temos <code>(λx.f(xx))(λx.f(xx))</code>, exatamente o combinador Y de Curry:<br />
<pre><code>Y = λf.(λx.f(xx))(λx.f(xx))</code></pre>
<br />
Em Lazy Racket:<br />
<pre><code>(define Y
(λ (f)
((λ (x) (f (x x))) (λ (x) (f (x x))))))</code></pre>
<br />
<br />
<h3>
Linguagens estritas</h3>
A maioria das linguagens não suporta <i>lazy evaluation</i>, então é preciso fazer um truque para que a definição do combinador não entre em <i>loop</i> infinito.<br />
<br />
O truque é aplicar a função <code>λsv.sv</code> (que é o número 1, equivalente à identidade: <code>I = λx.x</code>). Por exemplo:<br />
<pre><code>g = 1g
g = (λsv.sv) g
g = (λv.gv)</code></pre>
<br />
Por exemplo, o combinador Y em <a href="https://www.python.org/">Python</a>:<br />
<pre><code class="prettyprint">Y = lambda f: (lambda x: x(x))(lambda x: f(x(x)))</code></pre>
<br />
Esse código entra em <b><i>loop</i> infinito</b>! Mas se substituirmos <code>xx</code> por <code>λv.xxv</code>, tudo se resolve:<br />
<pre><code class="prettyprint">Y = lambda f: (lambda x: x(x))(lambda x: f(lambda *args: x(x)(*args)))</code></pre>
<br />
<br />
<h3>
Outros combinadores de ponto fixo</h3>
Curry foi o que obteve a solução mais simples para o combinador de ponto fixo, mas não foi o único. Há outras soluções possíveis.<br />
<br />
<b>Combinador de <a href="http://pt.wikipedia.org/wiki/Alan_Turing">Turing</a>:</b><br />
<pre><code>Θ = (λx.xx)(λab.b(aab))</code></pre>
<br />
<br />
<b>Combinador de ponto fixo estrito (vimos acima):</b><br />
<pre><code>Z = λf.(λx.xx)(λx.f(λv.xxv))</code></pre>
<br />
<b>Combinador de ponto fixo não padrão:</b><br />
<pre><code>B = λwyx.w(yx)
∆ = λx.xx
N = B∆(B(B ∆)B)</code></pre>
<br /></div>
[]’sℭacilhας, ℒa ℬatalemahttp://www.blogger.com/profile/14265747724618147106noreply@blogger.com0tag:blogger.com,1999:blog-1387996720436450649.post-75002645481554321742014-04-12T11:42:00.002-03:002014-04-12T11:42:43.018-03:00Modelo de dados em Python<div style="text-align: justify;">
<img alt="" src="http://photos1.blogger.com/blogger/6505/3295/200/python.png" style="border: none; cursor: crosshair; float: left; margin: 0pt 10px 10px 0pt;" /> Ao escrever uma classe em <a href="https://www.python.org/">Python</a>, é preciso ficar atento a algumas convenções a serem respeitadas.<br />
<br /></div>
<h3>
Métodos especiais</h3>
<div style="text-align: justify;">
Em Python, métodos começando e terminando com sublinha dobrado (<code>__*__</code>) são reservados e chamados <b>especiais</b>. Você <b>não deve</b> implementá-los se não souber o que está fazendo.<br />
<br />
Métodos especiais podem fazer coisas maravilhosas ou criar comportamentos inesperados que <i>zoarão</i> com sua aplicação. São divididos em 12 tipos:<br />
<ol>
<li>Personalização básica;</li>
<li>Acesso personalizado a atributos;</li>
<li>Criação personalizada de classes;</li>
<li>Verificações personalizadas de instâncias e subclasses;</li>
<li>Emulação de chamada;</li>
<li>Emulação de recipiente;</li>
<li>Emulação de tipos sequenciais;</li>
<li>Emulação de tipos numéricos;</li>
<li>Regras de coerção;</li>
<li>Gerenciamento de contexto;</li>
<li>Pesquisa de classes <i>old-style</i>;</li>
<li>Pesquisa de classes <i>new-style</i>.</li>
</ol>
<br />
Recomendo fortemente a leitura do documento <a href="https://docs.python.org/2/reference/datamodel.html">Data model</a>, é <b>essencial</b> ao bom programador.<br />
<br /></div>
<h3>
Métodos privados</h3>
<div style="text-align: justify;">
Métodos privados são iniciados com sublinha dobrado, mas <b>não</b> terminados da mesma forma (<code>__*</code>).<br />
Um método privado <b>não pode</b> ser acessado de outro contexto a não ser da classe onde ele foi definido.<br />
<br />
Isso não é de todo verdade… o que acontece de fato é que métodos privados são prefixados com um sublinha seguido do nome da classe, para evitar que sejam sobrescritos e prevenir acesso a partir de subclasses ou de fora do contexto da classe.<br />
<br />
Por exemplo, no contexto da classe <code>Person</code>, todos os métodos iniciados com sublinha dobrado serão prefixados com <code>_Person</code>. Assim, <code>__fix_data</code> vira <code>_Person__fix_data</code>.<br />
<br />
Isso permite que você faça herança múltipla de classes que possuem o <i>mesmo</i> nome de método privado sem conflitos.</div>
<h3>
Métodos protegidos</h3>
<div style="text-align: justify;">
Há uma convenção em Python de que métodos com nome iniciado com um sublinha (<code>_*</code>) são protegidos, ou seja, só devem ser acessados no contexto da classe e de suas subclasses.<br />
<br />
<b>Nenhum tratamento</b> é feito para evitar que sejam acessados de outros contextos, mas se espera que os programadores sigam a convenção.<br />
<br />
A orientação em Python é que o programador não é nenhum bebezinho que precisa ser guiado e sabe o que está fazendo, portanto não aja como um bebê e respeite as convenções.<br />
<br /></div>
<h3>
Atributos e propriedades</h3>
<div style="text-align: justify;">
<b>Tudo</b> o que foi dito sobre métodos especiais, privados e protegidos <b>também vale</b> para atributos e propriedades.
</div>
<br />
[]’s<br />
Cacilhας, La Batalema ℭacilhας, ℒa ℬatalemahttp://www.blogger.com/profile/14265747724618147106noreply@blogger.com0tag:blogger.com,1999:blog-1387996720436450649.post-7999621972812584002014-04-12T09:40:00.000-03:002015-04-18T17:54:40.357-03:00Ordem de chamada dos métodos de inicialização<div style="text-align: justify;">
<img alt="" src="http://photos1.blogger.com/blogger/6505/3295/200/python.png" style="border: none; cursor: crosshair; float: left; margin: 0pt 10px 10px 0pt;" /> Quando você cria e instancia uma classe em <a href="https://www.python.org/">Python</a>, muita coisa acontece e acho que nem todos estão familiarizados com os passos.<br />
<br />
Vamos dar uma olhada na sequência.<br />
<br />
<h3>
Criação da classe</h3>
Quando você cria uma classe com o <i>statement</i> <code>class</code>, a primeira coisa que acontece é o construtor (<code>__new__</code>) da metaclasse (por padrão <code>type</code>) ser chamado.<br />
<br />
O construtor recebe como parâmetros a própria metaclasse, o nome da classe (<code>str</code>), uma tupla contendo as classes base da classe criada e um dicionário com métodos e atributos declarados no corpo da classe.<br />
<br />
Caso você sobrescreva o construtor da metaclasse, ele precisa retornar o objeto instanciado, que você obtém do construtor da classe pai da metaclasse.<br />
<br />
Em seguida o método de inicialização (<code>__init__</code>) da metaclasse é chamado, recebendo como parâmetros o retorno do construtor, o nome da classe, a tupla de classes base e o dicionário de métodos e atributos.<br />
<br />
É recomendável <b>não</b> sobrescrever o construtor da metaclasse. Qualquer procedimento pode ser tranquilamente executado nos métodos e inicialização e de chamada.<br />
<br />
<h3>
Instanciação da classe</h3>
Quando você instancia a classe, o primeiro método a ser chamado é o método de chamada (<code>__call__</code>) da metaclasse. Ele recebe como parâmetros a classe e quaisquer parâmetros passados no comando de instanciação.<br />
<br />
Em seguida é evocado o construtor da classe, recebendo a classe e quaisquer parâmetros passados no comando de instanciação.<br />
<br />
É obrigatório que a instância criada pelo construtor de <code>super</code> seja retornada, caso o construtor seja sobrescrito.<br />
<br />
Após o construtor, o método de inicialização da classe é chamado, recebendo como parâmetros o retorno do construtor e quaisquer parâmetros passados no comando de instanciação.<br />
<br />
<h3>
Chamando a instância</h3>
Caso o método de chamada seja implementado na classe, a instância é “chamável” (<i>callable</i>). Na chamada o método de chamada é executado recebendo a instância e quaisquer parâmetros passados na chamada da instância.<br />
<br />
<h3>
Quem é quem</h3>
Um pequeno código apenas com <i>boilerplates</i> que não executam nada, com o único objetivo de demonstrar onde fica cada método citado:
</div>
<pre><code class="prettyprint">class Metaclasse(type):
def __new__(meta, name, base, dict_):
""" Este é o construtor da metaclasse """
return type.__new__(meta, name, base, dict_)
def __init__(cls, name, base, dict_):
""" Este é o método de inicialização da metaclasse """
type.__init__(cls, name, base, dict_)
def __call__(cls, *args, **kwargs):
""" Este é o método de chamada da metaclasse """
return type.__call__(cls, *args, **kwargs)
class Classe(object):
__metaclass__ = Metaclasse
def __new__(cls, *args, **kwargs):
""" Este é o construtor da classe """
return super(Classe, cls).__new__(cls)
def __init__(self, *args, **kwargs):
"""
Este é o método de inicialização da classe.
Como ela herda de object, não há necessidade de
chamar super, mas quando houver, a forma é:
super(Classe, self).__init__(*args, **kwargs)
"""
def __call__(self, *args, **kwargs):
""" Este é o método de chamada da classe """
return None
</code></pre>
<br />
<blockquote><tt>[update 2015-04-18]</tt>
Esta é a versão do código acima em Python 3:<br/><pre><code class="prettyprint"><small>class Metaclasse(type):
def __new__(meta, name: str, base: tuple, dict_: dict) -> type:
""" Este é o construtor da metaclasse """
return type.__new__(meta, name, base, dict_)
def __init__(cls, name: str, base: tuple, dict_: dict):
""" Este é o método de inicialização da metaclasse """
type.__init__(cls, name, base, dict_)
def __call__(cls, *args, **kwargs) -> object:
""" Este é o método de chamada da metaclasse """
return type.__call__(cls, *args, **kwargs)
class Classe(object, metaclass=Metaclasse):
def __new__(cls, *args, **kwargs) -> object:
""" Este é o construtor da classe """
return super().__new__(cls)
def __init__(self, *args, **kwargs):
"""
Este é o método de inicialização da classe.
Como ela herda de object, não há necessidade de
chamar super, mas quando houver, a forma é:
super().__init__(*args, **kwargs)
"""
def __call__(self, *args, **kwargs) -> None:
""" Este é o método de chamada da classe """
return None</small></code></pre>
<tt>[/update]</tt></blockquote><br/>
<div style="text-align: justify;">
<h3>
Dois dedos de prosa sobre método destruidor</h3>
O método destruidor (<code>__del__</code>) <b>não é chamado</b> quando o objeto perde todas as referências ativas, mas sim quando é coletado pelo <i>garbage collector</i> (<code>gc</code>).<br />
<br />
Como o <code>gc</code> não tem hora certa para rodar e seu comportamento varia muito de uma implementação de Python para outra, não é recomendável confiar nele para executar procedimentos críticos.<br />
<br />
O que você pode fazer é usá-lo para garantir determinados estados que podem ter sido esquecidos ou perdidos depois que a instância ficou sem referências.<br />
<br />
<h3>
Observações finais</h3>
As assinaturas do construtor e do método de inicialização da classe devem ser <b>rigorosamente iguais</b>.<br />
<br />
<blockquote>
<tt>[update]</tt><br />
Um detalhe importante é que, se o método de inicialização for sobrescrito alterando sua assinatura, não há necessidade de sobrescrever o construtor, porém se o construtor for sobrescrito alterando sua assinatura, é obrigatório que o método de inicialização também seja sobrescrito.<br />
<br />
Detalhes da linguagem…<br />
<tt>[/update]</tt></blockquote>
<br />
A chamada do método de chamada da classe pai da meta classe deve passar os parâmetros esperados pelos argumentos nas assinaturas do construtor e da inicialização da classe. No exemplo acima, esta chamada (precedida por <code>return</code>) é:
</div>
<pre><code class="prettyprint">type.__call__(cls, *args, **kwargs)</code></pre>
<br />
<blockquote>
<tt>[update]</tt><br />
Um detalhe que esqueci de mencionar é que é justamente nessa chamada que o construtor e o método de inicialização são evocados.<br />
<tt>[/update]</tt></blockquote>
<br />
<div style="text-align: justify;">
Os parâmetros passados são os esperados nas assinaturas citadas.
</div>
<br />
[]’s<br />
Cacilhας, La Batalema ℭacilhας, ℒa ℬatalemahttp://www.blogger.com/profile/14265747724618147106noreply@blogger.com0tag:blogger.com,1999:blog-1387996720436450649.post-32654465589867426412014-03-19T18:12:00.001-03:002015-04-18T18:00:43.572-03:00RLock<div style="text-align: justify;">
<img alt="" src="http://photos1.blogger.com/blogger/6505/3295/200/python.png" style="border: none; cursor: crosshair; float: left; margin: 0pt 10px 10px 0pt;" /> Dando continuidade o <a href="http://kodumaro.blogspot.com.br/2014/03/thread-safe.html">artigo sobre <em>thread</em></a>, um recurso muito útil é <a href="http://docs.python.org/2/library/threading.html?highlight=threading.lock#rlock-objects"><em>lock</em> reentrante</a>.<br />
<br />
<em>Lock</em> reentrante é uma variação de <em>lock</em> que pode ser realocado múltiplas vezes pelo mesmo <em>thread</em> e não pode ser liberado por outro <em>thread</em>. É muito útil em funções recursivas, mas funciona também para garantir que o <em>lock</em> seja alocado e liberado pelo mesmo <em>thread</em>.<br />
<br />
A fábrica (<em>factory</em>) para criar <em>locks</em> reentrantes é <code>threading.RLock</code>.<br />
<br />
É preciso tomar cuidado para que todos os <em>threads</em> compartilhem o mesmo <em>lock</em>, senão ele se torna inútil:
</div>
<pre><code class="prettyprint">lock = RLock()
thr1 = Thread(target=func1, args=(lock, ))
thr2 = Thread(target=func2, args=(lock, ))
thr3 = Thread(target=func3, args=(lock, ))</code></pre>
<br />
<div style="text-align: justify;">
Dentro da função, é preciso alocá-lo (<code>acquire</code>) no inicío e liberá-lo (<code>release</code>) ao final. Por exemplo:
</div>
<pre><code class="prettyprint">def func1(lock):
lock.acquire()
try:
# executa o procedimento
...
finally:
lock.release()</code></pre>
<br />
<h3>
Protegendo um objeto mutável</h3>
<div style="text-align: justify;">
Uma utilidade para o <em>lock</em> reentrante é proteger métodos que alterem o conteúdo de um objeto.<br />
<br />
Imagine que temos uma classe <code>Person</code> com dados, como <code>identity_code</code> (CPF) que podem sofrer alterações em <em>threads</em> diferentes (sei que não é uma boa abordagem, mas apenas como exemplo).<br />
<br />
Podemos criar um decorador que torna um método <em>thread-safe</em> usando <em>lock</em> reentrante:
</div>
<pre><code class="prettyprint">def lock(wrapped):
lock_ = RLock()
@wraps(wrapped)
def wrapper(*args, **kwargs):
with lock_:
return wrapped(*args, **kwargs)
return wrapper</code></pre>
<br />
<div style="text-align: justify;">
Esse decorador pode ser usado nos <em>setters</em> de cada propriedade:
</div>
<pre><code class="prettyprint">class Person(object):
...
@property
def identity_code(self):
return self.__identity_code
@identity_code.setter
@lock
def identity_code(self, value):
self.__identity_code = value
...</code></pre>
<br />
<div style="text-align: justify;">
Na verdade essa abordagem não resolve 100% o problema, mas já reduz muito a ocorrência de <em>bugs</em>.
</div>
<h3>
Protegendo qualquer objeto</h3>
<div style="text-align: justify;">
Porém a abordagem acima apenas protege parcialmente o objeto e não funciona para classes de terceiros.<br />
<br />
Outra abordagem é usar um <em>lock</em> para todo o objeto, tanto leitura quanto gravação, agnóstico a qual objeto está sendo protegido. Assim, não há necessidade de usarmos propriedades.<br />
<br />
Vamos criar uma classe para trancar qualquer objeto:
</div>
<pre><code class="prettyprint">class ObjectLocker(object):
def __init__(self, obj):
self.__obj = obj
self.__lock = RLock()
def __enter__(self):
self.__lock.acquire()
return self.__obj
def __exit__(self, etype, exc, traceback):
self.__lock.release()</code></pre>
<br />
<div style="text-align: justify;">
No código a instância dessa classe será passada para os <em>threads</em>, que terá de usar <code>with</code> para acessar o objeto original.<br />
<br />
Ao usar <code>with</code>, o objeto original será trancado para o <em>thread</em> atual e liberado ao final.<br />
<br />
A passagem será:
</div>
<pre><code class="prettyprint">locker = ObjectLocker(Person(...))
thr = Thread(target=func, args=(locker, ))</code></pre>
<br />
<div style="text-align: justify;">
Dentro da(s) função(ões) <code>func</code> a instância de <code>Person</code> deve ser acessa da seguinta forma:
</div>
<pre><code class="prettyprint">with locker as person:
name = person.name
person.identity_code = data['identity_code']</code></pre>
<br />
<div style="text-align: justify;">
Espero que os exemplos tenham sido úteis.<br />
<br /></div>
[]’s<br />
Cacilhας, La Batalemaℭacilhας, ℒa ℬatalemahttp://www.blogger.com/profile/14265747724618147106noreply@blogger.com0tag:blogger.com,1999:blog-1387996720436450649.post-35269322343976115712014-03-15T09:40:00.003-03:002014-03-15T09:45:17.381-03:00Thread-safe<div style="text-align: justify;">
<img alt="" src="http://photos1.blogger.com/blogger/6505/3295/200/python.png" style="border: none; cursor: crosshair; float: left; margin: 0pt 10px 10px 0pt;" /> Tenho tido a necessidade de lidar com muitas bibliotecas de terceiros e teno percebi um erro (ou seria uma <em>abordagem</em>?) comum nas mais novas: quase nenhuma delas é <em>thread-safe</em>.<br />
<br />
Acredito que, com o modismo do uso de <a href="https://pypi.python.org/pypi/greenlet">corrotinas</a> (chamadas <em>lightweight threads</em>), os programadores mais novos passaram a considerar os <em>threads</em> de sistema obsoletos, deixando de tomar cuidados essenciais para boas bibliotecas.<br />
<br />
Bem, tenho uma novidade para vocês: <em>threads</em> não são obsoletos e corrotinas não resolvem todos os problemas do mundo. Há situações em que usar corrotinas pode ser a melhor opção sim, mas em alguns casos os bons e velhos <em>threads</em> ainda são o salva vidas.<br />
<br />
Para que isso seja possível, na criação de bibliotecas é necessário tomar alguns cuidados:<br />
<ul>
<li>Prefira sempre que possível usar objetos imutáveis. Prefira tuplas, <em>strings</em>, tipos numéricos básicos, etc.</li>
<li>Evite permitir que objetos agregados façam alteração em seu objeto contentor sempre que possível.</li>
<li>Em todos métodos e propriedades de um objeto que pode ser compartilhado (como o contentor citado) que alterem o estado do objeto, inicie com um <a href="http://docs.python.org/2/library/threading.html?highlight=threading#rlock-objects"><code>RLock</code></a>, chamando seu método <code>acquire</code>, e encerre chamando seu método <code>release</code>. Tome cuidado para que seja usada a mesma instância de <code>RLock</code>!</li>
<li>Não tenha medo de usar objetos do módulo <code>threading</code>: <a href="http://docs.python.org/2/library/threading.html?highlight=threading#condition-objects"><code>Condition</code></a>, <a href="http://docs.python.org/2/library/threading.html?highlight=threading#event-objects"><code>Event</code></a>, <a href="http://docs.python.org/2/library/threading.html?highlight=threading#lock-objects"><code>Lock</code></a>, <a href="http://docs.python.org/2/library/threading.html?highlight=threading#rlock-objects"><code>RLock</code></a> e <a href="http://docs.python.org/2/library/threading.html?highlight=threading#semaphore-objects"><code>BoundedSemaphore</code></a>. Eles são seus amigos. ;-)</li>
<li>Se estiver difícil escrever testes, substitua <code>threading</code> por <code>dummy_threading</code> para os testes unitários, mas use <code>threading</code> para testes de aceitação.</li>
</ul>
</div>
<br />
[]’s<br />
Cacilhας, La Batalema ℭacilhας, ℒa ℬatalemahttp://www.blogger.com/profile/14265747724618147106noreply@blogger.com0tag:blogger.com,1999:blog-1387996720436450649.post-76732561442531745822014-02-18T13:39:00.001-03:002014-02-18T13:40:36.229-03:00Aspectos – parte II: mixins<div style="text-align: justify;">
<img alt="" src="http://photos1.blogger.com/blogger/6505/3295/200/python.png" style="border: none; cursor: crosshair; float: left; margin: 0pt 10px 10px 0pt;" /> Na <a href="http://kodumaro.blogspot.com.br/2014/02/aspectos-1.html">parte I</a> demos uma passada geral no conceito de aspectos. Aqui veremos os <a href="http://en.wikipedia.org/wiki/Mixin"><em>mixins</em></a>.<br />
<br />
<em>Mixins</em> são classes incompletas que apenas atribuem determinado comportamento às classes herdeiras.<br />
<br />
Vamos a um exemplo bem esdrúxulo, mas suficiente: um objeto que armazena notas de alunos em um arquivo.
</div>
<pre><code class="prettyprint">from cPickle import dumps, loads
import gdbm
__all__ = ['Banco', 'Notas']
class Banco(object):
def __init__(self, file=None):
if file is None:
file = 'notas.db'
if isinstance(file, basestring):
file = gdbm.open(file, 'c')
self.file = file
def close(self):
if self.file:
self.file.close()
self.file = None
class Notas(object):
def __init__(self, matricula, db):
self.matricula = matricula
if not isinstance(db, Banco):
raise TypeError(
'expected Banco instance, got {}'
.format(type(db).__name__)
)
self.db = db
try:
self.notas = loads(db[str(matricula)])
except KeyError:
self.notas = {}
def save(self):
db[str(self.matricula)] = dumps(self.notas)
@property
def primeiro_bimestre(self):
return self.notas.get('1bim')
@primeiro_bimestre.setter
def primeiro_bimestre(self, value):
self.notas['1bim'] = float(value)
@property
def segundo_bimestre(self):
return self.notas.get('2bim')
@segundo_bimestre.setter
def segundo_bimestre(self, value):
self.notas['2bim'] = float(value)
@property
def terceiro_bimestre(self):
return self.notas.get('3bim')
@terceiro_bimestre.setter
def terceiro_bimestre(self, value):
self.notas['3bim'] = float(value)
@property
def quarto_bimestre(self):
return self.notas.get('4bim')
@quarto_bimestre.setter
def quarto_bimestre(self, value):
self.notas['4bim'] = float(value)
@property
def recuperacao(self):
return self.notas.get('rec')
@recuperacao.setter
def recuperacao(self, value):
return self.notas['rec'] = float(value)
@property
def media(self):
soma = sum(
self.primeiro_bimestre or 0,
self.segundo_bimestre or 0,
self.terceiro_bimestre or 0,
self.quarto_bimestre or 0,
)
m = soma / 4.
rec = self.recuperacao
if rec is not None:
m = (m + rec) / 2.
return m</code></pre>
<br />
<div style="text-align: justify;">
Repare que temos o mesmo problema apresentando na parte I: está tudo misturado em uma única classe!<br />
<br />
Podemos separar as partes de gerência de banco e serialização em classes diferentes, dedicadas a seu próprio aspecto, chamadas <em>mixins</em>.<br />
<br />
A classe de faz serialização pode ser apenas isso:
</div>
<pre><code class="prettyprint">class NotaSerializavelMixin(object):
def load(self):
s = self.retrieve()
self.notas = loads(s) if s else {}
def __str__(self):
return dumps(self.notas)</code></pre>
<br />
<div style="text-align: justify;">
A gerência de banco vai para outro <em>mixin</em>:
</div>
<pre><code class="prettyprint">class PersistenciaMixin(object):
def retrieve(self):
try:
return self.db[str(self.matricula)]
except KeyError:
return None
def save(self):
db[str(self.matricula)] = str(self)</code></pre>
<br />
<div style="text-align: justify;">
Preferindo, é possível separar a gerência de notas em um <em>mixin</em> também:
</div>
<pre><code class="prettyprint">class NotasMixin(object):
@property
def primeiro_bimestre(self):
return self.notas.get('1bim')
@primeiro_bimestre.setter
def primeiro_bimestre(self, value):
self.notas['1bim'] = float(value)
...
@property
def media(self):
soma = sum(
self.primeiro_bimestre or 0,
self.segundo_bimestre or 0,
self.terceiro_bimestre or 0,
self.quarto_bimestre or 0,
)
m = soma / 4.
rec = self.recuperacao
if rec is not None:
m = (m + rec) / 2.
return m</code></pre>
<br />
<div style="text-align: justify;">
Ao final, a classe principal será apenas uma cola dos <em>mixins</em>:
</div>
<pre><code class="prettyprint">class Notas(NotaSerializavelMixin, PersistenciaMixin, MotasMixin):
def __init__(self, matricula, db):
self.matricula = matricula
if not isinstance(db, Banco):
raise TypeError(
'expected Banco instance, got {}'
.format(type(db).__name__)
)
self.db = db
self.load()</code></pre>
<br />
<div style="text-align: justify;">
A API da classe continua idêntica: recebe o número da matrícula e o banco na instanciação, propriedades para acessar as notas e método <code>save()</code> para salvá-las em arquivo, porém agora cada aspecto está isolado e encapsulado em seu próprio <em>mixin</em>.
</div>
<br />
[]’s<br />
Cacilhας, La Batalema ℭacilhας, ℒa ℬatalemahttp://www.blogger.com/profile/14265747724618147106noreply@blogger.com0tag:blogger.com,1999:blog-1387996720436450649.post-78471928646718640422014-02-18T12:34:00.001-03:002016-05-21T09:20:50.976-03:00Aspectos – parte I<div style="text-align: justify;">
<blockquote>Atualizado no <a href="http://cacilhas.info/kodumaro/2016/04/aspectos.html"><em>blog</em> novo</a>.</blockquote>
<br />
<img alt="" src="https://photos1.blogger.com/blogger/6505/3295/200/python.png" style="border: none; cursor: crosshair; float: left; margin: 0pt 10px 10px 0pt;"> Um paradigma muito útil é a <a href="http://pt.wikipedia.org/wiki/Programa%C3%A7%C3%A3o_orientada_a_aspecto">Programação orientada a Aspectos</a>.<br />
<br />
Consiste em separar e encapsular as funcionalidades de um código conforme sua importância.<br />
<br />
Nesta primeira parte, abordaremos de forma simples tal separação e deixaremos o conceito de <a href="http://en.wikipedia.org/wiki/Mixin"><em>mixins</em></a> para a <a href="http://kodumaro.blogspot.com.br/2014/02/aspectos-2.html">parte II</a>.<br />
<br />
Vamos começar com um exemplo: imagine uma <em>view</em> que modifica o estado de um objeto, retornando um <em>hash</em> do novo estado:
</div>
<pre><code class="prettyprint">@app.route('/people/<uuid>/', methods=['PATCH'])
def update_person(uuid):
person = db.person.find({ '_id': uuid }).first()
if not person:
raise Http404
try:
data = json.loads(request.data)
except ValueError:
return json.dumps({ 'error': 'invalid request' }), \
400, \
{ 'Content-Type': 'application/json' }
person.update(data)
db.person.save(person)
r = [(str(k), repr(v)) for k, v in person.iteritems()]
r.sort()
s = ';'.join('{}:{}'.format(k, v) for k, v in r)
return json.dumps({ 'etag': md5(s).hexdigest() }), \
200, \
{ 'Content-Type': 'application/json' }</uuid></code></pre>
<br />
<div style="text-align: justify;">
A solução atende, mas é de difícil manutenção. Perceba que a função chamada <code>update_person</code> (atualiza pessoa) faz muito mais do que simplesmente atualizar os dados:
</div>
<ul>
<li>Recupera o documento do banco, retornando 404 se não existir;</li>
<li>Faz os <em>parsing</em> dos dados recebidos, retornando 400 em caso de erro;</li>
<li>Efetivamente atualiza o documento;</li>
<li>Serializa o objeto para a resposta;</li>
<li>Gera um <em>hash</em> da serialização;</li>
<li>Responde a requisição com formato conveniente.</li>
</ul>
<br />
<div style="text-align: justify;">
Cada um desses passos é um <strong>aspecto</strong> do processo e pode ser isolado do restante.<br />
<br />
Vamos então separar o primeiro aspecto: recuperação do documento.
</div>
<pre><code class="prettyprint">def retrieve_person_aspect(view):
@wraps(view)
def wrapper(uuid):
person = db.person.find({ '_id': uuid }).first()
if not person:
raise Http404
return view(person)
return wrapper
@app.route('/people/<uuid>/', methods=['PATCH'])
@retrieve_person_aspect
def update_person(person):
try:
data = json.loads(request.data)
except ValueError:
return json.dumps({ 'error': 'invalid request' }), \
400, \
{ 'Content-Type': 'application/json' }
person.update(data)
db.person.save(person)
r = [(str(k), repr(v)) for k, v in person.iteritems()]
r.sort()
s = ';'.join('{}:{}'.format(k, v) for k, v in r)
return json.dumps({ 'etag': md5(s).hexdigest() }), \
200, \
{ 'Content-Type': 'application/json' }</uuid></code></pre>
<br />
<div style="text-align: justify;">
Agora a recuperação do documento está isolada, podendo inclusive ser usada em outras <em>views</em>. Nossa <em>view</em> já recebe o documento recuperado e não precisa lidar com o fato dele existir ou não.<br />
<br />
Porém ainda temos muita coisa misturada. Por exemplo, a obtenção e <em>parsing</em> dos dados recebidos: isso caracteriza outro aspecto do código, que não a atualização do documento.<br />
<br />
Podemos portanto, separá-los:
</div>
<pre><code class="prettyprint">def parse_data_aspect(view):
@wraps(view)
def wrapper(person):
try:
data = json.loads(request.data)
except ValueError:
return json.dumps({ 'error': 'invalid request' }), \
400, \
{ 'Content-Type': 'application/json' }
return view(person, data)
return wrapper
def retrieve_person_aspect(view):
...
@app.route('/people/<uuid>/', methods=['PATCH'])
@retrieve_person_aspect
@parse_data_aspect
def update_person(person, data):
person.update(data)
db.person.save(person)
r = [(str(k), repr(v)) for k, v in person.iteritems()]
r.sort()
s = ';'.join('{}:{}'.format(k, v) for k, v in r)
return json.dumps({ 'etag': md5(s).hexdigest() }), \
200, \
{ 'Content-Type': 'application/json' }</uuid></code></pre>
<br />
<div style="text-align: justify;">
A função <code>update_person</code> já está muito mais limpa: atualiza o documento, serializa e retorna o <em>hash</em>, mas ainda faz coisas demais. Vamos separar o tratamento do retorno:
</div>
<pre><code class="prettyprint">def respond_etag_aspect(view):
@wraps(view)
def wrapper(person, data):
response = view(person, data)
return json.dumps({ 'etag': md5(response).hexdigest() }), \
200, \
{ 'Content-Type': 'application/json' }
return wrapper
def parse_data_aspect(view):
...
def retrieve_person_aspect(view):
...
@app.route('/people/<uuid>/', methods=['PATCH'])
@retrieve_person_aspect
@parse_data_aspect
@respond_etag_aspect
def update_person(person, data):
person.update(data)
db.person.save(person)
r = [(str(k), repr(v)) for k, v in person.iteritems()]
r.sort()
return ';'.join('{}:{}'.format(k, v) for k, v in r)</uuid></code></pre>
<br />
<div style="text-align: justify;">
As coisas estão ficando cada vez mais separadas. A única coisa que a função <code>update_person</code> faz agora além de atualizar o documento é serializá-lo. Isso também pode ser isolado:
</div>
<pre><code class="prettyprint">def serialize_person_aspect(view):
@wraps(view)
def wrapper(person, data):
response = view(person, data)
r = [(str(k), repr(v)) for k, v in response.iteritems()]
r.sort()
return ';'.join('{}:{}'.format(k,v) for k, v in r)
def respond_etag_aspect(view):
...
def parse_data_aspect(view):
...
def retrieve_person_aspect(view):
...
@app.route('/people/<uuid>/', methods=['PATCH'])
@retrieve_person_aspect
@parse_data_aspect
@respond_etag_aspect
@serialize_person_aspect
def update_person(person, data):
person.update(data)
db.person.save(person)
return person</uuid></code></pre>
<br />
<div style="text-align: justify;">
Perceba que, com a separação dos aspectos em funções distintas, o código ficou muito mais semântico:
</div>
<ul>
<li><code>retrive_person_aspect</code> apenas recupera o documento do banco;</li>
<li><code>parse_data_aspect</code> apenas faz o <em>parsing</em> dos dados recebidos;</li>
<li><code>respond_etag_aspect</code> apenas gera o formato correto da resposta;</li>
<li><code>serialize_person_aspect</code> apenas serializa o documento;</li>
<li>finalmente, <code>update_person</code> apenas atualiza o documento.</li>
</ul>
<br />
<h3>
Observações</h3>
<ul>
<li><code>db</code> é um objeto de banco de dados MongoDB, apenas para fim de exemplo.</li>
<li><code>app</code> é uma aplicação <a href="http://flask.pocoo.org/">Flask</a>, apenas para fim de exemplo.</li>
<li>A ordem dos decoradores <strong>é importante</strong>.</li>
<li>Os <code>import</code>s foram omitidos:
</li>
</ul>
<pre><code class="prettyprint">import json
from functools import wraps
from hashlib import md5</code></pre>
<br />
<div style="text-align: justify;">
Na <a href="http://kodumaro.blogspot.com.br/2014/02/aspectos-2.html">parte II</a> abordaremos <em>mixins</em>.
</div>
<br />
[]’s<br />
Cacilhας, La Batalema ℭacilhας, ℒa ℬatalemahttp://www.blogger.com/profile/14265747724618147106noreply@blogger.com0tag:blogger.com,1999:blog-1387996720436450649.post-25780010911835185512013-10-12T15:17:00.000-03:002013-10-16T12:27:35.241-03:00Encadeamento de funções<div style="text-align: justify;">
<img alt="Poliedro" src="http://photos1.blogger.com/blogger/6505/3295/200/prog.png" style="border: medium none; cursor: crosshair; float: left; margin: 0pt 10px 10px 0pt;" /> Encadeamento de função é o <em>pattern</em> onde um grupo de funções é encadeado.<br />
<br />
Em uma cadeia, sempre a saída de uma função é a entrada da seguinte, o parâmetro inicial é fornecido à primeira função da cadeia, que segue executando as funções, e o resultado da última função é retornado como resultado da cadeia.<br />
<br />
Vejamos como criar cadeia.</div>
<br />
<h3>
Python</h3>
<div style="text-align: justify;">
A forma mais simples simples de encadear funções é simplesmente efetuar a chamada, como o código <a href="http://python.org/">Python</a> a seguir:</div>
<pre><code class="prettyprint">f1 = lambda x: x * 2
f2 = lambda x: x - 1
f3 = lambda x: x // 3
print(f3(f2(f1(8))))</code></pre>
<br />
<div style="text-align: justify;">
Porém esse código é visualmente confuso e difícil de ser depurado. É conveniente criar uma fábrica (<em>factory</em>) de cadeias:</div>
<pre><code class="prettyprint">chain = lambda *funcs: \
reduce(lambda acc, func: (lambda x: func(acc(x))),
funcs or [lambda x: x])
the_chain = chain(
lambda x: x * 2,
lambda x: x - 1,
lambda x: x // 3,
)
print(the_chain(8))</code></pre>
<br />
<div style="text-align: justify;">
Repare que as funções agora estão na ordem em que são executadas:
</div>
<ol>
<li>O número é dobrado;</li>
<li>O resultado da primeira função é decrementado;</li>
<li>O resultado da terceira função é triplicado e retornado.</li>
</ol>
<br />
<div style="text-align: justify;">
A função <code>reduce()</code> de Python executa a redução de um <a href="http://pt.wikipedia.org/wiki/MapReduce"><em>map/reduce</em></a>: o primeiro parâmetro é uma função que recebe o acumulador e o próximo valor, retornando o resultado parcial.<br />
<br />
O segundo parâmetro de <code>reduce()</code> é a lista a ser reduzida, no caso, a lista de funções ou, caso nenhuma função tenha sido informada, um função vazia que retorna o próprio parâmetro fornecido.<br />
<br />
Com este procedimento, é possível reduzir a lista de funções a uma função representando o encadeamento das funções fornecidas.</div>
<br />
<h3>
Erlang</h3>
<div style="text-align: justify;">
Em <a href="http://www.erlang.org/">Erlang</a> as coisas também podem ser interessantes.<br />
<br />
Criaremos duas funções, uma fábrica a ser exportado (<code>chain/1</code>) e uma função de redução (<code>chain/2</code>) que <strong>não</strong> deve ser exportada:
</div>
<pre><code class="prettyprint">-module(chain).
-export([chain/1]).
chain(Funs) when is_list(Funs) ->
chain(Funs, fun(X) -> X).
chain([], Acc) -> Acc;
chain([Fun|Rest], Acc) when is_function(Fun) ->
chain(Rest, fun(X) -> Fun(Acc(X)) end).</code></pre>
<br />
<div style="text-align: justify;">
Você pode então executar no <em>shell</em>:</div>
<pre><code>1> <strong>c(chain).</strong>
{ok,chain}
2> <strong>Fun = chain:chain([</strong>
2> <strong>fun(X) -> X * 2 end,</strong>
2> <strong>fun(X) -> X - 1 end,</strong>
2> <strong>fun(X) -> X div 3 end</strong>
2> <strong>]).</strong>
#Fun<chain.1.134034448>
3> <strong>Fun(8).</strong>
5
4> </code></pre>
<br />
<h3>
Haskell</h3>
<div style="text-align: justify;">
<a href="http://haskell.org/">Haskell</a> por outro lado já possui uma fábrica de cadeias, usando o carácter <code>$</code>, infelizmente na ordem inversa: primeiro as últimas funções a serem executadas, e por último as primeiras.<br />
<br />
Então, por exemplo: <code>f1 $ f2 $ f3 x</code> é executado como <code>f1 (f2 (f3 x))</code>.<br />
<br />
Crie um módulo:
</div>
<pre><code class="prettyprint">module Chain where
the_chain :: Int -> Int
the_chain n = (`div` 3) $ (\x -> x - 1) $ (* 2) n</code></pre>
<br />
<div style="text-align: justify;">
No <em>prompt</em> do <code>ghci</code> execute:
</div>
<pre><code>Prelude> <strong>:load chain.hs</strong>
[1 of 1] Compiling Chain ( chain.hs, interpreted )
Ok, modules loaded: Chain.
*Chain> <strong>the_chain 8</strong>
5
*Chain> </code></pre>
<br />
[]’s<br />
Cacilhας, La Batalemaℭacilhας, ℒa ℬatalemahttp://www.blogger.com/profile/14265747724618147106noreply@blogger.com0tag:blogger.com,1999:blog-1387996720436450649.post-37729633979197523242013-09-18T19:52:00.000-03:002014-04-12T13:19:44.487-03:00Um editor de textos rapidinho<div style="text-align: justify;">
<img alt="" src="http://photos1.blogger.com/blogger/6505/3295/200/python.png" style="border: none; cursor: crosshair; float: left; margin: 0pt 10px 10px 0pt;" /> Veremos como é simples criar uma aplicação gráfica rapidamente usando <a href="https://wiki.python.org/moin/TkInter">Tkinter</a>.<br />
<br />
Vamos começar a a partir de um <i>boilerplate</i>:
</div>
<br />
<pre><code class="prettyprint">#!/usr/bin/env python
# coding: UTF-8
from __future__ import absolute_import, division, print_function, unicode_literals
#-------------------------------------------------------------
# Cabeçalhos
#
# Aqui vão entrar os imports
__all__ = ['Editor']
#-------------------------------------------------------------
class Editor(object):
def mainloop(self):
pass
#-------------------------------------------------------------
# Rodapé
if __name__ == '__main__':
app = Editor()
app.mainloop()
</code></pre>
<br />
<div style="text-align: justify;">
A primeira coisa que precisamos fazer é criar nossa janela raiz. Para tanto, adicione aos cabeçalhos o <code>import</code>:
</div>
<br />
<pre><code class="prettyprint">from Tkinter import *
</code></pre>
<br />
<h3>
Método construtor</h3>
<div style="text-align: justify;">
Em seguida, no começo da classe <code>Editor</code>, vamos começar a escrever nosso construtor:
</div>
<br />
<pre><code class="prettyprint">class Editor(object):
def __init__(self):
root = self.root = Tk()
root.title('EditPy')
</code></pre>
<br />
<div style="text-align: justify;">
Precisamos então de um <i>frame</i> para colocar os botões de abrir, salvar e salvar-como:
</div>
<br />
<pre><code class="prettyprint"> fbuttons = Frame(root)
Button(fbuttons, text='Open', command=self.open) \
.pack(side=LEFT)
Button(fbuttons, text='Save', command=self.save) \
.pack(side=LEFT)
Button(fbuttons, text='Save As', command=self.save_as) \
.pack(side=LEFT)
</code></pre>
<br />
<div style="text-align: justify;">
O pai (<i>master</i>) do <i>frame</i> <code>fbuttons</code> é nossa janela raiz e o pai dos botões é o <i>frame</i>.<br />
<br />
Agora precisamos de uma caixa de texto para editar e exibir o conteúdo dos arquivos. Primeiro vá aos cabeçalhos novamente e acrescente o <code>import</code>:
</div>
<br />
<pre><code class="prettyprint">from ScrolledText import ScrolledText
</code></pre>
<br />
<div style="text-align: justify;">
Então, continuando o construtor, crie a caixa de texto com rolagem:
</div>
<br />
<pre><code class="prettyprint"> steditor = self.steditor = ScrolledText(root)
</code></pre>
<br />
<div style="text-align: justify;">
Para fechar o construtor só falta exibir o <i>frame</i> e a caixa de texto, e colocar o foco na caixa:
</div>
<br />
<pre><code class="prettyprint"> fbuttons.pack(expand=True, fill=X)
steditor.pack(expand=True, fill=BOTH)
steditor.focus()
</code></pre>
<br />
<h3>
<i>Loop</i> principal</h3>
<div style="text-align: justify;">
Se você tentar rodar o código agora, <b>nada</b> acontece. Isso porque o método <code>mainloop()</code> faz isso: absolutamente nada.<br />
<br />
Precisamos então, neste método, chamar o <i>loop</i> principal do Tkinter. Para isso, altere o método:
</div>
<br />
<pre><code class="prettyprint"> def mainloop(self):
self.root.mainloop()
</code></pre>
<br />
<div style="text-align: justify;">
Chamamos então o <i>loop</i> principal através da janela raiz (<code>root</code>).
</div>
<br />
<h3>
Abrir um arquivo</h3>
<div style="text-align: justify;">
Porém coisas estranhas e erros esdrúxulos podem acontecer, pois ainda não implementamos nenhum dos métodos chamados pelos botões – <code>self.open</code>, <code>self.save</code> e <code>self.save_as</code>.<br />
<br />
Parece-me justo que comecemos pelo método de abertura de arquivo. Criemos a assinatura do método:
</div>
<br />
<pre><code class="prettyprint"> def open(self):
</code></pre>
<br />
<div style="text-align: justify;">
Para abrir um arquivo, precisamos de uma ferramenta chamada <code>askopenfilename</code> que precisamos importar, então nos cabeçalhos acrescente:
</div>
<br />
<pre><code class="prettyprint">from tkFileDialog import askopenfilename
</code></pre>
<br />
<div style="text-align: justify;">
Então, de volta ao método <code>open()</code>, podemos pegar o nome do arquivo e carregá-lo:
</div>
<br />
<pre><code class="prettyprint"> filename = askopenfilename(title='EditPy | Open')
if filename:
self.filename = filename
self.load_file()
</code></pre>
<br />
<div style="text-align: justify;">
O método <code>load_file()</code> é quem vai de fato carregar o arquivo, mas precisamos antes apagar o conteúdo da caixa de texto:
</div>
<br />
<pre><code class="prettyprint"> def load_file(self):
filename = self.filename
steditor = self.steditor
steditor.delete('@0,0', 'end')
with open(filename) as fd:
steditor.insert('@0,0', fd.read())
</code></pre>
<br />
<div style="text-align: justify;">
O método <code>delete()</code> da caixa de texto apaga parte do conteúdo da caixa e os parâmetros são os índices de começo e fim:
</div>
<ul>
<li><code>@0,0</code> significa linha 0, coluna 0 (começo do texto);</li>
<li><code>end</code> significa o final do texto.</li>
</ul>
<br />
<div style="text-align: justify;">
O método <code>insert()</code> inserte a <i>string</i> (lida do descritor de arquivo) na posição indicada (<code>@0,0</code>).
</div>
<br />
<h3>
Salvando o texto</h3>
<div style="text-align: justify;">
Para salvar o texto, precisamos implementar o método <code>save()</code>. Ele precisa:
</div>
<ol>
<li>Verificar se temos um nome de arquivo – se não tivermos, é um <code>save_as</code>.</li>
<li>Abrir o arquivo para escrita.</li>
<li>Obter o texto da caixa de texto.</li>
<li>Gravar o texto no arquivo.</li>
</ol>
<br />
<div style="text-align: justify;">
Simples, indolor e o código é intuitivo:
</div>
<br />
<pre><code class="prettyprint"> def save(self):
filename = self.filename
if not filename:
return self.save_as()
with open(filename, 'w') as fd:
fd.write(
self.steditor.get('@0,0', 'end')
)
</code></pre>
<br />
<div style="text-align: justify;">
Repare nos índices usados no método <code>get()</code>.<br />
<br />
Em seguida é preciso saber o que fazer quando não temos o nome do arquivo ou quando escolhemos <i>Save As</i>.<br />
<br />
Primeiro a assinatura do método:
</div>
<br />
<pre><code class="prettyprint"> def save_as(self):
</code></pre>
<br />
<div style="text-align: justify;">
Caso haja um nome de arquivo, é honesto usá-lo como ponto de partida para caixa de diálogo de salvamento. Para isso precisamos separar o nome de diretório do nome base do arquivo e uma forma de fazer isso é com o método de <i>string</i> <code>rfind</code>.<br />
<br />
Para sabermos qual o separador de diretórios usado, adicione aos <code>import</code>s:
</div>
<br />
<pre><code class="prettyprint">from os import path
</code></pre>
<br />
<div style="text-align: justify;">
Então continue no método:
</div>
<br />
<pre><code class="prettyprint"> filename, directory = self.filename, None
if filename and path.sep in filename:
index = filename.rfind(path.sep) + 1
directory = filename[:index]
filename = filename[index:]
</code></pre>
<br />
<blockquote style="text-align: justify;"><tt>[update 2014-04-12]</tt><br/>
Optei por usar <code>rfind</code>, mas poderia ter usado <code>path.basename</code> e <code>path.dirname</code>. É uma questão de escolha e estilo.<br/>
<tt>[/update]</tt></blockquote>
<div style="text-align: justify;">
Feito o <em>slice</em>, já podemos exibir a caixa de diálogo de salvamento. Para isso precisamos da ferramenta <code>asksaveasfilename</code>.<br />
<br />
Modifique o <code>import</code> do <code>askopenfilename</code> para:
</div>
<br />
<pre><code class="prettyprint">from tkFileDialog import askopenfilename, asksaveasfilename
</code></pre>
<br />
<div style="text-align: justify;">
Continuando agora no método <code>save_as()</code>, podemos exibir a caixa de diálogo e, se algum arquivo for informado, chamar o método <code>save()</code>:
</div>
<br />
<pre><code class="prettyprint"> filename = asksaveasfilename(
title = 'EditPy | Save As',
initialfile = filename,
initialdir = directory,
)
if filename:
self.filename = filename
self.save()
</code></pre>
<br />
<div style="text-align: justify;">
E pronto! Já pode testar seu editor de texto!
</div>
<br />
<h3>
Lapidando</h3>
<div style="text-align: justify;">
Uma forma de melhorar um pouco o visual da aplicação é usando a extensão <i>ajulejada</i> do <code>tk</code>, chamada <a href="http://wiki.tcl.tk/14796"><code>ttk</code></a>.<br />
<br />
Para tanto, basta adicionar ao cabeçalho, no <b>final</b> dos <code>import</code>s:
</div>
<br />
<pre><code class="prettyprint">from ttk import *
</code></pre>
<br />
<div style="text-align: justify;">
Você também pode usar os métodos <code>wm_protocol()</code> e <code>bind_all()</code> da janela raiz para adicionar funcionalidades interessantes, mas isso é história pra outro dia. ;-)
</div>
<br />
[]’s<br />
Cacilhας, La Batalemaℭacilhας, ℒa ℬatalemahttp://www.blogger.com/profile/14265747724618147106noreply@blogger.com0tag:blogger.com,1999:blog-1387996720436450649.post-73298467138377004512011-09-14T22:52:00.014-03:002011-09-15T08:32:55.293-03:00Falsa indução ou a falácia da metodologia infalível<img style="float: left; margin: 0 10px 10px 0; cursor: crosshair; width: 55px; height: 55px; border: none;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgjbk9FiJ0AyG1etlZdJdzcq0D0pGTaz_N6qtMS0U6GNgVG3K9QEO2rYvQRO-wy1DsTOWP78J03gmtb0Fd8LcaUZG5cL5yt4bA1BTFUarOaLyrnHNU7t85bS775VqVBp5Wh6kc-k38f05A/s200/glider.png" alt="Glider" /> Reza a lenda que um código bem testado é um código sem <em>bugs</em>.<br /><br />Além de não acreditar em código sem <em>bugs</em>, ainda há um problema com a definição de «bem testado».<br /><br />Hoje em dia os evangelistas das novas <del>velhas</del> metodologias de programação defendem que a experiência profissional seja secundária – quiçá irrelevante – caso as metodologias <em>agile</em> sejam corretamente aplicadas, e uma das regras de outro é <strong>entupir</strong> o código de testes.<br /><br />Porém <strong>quantidade não é qualidade</strong>. Não adianta encher o código de testes se você não tem <em>feeling</em> sobre o que testar – e <em>feeling</em> só se adquire com <strong>experiência profissional</strong>.<br /><br />Há uma série de armadilhas prontas para pegar os incautos que se creem blindados pelas metodologias.<br /><br />Uma armadilha comum é a <a href="http://www.daemon.com.br/wiki/index.php?title=Fal%C3%A1cias#Generaliza.C3.A7.C3.A3o_apressada_.28falsa_indu.C3.A7.C3.A3o.29">falsa indução</a>.<br /><br />Por exemplo, tenho um código que calcula a raiz quadrada e trinta testes para ele, todos passando:<br /><pre><code><em>bash$</em> <strong>nosetests</strong><br /><em>..............................</em><br /><em>----------------------------------------------------------------------</em><br /><em>Ran 30 tests in 0.013s</em><br /><br /><em>OK</em></code></pre><br /><br />No entanto, quando eu tento alguns valores:<br /><pre><code><em>bash$</em> <strong>python</strong><br /><em>Python 2.6.1 (r261:67515, Jun 24 2010, 21:47:49) </em><br /><em>[GCC 4.2.1 (Apple Inc. build 5646)] on darwin</em><br /><em>Type "help", "copyright", "credits" or "license" for more information.</em><br /><em>>>></em> <strong>from sqr import sqr</strong><br /><em>>>></em> <strong>sqr(16) # 4</strong><br /><em>7</em><br /><em>>>></em> <strong>sqr(25) # 5</strong><br /><em>7</em><br /><em>>>></em> <strong>sqr(144) # 12</strong><br /><em>45</em><br /><em>>>></em> <strong>sqr(225) # 15</strong><br /><em>27</em><br /><em>>>></em> <strong>sqr(256) # 16</strong><br /><em>58</em></code></pre><br /><br />Valores completamente loucos! Isso porque o código caiu em uma falsa indução:<br /><pre><code class="prettyprint"># coding: UTF-8<br /><br />from math import ceil<br /><br />__all__ = ['sqr']<br /><br />def sqr(num):<br /> d = 10 ** int(ceil(len(str(num)) / 2.))<br /> return (num % d) + (num // d)</code></pre><br /><br />É claro que uma boa metodologia, como <a href="http://pt.wikipedia.org/wiki/Test_Driven_Development">TDD</a>, é um <strong>ferramenta poderosíssima</strong> nas mãos de um programador experiente, mas pode se tornar uma arma de autodestruição nas mãos de um novato com excesso de confiança.<br /><br />Então repito o conselho de um autor que escreveu, mais ou menos isto: não aprenda a programar em dez horas ou em dez dias; aprenda a programar em <strong>dez anos</strong>.<br /><br />[]’jn<br />Cacilhας, La Batalema<br /><br /><br />PS: Você pode baixar os testes de <a href="http://cacilhas.info/falsa-inducao/tests.py">http://cacilhas.info/falsa-inducao/tests.py</a>. Repare que se substituir:<br /><pre><code class="prettyprint">from sqr import sqr</code></pre><br />por:<br /><pre><code class="prettyprint">from math import sqrt as sqr</code></pre><br />os testes continuam passando.ℭacilhας, ℒa ℬatalemahttp://www.blogger.com/profile/14265747724618147106noreply@blogger.com0tag:blogger.com,1999:blog-1387996720436450649.post-44566025522915644952011-03-13T23:08:00.010-03:002011-04-29T22:27:57.547-03:00Decoradores em Lua<img style="float: left; margin: 0 10px 10px 0; cursor: crosshair; width: 100px; height: 100px; border: none;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiN5laTKpMMgnkxNBnCxgzjfgLSeuutt9oIxGbQC0iaJmNYDzhIjv-BCWjZCn-hg4I0qLc1p__v5P-T2pwjcfyURiUrbabgruQ_fDXPhSIndkezKwCLBnLnp-W8ThiAOVg_EQZG3-uQkR6l/s200/at-char.jpg" alt="@" id="BLOGGER_PHOTO_ID_5583751901156142162" /><br /><br /><br /><br />Um recurso extremamente útil em <a href="http://python.org/">Python</a> é o uso de decoradores.<br /><br />Por exemplo, um decorador que <em>memoíze</em> o resultado de uma função:<br /><br /><pre><code class="prettyprint">def memo(func, buffer={}):<br /> def wrapper(*args):<br /> if args in buffer:<br /> response = buffer[args]<br /> else:<br /> response = buffer[args] = func(*args)<br /> return response<br /><br /> return wrapper</code></pre><br /><br />Assim funções como o cálculo <a href="http://pt.wikipedia.org/wiki/Recursividade">recursivo</a> da <a href="http://pt.wikipedia.org/wiki/N%C3%BAmero_de_Fibonacci">sequência de Fibonacci</a> se tornam menos custosas:<br /><pre><code class="prettyprint">@memo<br />def fibonacci(n):<br /> if n < 2:<br /> return 1<br /> else:<br /> return fibonacci(n - 2) + fibonacci(n - 1)</code></pre><br /><br />Observação: <a href="http://en.wikipedia.org/wiki/Memoization"><em>memoização</em></a> consome muita memória!<br /><br />É possível ver que, em Python, decoradores são extremamente fáceis de se implementar e usar, o que permite usos bastantes complexos, como a <a href="http://pt.wikipedia.org/wiki/Programa%C3%A7%C3%A3o_orientada_a_aspecto">orientação a aspecto</a>.<br /><br /><a href="http://lua.org/">Lua</a> também suporta decoradores, mas de uma forma um pouco menos intuitiva, mais complexa e totalmente <em>macetada</em>.<br /><br />Para a criação de decoradores em Lua, é usada a sobrecarga do operador de concatenação, como sugerido na <a href="http://lua-users.org/wiki/DecoratorsAndDocstrings">literatura de referência</a>.<br /><br />Para criarmos então o decorador de <em>memoização</em>, precisamos de uma <a href="http://www.lua.org/pil/13.html">metatabela</a> que sobrecarregue o operador:<br /><pre><code class="prettyprint">local mt = {<br /> __concat = function(self, func)<br /> return function(...)<br /> local response<br /> local args = serialize(...)<br /> if self[args] then<br /> response = self[args]<br /> else<br /> response = func(...)<br /> self[args] = response<br /> end<br /> return response<br /> end<br /> end,<br />}</code></pre><br /><br />A função <code>serialize()</code> não existe, então temos de criá-la:<br /><pre><code class="prettyprint">local function serialize(...)<br /> local response = ""<br /> local i, v<br /> for i, v in ipairs {...} do<br /> response =<br /> response .. "[" .. ("%g"):format(i) .. "] = " ..<br /> ("%g"):format(v) .. ", "<br /> end<br /> return response<br />end</code></pre><br /><br />Agora criamos o decorador:<br /><pre><code class="prettyprint">function memo()<br /> return setmetatable({}, mt)<br />end</code></pre><br /><br />Isso tudo pode ser colocado em módulo, chamado <code>memo.lua</code> por exemplo. Na última linha do módulo coloque:<br /><pre><code class="prettyprint">return memo</code></pre><br /><br />Agora, para usar o decorador:<br /><pre><code class="prettyprint">require "memo"<br /><br />fibonacci =<br /> memo() ..<br /> function(n)<br /> if n < 2 then<br /> return 1<br /> else<br /> return fibonacci(n - 2) + fibonacci(n - 1)<br /> end<br /> end</code></pre><br /><br />O uso do decorador está na linha: <code>memo() ..</code> – a sintaxe não é tão elegante quanto a de Python – <code>@memo</code> –, mas funciona da mesma forma, inclusive a criação de cadeias de decoradores.<br /><br />A criação de decoradores em Lua também não é tão intuitiva e simples quanto em Python, em função da diferença de filosofias entre as duas linguagens, mas com o hábito se torna um recurso poderoso.<br /><br />[]’s<br />Cacilhας, La Batalemaℭacilhας, ℒa ℬatalemahttp://www.blogger.com/profile/14265747724618147106noreply@blogger.com0tag:blogger.com,1999:blog-1387996720436450649.post-38948131066013936902010-11-27T19:01:00.015-03:002010-11-27T19:15:41.251-03:00Flask<img style="float: left; margin: 0 10px 10px 0; cursor: crosshair;width: 100px; height: 89px; border: none;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjn9pjvsjH0oLcTSTPn9Cckvjk7HDFoJmM4_rx8aqKl4jH4vixd9s706wgcDEud05yUmSaprOPEcqIGzrBXQQGfpcC-w4-R9qZMoxiGyiQ1UWlAAqM8ceQWHSbbokwf00kNai3ljcXyW81B/s200/flask.jpg" alt="Flask" id="BLOGGER_PHOTO_ID_5544353911308592274" /> Há diversos <em>frameworks</em> em <a href="http://python.org/">Python</a> para Web, <a href="http://www.djangoproject.com/">Django</a>, <a href="http://zope2.zope.org/">Zope</a>, <a href="http://web2py.com/">web2py</a>, <a href="http://wiki.python.org/moin/PythonWebsitePyramidDocs">Pyramid</a> e <a href="http://turbogears.org/">TurboGears</a> e <a href="http://pylonshq.com/">Pylons</a>.<br /><br />Venho aqui para falar de um <em>microframework</em> chamado <a href="http://flask.pocoo.org/">Flask</a>.<br /><br />Simples, flexível e poderoso, é capaz de manter desde o projeto mais simples até CMS no estilo Django ou grandes aplicações como em Pylons.<br /><br />A instalação é também extremamente simples, já que se encontra no <a href="http://pypi.python.org/pypi">PyPI</a>:<br /><pre><code><em>bash$</em> <strong>easy_install Flask</strong></code></pre><br /><br />Como exemplo vou mostrar uma aplicação ao estilo Flask, registrando as URLs com decorador <code>route</code>, mas para aplicações mais complexas sugiro criar um arquivo <code>url_rules.py</code> que importe o objeto aplicação e use o método <code>add_url_rule</code> em vez do decorador.<br /><br />Nossa aplicação simples exibirá o conteúdo dos arquivos em <code>/proc/</code> dos sistemas Unix (por falta de ideia melhor).<br /><br />Podemos começar importanto os recursos necessários:<br /><pre><code class="prettyprint">from flask import Flask, make_response</code></pre><br /><br /><ul> <li><code>Flask</code>: classe da aplicação</li> <li><code>make_response</code>: função capaz de criar um objeto de resposta HTTP</li> </ul><br /><br />Para a leitura dos arquivos e diretórios vamos precisar do módulo <code>os</code>:<br /><pre><code class="prettyprint">import os</code></pre><br /><br />Vamos agora criar duas funções, uma que retorne a listagem de arquivos para um diretório e outra que retorne o conteúdo de um arquivo:<br /><pre><code class="prettyprint">def list_dir(path):<br /> filelist = os.listdir(path)<br /> filelist.sort()<br /> return '\n'.join(filelist)<br /><br /><br />def read_file(path):<br /> data = 'NO DATA'<br /> with open(path, 'r') as fd:<br /> data = fd.read()<br /> return data</code></pre><br /><br />Agora é possível criar a aplicação:<br /><pre><code class="prettyprint">app = Flask(__name__)<br />app.debug = True</code></pre><br /><br />Podemos então criar uma função que receba o caminho do arquivo/diretório a ser lido e retorne para o navegador.<br /><br />Caso seja necessário editar os cabeçalhos ou lidar com qualquer peculiaridade do objeto de resposta HTTP, é possível instanciá-lo manualmente. Caso contrário, retornar uma simples <em>string</em> já faz com que ela seja entendida como o conteúdo do objeto.<br /><br />Se uma tupla for retornada, o primeiro valor será o conteúdo e o segundo o <em>status</em> de retorno.<br /><br />Sabendo disso, podemos definir a função:<br /><pre><code class="prettyprint">@app.route('/<path:filename>/')<br />def read_proc(filename):<br /> real_path = os.path.join('/proc', filename)<br /> data = 'NO DATA'<br /><br /> try:<br /> if os.path.isfile(real_path):<br /> data = read_file(real_path)<br /><br /> elif os.path.isdir(real_path):<br /> data = list_dir(real_path)<br /><br /> else:<br /> return 'NOT FOUND', 404<br /><br /> # Criando HTTP response apenas para editar headers<br /> response = make_response(data)<br /> response.headers['Content-Type'] = 'text/plain'<br /> return response<br /><br /> except IOError:<br /> # Você não pode ler este arquivo/diretório!<br /> return 'BAD REQUEST', 400</code></pre><br /><br />A rota indica que o argumento <em>filename</em> receberá como parâmetro toda a URL passada (<code>path:</code> indica uma <em>string</em> com <code>/</code>).<br /><br />Para a raiz da aplicação, poderíamos usar as funções <code>redirect</code> e <code>url_for</code>, por exemplo:<br /><pre><code class="prettyprint">@app.route('/')<br />def index():<br /> return redirect(url_for('read_proc', filename='cpuinfo'))</code></pre><br /><br />O que redirecionaria para a URL referente à função <code>read_proc</code>, passando <code>'cpuinfo'</code> como parâmetro, mas faz mais sentido retornar o resultado da função:<br /><pre><code class="prettyprint">@app.route('/')<br />def index():<br /> return read_proc('')</code></pre><br /><br />Agora só falta colocar a aplicação para rodar:<br /><pre><code class="prettyprint">if __name__ == '__main__':<br /> app.run(host='0.0.0.0')</code></pre><br /><br />Execute o <em>script</em> e acesse no navegador: <a href="http://localhost:5000/">http://localhost:5000/</a> – você verá a listagem do diretório <code>/proc/</code>.<br /><br />Acesse no navegador: <a href="http://localhost:5000/cpuinfo/">http://localhost:5000/cpuinfo/</a> e você verá o conteúdo do arquivo <code>/proc/cpuinfo</code>.<br /><br />Pretendo em uma próxima oportunidade demonstrar com construir uma estrutura mais complexa usando um arquivo com roteamente em vez de decorador.<br /><br />Para mais diversão, leia a <a href="http://flask.pocoo.org/docs/">documentação</a> e fascine-se também com a quantidade de vezes que a expressão «<em>you don’t have to deal with that</em>» se repete.<br /><br />[]’s<br />Cacilhας, La Batalemaℭacilhας, ℒa ℬatalemahttp://www.blogger.com/profile/14265747724618147106noreply@blogger.com0tag:blogger.com,1999:blog-1387996720436450649.post-55839940104687490892010-03-05T15:23:00.004-03:002010-03-05T15:35:39.327-03:00Python é capa da TIdigital de MarçoGente,<br /><br />Segunda-feira todo mundo correndo pras bancas!<br /><img style="display: block; margin: 0px auto 10px; text-align: center; cursor: crosshair; width: 242px; height: 324px; border: none;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjhSlasmNbyvpLhGTa0G1JS-Xbv8xJbVQCQnH2we1Y_MFZ5rJVWzOj8-lioS-RU_HVv7Bug8g6PDLW7P_pL02Dv-vcoPWNgd7MYYaJtiKNMTzAFRb6jI6QLWGSnwaa-shIfGce5CnESkSF-/s320/capapython.jpg" alt="TIdigital Março" id="BLOGGER_PHOTO_ID_5445217462760004914" /><br /><br />Em março, <a href="http://python.org/">Python</a> em foco na revista TIdigital.<br /><br />Fonte: <a href="http://pythonrio.org/python-e-capa-da-ti-digital-de-marco">PythOn Rio</a>.<br /><br />[]’s<br />Cacilhας, La Batalemaℭacilhας, ℒa ℬatalemahttp://www.blogger.com/profile/14265747724618147106noreply@blogger.com0tag:blogger.com,1999:blog-1387996720436450649.post-83493208471105186262010-02-27T14:44:00.001-03:002010-03-11T15:31:50.410-03:00MongoDB<img style="float: left; margin: 0 10px 10px 0; cursor: crosshair; border: none;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgiaZ6_bpaXOKEJSwda5OsKtETJIjVzCXOdnfSzoxp5WfrauH7Q5b7C6A191IeA3h6GfhES7UxmA35U-MuvcUaiZjDPOCipqq1MqlUcXl15ch8UOY6w8br5A03cXHCf0Zito5BZoX1ARyU/s1600/mongodb.jpg" alt="Logo" /> Há atualmente na Computação uma onda de adoção de <a href="http://pt.wikipedia.org/wiki/NoSQL">bancos de dados não-relacionais, comumente chamados NoSQL</a>.<br /><br />O foco principal dos bancos NoSQL atuais é a <a href="http://en.wikipedia.org/wiki/Document-oriented_database">orientação a documentos</a>, uma variação <em>hash</em> da <a href="http://pt.wikipedia.org/wiki/Banco_de_dados_orientado_a_objetos">orientação a objetos</a> e alternativa aos <a href="http://pt.wikipedia.org/wiki/Banco_de_dados_relacional">RDBMSs</a>, que dominaram a orientação dos gerenciadores de banco de dados por décadas.<br /><br />O principal banco NoSQL é o <a href="http://couchdb.apache.org/">CouchDB</a>, DBMS do Projeto Apache baseado em <a href="http://ftp.sunet.se/pub/lang/erlang/">Erlang</a>, porém há uma alternativa bem mais simples e aparentemente tão poderosa quanto: <a href="http://www.mongodb.org/">MongoDB</a>.<br /><br />MongoDB combina os melhores funcionalidades de orientação a documentos, <em>hashes</em> e RDBMSs. É um banco de dados orientado a documentos, escalável, livre de esquema, de alto desempenho e código aberto escrito em <a href="http://www.cplusplus.com/">C++</a>.<br /><br />Este artigo dá continuidade ao <a href="http://montegasppa.blogspot.com/2010/02/mongodb.html">artigo nas Reflexões de Monte Gasppa e Giulia C.</a> sobre a instalação do MongoDB. Aqui você poderá ver um pouquinho do uso de um banco de dados orientado a documentos.<br /><br />Este artigo é baseado na <a href="http://www.mongodb.org/display/DOCS/Tutorial">documentação oficial</a> e entende que você instalou o MongoDB segundo descrito nas <a href="http://montegasppa.blogspot.com/2010/02/mongodb.html">Reflexões de Monte Gasppa e Giulia C.</a>.<br /><br /><h3>Conexão na base de dados</h3><br />Para conectar na base basta usar o comando <code>mongo</code>:<br /><pre><code><em>bash$</em> <strong>mongo</strong><br /><em>MongoDB shell version: 1.2.2</em><br /><em>url: test</em><br /><em>connecting to: test</em><br /><em>type "help" for help</em><br /><em>></em></code></pre><br /><br />Caso você tenha configurado uma porta alternativa em <code>/srv/mongodb/etc/mongodb.conf</code>, você pode especificar isso na linha de comando do cliente:<br /><pre><code><em>bash$</em> <strong>mongo --port 27018</strong></code></pre><br /><br />Você também pode se conectar a um servidor remoto, caso ele esteja habilitado para conexão externa, usando a <em>flag</em> <code>--host</code>.<br /><br />Para escolher qual base de dados usar, o comando é <code>use</code>:<br /><pre><code><em>></em> <code>use mydb</code></code></pre><br /><br /><h3>Autorização</h3><br />Você pode se autorizar na linha de comando com as opções <code>-u</code> (usuário) e <code>-p</code> (senha), ou já do <em>prompt</em> do interpretador:<br /><pre><code><em>></em> <strong>use admin</strong><br /><em>></em> <strong>db.auth('admin', 'ra35EG/dz');</strong></code></pre><br /><br /><h3>Comandos básicos</h3><br />A linguagem de interação com MongoDB é <a href="http://pt.wikipedia.org/wiki/JavaScript">Javascript</a>, mas há alguns comandos úteis no interpretador.<br /><br />Para listar comandos úteis:<br /><pre><code><em>></em> <strong>help</strong></code></pre><br /><br />Listar recursos uteis da base atual:<br /><pre><code><em>></em> <strong>db.help();</strong></code></pre><br /><br />Listar recursos uteis de uma coleção (levemente equivalente a tabela de um banco relacional), no exemplo <code>foo</code>:<br /><pre><code><em>></em> <strong>db.foo.help();</strong></code></pre><br /><br />Para listar as bases instaladas:<br /><pre><code><em>></em> <strong>show dbs</strong></code></pre><br /><br />Para listar as coleções da base atual:<br /><pre><code><em>></em> <strong>show collections</strong></code></pre><br /><br />Listar usuários da base atual:<br /><pre><code><em>></em> <strong>show users</strong></code></pre><br /><br />Criar usuário na base atual:<br /><pre><code><em>></em> <strong>db.addUser('admin', 'ra35EG/dz');</strong></code></pre><br /><br />Autenticar-se:<br /><pre><code><em>></em> <strong>db.auth('admin', 'ra35EG/dz');</strong></code></pre><br /><br />Listar o conteúdo de uma coleção:<br /><pre><code><em>></em> <strong>db.foo.find();</strong></code></pre><br /><br />Apagar uma coleção:<br /><pre><code><em>></em> <strong>db.foo.drop();</strong></code></pre><br /><br />Apagar a base de dados atual:<br /><pre><code><em>></em> <strong>db.dropDatabase();</strong></code></pre><br /><br /><h3>Armazenamento de dados</h3><br />Repare que o MongoDB é <strong>livre de esquema</strong>, portanto uma coleção pode conter estruturas de dados radicalmente diferentes, sempre no formato <a href="http://json.org/">JSON</a>.<br /><br />Por exemplo, vamos inserir dois dados distintos na coleção <code>foo</code> da base <code>mydb</code>:<br /><pre><code><em>></em> <strong>use mydb</strong><br /><em>></em> <strong>var j = { name: 'mongo' };</strong><br /><em>></em> <strong>var t = { x: 3 };</strong><br /><em>></em> <strong>db.foo.save(j);</strong><br /><em>></em> <strong>db.foo.save(t);</strong><br /><em>></em> <strong>db.foo.find();</strong><br /><em>{ "_id" : ObjectId("4b8949d9cd50237b8573833b"), "name" : "mongo" }</em><br /><em>{ "_id" : ObjectId("4b8949dbcd50237b8573833c"), "x" : 3 }</em></code></pre><br /><br />Você não precisa predefinir a coleção, que é criada automaticamente no salvamento da primeira estrutura. O campo <code>_id</code> é criado automaticamente e é a <strong>chave primária</strong>.<br /><br />Para apagar o objeto <code>j</code>:<br /><pre><code><em>></em> <strong>j = db.foo.findOne({ name: 'mongo' });</strong><br /><em>></em> <strong>db.foo.remove(j);</strong></code></pre><br /><br />Para apagar todos os objetos basta remover a coleção:<br /><pre><code><em>></em> <strong>db.foo.drop();</strong></code></pre><br /><br />Vamos usar um pouco mais de lógica:<br /><pre><code><em>></em> <strong>for (var i=0; i<100; ++i) db.foo.save({ x: i, y: i*i });</strong></code></pre><br /><br />Esse <em>loop</em> cria cem registros, com <code>x</code> variando de zero a noventa e nove e <code>y</code> igual a seu quadrado:<br /><pre><code><em>></em> <strong>db.foo.find();</strong><br /><em>{ "_id" : ObjectId("4b894c05cd50237b8573833e"), "x" : 0, "y" : 0 }</em><br /><em>{ "_id" : ObjectId("4b894c05cd50237b8573833f"), "x" : 1, "y" : 1 }</em><br /><em>{ "_id" : ObjectId("4b894c05cd50237b85738340"), "x" : 2, "y" : 4 }</em><br /><em>{ "_id" : ObjectId("4b894c05cd50237b85738341"), "x" : 3, "y" : 9 }</em><br /><em>{ "_id" : ObjectId("4b894c05cd50237b85738342"), "x" : 4, "y" : 16 }</em><br /><em>{ "_id" : ObjectId("4b894c05cd50237b85738343"), "x" : 5, "y" : 25 }</em><br /><em>{ "_id" : ObjectId("4b894c05cd50237b85738344"), "x" : 6, "y" : 36 }</em><br /><em>{ "_id" : ObjectId("4b894c05cd50237b85738345"), "x" : 7, "y" : 49 }</em><br /><em>{ "_id" : ObjectId("4b894c05cd50237b85738346"), "x" : 8, "y" : 64 }</em><br /><em>{ "_id" : ObjectId("4b894c05cd50237b85738347"), "x" : 9, "y" : 81 }</em><br /><em>{ "_id" : ObjectId("4b894c05cd50237b85738348"), "x" : 10, "y" : 100 }</em><br /><em>{ "_id" : ObjectId("4b894c05cd50237b85738349"), "x" : 11, "y" : 121 }</em><br /><em>{ "_id" : ObjectId("4b894c05cd50237b8573834a"), "x" : 12, "y" : 144 }</em><br /><em>{ "_id" : ObjectId("4b894c05cd50237b8573834b"), "x" : 13, "y" : 169 }</em><br /><em>{ "_id" : ObjectId("4b894c05cd50237b8573834c"), "x" : 14, "y" : 196 }</em><br /><em>{ "_id" : ObjectId("4b894c05cd50237b8573834d"), "x" : 15, "y" : 225 }</em><br /><em>{ "_id" : ObjectId("4b894c05cd50237b8573834e"), "x" : 16, "y" : 256 }</em><br /><em>{ "_id" : ObjectId("4b894c05cd50237b8573834f"), "x" : 17, "y" : 289 }</em><br /><em>{ "_id" : ObjectId("4b894c05cd50237b85738350"), "x" : 18, "y" : 324 }</em><br /><em>{ "_id" : ObjectId("4b894c05cd50237b85738351"), "x" : 19, "y" : 361 }</em><br /><em>has more</em></code></pre><br /><br /><h3>Consultas (<em>queries</em>)</h3><br />É possível fazer consultas simples ou bem complexas assim como em modelos relacionais usando MongoDB.<br /><br />Vamos obter uma contagem de objetos:<br /><pre><code><em>></em> <strong>db.foo.find().count();</strong><br /><em>100</em></code></pre><br /><br />Só os cinco primeiros objetos:<br /><pre><code><em>></em> <strong>db.foo.find().limit(5);</strong><br /><em>{ "_id" : ObjectId("4b894c05cd50237b8573833e"), "x" : 0, "y" : 0 }</em><br /><em>{ "_id" : ObjectId("4b894c05cd50237b8573833f"), "x" : 1, "y" : 1 }</em><br /><em>{ "_id" : ObjectId("4b894c05cd50237b85738340"), "x" : 2, "y" : 4 }</em><br /><em>{ "_id" : ObjectId("4b894c05cd50237b85738341"), "x" : 3, "y" : 9 }</em></code></pre><br /><br />Agora os cinco seguintes:<br /><pre><code><em>></em> <strong>db.foo.find().skip(5).limit(5);</strong><br /><em>{ "_id" : ObjectId("4b894c05cd50237b85738343"), "x" : 5, "y" : 25 }</em><br /><em>{ "_id" : ObjectId("4b894c05cd50237b85738344"), "x" : 6, "y" : 36 }</em><br /><em>{ "_id" : ObjectId("4b894c05cd50237b85738345"), "x" : 7, "y" : 49 }</em><br /><em>{ "_id" : ObjectId("4b894c05cd50237b85738346"), "x" : 8, "y" : 64 }</em><br /><em>{ "_id" : ObjectId("4b894c05cd50237b85738347"), "x" : 9, "y" : 81 }</em></code></pre><br /><br />Para obter o elemento com valor de <code>x</code> vinte e quatro:<br /><pre><code><em>></em> <strong>var e = db.foo.findOne({ x: 24 });</strong><br /><em>></em> <strong>print(e.y);</strong><br /><em>576</em><br /><em>></em> <strong>print(tojson(e));</strong><br /><em>{ "_id" : ObjectId("4b894c05cd50237b85738356"), "x" : 24, "y" : 576 }</em></code></pre><br /><br /><h3>Consultas avançadas</h3><br />Vamos complicar um pouco…<br /><br />Para obter todos os registros com <code>x</code> maior que cinquenta:<br /><pre><code><em>></em> <strong>db.foo.find('this.x > 50');</strong><br /><em>{ "_id" : ObjectId("4b894c05cd50237b85738371"), "x" : 51, "y" : 2601 }</em><br /><em>{ "_id" : ObjectId("4b894c05cd50237b85738372"), "x" : 52, "y" : 2704 }</em><br /><em>{ "_id" : ObjectId("4b894c05cd50237b85738373"), "x" : 53, "y" : 2809 }</em><br /><em>{ "_id" : ObjectId("4b894c05cd50237b85738374"), "x" : 54, "y" : 2916 }</em><br /><em>{ "_id" : ObjectId("4b894c05cd50237b85738375"), "x" : 55, "y" : 3025 }</em><br /><em>{ "_id" : ObjectId("4b894c05cd50237b85738376"), "x" : 56, "y" : 3136 }</em><br /><em>{ "_id" : ObjectId("4b894c05cd50237b85738377"), "x" : 57, "y" : 3249 }</em><br /><em>{ "_id" : ObjectId("4b894c05cd50237b85738378"), "x" : 58, "y" : 3364 }</em><br /><em>{ "_id" : ObjectId("4b894c05cd50237b85738379"), "x" : 59, "y" : 3481 }</em><br /><em>{ "_id" : ObjectId("4b894c05cd50237b8573837a"), "x" : 60, "y" : 3600 }</em><br /><em>{ "_id" : ObjectId("4b894c05cd50237b8573837b"), "x" : 61, "y" : 3721 }</em><br /><em>{ "_id" : ObjectId("4b894c05cd50237b8573837c"), "x" : 62, "y" : 3844 }</em><br /><em>{ "_id" : ObjectId("4b894c05cd50237b8573837d"), "x" : 63, "y" : 3969 }</em><br /><em>{ "_id" : ObjectId("4b894c05cd50237b8573837e"), "x" : 64, "y" : 4096 }</em><br /><em>{ "_id" : ObjectId("4b894c05cd50237b8573837f"), "x" : 65, "y" : 4225 }</em><br /><em>{ "_id" : ObjectId("4b894c05cd50237b85738380"), "x" : 66, "y" : 4356 }</em><br /><em>{ "_id" : ObjectId("4b894c05cd50237b85738381"), "x" : 67, "y" : 4489 }</em><br /><em>{ "_id" : ObjectId("4b894c05cd50237b85738382"), "x" : 68, "y" : 4624 }</em><br /><em>{ "_id" : ObjectId("4b894c05cd50237b85738383"), "x" : 69, "y" : 4761 }</em><br /><em>{ "_id" : ObjectId("4b894c05cd50237b85738384"), "x" : 70, "y" : 4900 }</em><br /><em>has more</em></code></pre><br /><br />Outra opção é usar o <a href="http://www.mongodb.org/display/DOCS/Advanced+Queries">operador condicional</a> <code>$gt</code>:<br /><pre><code><em>></em> <strong>db.foo.find({ x: { $gt: 50 } });</strong></code></pre><br /><br />Da mesma forma outros operadores. Por exemplo, todos os elementos com <code>x</code> entre vinte e três e vinte e seis, inclusive:<br /><pre><code><em>></em> <strong>db.foo.find('23 <= this.x && this.x <= 26');</strong></code></pre><br /><br />Ou:<br /><pre><code><em>></em> <strong>db.foo.find({ x: { $gte: 23, $lte: 26 } });</strong></code></pre><br /><br />Os principais operadores são:<br /><ul><li><code>$lt</code> – menor que</li> <li><code>$gt</code> – maior que</li> <li><code>$lte</code> – menor ou igual a</li> <li><code>$gte</code> – maior ou igual a</li> <li><code>$ne</code> – diferente de</li> <li><code>$in</code> – está em (recebe uma lista)</li> <li><code>$nin</code> – não está em</li> <li><code>$mod</code> – resto igual a (recebe uma lista onde o primeiro valor é o divisor e o segundo o resto)</li> <li><code>$exists</code> – contém ou não o atributo</li> <li><code>$not</code> – negação de uma condição</li></li> </ul><br /><br /><h3>Ordenação</h3><br />A ordenação é feita usando o método <code>sort</code>. Por exemplo, para ordenar de modo descrescente pelo atributo <code>y</code>:<br /><pre><code><em>></em> <strong>db.foo.find().sort({ y: -1 });</strong></code></pre><br /><br /><h3>Expressões regulares</h3>Quando há campos com valor <em>string</em>, é possível usar expressões regulares:<br /><pre><code><em>></em> <strong>db.foo.find({ name: /^mon.o$/i });</strong></code></pre><br /><br /><h3>Índices</h3>Quando você faz muitas consultas baseadas em um determinado atributo, é interessante criar um índice para ele. Por exemplo, se quisermos criar um índice para nosso atributo <code>x</code>:<br /><pre><code><em>></em> <strong>db.foo.ensureIndex({ x: 1 });</strong></code></pre><br /><br />Obs.¹: o atributo <code>_id</code> possui índice por padrão.<br /><br />Obs.²: é possível criar índices compostos.<br /><br /><h3>Agregação</h3><br />Não vou entrar nesse mérito, mas é possível criar agregações, como <code>GROUP_BY</code> de SQL. Para tanto veja a <a href="http://www.mongodb.org/display/DOCS/Aggregation">documentação oficial</a>.<br /><br /><h3>Módulos de conexão com linguagens de programação</h3><br />Há módulos para várias linguagens, que podem ser encontrados na <a href="http://api.mongodb.org/">lista de APIs</a>.<br /><br />O módulo que pude e me fascinou foi para <a href="http://api.mongodb.org/python">Python</a>. Sua instalação usando <a href="http://peak.telecommunity.com/dist/ez_setup.py"><code>easy_install</code></a> foi muito simples:<br /><pre><code><em>bash$</em> <strong>sudo easy_install -U pymongo</strong></code></pre><br /><br />E seu uso extremamente fácil devido à similiaridade entre <a href="http://diveintopython.org/getting_to_know_python/dictionaries.html">dicionários de Python</a> e JSON. A única reclamação que faço é o fato do método <code>db.auth()</code> do MongoDB ter sido traduzido como <code>db.authenticate()</code> em Python, o que me fez perder alguns segundos tentando entender por que não funcionava.<br /><br /><h3>**</h3><br />Espero que este artigo seja mais do que útil e incite o uso dessa intrigante ferramenta.<br /><br />[]’s<br />Cacilhas, La Batalemaℭacilhας, ℒa ℬatalemahttp://www.blogger.com/profile/14265747724618147106noreply@blogger.com7tag:blogger.com,1999:blog-1387996720436450649.post-67799386894452266582010-01-14T21:13:00.007-03:002010-09-30T14:07:45.630-03:00FizzBuzz (No Stairway to Heaven)<img style="float: left; margin: 0 10px 10px 0; cursor: crosshair; width: 55px; height: 55px; border: none;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgjbk9FiJ0AyG1etlZdJdzcq0D0pGTaz_N6qtMS0U6GNgVG3K9QEO2rYvQRO-wy1DsTOWP78J03gmtb0Fd8LcaUZG5cL5yt4bA1BTFUarOaLyrnHNU7t85bS775VqVBp5Wh6kc-k38f05A/s200/glider.png" alt="Glider" /> Hoje um <a href="http://twitter.com/flavioamieiro">amigo meu</a> veio falar comigo de uma brincadeira velha – e que eu não conhecia – chamada <a href="http://stackoverflow.com/questions/437/what-is-your-solution-to-the-fizzbuzz-problem">FizzBuzz</a>.<br /><br /><tt>[update 2010-01-15]</tt><br />Sugestão de apontador do <a href="http://twitter.com/identica_cesarb">Cesar Barros</a>: <a href="http://wolfbyte-net.blogspot.com/2007/09/if-something-is-worth-doing.html">If something is worth doing…</a>.<br /><br />A propósito, atualizei o código Lisp.<br /><tt>[/update]</tt><br /><br />A brincadeira consiste em imprimir uma sequência de números, tradicionalmente de 1 a 100. Porém os números múltiplos de 3 devem ser substituídos por <em>fizz</em> e os números múltiplos de 5 por <em>buzz</em>. Os números que são divisíveis por 3 e por 5 devem ser substituídos por <em>fizzbuzz</em>.<br /><br />No Stackoverflow você pode encontrar as versões mais loucas, como <a href="http://stackoverflow.com/questions/437/what-is-your-solution-to-the-fizzbuzz-problem/21328#21328">LOLCODE</a>, <a href="http://stackoverflow.com/questions/437/what-is-your-solution-to-the-fizzbuzz-problem/4295#4295">Brainf*ck</a>, <a href="http://stackoverflow.com/questions/437/what-is-your-solution-to-the-fizzbuzz-problem/10452#10452">Ook</a>, <em>assembly</em> de <a href="http://stackoverflow.com/questions/437/what-is-your-solution-to-the-fizzbuzz-problem/10421#10421">8051</a>, <a href="http://stackoverflow.com/questions/437/what-is-your-solution-to-the-fizzbuzz-problem/66914#66914">6502</a> e <a href="http://stackoverflow.com/questions/437/what-is-your-solution-to-the-fizzbuzz-problem/37420#37420">Linux IA-32</a>, <a href="http://stackoverflow.com/questions/437/what-is-your-solution-to-the-fizzbuzz-problem/48722#48722">Hebreu Estruturado</a>, <a href="http://stackoverflow.com/questions/437/what-is-your-solution-to-the-fizzbuzz-problem/35629#35629">Cobol</a> e <a href="http://stackoverflow.com/questions/437/what-is-your-solution-to-the-fizzbuzz-problem/124485#124485">Forth</a>, como destaque para o poético código em <a href="http://stackoverflow.com/questions/437/what-is-your-solution-to-the-fizzbuzz-problem/91068#91068">Perl</a>.<br /><br />Aqui quero postar algumas soluções que achei interessantes.<br /><br /><strong>**</strong><br /><br />Em <a href="http://haskell.org/">Haskell</a>:<br /><pre><code class="prettyprint">putStr (concat [if (mod x 15)==0 then "fizzbuzz\n" else if (mod x 3)==0 then "fizz\n" else if (mod x 5)==0 then "buzz\n" else (show x) ++ "\n" | x <- [1..100]])</code></pre><br /><br />Em <a href="http://python.org/">Python</a>:<br /><pre><code class="prettyprint">print "\n".join('fizzbuzz' if x%15==0 else 'fizz' if x%3==0 else 'buzz' if x%5==0 else str(x) for x in xrange(1, 101))</code></pre><br /><br />Em <a href="http://www.perl.org/">Perl</a>:<br /><pre><code class="prettyprint">print +(fizz)[$_%3] . (buzz)[$_%5] || $_, $/ for 1..100;</code></pre><br /><br />Em <a href="http://clisp.cons.org/">Common Lisp</a>:<br /><pre><code class="prettyprint lang-cl">(format t "~{~A~%~}" (loop for x from 1 to 100 collect (if (zerop (mod x 15)) "fizzbuzz" (if (zerop (mod x 3)) "fizz" (if (zerop (mod x 5)) "buzz" x)))))</code></pre><br /><br />[]’s<br />Cacilhas, La Batalemaℭacilhας, ℒa ℬatalemahttp://www.blogger.com/profile/14265747724618147106noreply@blogger.com2tag:blogger.com,1999:blog-1387996720436450649.post-65761412600068790942010-01-08T23:07:00.003-03:002010-01-08T23:22:49.095-03:00Ajude a mostrar a PythonBrasil na PyCon 2010<img style="border: none; margin: 0pt 10px 10px 0pt; float: left; cursor: crosshair;" src="http://photos1.blogger.com/blogger/6505/3295/200/python.png" alt="" /> Meu amigo <a href="http://henriquebastos.net/">Henrique Bastos</a> publicou hoje um artigo entitulado <a href="http://henriquebastos.net/2010/01/08/ajude-a-mostrar-a-pythonbrasil-na-pycon-2010/">Ajude a mostrar a PythonBrasil na PyCon 2010</a>.<br /><br />A proposta é muito legal e já preenchi o <a href="http://henriquebastos.wufoo.com/forms/vamos-divulgar-nossa-comunidade-python-na-pycon/">formulário</a> para reunir informações sobre a Comunidade Python no Brasil.<br /><br />Segue uma reprodução do artigo:<br /><blockquote>Salve Pythonistas!<br /><br />De 19 a 21 de fevereiro será realizada a <a title="Site do evento." href="http://us.pycon.com">PyCon</a>, <a title="Veja onde será realizada a conferência." href="http://maps.google.com.br/maps/place?cid=16656910773578529375&q=hyatt+atlanta&hl=pt-BR&cd=1&cad=src:pplink&ei=TL1HS9TrJp_EyQTwxqTmBw&sig2=FbA0IOSLu6RvNabKmyeADg">em Atlanta nos Estados Unidos</a>. Como alguns de vocês já sabem, participarei do evento apresentando a <a title="Veja o resumo na página das palestras." href="http://us.pycon.org/2010/conference/talks">palestra #78 entitulada «Small acts make great revolutions»</a>.<br /><br />Esta apresentação será uma evolução da palestra relâmpago que fiz na <a title="Site do evento." href="http://www.pythonbrasil.org.br/2009">PythonBrasil[5] em Caxias do Sul</a>, onde tento difundir os conceitos por trás das iniciativas que vêm movimentando a comunidade no Rio de Janeiro.<br /><br />No entanto, falar só do que acontece no Rio me parece muito pouco para uma oportunidade dessas. Quero muito aproveitar a <a title="Site do evento." href="http://us.pycon.com">PyCon</a> para mostrar o grande desenvolvimento da <a href="http://www.python.org.br/wiki">Comunidade Python no Brasil</a>. Pessoasfantásticas estão fazendo as coisas acontecerem em Terras Brasilis. E a <a title="Video da 1a palestra relâmpago sobre "Small Acts" " href="http://www.vcasmo.com/video/pythonrio/5494">exemplo do que fiz no FISL 10</a>, pretendo continuar na linha de tentar evidenciar que na nossa comunidade, o todo é muito maior do que a soma das partes. Mas para isto, eu preciso da ajuda de vocês.<br /><br />Para poder falar com propriedade sobre a <a href="http://www.python.org.br/wiki">Comunidade Python no Brasil</a>, precisamos traçar o perfil do nosso grupo. Por isso, preparei um rápido questionário <em>online</em> que ajudará a levantar informações sobre nossa comunidade. Ele está disponível em: <a title="Não deixe de responder à pesquisa!" href="http://henriquebastos.wufoo.com/forms/vamos-divulgar-nossa-comunidade-python-na-pycon/">http://henriquebastos.wufoo.com/forms/vamos-divulgar-nossa-comunidade-python-na-pycon/</a><br /><br />É muito importante que o maior número de <em>pythonistas</em> respondam à pesquisa. Isso vai ajudar muito! Portanto, <strong>não economizem nos <em>retweets</em> e espalhem o <em>link</em> da pesquisa por todas as listas de email</strong> no Brasil, relacionadas com <a title="Site da linguagem" href="http://python.org">Python</a>.<br /><br />Além disso, também estou buscando <em>links</em> para boas fotos em alta resolução, como as tiradas na escadaria da PUC-RS durante o FISL 10 e as fotos «aéreas» da <a href="http://www.pythonbrasil.org.br/2008">PythonBrasil[4] na UVA</a> etc. Basta publicarem os <em>links</em> para as fotos nos comentários deste <em>post</em> ou me enviar por <em>email</em>.<br /><br />Obrigado à todos, e <em>vamo que vamo</em>!<br /><br />[]’s!</blockquote><br /><br />Já andei <a href="http://twitter.com/batalema/status/7541409617">tuitando</a> e divulguei nas <a href="http://montegasppa.blogspot.com/2010/01/ajude-mostrar-pythonbrasil-na-pycon.html">Reflexões de Monte Gasppa e Giulia C.</a> também. Convido todos então a <a href="http://henriquebastos.net/2010/01/08/ajude-a-mostrar-a-pythonbrasil-na-pycon-2010/">ler o artigo</a> e <a href="http://henriquebastos.wufoo.com/forms/vamos-divulgar-nossa-comunidade-python-na-pycon/">preencher o formulário</a>, ajudando assim o Henrique a revelar e expor a Comunidade Python no Brasil ao Mundo!<br /><br />[]’s<br />Cacilhas, La Batalemaℭacilhας, ℒa ℬatalemahttp://www.blogger.com/profile/14265747724618147106noreply@blogger.com0tag:blogger.com,1999:blog-1387996720436450649.post-49780135447848632302009-10-06T21:20:00.005-03:002009-10-06T21:31:37.620-03:00IDEs em ambiente GNU/Linux<img style="border: none; margin: 0pt 10px 10px 0pt; float: left; cursor: crosshair;" src="http://photos1.blogger.com/blogger/6505/3295/200/prog.png" alt="Poliedro" /> Semana passada escrevi um artigo sobre alguns <a href="http://pt.wikipedia.org/wiki/Ambiente_de_desenvolvimento_integrado">ambientes de desenvolvimento integrado</a> e editores de texto, alguns deles <a href="http://pt.wikipedia.org/wiki/Rapid_Application_Development">RAD</a>. No entanto o artigo ficou muito longo, daí tive a ideia de quebrá-lo em três partes.<br /><br />Publiquei em meu <em>blog</em> <a href="http://montegasppa.blogspot.com/">Reflexões de Monte Gasppa e Giulia C.</a>, mas agora, pensando bem, acho que cabe pelo menos uma referência aqui no <a href="http://kodumaro.blogspot.com/">Kodumaro</a>.<br /><br />As três partes do artigo são:<br /><ol> <li><a href="http://montegasppa.blogspot.com/2009/10/ide-1.html">Editores de texto</a></li> <li><a href="http://montegasppa.blogspot.com/2009/10/ide-2.html">IDEs focadas em projeto</a></li> <li><a href="http://montegasppa.blogspot.com/2009/10/ide-3.html">RAD</a></li> </ol><br /><br />A quem se interessar, boa leitura!<br /><br />[]'s<br />Cacilhas, La Batalemaℭacilhας, ℒa ℬatalemahttp://www.blogger.com/profile/14265747724618147106noreply@blogger.com0tag:blogger.com,1999:blog-1387996720436450649.post-25119961792691258932009-08-21T11:00:00.001-03:002009-08-21T11:02:19.360-03:00Dev in Rio 2009Gente,<br /><br />Notícia fresquinha no <a href="http://henriquebastos.net/2009/08/21/dev-in-rio-2009-eu-vou/"><em>blog</em> do Henrique Bastos</a>:<br /><br /><blockquote><h2>Dev in Rio 2009: EU VOU!</h2><br />Finalmente o Rio de Janeiro ganhou um grande evento de tecnologia focado no que é mais importante: <strong>Pessoas</strong>!<br /><br />É com muito orgulho que apresentamos o <a href="http://devinrio.com.br"><strong><em>Dev in Rio 2009</em></strong></a>, uma conferência bombástica sobre desenvolvimento de software que acontecerá no próximo dia <strong>14 de setembro</strong> no <a href="http://www.ccsulamerica.com.br/PgLocalizacao.php">Centro de Convenções SulAmérica</a>, no Rio de Janeiro!<br /><br />O <a href="http://devinrio.com.br"><strong><em>Dev in Rio 2009</em></strong></a>, contará com palestrantes nacionais e internacionais que falarão sobre <a href="http://java.sun.com/">Java</a>, <a href="http://www.ruby-lang.org/">Ruby</a> e <a href="http://rubyonrails.org/">Ruby on Rails</a>, <a href="http://python.org">Python</a>, <a href="http://djangoproject.com">Django</a>, <a href="http://www.opensource.org/">Open Source</a>, <a href="http://www.joomla.org/">Joomla!</a>, <a href="http://improveit.com.br/xp/manifesto_agil">Métodos Ágeis</a>, e muito mais. Nomes como <a title="Akita é a referência de Rails no Brasil." href="http://akitaonrails.com/">Fábio Akita</a>, <a title="Vinícius é especialista em Extreme Programming e produtos Web." href="http://improveit.com.br/">Vinícius Manhães Teles</a>, <a title="Jacob é co-criador do Django" href="http://jacobian.org">Jacob Kaplan-Moss</a>, <a title="Conheça mais sobre o Guilherme e sua particpação na Comunidade Java." href="http://guilhermesilveira.wordpress.com/">Guilherme Silveira</a>, <a title="Conheça mais sobre o Nico e seu trabalho na Caelum." href="http://blog.caelum.com.br/">Nico Steppat</a>, <a title="Conheça o Ryan e seu trabalho com Open Source e Joomla!" href="http://www.picnet.net/blog/author/cozimek/">Ryan Ozimek</a> e <a title="Jeff é consultor especializado em métodos ágeis." href="http://www.agileproductdesign.com/">Jeff Patton</a> agitarão um dia inteiro de muita tecnologia e diversão. Veja a <a href="http://devinrio.com.br">programação detalhada</a> no site da <a href="http://devinrio.com.br/"><strong><em>Dev in Rio 2009</em></strong></a>.<br /><br />Mas não é só isso. Como eu disse no início, este é um evento focado em <strong>pessoas</strong>. Toda a organização do evento está voltada para promover ao máximo a interação e integração entre os presentes.<br /><br />Por isso, o <a href="http://devinrio.com.br/"><strong><em>Dev in Rio 2009</em></strong></a> contará com uma <a href="http://dojorio.org">Arena DojoRio</a> onde participantes e palestrantes poderão programar lado à lado, experimentando diversas linguagens e técnicas, buscando juntos as melhores formas de desenvolver software.<br /><br />O evento está sendo organizado por <a href="http://henriquebastos.net">mim (Henrique Bastos)</a> em parceria com o meu amigo <a href="http://gc.blog.br/2009/08/20/dev-in-rio-2009-eu-vou/">Guilherme Chapiewski</a>, contando com todo o apoio dos membros da <a href="http://pythonrio.org">PythOnRio</a>, <a href="http://dojorio.org">DojoRio</a> e <a href="http://horaextra.org">#Horaextra</a>.<br /><br />A realização está sendo coordenada pelas nossas experientes amigas da <a href="http://www.arteccom.com.br">Arteccom</a>. E tê-las ao nosso lado já garante que este será um evento para marcar o circuito carioca.<br /><br /><strong>Você</strong> não pode perder esta incrível oportunidade de participar desse mega evento, interagir com grandes nomes do cenário mundial de tecnologia, conhecer pessoas interessantes que compartilham a paixão pelo desenvolvimento de software, e ainda por cima começar sua semana só na terça-feira. <a href="http://devinrio.com.br">Inscreva-se já!</a><br /><br /><em><strong>Nos vemos por lá!</strong></em><br /><br />[]’s!</blockquote><br /><br />[]'s<br />Cacilhas, La Batalemaℭacilhας, ℒa ℬatalemahttp://www.blogger.com/profile/14265747724618147106noreply@blogger.com0tag:blogger.com,1999:blog-1387996720436450649.post-33331947494402881052009-03-04T12:25:00.002-03:002009-03-04T12:27:12.761-03:00Calculadora científica com o Vim<img style="border: none; margin: 0pt 10px 10px 0pt; float: left; cursor: crosshair;" src="http://photos1.blogger.com/blogger/6505/3295/200/python.png" alt="" /> Desta vez vou <em>reblogar</em>…<br /><br />O <a href="http://www.blogger.com/profile/11511626443237920795">voyeg3r</a> publicou um artigo curtinho e muito legal sobre como criar uma calculadora científica no <a href="http://www.vim.org/">Vim</a> usando <a href="http://python.org/">Python</a>.<br /><br />O artigo pode ser encontrado no <a href="http://vivaotux.blogspot.com/2009/03/calculadora-cientifica-com-o-vim.html">vivaotux</a>.<br /><br />Boa leitura!<br /><br />[]'s<br />Cacilhas, La Batalemaℭacilhας, ℒa ℬatalemahttp://www.blogger.com/profile/14265747724618147106noreply@blogger.com2tag:blogger.com,1999:blog-1387996720436450649.post-62356022953205171292009-01-29T23:30:00.008-03:002015-04-16T00:57:15.224-03:00Propriedades e acessores<img style="border: none; margin: 0pt 10px 10px 0pt; float: left; cursor: crosshair;" src="http://photos1.blogger.com/blogger/6505/3295/200/python.png" alt="" /> Em <a href="http://java.sun.com">Java</a> e em <a href="http://http://www.cplusplus.com/">C++</a> há um bom motivo para que os atributos sejam sempre privados: preservar a interface das classes.<br />
<br />
Por exemplo, imagine que temos uma classe que representa o registro de uma pessoa. Nela temos a idade. Sendo um número inteiro, poderia ser um atributo público:<br />
<pre><code class="prettyprint">Person person;
person.age = 23;
std::cout << person.age << std::endl;</code></pre><br />
<br />
Faz sentido. Agora imagine que mais tarde descobrimos que é preciso fazer um tratamento do valor recebido – para estar numa faixa aceitável, por exemplo…<br />
<br />
Sem alterar a interface – e efetuar uma refatoração total de todo código onde ocorre instância – é impossível.<br />
<br />
Então, em Java e C++ é <strong>importante</strong> usar métodos acessores e manter os atributos privados. Dessa forma qualquer alteração futura pode ser feita na classe sem alterar o restante do código:<br />
<pre><code class="prettyprint">Person person;
person.setAge(23);
std::cout << person.getAge() << std::endl;</code></pre><br />
<h3>Propriedades</h3>
Já em <a href="http://python.org/">Python</a> isso não faz sentido!<br />
<br />
Em Python, além de atributos e métodos, há as propriedades. Então atributos podem ser públicos e, caso seja necessário adicionar alguma lógica na leitura ou gravação do atributo, basta substituí-lo por uma propriedade, sem prejudicar a interface.<br />
<pre><code class="prettyprint">class Person(object):
def __init__(self):
self.name = ""
self.age = 0</code></pre><br />
<br />
Substituindo por propriedades:<br />
<pre><code class="prettyprint">class Person(object):
def __init__(self):
self.name = ""
self.__age = 0
def _get_age(self):
return self.__age
def _set_age(self, age):
if 0 <= age < 150:
self.__age = age
age = property(_get_age, _set_age)</code></pre><br />
<br />
E a interface permanece imutável!<br />
<br />
<strong>Aviso:</strong> não use propriedade quando não for necessário! Use atributo público. Um pouco de desempenho não faz mal a ninguém, mesmo em <em>scripts</em>.<br />
<blockquote>Aviso²: testei esse código no <em>prompt</em> e não funciona, mas funciona em <em>script</em>. Se alguém puder descobrir o que está acontecendo, agradeço.<br />
<br />
<tt>[update 2009-08-26]</tt>Usando <a href="http://ipython.scipy.org/moin/">IPython</a> funcionou corretamente, portanto não sei o que causou o erro. =P<br />
<br />
Assim assim, valeu <a href="http://thiagoc.net/">Thiago Coutinho</a> por tê-lo identificado (acho que ainda ocorre durante a interpretação em linha do CPython tradicional).<tt>[/update]</tt></blockquote>
<h3>Usando metaclasse</h3>
Uma forma de implementar automaticamente autopropriedades foi sugerida por Guido van Rossum em <a href="http://www.pythonbrasil.com.br/moin.cgi/UnificandoTiposClasses">Unificando Tipos e Classes</a> (<a href="http://python.org/download/releases/2.2.3/descrintro/">original</a>).<br />
<br />
Usaremos uma metaclasse:<br />
<pre><code class="prettyprint">class autoprop(type):
def __init__(cls, name, base, dict):
super(autoprop, cls).__init__(name, base, dict)
props = {}
for method in dict:
if method.startswith("_get_") \
or method.startswith("_set_") \
or method.startswith("_del_"):
props[method[5:]] = True
for prop in props:
fget = getattr(cls, "_get_" + prop, None)
fset = getattr(cls, "_set_" + prop, None)
fdel = getattr(cls, "_del_" + prop, None)
setattr(cls, prop, property(fget, fset, fdel))</code></pre><br />
A ideia por trás dessa metaclasse é identificar métodos iniciados por <code>_get_</code>, <code>_set_</code> ou <code>_del_</code> e criar propriedades a partir daí. O método iniciado por <code>_get_</code> representa a leitura, <code>_set_</code> representa a escrita e <code>_del_</code> representa a remoção.<br />
<br />
Nossa classe então ficaria assim:<br />
<pre><code class="prettyprint">class Person(object):
__metaclass__ = autoprop
def __init__(self):
self.name = ""
self.__age = 0
def _get_age(self):
return self.__age
def _set_age(self, age):
if 0 <= age < 150:
self.__age = age</code></pre>
<h3>Usando decoradores</h3>
Mas realmente, ao contrário da metaclasse para <a href="http://kodumaro.blogspot.com/2009/01/s-ingleton.html"><em>singleton</em></a>, usar metaclasse para autopropriedade é como usar uma escopeta para espantar uma mosca.<br />
<br />
Vamos usar decoradores!<br />
<br />
O decorador <code>@property</code> pode ser usado para criar propriedades. Funciona muito bem e deixa o código bastante limpo:<br />
<pre><code class="prettyprint">class Person(object):
def __init__(self):
self.name = ""
self.__age = 0
@property
def age(self):
return self.__age
@age.setter
def age(self, age):
if 0 <= age < 150:
self.__age = age</code></pre><br />
A função deve ter o nome da propriedade. O método de escrita deve chamar-se <code>fset</code>, o de leitura <code>fget</code> e o de remoção <code>fdel</code>. A parte mais feia desse código é o retorno. =)<br />
<br />
**<br />
<br />
Fica aqui a dica!<br />
<br />
[]'s<br />
Cacilhas, La Batalema
ℭacilhας, ℒa ℬatalemahttp://www.blogger.com/profile/14265747724618147106noreply@blogger.com16tag:blogger.com,1999:blog-1387996720436450649.post-41572828831395472082009-01-27T21:39:00.006-03:002009-01-28T20:52:18.942-03:00Singleton<img style="border: none; margin: 0pt 10px 10px 0pt; float: left; cursor: crosshair;" src="http://photos1.blogger.com/blogger/6505/3295/200/python.png" alt="" /> Um <a href="http://en.wikipedia.org/wiki/Design_pattern"><em>design pattern</em></a> bastante conhecido na engenharia de <em>software</em> é <a href="http://pt.wikipedia.org/wiki/Singleton"><em>singleton</em></a>.<br /><br /><em>Singleton</em> é quando uma classe possui apenas uma instância e não se deseja que em uma mesma aplicação haja mais de uma.<br /><br />Exemplos de classes desejadamente <em>singleton</em> são <em>pools</em> e carregadores.<br /><br />Em <a href="http://www.cplusplus.com/">C++</a> a saída para criar uma classe <em>singleton</em> é tornar protegido seu método construtor e criar um método <code>getInstance</code> para retornar a instância única:<br /><pre><code class="prettyprint">class Loader {<br /> protected:<br /> Loader();<br /><br /> public:<br /> ~Loader();<br /><br /> static Loader& getInstance();<br />}</code></pre><br /><br />O método <code>getInstance</code> deve guardar uma referência estática para a instância:<br /><pre><code class="prettyprint">static Loader& Loader::getInstance() {<br /> static Loader *instance = 0;<br /> if (!instance)<br /> instance = new Loader();<br /> return *instance;<br />}</code></pre><br /><br /><a href="http://java.sun.com/">Java</a> utiliza a mesma abordagem, já outras linguagens, como <a href="http://www.perl.org/">Perl</a>, <a href="http://www.python.org/">Python</a> e <a href="http://www.ruby-lang.org/en/">Ruby</a> têm uma abordagem bem mais elegante.<br /><br /><h3><em>Singleton</em> em Python</h3><br />A ideia de <em>singleton</em> em Python – Perl e Ruby – é usar o próprio construtor da classe para obter a instância.<br /><br />Por exemplo, em vez de (Java):<br /><pre><code class="prettyprint">Image character = Loader.getInstance().getImage("char.png");</code></pre><br /><br />Teremos (Python):<br /><pre><code class="prettyprint">character = Loader().getImage("char.png")</code></pre><br /><br />Há três formas de implementar <em>singleton</em> em Python: <sup>1</sup>implementando diretamente na classe, <sup>2</sup>herdando um classe pai ou <sup>3</sup>usando metaclasse (o mais divertido!).<br /><br /><h3>Implementação direta na classe</h3><br />Você pode implementar uma classe <em>singleton</em> diretamente, usando os metamétodos <code>__new__</code> (construtor real) e <code>__init__</code> (construtor de inicialização).<br /><br />A classe ainda precisa ter um atributo de classe para armazenar a instância.<br /><br />A ideia mais simples é implementar <code>__new__</code> para devolver a instância em vez de uma nova a cada vez:<br /><pre><code class="prettyprint">class Loader:<br /><br /> __instance = None<br /> __initialized = False<br /><br /><br /> def __new__(cls, *args, **keyw):<br /> if cls.__instance is None:<br /> cls.__instance = object.__new__(cls)<br /> return cls.__instance</code></pre><br /><br />Repare no atributo <code>__initialized</code>… isso é porque ainda temos um problema: o construtor de inicialização (<code>__init__</code>) será chamado novamente a cada tentativa de obter a instância.<br /><br />Então precisamos verificar se a inicialização já foi executada:<br /><pre><code class="prettyprint"> def __init__(self, *args, **keyw):<br /> if not self.__initialized:<br /> self.__initialized = True<br /> # Restante do método...</code></pre><br /><br />Pronto! Nossa classe já é <em>singleton</em>.<br /><br /><h3>Herança</h3><br />Podemos usar uma classe pai que implemente unicidade e estendê-la:<br /><pre><code class="prettyprint">class Singleton:<br /><br /> __instance = None<br /> __initialized = False<br /><br /> def __new__(cls, *args, **keyw):<br /> if cls.__instance is None:<br /> cls.__instance = object.__new__(cls)<br /> return cls.__instance<br /><br /> def __init__(self, *args, **keyw):<br /> if not self.__initialized:<br /> self.__initialized = True<br /> self._init(*args, **keyw)</code></pre><br /><br />Assim, para implementar uma classe <em>singleton</em>:<br /><pre><code class="prettyprint">class Loader(Singleton):<br /><br /> def _init(self, *args, **keyw):<br /> # Este será o construtor...</code></pre><br /><br />A vantagem desta abordagem é sua capacidade de reaproveitamento: é possível reutilizar a classe <code>Singleton</code> como classe pai de cada nova classe <em>singleton</em>.<br /><br />No entanto traz dois inconvenientes: primeiro os métodos podem conflitar com métodos de outras classes pai em herança múltipla; segundo é preciso prestar atenção à implementação estranha (uso de <code>_init</code> em vez de <code>__init__</code>) e ter cuidado com a sobrescrita de métodos.<br /><br />A solução para tornar isso mais transparente é usar uma metaclasse.<br /><br /><h3>Metaclasse</h3><br />Metaclasse é um classe cujas instâncias são classes. Vamos criar uma metaclasse cujas classes sejam <em>singleton</em>:<br /><pre><code class="prettyprint">class singleton(type):<br /><br /> def __init__(cls, name, base, dict):<br /> super(singleton, cls).__init__(name, base, dict)<br /> cls.__instance = None<br /> cls.__copy__ = lambda self: self<br /> cls.__deepcopy__ = lambda self, memo=None: self<br /><br /> def __call__(cls, *args, **keyw):<br /> if cls.__instance is None:<br /> cls.__instance = \<br /> super(singleton, cls).__call__(*args, **keyw)<br /> return cls.__instance</code></pre><br /><br />Agora vem a beleza da metaclasse: para criar uma classe <em>singleton</em> basta fazer:<br /><pre><code class="prettyprint">class Loader:<br /> __metaclass__ = singleton</code></pre><br /><br />Nada mais! Todo o resto da classe pode ser implementado sem preocupações.<br /><br />A única dúvida que pode ocorrer é: e se a classe tiver outra metaclasse?<br /><br />É fácil resolver! Por exemplo: imagine uma classe <em>singleton</em> que implemente autopropriedades:<br /><pre><code class="prettyprint">class Loader:<br /><br /> class __metaclass__(autoprop, singleton):<br /> pass</code></pre><br /><br />**<br /><br />Está aqui outra dica! Mais sobre <em>singleton</em> pode ser encontrado nas <a href="http://montegasppa.blogspot.com/2006/09/metaclasses-singleton.html">Reflexões de Monte Gasppa e Giulia C.</a>.<br /><br />[]'s<br />Cacilhas, La Batalemaℭacilhας, ℒa ℬatalemahttp://www.blogger.com/profile/14265747724618147106noreply@blogger.com6