La pythonicidad al palo

Martin Gaitán / @tin_nqn_ / #PyconAr2013

No da igual cualquier lenguaje

Un lenguaje que no afecta tu manera de pensar sobre la programación, no merece ser aprendido.

Source: Alan Perlis, Epigrams on Programming

Python nos afecta

para bien

Así se itera en muchos lenguajes

for (i=0; i < mylist_length; i++) {
   do_something(mylist[i]);
}

Esto es "python"

i = 0
while i < len(mylist):
   do_something(mylist[i])
   i += 1

Buuuh!

for i in range(len(mylist)):
   do_something(mylist[i])

Ok, dale que va queriendo

Esto es Python

for element in mylist:
   do_something(element)

Simple is better than complex. Beautiful is better than ugly.

Source: Tim Peters, The Zen of Python

El Zen

>>> import this

No es sólo un huevo de pascua

Es la guía filosófica de la pythonicidad

Por qué programar pythónicamente ?

PEP8

la guía de estilo de codificación.

Programs must be written for people to read, and only incidentally for machines to execute.

Source: Abelson & Sussman, Structure and Interpretation of Computer Programs

Atenti...

flake8 FTW! (en el editor o como VCS hook)

Otras herramientas pythonistas

Para los exquisitos: imports

pip install isort

Algunos conceptos: ducktyping

Es más fácil pedir perdón que pedir permiso

def f(animal):
    if isinstance(animal, Duck):
        animal.quack()
    else:
        print("%s can't quack" % animal)

def f(animal):
    try:
        animal.quack()
    except (AttributeError, TypeError):
        print("%s can't quack" % animal)

getter y setters

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

Source: Phillip J. Eby, Python no es Java

La mayoría de las veces no hacen falta

x1 = p.get_x()      # buuh
p.set_x(x1)

x1 = p.x
p.x = x1

Cuando de verdad hacen falta, se pueden definir con property

@property
def edad(self):
    return (date.today() - self.fecha_nacimiento).days / 365

>>> p.edad
31

Pythonicemosnos un poco

Condiciones

if x > 0 and x < 100:       # buuh
    ...

if 0 < x < 100:
    ...

Otra

if x == 0 or x == 2 or x == 4:
    ....

if x in (0, 2, 4):

Expresiones condicionales (operador ternario)

if condition:
    a = x
else:
    a = y

a = x if condition else y

Unir cadenas

names = ['x-ip', 'facundobatista', 'nessita', 'lipe_p']

s = names[0]
for name in names[1:]:
    s += ', ' + name

s = ', '.join(names)

Packing/Unpacking (asignación múltiple)

p = u'Martín', u'Gaitán', 31

fname = p[0]        # buuhh
lname = p[1]
age = p[2]

fname, lname, age = p

en python 3 el unpacking es mucho más poderoso

Packing/Unpacking 2

def fibonacci(n):
    x, y = 0, 1
    for i in xrange(n):
        yield x             # btw, yield
        x, y = y, x + y

No muevas los datos innecesariamente

Construir diccionarios desde secuencias

names = ['raymond', 'rachel', 'matthew']
colors = ['red', 'green', 'blue']

d = dict(zip(names, colors))
{'matthew': 'blue', 'rachel': 'green', 'raymond': 'red'}

Listas por comprehensión / Expresiones generadoras

result = []
for i in range(10):
    if i % 2 == 0:
    s = i ** 2
    result.append(s)
sum(result)

sum([i**2 for i in xrange(10) if i % 2 == 0])

sum(i**2 for i in xrange(10) if i % 2 == 0)
  • no abusar de los oneliner
  • regla: una línea == una oración.

La legibilidad cuenta: usá los kwargs

twitter_search('#PyconAr2013', False, 20, True)

twitter_search('#PyconAr', retweets=False,
               numtweets=20, popular=True)

La legibilidad cuenta: namedtuple

>>> doctest.testmod()
    (0, 4)

from collections import namedtuple
TestResults = namedtuple('TestResults', ['failed', 'attempted'])

>>> doctest.testmod()
    TestResults(failed=0, attempted=4)

collections tiene estructuras buenísimas

Conjuntos

Son muy útiles!

engineers = {'John', 'Jane', 'Jack', 'Janice'}
programmers = {'Jack', 'Sam', 'Susan', 'Janice'}
managers = {'Jane', 'Jack', 'Susan', 'Zack'}
employees = engineers | programmers | managers           # union
fulltime_management = managers - engineers - programmers # difference
engineering_management = engineers & managers            # intersection

>>> engineering_management
set(['Jane', 'Jack'])

Decoradores: factorizá lo administrativo

def web_lookup(url, saved={}):
    if url in saved:
        return saved[url]
    page = urllib.urlopen(url).read()
    saved[url] = page
    return page

@cache
def web_lookup(url):
    return urllib.urlopen(url).read()

Contextos: sentencia with

@contextmanager
def tag(name):
    print("<%s>" % name)
    yield
    print("</%s>" % name)

>>> with tag("h1"):
...    print("foo")

py3 tiene ContextDecorator que es una clase que funciona como decorador o administrador de contexto.

Bucles anidados

combs = []
for a in x:
    for b in y:
        for c in z:
            combs.append((a, b, c))

combs = itertools.product(x, y, z)

itertools es groso!

Para discutir después...

blocks = []
while True:
    block = f.read(32)
    if block == '':
        break
    blocks.append(block)

blocks = []
for block in iter(partial(f.read, 32), ''):
    blocks.append(block)

un código demasiado avanzado puede ser críptico para otros programadores es elegante pero no más rápido. Al menos requiere un comentario

Consejos finales

Y aprovechá PyAr

Participá de la lista

¡PyCamps!

Y queremos tu charla en la PyCon 2014!



Muchas gracias

SpaceForward
Left, Down, Page DownNext slide
Right, Up, Page UpPrevious slide
POpen presenter console
HToggle this help