My favorites | Sign in
Project Home Downloads Wiki Issues Source
Repository:
Checkout   Browse   Changes   Clones    
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
Pensar en Python
----------------

Lo triste es que esta pobre gente trabajó mucho más de lo necesario, para
producir mucho más código del necesario, que funciona mucho más lento que el
código python idiomático correspondiente.

-- Phillip J. Eby en `Python no es Java
<http://dirtsimple.org/2004/12/python-is-not-java.html>`_


Nuestra misión en este capítulo es pensar en qué quiere decir Eby con "código
python idiomático" en esa cita. Nunca nadie va a poder hacer un pythonómetro que
te mida cuán idiomático es un fragmento de código, pero es posible desarrollar
un instinto, una "nariz" para sentir el "olor a python", así como un enófilo
[#]_ aprende a distinguir el aroma a clavos de hierro-níquel número 7
ligeramente oxidados en un Cabernet Sauvignon. [#]_

.. [#] En mi barrio los llamábamos curdas.

.. [#] Con la esperanza de ser un poco menos pretencioso y/o chanta, si Zeus
quiere.

Y si la mejor forma de conocer el vino es tomar vino, la mejor forma de conocer
el código es ver código. Este capítulo no es exhaustivo, no muestra todas las
maneras en que python es peculiar, ni todas las cosas que hacen que tu código
sea "pythonic" -- entre otros motivos porque *no las conozco* -- pero muestra
varias. El resto es cuestión de gustos.

Get/Set
~~~~~~~

Una instancia de una clase contiene valores. ¿Cómo se accede a ellos? Hay dos
maneras. Una es con "getters y setters", y estas son algunas de sus
manifestaciones:

.. code-block:: python

# Un getter te "toma" (get) un valor de adentro de un objeto y
# se puede ver así:
x1 = p.x()
x1 = p.get_x()
x1 = p.getX()

# Un setter "mete" un valor en un objeto y puede verse así:
p.set_x(x1)
p.setX(x1)


Otra manera es simplemente usar un miembro ``x`` de la clase:

.. code-block:: python

p.x = x1
x1 = p.x

La ventaja de usar getters y setters es el "encapsulamiento". No dicta que la
clase tenga un miembro ``x``, tal vez el valor que yo ingreso via ``setX`` es
manipulado, validado, almacenado en una base de datos, o tatuado en el estómago
de policías retirados con problemas neurológicos, lo único que importa es que
luego cuando lo saco con el getter me dé lo que tenga que dar (que no quiere
decir "me dé lo mismo que puse").

Muchas veces, los getters/setters se toman como un hecho de la vida, hago
programación orientada a objetos ``=>`` hago getters/setters.

Bueno, no.

.. admonition:: Analogía rebuscada

En un almacén, para tener un paquete de yerba, hay que pedírselo al
almacenero.
En un supermercado, para tener un paquete de yerba, hay que agarrar un
paquete de yerba.
En una farmacia (de las grandes), para obtener un paquete de yerba hay que
agarrar un paquete de yerba, pero para tener un Lexotanil hay que pedirlo al
farmacéutico.

En Java o C++, la costumbre es escribir programas como almacenes, porque la
alternativa es escribir supermercados donde chicos de 5 compran raticida.

En Python, la costumbre es escribir programas como supermercados, porque se
pueden convertir en farmacias apenas decidamos que tener raticida es buena idea.

Imaginemos que estamos escribiendo un programa que trabaja con "puntos" o sea
coordenadas (X,Y), y que queremos implementarlos con una clase. Por ejemplo:

.. class:: titulo-listado

Listado 1

.. class:: listado

.. code-block:: python
:linenos:

class Punto(object):
def __init__(self, x=0, y=0):
self.set_x(x)
self.set_y(y)

def x(self):
return self._x

def y(self):
return self._y

def set_x(self,x):
self._x=x

def set_y(self,y):
self._y=y

Esa es una implementación perfectamente respetable de un punto. Guarda X, guarda
Y, permite volver a averiguar sus valores... el problema es que eso no es
python. Eso es C++. Claro, un compilador C++ se negaría a procesarlo, pero a mí
no me engañan tan fácil, *eso es C++ reescrito para que parezca python*.

¿Por qué eso no es python? Por el obvio abuso de los métodos de acceso
(accessors, getter/setters), que son completamente innecesarios.

Si la clase punto es simplemente esto, y nada más que esto, y no tiene otra
funcionalidad, entonces prefiero esta:

.. class:: titulo-listado

Listado 2

.. class:: listado

.. code-block:: python
:linenos:

class Punto(object):
def __init__(self, x=0, y=0):
self.x=x
self.y=y

No sólo es más corta, sino que su funcionalidad es completamente equivalente, es
más fácil de leer porque es obvia (se puede leer de un vistazo), y hasta es más
eficiente.

La única diferencia es que lo que antes era ``p.x()`` ahora es ``p.x`` y que
``p.set_x(14)`` es ``p.x=14``, que no es un cambio importante, y es una
mejora en legibilidad.

Es más, si la clase punto fuera solamente ésto, podría ni siquiera ser una
clase, sino una ``namedtuple``:


.. class:: titulo-listado

Listado 3

.. class:: listado

.. code-block:: python
:linenos:

Punto = namedtuple('Punto', 'x y')

Y el comportamiento es *exactamente el del listado 2* excepto que es aún más
eficiente.

.. admonition:: Nota

Es fundamental conocer las estructuras de datos que te da el lenguaje. En
Python eso significa conocer diccionarios, tuplas y listas y el módulo
collections de la biblioteca standard.

Por supuesto que siempre está la posibilidad de que la clase Punto evolucione, y
haga otras cosas, como por ejemplo calcular la distancia al origen de un punto.

Si bien sería fácil hacer una función que tome una ``namedtuple`` y calcule
ese valor, es mejor mantener todo el código que manipula los datos de Punto
dentro de la clase en vez de crear una colección de funciones ad-hoc. Una
``namedtuple`` es un reemplazo para las clases sin métodos o los ``struct`` de
C/C++.

Pero... hay que considerar el programa como una criatura en evolución. Tal vez
al comenzar con una ``namedtuple`` *era suficiente*. No valía la pena demorar lo
demás mientras se diseñaba la clase Punto. Y pasar de una ``namedtuple`` a la
clase Punto del listado 2 es sencillo, ya que la interfaz que presentan es
idéntica.

La crítica que un programador que conoce OOP [#]_ haría (con justa razón) es que
no tenemos encapsulamiento. Que el usuario accede directamente a ``Punto.x`` y
``Punto.y`` por lo que no podemos comprobar la validez de los valores asignados,
o hacer operaciones sobre los mismos, etc.

.. [#] Object Oriented Programming, o sea, Programación Orientada a Objetos,
pero me niego a usar la abreviatura POO porque pienso en ositos.

Muy bien, supongamos que queremos que el usuario pueda poner sólo valores
positivos en x, y que los valores negativos deban ser multiplicados por -1.

En la clase del listado 1:

.. class:: titulo-listado

Listado 4

.. class:: listado

.. code-block:: python
:linenos:

class PuntoDerecho(Punto):
'''Un punto que solo puede estar a la derecha del eje Y'''

def set_x(self, x):
self._x = abs(x)

Pero... también es fácil de hacer en el listado 2, *sin cambiar la interfaz
que se presenta al usuario*:

.. class:: titulo-listado

Listado 5

.. class:: listado

.. code-block:: python
:linenos:

class PuntoDerecho(object):
'''Un punto que solo puede estar a la derecha del eje Y'''

def get_x(self):
return self._x

def set_x(self, x):
self._x = abs(x)

x = property(get_x, set_x)

Obviamente esto es casi lo mismo que si partimos del listado 1, pero con algunas
diferencias:

* La forma de acceder a ``x`` o de modificarlo es mejor -- ``print p.x`` en
lugar de ``print p.x()``. Sí, es cuestión de gustos nomás.

* No se hicieron los métodos para ``y`` por ser innecesarios.

Esto es importante: de ser necesarios esos métodos en el futuro es fácil
agregarlos. Si nunca lo son, entonces el listado 1 tiene dos funciones inútiles.

Sí, son dos funciones cortas, que seguramente no crean bugs pero tienen
implicaciones de performance, y tienen un efecto que a mí personalmente me
molesta: separan el código que hace algo metiendo en el medio código que no hace
nada.

Si esos métodos son funcionalmente nulos, cada vez que están en pantalla es
como una franja negra de censura de 5 líneas de alto cruzando mi editor. Es
*molesto*.

Singletons
~~~~~~~~~~

En un lenguaje funcional, uno no necesita patrones de diseño porque el
lenguaje es de tan alto nivel que terminás programando en conceptos que
eliminan los patrones de diseño por completo.

-- Slava Akhmechet

Una de las preguntas más frecuentes de novicios en python, pero con experiencia
en otros lenguajes es "¿cómo hago un singleton?". Un singleton es una clase que
sólo puede instanciarse una vez. De esa manera, uno puede obtener esa única
instancia simplemente reinstanciando la clase.

Hay varias maneras de hacer un singleton en python, pero antes de eso, dejemos
en claro **qué** es un singleton: un singleton es una variable global "lazy".

En este contexto "lazy" quiere decir que hasta que la necesito no se instancia.
Excepto por eso, no habría diferencias visibles con una variable global.

El mecanismo "obvio" para hacer un singleton en python es un módulo, que son
singletons porque así están implementados.

Ejemplo:

.. code-block:: pycon

>>> import os
>>> os.x=1
>>> os.x
1
>>> import os as os2
>>> os2.x
1
>>> os2.x=4
>>> os.x
4
>>>

No importa cuantas veces importe ``os`` (o cualquier otro módulo),
no importa con qué nombre lo haga, siempre es el mismo objeto.

Por lo tanto, podríamos poner todos nuestros singletons en un módulo
(o en varios) e instanciarlos con ``import`` y funciones dentro
de ese módulo.

Ejemplo:

.. class:: titulo-listado

singleton1.py

.. class:: listado

.. code-block:: python
:linenos:
:include: codigo/1/singleton1.py

.. code-block:: pycon

>>> import singleton1
>>> uno=singleton1.misingle()
>>> dos=singleton1.misingle()
>>> print uno
[]
>>> uno.append('xx')
>>> print dos
['xx']

Como pueden ver, ``uno`` y ``dos`` son el mismo objeto.

Una alternativa es no usar un singleton, sino lo que Alex Martelli
llamó un `Borg <http://code.activestate.com/recipes/66531-singleton-we-dont-need-no-stinkin-singleton-the-bo/>`_:


.. code-block:: python

class Borg:
__shared_state = {}
def __init__(self):
self.__dict__ = self.__shared_state

¿Cómo funciona?

.. code-block:: python

>>> a=Borg()
>>> b=Borg()
>>> a.x=1
>>> print b.x
1

Si bien ``a`` y ``b`` *no son el mismo objeto* por lo que no son
realmente singletons, el efecto final es el mismo.

Por último, si andás con ganas de probar magia más potente, es posible
hacer un singleton `usando metaclases <http://code.activestate.com/recipes/102187-singleton-as-a-metaclass/>`_,
según esta receta de Andres Tuells:

.. code-block:: python
:linenos:

## {{{ http://code.activestate.com/recipes/102187/ (r1)
"""
USAGE:
class A:
__metaclass__ = Singleton
def __init__(self):
self.a=1

a=A()
b=A()
a is b #true

You don't have access to the constructor,
you only can call a factory that returns always
the same instance.
"""

_global_dict = {}

def Singleton(name, bases, namespace):
class Result:pass
Result.__name__ = name
Result.__bases__ = bases
Result.__dict__ = namespace
_global_dict[Result] = Result()
return Factory(Result)


class Factory:
def __init__(self, key):
self._key = key
def __call__(self):
return _global_dict[self._key]

def test():
class A:
__metaclass__ = Singleton
def __init__(self):
self.a=1
a=A()
a1=A()
print "a is a1", a is a1
a.a=12
a2=A()
print "a.a == a2.a == 12", a.a == a2.a == 12
class B:
__metaclass__ = Singleton
b=B()
a=A()
print "a is b",a==b
## end of http://code.activestate.com/recipes/102187/ }}}

Seguramente hay otras implementaciones posibles. Yo opino que ``Borg``
al **no** ser un verdadero singleton, es la más interesante: hace lo
mismo, son tres líneas de código fácil, *eso es python*.


Loops y medios loops
~~~~~~~~~~~~~~~~~~~~

Repetirse es malo.

-- Anónimo

Repetirse es malo.

-- Anónimo


Hay una estructura de control que Knuth llama el "loop n y medio" (n-and-half
loop). Es algo así:

.. figure:: loop-n-y-medio.graph.pdf
:height: 7cm

¡Se sale por el medio! Como siempre se pasa al menos por una parte del loop
(A), Knuth le puso "loop n y medio".

Ésta es la representación de esta estructura en Python:

.. code-block:: python

while True:
frob(gargle)
# Cortamos?
if gargle.blasted:
# Cortamos!
break
refrob(gargle)

No, no quiero que me discutan. Ésa es la forma de hacerlo. No hay que tenerle
miedo al ``break``! En particular la siguiente forma me parece mucho peor:

.. code-block:: python

frob(gargle)
# Seguimos?
while not gargle.blasted:
refrob(gargle)
frob(gargle)

Es más propensa a errores. Antes, podía ser que ``frob(gargle)`` no fuera lo
correcto. Ahora no solo puede ser incorrecto, sino que puede ser incorrecto o inconsistente, si cambio solo una de las dos veces que se usa.

Claro, en un ejemplo de juguete esa repetición no molesta. En la vida real, tal
vez haya 40 líneas entre una y otra y no sea obvio que esa línea se repite.

Switches
~~~~~~~~

Hay una cosa que muchas veces los que programan en Python envidian de
otros lenguajes... ``switch`` (o ``case``).

Sí, Python no tiene un "if multirrama" ni un "goto computado" ni nada
de eso. Pero ... hay maneras y maneras de sobrevivir a esa carencia.

Esta es la peor:

.. code-block:: python

if codigo == 'a':
return procesa_a()
if codigo == 'b':
return procesa_b()
:
:
etc.

Esta es apenas un cachito mejor:

.. code-block:: python

if codigo == 'a':
return procesa_a()
elif codigo == 'b':
return procesa_b()
:
:
etc.

Esta es la buena:

.. code-block:: python

procesos = {
'a': procesa_a,
'b': procesa_b,
:
:
etc.
}

return procesos[codigo]()

Al utilizar un diccionario para clasificar las funciones, es mucho
más eficiente que una cadena de ``if``. Es además muchísimo más
fácil de mantener (por ejemplo, podríamos poner ``procesos``
en un módulo separado).

Patos y Tipos
~~~~~~~~~~~~~

"Estás en un laberinto de pasajes retorcidos, todos iguales."

-- Will Crowther en "Adventure"

..

"Estás en un laberinto de pasajes retorcidos, todos distintos."

-- Don Woods en "Adventure"


Observemos este fragmento de código:

.. code-block:: python

def diferencia(a,b):
# Devuelve un conjunto con las cosas que están
# en A pero no en B
return set(a) - set(b)

.. Admonition:: Set

Un set (conjunto) es una estructura de datos que almacena cosas sin
repeticiones. Por ejemplo, ``set([1,2,3,2])`` es lo mismo que ``set([1,2,3])``.

También soporta las típicas operaciones de conjuntos, como intersección,
unión y diferencia.

Ver también: `Sets en la biblioteca standard <http://docs.python.org/library/stdtypes.html#set-types-set-frozenset>`_

Es obvio como funciona con, por ejemplo, una lista:

.. code-block:: pycon

>>> diferencia([1,2],[2,3])
set([1])

¿Pero es igual de obvio que funciona con cadenas?

.. code-block:: pycon

>>> diferencia("batman","murciélago")
set(['b', 't', 'n'])

¿Por qué funciona? ¿Es que las cadenas están implementadas como una subclase de
``list``? No, la implementación de las clases ``str`` o ``unicode`` es
completamente independiente. Pero son *parecidos*. Tienen muchas cosas en común.

.. code-block:: pycon

>>> l=['c','a','s','a']
>>> s='casa'
>>> l[0] , s[0]
('c', 'c')
>>> l[-2:] , s[-2:]
(['s', 'a'], 'sa')
>>> '-'.join(l)
'c-a-s-a'
>>> '-'.join(s)
'c-a-s-a'
>>> set(l)
set(['a', 'c', 's'])
>>> set(s)
set(['a', 'c', 's'])

Para la mayoría de los usos posibles, listas y cadenas son *muy* parecidas. Y
resulta que son lo bastante parecidas como para que en nuestra función
``diferencia`` sean completamente equivalentes.

Un programa escrito sin pensar en "¿De qué clase es este objeto?" sino en "¿Qué
puede hacer este objeto?", es un programa muy diferente.

Para empezar, suele ser un programa más "informal" en el sentido de que
simplemente asumimos que nos van a dar un objeto que nos sirva. Si no nos sirve,
bueno, habrá una excepción.

Al mismo tiempo que da una sensación de libertad (¡Hey, puedo usar dos clases
sin un ancestro común!) también puede producir temor (¿Qué pasa si alguien llama
``hacerpancho(Perro())``?). Pues resulta que ambas cosas son ciertas. Es posible
hacer un pancho de perro, en cuyo caso es culpa del que lo hace, y es *problema
suyo*, no un error en la definición de ``hacerpancho``.

Esa es una diferencia filosófica. Si ``hacerpancho`` verifica que la entrada sea
una salchicha, siempre va a producir *por lo menos* un pancho. Nunca va a
producir un sandwich con una manguera de jardín en el medio, pero tampoco va a
producir un sandwich de portobelos salteados con ciboulette.

Es demasiado fácil imponer restricciones arbitrarias al limitar los tipos de
datos aceptables.

Y por supuesto, si es posible hacer funciones genéricas que funcionan con
cualquier tipo medianamente compatible, uno evita tener que implementar veinte
variantes de la misma función, cambiando sólo los tipos de argumentos. Evitar
esa repetición descerebrante es uno de los grandes beneficios de los lenguajes
de programación dinámicos como python.

Genéricos
~~~~~~~~~

Supongamos que necesito poder crear listas con cantidades arbitrarias de
objetos, todos del mismo tipo, inicializados al mismo valor.

.. Admonition:: Comprensión de lista

En las funciones que siguen, ``[tipo() for i in range(cantidad)]`` se llama
una comprensión de lista, y es una forma más compacta de escribir un ``for``
para generar una lista a partir de otra:

.. code-block:: python

resultado=[]
for i in range(cantidad):
resultado.append(tipo())

No conviene utilizarlo si la expresión es demasiado complicada.

Ver también: `Listas por comprensión en el tutorial de Python <http://docs.python.org.ar/tutorial/datastructures.html#listas-por-comprensi-n>`_

Un enfoque ingenuo podría ser este:

.. code-block:: python

def listadestr(cantidad):
return ['' for i in range(cantidad)]

def listadeint(cantidad):
return [0 for i in range(cantidad)]

# Y así para cada tipo que necesite...

Los defectos de esa solución son obvios. Una mejor solución:

.. code-block:: python


def listadecosas(tipo, cantidad):
return [tipo() for i in range(cantidad)]

Esa es una aplicación de programación genérica. Estamos creando código que solo
puede tener un efecto cuando, más adelante, lo apliquemos a un tipo. Es un caso
extremo de lo mostrado anteriormente, en este caso literalmente el tipo a usar
*no importa*. ¡Cualquier tipo que se pueda instanciar sin argumentos sirve!

Desde ya que es posible -- como diría un programador C++ -- "especializar el
template":

.. code-block:: pycon

def templatelistadecosas(tipo):
def listadecosas(cantidad):
return [tipo() for i in range(cantidad)]
return listadecosas

>>> listadestr=templatelistadecosas(str)
>>> listadeint=templatelistadecosas(int)
>>>
>>> listadestr(10)
['', '', '', '', '', '', '', '', '', '']
>>> listadeint(10)
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]


El truco de ese fragmento es que ``templatelistadecosas`` crea y devuelve una
nueva función cada vez que la invoco con un tipo específico. Esa función es la
"especialización" de ``templatelistadecosas``.

Otra forma de hacer lo mismo es utilizar la función ``functools.partial`` de la
biblioteca standard:

.. code-block:: python

import functools
def listadecosas(tipo, cantidad):
return [tipo() for i in range(cantidad)]

listadestr=functools.partial(listadecosas, (str))
listadeint=functools.partial(listadecosas, (int))

Este enfoque para resolver el problema es más típico de la así llamada
"programación funcional", y ``partial`` es una función de orden superior
(higher-order function) que es una manera de decir que es una función que se
aplica a funciones.

¿Notaron que todo lo que estamos haciendo es crear funciones muy poco
específicas?

Por ejemplo, ``listadecosas`` también puede hacer esto:

.. code-block:: python

import random
>>> listaderandom=functools.partial(listadecosas,
(lambda : random.randint(0,100)))
>>> listaderandom(10)
[68, 92, 83, 55, 89, 2, 9, 74, 9, 58]

Después de todo... ¿Quién dijo que ``tipo`` era un tipo de datos? ¡Todo lo que
hago con ``tipo`` es ``tipo()``!

O sea que tipo puede ser una clase, o una función, o cualquiera de las cosas que
en python se llaman ``callables``.

.. admonition:: lambdas

``lambda`` define una "función anónima". EL ejemplo usado es el equivalente
de

.. code-block:: python

def f():
return random.randint(0,100)
listaderandom=functools.partial(listadecosas, f)

La ventaja de utilizar ``lambda`` es que, si no se necesita reusar la
función, mantiene la definición en el lugar donde se usa y evita tener
que buscarlo en otra parte al leer el código.

`Más información <http://docs.python.org.ar/tutorial/controlflow.html#formas-con-lambda>`_


Decoradores
~~~~~~~~~~~

En un capítulo posterior vamos a ver fragmentos de código como este:

.. code-block:: python
:include: codigo/2/pyurl3.py
:start-at: @bottle.post('/')
:end-at: """Crea un nuevo slug."""
:linenos:
:linenos_offset:

Esos misteriosos ``@algo`` son decoradores. Un decorador es
simplemente una cosa que se llama pasando la función a decorar como
argumento. Lo que en matemática se denomina "composición de funciones".

Usados con cuidado, los decoradores mejoran mucho la legibilidad
de forma casi mágica. ¿Querés un ejemplo? Así se vería ese código
sin decoradores:

.. code-block:: python

def alta():
"""Crea un nuevo slug"""
:
:

# UGH
alta = bottle.route('/')(bottle.view('usuario.tpl')(alta))

¿Cuándo usar decoradores? Cuando querés cambiar el comportamiento
de una función, y el cambio es:

* Suficientemente genérico como para aplicarlo en más de un lugar.
* Independiente de la función en sí.

Como decoradores no está cubierto en el
`tutorial <http://docs.python.org.ar/tutorial/contenido.html>`_
vamos a verlos con un poco de detalle, porque es una de las técnicas
que más diferencia pueden hacer en tu código.

Los decoradores se podrían dividir en dos clases, los "con
argumentos" y los "sin argumentos".

Los decoradores sin argumentos son más fáciles, el ejemplo clásico es
un "memoizador" de funciones. Si una función es "pesada", no
tiene efectos secundarios, y está garantizado que *siempre*
devuelve el mismo resultado a partir de los mismos parámetros,
puede valer la pena "cachear" el resultado. Ejemplo:

.. class:: titulo-listado

deco.py

.. code-block:: python
:include: codigo/1/deco.py
:linenos:
:linenos_offset:

¿Qué sucede cuando lo ejecutamos?

::

$ python codigo/1/deco.py
Calculando, n = 4
Calculando, n = 3
Calculando, n = 2
24
24
Calculando, n = 5
120
6

Resulta que ahora no siempre se ejecuta ``factorial``. Por ejemplo,
el segundo llamado a ``factorial(4)`` ni siquiera entró en
``factorial``, y el ``factorial(5)`` entró una sola vez en vez de 4. [#]_

.. [#] Usando un cache de esta forma, la versión recursiva puede ser
más eficiente que la versión iterativa, dependiendo de con
qué argumentos se las llame (e ignorando los problemas de
agotamiento de pila).

Hay un par de cosas ahí que pueden sorprender un poquito.

* ``memo`` toma una función ``f`` como argumento y devuelve otra
(``memof``). Eso ya lo vimos en genéricos_.

* ``cache`` queda asociada a ``memof``, para cada función "memoizada" hay
un ``cache`` separado.

Eso es así porque es local a ``memo``. Al usar el decorador hacemos
``factorial = memo(factorial)`` y como **esa** ``memof`` tiene una
referencia al ``cache`` que se creó localmente en esa llamada a
``memo``, ese ``cache`` sigue existiendo mientras ``memof`` exista.

Si uso ``memo`` con otra función, es otra ``memof`` y otro ``cache``.

Los decoradores con argumentos son... un poco más densos. Veamos un
ejemplo en detalle.

Consideremos este ejemplo "de juguete" de un programa cuyo flujo
es impredecible [#]_

.. [#] Sí, ya sé que realmente es un poco predecible porque no uso
bien ``random``. Es a propósito ;-)

.. class:: titulo-listado

deco1.py

.. code-block:: python
:include: codigo/1/deco1.py
:linenos:
:linenos_offset:

Al ejecutarlo hace algo así::

$ python codigo/1/deco1.py
Hago varias cosas
Estoy haciendo algo no tan importante
Estoy haciendo algo importante
Estoy haciendo algo no tan importante
Estoy haciendo algo no tan importante

Si no fuera tan obvio cuál función se ejecuta en cada momento, tal
vez nos interesaría saberlo para poder depurar un error.

Un tradicionalista te diría "andá a cada función y agregále logs".
Bueno, pues es posible hacer eso sin tocar cada función (por lo menos
no mucho) usando decoradores.

.. class:: titulo-listado

deco2.py

.. code-block:: python
:include: codigo/1/deco2.py
:linenos:
:linenos_offset:

¿Y qué hace?

::

$ python codigo/1/deco2.py
===> Entrando a Master
Hago varias cosas
===> Entrando a F1
Estoy haciendo algo importante
<=== Saliendo de F1
===> Entrando a F1
Estoy haciendo algo importante
<=== Saliendo de F1
===> Entrando a F2
Estoy haciendo algo no tan importante
<=== Saliendo de F2
===> Entrando a F2
Estoy haciendo algo no tan importante
<=== Saliendo de F2
<=== Saliendo de Master

Este decorador es un poco más complicado que ``memo``, porque tiene
dos partes.

Recordemos que un decorador tiene que tomar como argumento una función
y devolver una función [#]_.

.. [#] No es estrictamente cierto, podría devolver una clase, o
cualquier cosa ``x`` que soporte ``x(f)`` pero digamos que una
función.

Entonces al usar ``logger`` en ``f1`` en realidad no voy a pasarle
``f1`` a la función ``logger`` si no al **resultado** de ``logger('F1')``

Eso es lo que hay que entender, así que lo repito: ¡No a ``logger``
sino al resultado de ``logger('F1')``!

En realidad ``logger`` no es el decorador, es una "fábrica" de decoradores.
Si hago ``logger('F1')`` crea un decorador que imprime ``===> Entrando a F1``
y ``<=== Saliendo de F1`` antes y después de llamar a la función decorada.

Entonces ``wrapper`` es el decorador "de verdad", y es comparable con ``memo``
y ``f2`` es el equivalente de ``memof``, y tenemos exactamente el
caso anterior.


Claro pero corto pero claro
~~~~~~~~~~~~~~~~~~~~~~~~~~~

Depurar es dos veces más difícil que programar. Por lo tanto, si
escribís el código lo más astuto posible, por definición, no sos lo
suficientemente inteligente para depurarlo.

--Brian W. Kernighan

Una de las tentaciones de todo programador es escribir código corto [#]_. Yo
mismo soy débil ante esa tentación.

.. [#] Esta peculiar perversión se llama "code golfing". Y es muy divertida, si
no se convierte en un modo de vida.

.. admonition:: Código Corto

.. code-block:: pycon

j=''.join
seven_seg=lambda z:j(j(' _ |_ _|_| |'[ord(\
"ucd*\]Rml"[int(a)])/u%8*2:][:3]for a in z)+\
"\n"for u in(64,8,1))
>>> print seven_seg('31337')
_ _ _ _
_| | _| _| |
_| | _| _| |


El problema es que el código se escribe una sola vez, pero se lee cientos. Cada
vez que vayas a cambiar algo del programa, vas a leer más de lo que escribís.
Por lo tanto es fundamental que sea fácil de leer. El código *muy* corto es
ilegible. El código demasiado largo *también*.

Funciones de 1000 líneas, ifs anidados de 5 niveles, cascadas de condicionales
con 200 ramas... todas esas cosas son a veces tan ilegibles como el ejemplo
anterior.

Lo importante es lograr un balance, hacer que el código sea corto, pero *no
demasiado corto*. En python hay varias estructuras de control o de datos que
ayudan en esa misión.

Consideremos la tercera cosa que aprende todo programador: iteración. En python,
se itera sobre listas [#]_ por lo que no sabemos, a priori, la posición del ítem
que estamos examinando, y a veces es necesaria.

.. [#] No exactamente, se itera sobre iterables, valga la redundancia, pero los
podemos pensar como listas.

Malo:

.. code-block:: python

index=0
happy_items=[]
for item in lista:
if item.is_happy:
happy_items.append(index)
index+=1

Mejor:

.. code-block:: python

happy_items=[]
for index, item in enumerate(lista):
if item.is_happy:
happy_items.append(index)

Mejor si te gustan las comprensiones de lista:

.. code-block:: python

happy_items=[ index for (index, item) in enumerate(lista) \
if item.is_happy ]

Tal vez demasiado:

.. code-block:: python

filter(lambda x: x[0] if x[1].is_happy else None, enumerate(lista))

¿Por qué demasiado? Porque **yo** no entiendo que hace a un golpe de vista,
necesito "desanidarlo", leer el lambda, desenredar el operador ternario,
darme cuenta de qué filtra, ver a qué se aplica el filtro.

Seguramente otros, mejores programadores sí se dan cuenta. En cuyo caso el
límite de "demasiado corto" para ellos estará más lejos.

Sin embargo, el código no se escribe para uno (o al menos no se escribe
sólo para uno), sino para que lo lean otros. Y no es bueno hacerles la vida
difícil al divino botón, o para ahorrar media línea.

.. admonition:: Nota

La expresión ternaria u operador ternario se explica en `Ternarios vs ifs`_

Lambdas vs alternativas
~~~~~~~~~~~~~~~~~~~~~~~

En ejemplos anteriores he usado ``lambda``. ¿Qué es ``lambda``? Es otra manera
de definir una función, nada más. En lo que a python respecta, estos dos
fragmentos son exactamente lo mismo:

.. code-block:: python

suma = lambda a,b: a+b
..

.. code-block:: python

def suma(a,b):
return a+b


Lambda tiene una limitación: Su contenido solo puede ser una expresión,
es decir, algo que "devuelve un resultado". El resultado de esa expresión es el resultado del lambda.

¿Cuando conviene usar ``lambda``, y cuándo definir una función? Más allá de la
obviedad de "cuando ``lambda`` no alcanza, usá funciones", en general, me parece
más claro usar funciones, a menos que haya un excelente motivo.

Por otro lado, hay veces que queda muy bonito como para resistirse, especialmente
combinado con filter:

.. code-block:: python

# Devuelve los items mayores que 0 de una lista
filter (lambda x: x > 0 , lista)

Pero yo probablemente haría esto:

.. code-block:: python

# Devuelve los items mayores que 0 de una lista
[ x for x in lista if x > 0 ]

¿Es uno más legible que el otro? No lo sé. Si sé que el primero tiene un "gusto"
más a programación funcional, mientras que el segundo es más únicamente python,
pero es cuestión de preferencias personales.

Usar ``lambda`` en el medio de líneas de código o como argumentos a funciones
puede hacer que la complejidad de la línea pase el umbral de "expresivo" a
"farolero", y disminuye la legibilidad del código.

Un caso en el que ``lambda`` es mejor que una función es cuando se usa una única
vez en el código y el significado es obvio, porque insertar definiciones de
funciones "internas" en el medio del código arruina el flujo.

.. code-block:: python

import random
>>> listaderandom=functools.partial(listadecosas,
(lambda : random.randint(0,100)))
>>> listaderandom(10)
[68, 92, 83, 55, 89, 2, 9, 74, 9, 58]

Me parece más elegante que esto:

.. code-block:: python

import random
def f1():
return random.randint(0,100)
>>> listaderandom=functools.partial(listadecosas,
(f1))
>>> listaderandom(10)
[68, 92, 83, 55, 89, 2, 9, 74, 9, 58]


Especialmente en un ejemplo real, donde ``f1`` se va a definir en el medio de
un algoritmo cualquiera con el que no tiene nada que ver.

Como el lector verá... me cuesta elegir. En general, trato de no usar lambda a
menos que la alternativa sea farragosa y ensucie el entorno de código.

Ternarios vs ifs
~~~~~~~~~~~~~~~~

El operador ternario en python es relativamente reciente, apareció en la versión
2.5 y es el siguiente:

.. code-block:: pycon

>>> "A" if True else "B"
'A'
>>> "A" if False else "B"
'B'

Es una forma abreviada del ``if`` que funciona como expresión (se evalúa y
devuelve un valor).

La forma general es::

VALOR1 if CONDICION else VALOR2

Si ``CONDICION`` es verdadera, entonces la expresión devuelve ``VALOR1``, si no,
devuelve ``VALOR2``.

¿Cuál es el problema del operador ternario?

Sólo se puede usar cuando no te importe no ser compatible con python 2.4.
Acordáte que hay (y va a haber hasta el 2013 por lo menos) versiones de Linux en amplio uso con python 2.4

Si ignoramos eso, hay casos en los que simplifica mucho el código. Tomemos el
ejemplo de un argumento por default, de un tipo modificable a una función.
Ésta es la versión clásica:

.. code-block:: python

class c:
def f(self, arg = None):
if arg is None:
self.arg = []
else:
self.arg = arg

Y esta es la versión "moderna":

.. code-block:: python

class c:
def f(self, arg = None):
self.arg = 42 if arg is None else arg

¿La ventaja? ¡Se lee de corrido! "self.arg es 42 si arg es None, si no, es arg"

.. admonition:: Nota

La versión realmente obvia:

.. code-block:: pycon

>>> class c:
... def f(self, arg=[]):
... self.arg=arg

Tiene el problema de que... no funciona. Al ser ``[]`` modificable,
cada vez que se llame a ``instancia.f()`` sin argumentos se va a
asignar **la misma lista** a ``instancia.arg``. Si luego se modifica su
contenido en alguna instancia... ¡Se modifica en **todas las
instancias!** Ejemplo:

.. code-block:: pycon

>>> c1=c()
>>> c1.f()
>>> c2=c()
>>> c2.f()
>>> c1.arg.append('x')
>>> c2.arg
['x']

Sí, es raro. Pero tiene sentido si se lo piensa un poco. En python la
asignación es únicamente decir "este nombre apunta a este objeto".

El ``[]`` de la declaración es un objeto único. Estamos haciendo que
``self.arg`` apunte a **ese** objeto cada vez que llamamos a ``c.f``.

Con un tipo inmutable (como un string) esto no es problema.


Pedir perdón o pedir permiso
~~~~~~~~~~~~~~~~~~~~~~~~~~~~

"Puede fallar."

-- Tu Sam

No hay que tener miedo a las excepciones. Las cosas pueden fallar, y cuando
fallen, es esperable *y deseable* que den una excepción.

¿Cómo sabemos si un archivo se puede leer? ¿Con ``os.stat("archivo")``?
¡No, con ``open("archivo","r")``!

Por ejemplo, esto no es buen python:

.. class:: titulo-listado

esnumero.py

.. code-block:: python
:include: codigo/1/esnumero.py
:linenos:
:linenos_offset:

Eso lo que muestra es miedo a que falle ``float()``. ¿Y sabés qué?
``float`` está mucho mejor hecha que mi ``es_numero``...

Esto es mucho mejor Python:

.. code-block:: python

s = raw_input()
try:
print "El doble es ",2 * float(s)
except ValueError:
print "No es un número"


Esto está muy relacionado con el tema de "duck typing" que vimos antes.
Si vamos a andarnos preocupando por como puede reaccionar cada uno
de los elementos con los que trabajamos, vamos a programar de forma
completamente burocrática y descerebrante.

Lo que queremos es tratar de hacer las cosas, y manejar las excepciones
como corresponda. ¿No se pudo calcular el doble? ¡Ok, avisamos y listo!

No hay que programar a la defensiva, hay que ser cuidadoso, no miedoso.

Si se produce una excepción que no te imaginaste, está **bien** que se
propague. Por ejemplo, si antes en vez de un ``ValueError`` sucediera
otra cosa, **queremos enterarnos**.

-------------------------

Faltan subsecciones? Se pueden agregar si la idea surge viendo los otros
capítulos.

Change log

278ab22cd6e1 by Roberto Alsina <rals...@netmanagers.com.ar> on Jul 24, 2011   Diff
Hacer que compile
Go to: 
Sign in to write a code review

Older revisions

29fed9791e40 by Roberto Alsina <rals...@netmanagers.com.ar> on Jul 24, 2011   Diff
pep8
2fe706652bf8 by Roberto Alsina <rals...@netmanagers.com.ar> on Apr 21, 2010   Diff
correccion de jjconti
0faa38f310a9 by Roberto Alsina <rals...@netmanagers.com.ar> on Apr 20, 2010   Diff
Correccion de jjconti
All revisions of this file

File info

Size: 37151 bytes, 1244 lines
Powered by Google Project Hosting