pythonnerdlabs.com.ar/media/2017/07/12/python.pdf · 2017-07-12 · estilos python es un lenguaje...

Post on 25-Jul-2020

14 Views

Category:

Documents

0 Downloads

Preview:

Click to see full reader

TRANSCRIPT

Python

Leo Vidarte

Python fue creado porGuido van Rossumen 1991

Características principales:

● Lenguaje de alto nivel● Interpretado● Tipado fuerte, pero dinámico● Los bloques no necesitan llaves● La documentación es parte del código● Posee un intérprete interactivo

Filosofía

Python hace foco en la simplicidad, la claridad, el pragmatismo y la diversión.

Es sencillo de aprender y su código es elegante:

def fib(n):

a, b = 0, 1

while a < n:

print(a, end=' ')

a, b = b, a+b

0 1 1 2 3 5 8 13 21...

EstilosPython es un lenguaje de programación multiparadigma.

Esto significa que más que forzar a los programadores a adoptar un estilo particular de programación, permite varios estilos:

● Programación orientada a objetos● Programación imperativa● Programación funcional

Usos

Python es un lenguaje multipropósito.

Sirve para crear todo tipo de software:

● Aplicaciones de escritorio● Aplicaciones Web● Servicios (API's)● Automatizar tareas en servidores● Internet of Things

Su intérprete interactivo es idealpara aprender y hacer pruebas

Intérprete interactivo

$> python3

Python 3.5.2 (default, Jul 5 2016, 12:43:10)

Type "help", "copyright", "credits" or "license" for more

information.

>>>

Intérprete interactivo

$> python3

Python 3.5.2 (default, Jul 5 2016, 12:43:10)

Type "help", "copyright", "credits" or "license" for more

information.

>>> print("hola a todos")

Intérprete interactivo

$> python3

Python 3.5.2 (default, Jul 5 2016, 12:43:10)

Type "help", "copyright", "credits" or "license" for more

information.

>>> print("hola a todos")

hola a todos

Python como calculadora>>> 2 + 2

4

>>> 2 - 2

0

>>> 2 * 3

6

>>> 10 / 2

5.0

>>> 10 // 3

3

>>> 2 ** 3

8

>>> (10 - 3) * 5

35

>>> _ / 3

2.6666666666666665

>>> 8 % 3

2

Juguemos un poco !

Logo en Python

>>> import turtle

>>>

Logo en Python

>>> import turtle

>>> for i in range(5):

... turtle.forward(100)

... turtle.right(144)

...

>>>

Logo en Python

>>> import turtle

>>> for i in range(5):

... turtle.forward(100)

... turtle.right(144)

...

>>>

Logo en Python

>>> import turtle

>>> for _ in range(20):

... turtle.forward(i * 10)

... turtle.right(144)

Logo en Python

>>> import turtle

>>> for _ in range(20):

... turtle.forward(i * 10)

... turtle.right(144)

...

>>>

Un ejemplomás interesante 1 import turtle

2

3 colors = [

4 'red', 'purple', 'blue',

5 'green', 'yellow', 'orange'

6 ]

7

8 turtle.bgcolor('black')

9

10 for x in range(360):

11 turtle.pencolor(colors[x % 6])

12 turtle.width(x/100+1)

13 turtle.forward(x)

14 turtle.left(59)

VariablesTipado dinámico

>>> n = 1

>>> type(n)

<class 'int'>

>>> n = "ahora soy un str"

>>> type(n)

<class 'str'>

>>> n = 1.2

>>> type(n)

<class 'float'>

Tipado fuerte

>>> a = 1

>>> b = "2"

>>> a + b

Traceback (most recent call last):

File "<stdin>", line 1, in <module>

TypeError: unsupported operand type(s) for

+: 'int' and 'str'

>>> a + int(b)

3

Tipos básicos: int, float, str, bool>>> i = 1

>>> type(i)

<class 'int'>

>>> f = 3.14

>>> type(f)

<class 'float'>

>>> s = "2"

>>> type(s)

<class 'str'>

>>> b = True

>>> type(b)

<class 'bool'>

Notación binaria, octal y hexadecimal

>>> 0b111

7

>>> 0o111

73

>>> 0x111

273

>>> bin(7)

'0b111'

>>> oct(73)

'0o111'

>>> hex(273)

'0x111'

Casting

>>> int("1")

>>> float(3)

>>> str(2)

>>> bool(1)

Chars

>>> chr(65)

'A'

>>> ord('A')

65

Operaciones con booleanos

>>> bool(0)

False

>>> bool(0.0)

False

>>> bool("")

False

>>> bool(None)

False

>>> bool(1)

True

>>> bool(0.1)

True

>>> bool("hello")

True

>>> bool(not None)

True

>>> "s" == True

False

>>> bool("s") == True

True

Operadores lógicos

>>> not False

True

>>> True and True

True

>>> True or False

True

Los strings son inmutables

En Python todo es un objeto.Inmutables son aquellos objetos

que una vez creadosno se pueden alterar.

>>> s = "hello"

>>> id(s)

139752245777776

>>> s += " world"

>>> id(s)

139752246378160

>>> s

'hello world'

>>> s[0] = "H"

Traceback (most recent call last):

File "<stdin>", line 1, in <module>

TypeError: 'str' object does not support item assignment

Métodos de str>>> s = "">>> s.<Tab>s.__add__( s.__le__( s.capitalize( s.islower( s.rpartition(s.__class__( s.__len__( s.casefold( s.isnumeric( s.rsplit(s.__contains__( s.__lt__( s.center( s.isprintable( s.rstrip(s.__delattr__( s.__mod__( s.count( s.isspace( s.split(s.__dir__( s.__mul__( s.encode( s.istitle( s.splitlines(s.__doc__ s.__ne__( s.endswith( s.isupper( s.startswith(s.__eq__( s.__new__( s.expandtabs( s.join( s.strip(s.__format__( s.__reduce__( s.find( s.ljust( s.swapcase(s.__ge__( s.__reduce_ex__( s.format( s.lower( s.title(s.__getattribute__( s.__repr__( s.format_map( s.lstrip( s.translate(s.__getitem__( s.__rmod__( s.index( s.maketrans( s.upper(s.__getnewargs__( s.__rmul__( s.isalnum( s.partition( s.zfill(s.__gt__( s.__setattr__( s.isalpha( s.replace( s.__hash__( s.__sizeof__( s.isdecimal( s.rfind( s.__init__( s.__str__( s.isdigit( s.rindex( s.__iter__( s.__subclasshook__( s.isidentifier( s.rjust(

>>> help(s.join) join(...) method of builtins.str instance S.join(iterable) -> str Return a string which is the concatenation of the strings in the iterable. The separator between elements is S.

print(value1, ..., sep=' ', end='\n', file=sys.stdout, flush=False)>>> a, b = 1, 2

>>> print("a =", a, ", b =", b)

a = 1 , b = 2

>>> print(192, 168, 0, 1, sep=".")

192.168.0.1

>>> f = open("data.txt", "w")

>>> print("42 is the answer", file=f)

>>> f.close()

>>> import sys

>>> print("Error 42!", file=sys.stderr)

Error 42!

>>> for i in range(4):

... print(i)

...

1

2

3

4

>>> for i in range(4):

... print(i, end=" ")

...

1 2 3 4 >>>

String format>>> "{} {} {}".format(1, "dos", True)

'1 dos True'

>>> "{2} {1} {0}".format(1, "dos", True)

'True dos 1'

>>> "{0} {1} {bool}".format(1, "dos", bool=True)

'1 dos True'

>>> "{0:4} {1:8} {pi:10}".format(5, 10, pi=3.1416)

' 5 10 3.1416'

>>> "{0:<4} {1:^8} {pi:>10}".format(5, 10, pi=3.1416)

'5 10 3.1416'

Python y Unicodeα í ∞ U დ й น ይ ☺

Desde Python 3todo string es Unicode>>> pi = "π">>> print(pi)

π

>>> type(pi)

<class 'str'>

>>> len(pi)

1

>>> pi.encode('utf8')

b'\xcf\x80'

La filosofía en Python para leer yprocesar archivos de textos es la siguiente:1. Al leer desencodear.2. Procesar como unicode.3. Encodear al escribir.

import io

# readwith io.open(filename, 'r', encoding='utf8') as f: text = f.read()

# process Unicode text

# writewith io.open(filename, 'w', encoding='utf8') as f: f.write(text)

Listas [<element>, <element>, ... <element>]>>> l = [1, 2, 3]

>>> type(l)

<class ‘list’>

>>> l.append(4)

>>> l

[1, 2, 3, 4]

>>> l[0]

1

>>> l[-1]

4

>>> l[0] = 0

>>> l

[0, 2, 3, 4]

Simple stack>>> l.pop()

4

Simple queue>>> l.pop(0)

1

Slicing

>>> l[1:3]

[2, 3]

>>> l[::2]

[2, 4]

>>> l[::-1]

[4, 3, 2, 0]

Tamaño de la lista

>>> len(l)

4

Mixed lists

mixedList = [True, 5,

'some string',

123.45]

Listas vacías

Una lista vacía evalúa como False

>>> bool([])

False

Tratar de obtener un elemento que no existe dispara una Exception de tipo IndexError

>>> [][0]

Traceback (most recent call last):

File "<stdin>", line 1, in <module>

IndexError: list index out of range

>>> try:

... print([][0])

... except IndexError:

... print("out of range")

...

out of range

Recorrer una lista con forEl siguiente es un patrón muy usado en Python, en el que se recorre una lista para generar un bucle del tipo for(i=0; i<10; i++). La lista es generada por range(), una función para generar listas numéricas:

>>> for i in range(5):

... print(i)

...

0

1

2

3

4

Si quisiéramos enumerar una lista tenemos la función enumerate()

>>> for i, e in enumerate(["aa", "bb", "cc"]):

... print(i, e)

...

0 aa

1 bb

2 cc

También podemos iterar un string

>>> for c in "hola":

... print(c)

...

h

o

l

a

Tuplas (<element1>, <element2>, ... <elementN>)

Son similares a las listas pero son inmutables

>>> t = (1, 2, 3, 4)

>>> t = 1, 2, 3, 4

>>> t

(1, 2, 3, 4)

>>> type(t)

<class 'tuple'>

>>> t[0]

1

>>> t[1:3]

(2, 3)

>>> len(t)

4

Tratar de cambiar una tupla dispara una Exception de tipo TypeError

>>> t[1] = 2Traceback (most recent call last):

File "<stdin>", line 1, in <module>

TypeError: 'tuple' object does not support item assignment

Tip acerca de las tuplas

Aunque es una buena práctica ponerlos, los paréntesis son opcionales al construir tuplas

>>> x = 1, 2, 3, 4>>> x(1, 2, 3, 4)

>>> type(x)<class 'tuple'>

La asignación de múltiples variables en una única instrucción usa esta propiedad de las tuplas, ya que en realidad es una descomposición de la misma

>>> x, y = 1, 2>>> x1>>> y2

Así que podemos aprovechar esto para intercambiar variables sin necesidad de usar un auxiliar :)

>>> x, y = y, x>>> x2>>> y1

Diccionarios {<key>:<value>, <key>:<value>, ..., <key>:<value>}

Son colecciones de key:value que no tienen un orden en particular

>>> d = {"x": 0, "y": 1}

>>> d["x"]

0

>>> type(d)

<class 'dict'>

>>> d["y"] = 1.2

>>> d.items()

dict_items([('x', 0), ('y', 1.2)])

>>> d.keys()

dict_keys(['x', 'y'])

>>> d.values()

dict_values([0, 1.2])

Sets {<value>, <value>, ..., <value>}Un set es una colección de datos sin orden pero donde no existen los elementos duplicados.

>>> s1 = {1, 2, 1}

>>> s1

{1, 2}

>>> type(s1)

<class 'set'>

>>> s1.add(3)

{1, 2, 3}

Operaciones>>> s1 = {1, 2, 3}

>>> s2 = {2, 3, 4}

En s1 pero no en s2:>>> s1 - s2

{1}

Union:>>> s1 | s2

{1, 2, 3, 4}

Sólo los que están en ambos:>>> s1 & s2

{2, 3}

Los diferentes:>>> s1 ^ s2

{1, 4}

Funciones

Built-in functionsabs() divmod() input() open() staticmethod()

all() enumerate() int() ord() str()

any() eval() isinstance() pow() sum()

basestring() execfile() issubclass() print() super()

bin() file() iter() property() tuple()

bool() filter() len() range() type()

bytearray() float() list() raw_input() unichr()

callable() format() locals() reduce() unicode()

chr() frozenset() long() reload() vars()

classmethod() getattr() map() repr() xrange()

cmp() globals() max() reversed() zip()

compile() hasattr() memoryview() round() __import__()

complex() hash() min() set()

delattr() help() next() setattr()

dict() hex() object() slice()

dir() id() oct() sorted()

El intérprete de Python tiene un conjunto de funcionesbuilt-in que están siempre disponibles:

range()En Python 3, range() produce un tipo nativo de datos llamado range, que junto con list y tuple conforman los tres tipos básicos de secuencias. Al igual que tuple, es un objeto inmutable.

>>> type(range(10))

<class 'range'>

Este objeto es iterable al igual que las listas y las tuplas, pero no devuelve inmediatamente todos sus valores, sino que los va generando a medida que se los pedimos. Podemos obtener inmediatamente sus valores de esta forma:

>>> list(range(10))

[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

>>> tuple(range(10))

(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)

Funciones definidaspor el usuario>>> def foo(a, b):

... return a + b

...

>>> foo(2, 4)

6

>>> type(foo)

<class 'function'>

>>> f = foo

>>> f(2, 4)

6

Argumentos por default

>>> def pow(x, y=2):

... return x ** y

...

>>> pow(3)

9

>>> pow(y=2, x=4)

16

>>> t = (3, 4)

>>> pow(*t)

81

>>> d = {'x':5, 'y':2}

>>> pow(**d)

25

Funciones lambdaEn Python existe un tipo de función especial que sólo permite una expresión y que se declara en una sóla línea. La forma general es

lambda argument_list: expression

No necesita la palabra return, ya que lo que se devuelve es el resultado de esa expresión

>>> pow = lambda x, y: x ** y

>>> pow(2, 3)

8

>>> type(pow)

<class 'function'>

List comprehensionLas comprensiones de listas ofrecen una manera concisa de crear listas. Supongamos que queremos crear una lista con los cuadrados de los primeros 10 números enteros, La forma clásica sería

>>> cuadrados = []

>>> for x in range(10):

... cuadrados.append(x**2)

…>>> cuadrados

[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

>>> x

9

Notar que esto crea (o sobreescribe) una variable llamada x que sigue existiendo luego de que el bucle haya terminado. Podemos calcular la lista de cuadrados sin ningún efecto secundario haciendo:

cuadrados = list(map(lambda x: x**2, range(10)))

O, el equivalente usando list comprehension

cuadrados = [x ** 2 for x in range(10)]

Ejemplo de acumulador funcional

>>> def fold_list(fn, acc, _list):

... if _list:

... return fold_list(fn, acc + fn(_list.pop()), _list)

... return acc

...

>>> fold_list(lambda x: x ** 2, 0, [1, 2, 3])

14

Más list comprehensionA partir de una lista con los diez primeros enteros obtener sólo los impares:

>>> list(filter(lambda x: x % 2, range(10)))

[1, 3, 5, 7, 9]

También podemos obtener un generador (similar a range)

>>> [x for x in range(10) if x % 2]

<generator object <genexpr> at 0x7f26b3da68e0>

Crear un query string a partir de un diccionario:

>>> query = {'ord': 'asc', 'q': 'text', 'limit': 10}

>>> "&".join(["%s=%s" % (n, v) for n, v in query.items()])

'q=text&limit=10&ord=asc'

GeneradoresLos generadores son funciones que van generando y devolviendo datos a medida que se los pedimos. Utilizan yield en lugar de return, y son útiles para ahorrar memoria

>>> def my_range(n):... i = 0... while i < n:... yield i... i += 1...

>>> my_range(10)<generator object my_range>

Podemos recorrer manualmente sus datos usando la función built-in next

>>> g = my_range(3)>>> next(g)0>>> next(g)1>>> next(g)2>>> next(g)3>>> next(g)4>>> next(g)Traceback (most recent call last): File "<stdin>", line 1, in <module>StopIteration

Decoradores

Los decoradores son funciones que reciben como parámetros otras funciones y retornan como resultado otras funciones con el objetivo de alterar el funcionamiento original de la función que se pasa como parámetro

>>> def logger(fn):... def wrapper(*args, **kwargs):... print("Llamando a", fn.__name__)... return fn(*args, **kwargs)... return wrapper...

>>> @logger... def sum(a, b):... return a + b...

>>> sum(1, 2)Llamando a sum3

>>> sum.__name__'wrapper'

@wraps

Para evitar que la función decorada pierda su nombre y su docstring se utiliza el decorador wraps

>>> from time import time>>> from functools import wraps

>>> def timethis(fn):... '''... Decorator that reports the execution time.... '''... @wraps(fn)... def wrapper(*args, **kwargs):... start = time()... result = fn(*args, **kwargs)... end = time()... print(fn.__name__, end - start)... return result... return wrapper...

>>> @timethis... def countdown(n):... '''... Counts down.... '''... while n > 0:... n -= 1...

>>> countdown(1000000)countdown 0.06418180465698242

>>> countdown.__name__'countdown'

>>> countdown.__doc__'\n Counts down.\n '

Decoradores conargumentos

>>> def tags(tag_name):... def decorator(fn):... @wraps(fn)... def wrapper(*args, **kwargs):... return "<{0}>{1}</{0}>".format(tag_name, fn(*args, **kwargs))... return wrapper... return decorator...

>>> @tags("p")... def get_greeting(name, lastname):... return "Hello {} {}".format(name, lastname)...

>>> get_greeting("John", "Anderson")'<p>Hello John Anderson</p>'

Sin azúcar sintáctico

>>> def get_title(section):... return "Welcome to {}".format(section)...

>>> get_title = tags("h1")(get_title)>>> get_title("index")'<h1>Welcome to index</h1>'

Un tercer nivel de funciones anidadas es necesario para pasarle argumentos al decorador

Tips con la consola

# export PYTHONSTARTUP=$HOME/.pythonstartup

import atexitimport osimport readlineimport rlcompleter

historyPath = os.path.expanduser("~/.pyhistory")

#readline.parse_and_bind('tab: menu-complete')readline.parse_and_bind('tab: complete')

def save_history(historyPath=historyPath): import readline readline.write_history_file(historyPath)

if os.path.exists(historyPath): readline.read_history_file(historyPath)

atexit.register(save_history)del os, atexit, readline, rlcompleter, save_history, historyPath

Activar el autocompletadoy la historiaEl intérprete lee el archivo al que apunte la variable de entorno PYTHONSTARTUP.

Podemos poner ahí funciones o cualquier cosa que necesitemos tener siempre a mano en la consola.

Para activar el autocompletado (con tab) y el historial de comandos ingresados, hay que poner el siguiente código:

# fib.py

def fib(n): l = [] a, b = 0, 1 for _ in range(n): l.append(a) a, b = b, a+b return l

Levantar tus archivos .pycon la consolaAlgo muy útil de la consola es que te permite levantar código de un archivo y jugar con él interactivamente:

$ python3 -i fib.py>>> fib(5)[0, 1, 1, 2, 3]>>> fib(10)[0, 1, 1, 2, 3, 5, 8, 13, 21, 34]

Ayuda!También para eso es útil la consola. Mediante la función help() se puede acceder a la documentación de cada método, clase o paquete.

# fib.py

def fib(n): """Gets a list for the first n numbers in the Fibonacci sequence.""" l = [] a, b = 0, 1 for _ in range(n): l.append(a) a, b = b, a+b return l

$ python3 -i fib.py>>> help(fib)fib(n) Gets a list for the first n numbers in the Fibonacci sequence.

Guardar la sesión actual en un archivo

>>> import readline

>>> readline.write_history_file('/file/to/session')

Exceptionsraise Exception("Error message")

def divide(x, y): try: result = x / y except ZeroDivisionError: print("division by zero!") except TypeError as e: print(e) else: print("result is", result) finally: print("executing finally clause")

BaseException +-- SystemExit +-- KeyboardInterrupt +-- GeneratorExit +-- Exception +-- StopIteration +-- StandardError | +-- BufferError | +-- ArithmeticError | | +-- FloatingPointError | | +-- OverflowError | | +-- ZeroDivisionError | +-- EnvironmentError | | +-- IOError | +-- EOFError | +-- ImportError | +-- LookupError | | +-- IndexError | | +-- KeyError | +-- MemoryError | +-- NameError | +-- ReferenceError | +-- RuntimeError | | +-- NotImplementedError | +-- SystemError | +-- TypeError | +-- ValueError | +-- UnicodeError | +-- UnicodeDecodeError | +-- UnicodeEncodeError | +-- UnicodeTranslateError +-- Warning +-- DeprecationWarning +-- PendingDeprecationWarning +-- RuntimeWarning +-- SyntaxWarning

Clases

La clase más sencilla que podés hacer

Las clases también son objetos

>>> class Foo:

... pass

...

>>> type(Foo)

<class 'type'>

>>> f = Foo()

>>> type(f)

<class '__main__.Foo'>

>>> f.n = 2

>>> f.pow = lambda e: f.n ** e

>>> f.pow(2)

4

>>> f.pow(3)

8

>>> f.n = 3

>>> f.pow(2)

9

Un ejemplo más realimport math

class Point:

""" Point class """

def __init__(self, x, y):

self.x = x

self.y = y

def distance(self, other):

dx = self.x - other.x

dy = self.y - other.y

return math.sqrt(dx**2 + dy**2)

def __str__(self):

return "Point(%s,%s)" % (self.x, self.y)

>>> p0 = Point(5, 10)

>>> p1 = Point(10, 15)

>>> p0.distance(p1)

7.0710678118654755

>>> p0

<__main__.Point object at

0x7f4f94d4fd68>

>>> p1

<__main__.Point object at

0x7f4f94ceb240>

>>> print(p0)

Point(5,10)

>>> print(p1)

Point(10,15)

Singleton en PythonUn singleton en Python es trivial, sólo hay que

usar la propiedad de los módulo de que sólo son

importados una única vez:

PythónicoLos usuarios de Python se refieren a menudo a laFilosofía Python que es bastante análoga a lafilosofía de Unix. El código que sigue los principios de Python de legibilidad y transparencia se dice que es "pythonico". Contrariamente, el código opaco u ofuscado es bautizado como "no pythonico".

Estos principios fueron famosamente descritos por el desarrollador de Python Tim Peters en El Zen de Python

# singleton.py

class Singleton:

pass

singleton = Singleton()

# main.py

from singleton import singleton

El ZendePython>>> import this

● Bello es mejor que feo.● Explícito es mejor que implícito.● Simple es mejor que complejo.● Complejo es mejor que complicado.● Plano es mejor que anidado.● Espaciado es mejor que denso.● La legibilidad cuenta.● Los casos especiales no son tan especiales como para quebrantar las

reglas.● Los errores nunca deberían dejarse pasar silenciosamente.● A menos que hayan sido silenciados explícitamente.● Frente a la ambigüedad, rechazar la tentación de adivinar.● Debería haber una -y preferiblemente sólo una- manera obvia de hacerlo.● Aunque esa manera puede no ser obvia al principio a menos que usted sea

Holandés (Guido es holandés).● Ahora es mejor que nunca.● Aunque nunca es a menudo mejor que ya.● Si la implementación es difícil de explicar, es una mala idea.● Si la implementación es fácil de explicar, puede que sea una buena idea.● Los espacios de nombres (namespaces) son una gran idea ¡Hagamos más

de esas cosas!.

Clases: Variables de clase y de instanciaEn general, las variables de instancia son para datos únicos de cada instancia y las variables de clase son para atributos y métodos compartidos por todas las instancias de la clase:

class Perro:

tipo = 'canino' # variable de clase compartida por

# todas las instancias

def __init__(self, nombre):

self.nombre = nombre # variable de instancia única para la instancia

Clases: HerenciaHerencia simple:

class ClaseDerivada(ClaseBase):

<declaración-1>

.

.

.

<declaración-N>

Herencia múltiple:

class ClaseDerivada(Base1, Base2, Base3):

<declaración-1>

.

.

.

<declaración-N>

Para la mayoría de los propósitos, en los casos más simples, podés pensar en la búsqueda de los atributos heredados de clases padres como primero en profundidad, de izquierda a derecha, sin repetir la misma clase cuando está dos veces en la jerarquía. Por lo tanto, si un atributo no se encuentra en ClaseDerivada, se busca en Base1, luego (recursivamente) en las clases base de Base1, y sólo si no se encuentra allí se lo busca en Base2, y así sucesivamente.

Clases: Variables privadas

>>> class Point:

... def __init__(self, x, y):

... self._x = x

... self.__y = y

...

>>> p = Point(5, 10)

>>> p._x

5

Las variables “privadas” de instancia, que no pueden accederse excepto desde dentro de un objeto, no existen en Python. Sin embargo, hay una convención que se sigue en la mayoría del código Python: un nombre prefijado con un guión bajo (por ejemplo, _x) debería tratarse como una parte no pública de la API (más allá de que sea una función, un método, o un dato). Si querés protegerla un “poco más” usá doble guión bajo:

>>> p.__y

Traceback (most recent call last):

File "<stdin>", line 1, in <module>

AttributeError: 'Point' object has no

attribute '__y'

>>> p._Point__y

10

Clases: Setters y Getters class Foo:

def __init__(self):

self.__x = None

def __get_x(self):

return self.__x

def __set_x(self, value):

self.__x = value

def __del_x(self):

del self.__x

x = property(__get_x, __set_x, __del_x,

"I'm the x property")

Python adhiere al Uniform Access Principle, que dice más o menos que desde el punto de vista de un cliente del programa que hace una llamada a una función de clase, si una consulta es un atributo (campo en cada objeto) o una función (algoritmo) no tiene que tener ninguna diferencia.

Y lo que significa es que en Python no se usan los get_x() y set_x(), al menos no externamente.

Para cualquier atributo que deba ser computado tenemos la función built-in property()

Módulos y Paquetes

Módulosfibo.py

"""Fibonacci module

"""

def fib(n):

"""Return Fibonacci series up to n"""

result = []

a, b = 0, 1

while b < n:

result.append(b)

a, b = b, a+b

return result

def fib2(n):

"""Prints Fibonacci series up to n"""

print(", ".join(map(str, fib(n))))

$ python3

>>> import fibo

>>> help(fibo)

Help on module fibo:

NAME

fibo - Fibonacci module

FUNCTIONS

fib(n)

Return Fibonacci series up to n

fib2(n)

Prints Fibonacci series up to n

FILE

/home/lvidarte/fibo.py

Módulos (import)Módulos como comandos

La ejecución de fibo.py no produce un resultado

$ python3 fibo.py

$

Para usar directamente un módulo es muy común agregar algo así al final del archivo:

if __name__ == "__main__":

import sys

fib2(int(sys.argv[1]))

Y agregar el shebang al inicio del archivo:

#!/usr/bin/python3

Luego de darle permisos de ejecución podemos hacer:

$ ./fibo.py 10

1, 1, 2, 3, 5, 8

Existen varias formas en las cuales podemos importar el contenido de un módulo:

1. import fibo

fibo.fib(10)

fibo.fib2(10)

2. import fibo as fibonacci

fibonacci.fib(10)

fibonacci.fib2(10)

3. from fibo import *

fib(10)

fib2(10)

4. from fibo import fib2 as fibonacci

fibonacci(10)

Packages

Packages es la manera en que Python permite estructurar módulos con características similares.

Un paquete es un directorio conteniendo módulos Python y un archivo __init__.py

sound/ Top-level package __init__.py Initialize the sound package formats/ Subpackage for format conversions __init__.py wavread.py wavwrite.py aiffread.py aiffwrite.py auread.py auwrite.py ... effects/ Subpackage for sound effects __init__.py echo.py surround.py reverse.py ... filters/ Subpackage for filters __init__.py equalizer.py vocoder.py karaoke.py ...

.└── sound ├── effects │ ├── echo.py │ ├── __init__.py │ └── reverse.py ├── filters │ └── __init__.py └── __init__.py

Contenido de los archivos

# sound/__init__.py__all__ = ["effects", "filters"]from . import effects, filters

# sound/effects/__init__.py__all__ = ["echo", "reverse"]from . import echo, reverse

# sound/effects/echo.pydef echofilter(): print("echofilter function")

# sound/effects/reverse.pydef f1(): print("reverse f1")

def f2(): print("reverse f2")

# sound/filters/__init__.pydef foo(): print("filters foo")

Distintos imports que puedo hacer

>>> import sound>>> sound.effects.echo.echofilter<function echofilter at 0x7f0a420b3ea0>

>>> from sound import *>>> effects.echo.echofilter<function echofilter at 0x7f795f2bcd90>>>> filters.foo<function foo at 0x7f795d61b048>

>>> from sound.effects import *>>> echo.echofilter<function echofilter at 0x7f07c0046d90>>>> reverse.f1<function f1 at 0x7f07c0046ea0>

>>> from sound.effects.echo import echofilter>>> echofilter<function echofilter at 0x7f0a420b3ea0>

Módulos de lalibrería estándarLa librería estándar es realmente grande. Para mencionar sólo algunos de los módulos más usados tenemos: sys, re, os, math, logging, datetime, time, collections, threading, multiprocessing, sqlite, json, random, urllib2

● Pequeño paseo por la Biblioteca Estándar

● Pequeño paseo por la Biblioteca Estándar - Parte II

Batteries included!

>>> import antigravity

Servidor HTTP en una línea

$ python3 -m http.server

Serving HTTP on 0.0.0.0 port 8000 …

usage: server.py [-h] [--cgi] [--bind ADDRESS] [port]

Manejo de Paquetes y Entornos Virtuales

Manejando paquetes con pip

Es posible instalar, actualizar y quitar paquetes usando pip.Por defecto pip instalará paquetes desde PyPI (Python Package Index).

$ sudo pip install requests==2.6.0

Collecting requests==2.6.0

Using cached requests-2.6.0-py2.py3-none-any.whl

Installing collected packages: requests

Successfully installed requests-2.6.0

Commandos comunes

pip search <consulta>

pip install <paquete>pip install <paquete>==<version>pip install -r requirements.txt

pip uninstall <paquete>

pip freeze > requirements.txt

pip --version

En Ubuntu conviven pip y pip3,para python2 y python3

respectivamente

Entornos virtualesPodés crear un entorno virtual que maneje una versión específica de Python y sus propios paquetes, esto es un ambiente aislado del resto del sistema, donde podés controlar las versiones específicas de cada módulo que use tu programa, independientemente de lo que tengas instalado a nivel sistema.

$ python3 -m venv mi-entorno

Luego de crearlo hay que activarlo

$ source mi-entorno/bin/activate

Vas a ver que ahora en el prompt aparece el nombre del entorno que estás usando

(mi-entorno)

$

A partir de acá podés instalar paquetes con pip en forma local.

apt-get install python3-venv

RequestsHTTP forHumans

pip install requests

>>> import requests

>>> r = requests.get('https://api.minirobots.com.ar')

>>> r.status_code

200

>>> r.headers['content-type']

'application/json; charset=utf-8'

>>> r.encoding

'utf-8'

>>> r.text

'{"message":"Welcome to minirobots api"...

>>> r.json()

{'version': '0.0.2', 'date': 'Tuesday, June 06, 2017',

'message': 'Welcome to minirobots api', 'time': '00:29:49'}

Un servicio con Flask

1 from flask import Flask

2 app = Flask(__name__)

3

4 @app.route('/')

5 def index():

6 return "Hola a todos !"

7

8 if __name__ == '__main__':

9 app.run()

app.py

pip install flask

Un servicio con Flask

1 from flask import Flask

2 app = Flask(__name__)

3

4 @app.route('/')

5 def index():

6 return "Hola a todos !"

7

8 if __name__ == '__main__':

9 app.run()

app.py

$> python3 app.py * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)

Un servicio con Flaskapp.py

$> python3 app.py * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)127.0.0.1 - - [29/Sep/2016 22:54:28] "GET / HTTP/1.1" 200 -

1 from flask import Flask

2 app = Flask(__name__)

3

4 @app.route('/')

5 def index():

6 return "Hola a todos !"

7

8 if __name__ == '__main__':

9 app.run()

Servicio Flask para interfacear con Arduinofrom flask import Flask, request, render_template

app = Flask(__name__)

commands = ['forward', 'backward', 'left', 'right']

@app.route('/')

def index():

if len(request.args.keys()) == 1 and \

request.args.keys()[0] in commands:

command = request.args.keys()[0]

value = int(request.args.get(command))

getattr(turtle, command)(value)

return render_template('index.html')

if __name__ == '__main__':

app.run(host='0.0.0.0', port=8000 )

Código completo: server.py, index.html

Paralelismo

LoggingLogging provee un set de funciones para loguear tus mensajes, ellas son debug(), info(), warning(), error() y critical().

El nivel por defecto es WARNING y la salida por defecto stdout (la consola)

import logging

# Esto saldrá en la consola

logging.warning("Ojo!")

# Esto no imprimirá nada

logging.info("No me verás")

Loguear a un archivo en modo DEBUG ycambiando el formato por default:

import logging

logging.basicConfig(

filename='example.log',

format='%(asctime)s [%(levelname)s] %(message)s',

level=logging.DEBUG

)

logging.debug('Mensaje que irá al log')

logging.info('Este también')

logging.warning('Y este')

# example.log2017-06-04 19:25:52,550 [DEBUG] Mensaje que irá al log

2017-06-04 19:25:52,550 [INFO] Este también

2017-06-04 19:25:52,550 [WARNING] Y este

QueueExisten varias implementaciones de colas en Python, usaremos la del módulo multiprocessing que es segura tanto para threads como para procesos.

>>> from multiprocessing import Queue

>>> q = Queue()

>>> q.put(1)

>>> q.put(2)

>>> q.get()

1

>>> q.empty()

False

>>> q.get()

2

>>> q.empty()

True

Threading>>> from threading import Thread

>>> from multiprocessing import Queue

>>> import time

>>> q = Queue()

>>> def worker(q):

... for i in range(5):

... q.put(i)

... time.sleep(10)

...

>>> t = Thread(target=worker, args=(q,))

>>> t.start()

>>> for i in range(5):

... print(q.get())

...

0

1

2

3

4

>>> t

<Thread(Thread-1, stopped)>

Python threads are real system threads

$ ps fax | grep threads.py

7484 pts/2 Sl+ 0:20 | \_ python3 threads.py

$ ps -T -p 7484

PID SPID TTY TIME CMD

7484 7484 pts/2 00:00:03 python3

7484 7485 pts/2 00:00:02 python3

7484 7486 pts/2 00:00:02 python3

7484 7487 pts/2 00:00:02 python3

7484 7497 pts/2 00:00:02 python3

Python utiliza un Global Interpreter Lock (GIL), el cual es un mecanismo que sincroniza la ejecución de threads haciendo que sólamente un thread se ejecute a la vez. La limitación de esto es que no aprovecha múltiples cores.

Threadingq = Queue()

# Put some data into the queue

for item in range(total_data):

q.put(item)

# Put stop signal for workers

for _ in range(total_threads):

q.put(None)

threads = []

for _ in range(total_threads):

t = Thread(target=worker, args=(q,))

t.start()

threads.append(t)

for t in threads:

t.join()

logging.debug("Exiting")

import logging

from threading import Thread

from multiprocessing import Queue

total_threads = 4

total_data = 200

logging.basicConfig(

level=logging.DEBUG,

format='%(asctime)s [%(levelname)s] %(threadName)s

%(message)s'

)

def worker(q):

logging.debug("Starting worker")

while True:

item = q.get()

if item is None:

logging.debug("Exiting worker")

Break

logging.debug("Processing item {}".format(item))

MultiprocessingMultiprocessing es un paquete que permite crear nuevos procesos utilizando un API similar a la del módulo threading. Debido a que utiliza subprocesos en lugar de hilos (threads), permite llevar a cabo varias operaciones concurrentes sin las limitaciones del Global Interpreter Lock. Corre en sistemas Unix y Windows.

$ ps fax | grep multiprocess

17227 pts/2 Sl+ 0:02 | \_ python3 multiprocess.py

17228 pts/2 S+ 0:01 | \_ python3 multiprocess.py

17229 pts/2 S+ 0:01 | \_ python3 multiprocess.py

17230 pts/2 S+ 0:01 | \_ python3 multiprocess.py

17231 pts/2 S+ 0:01 | \_ python3 multiprocess.py

DEBUG:root:Starting 0DEBUG:root:Starting 1INFO:root:Worker 0, item 0INFO:root:Worker 0, item 1INFO:root:Worker 0, item 3DEBUG:root:Starting 2INFO:root:Worker 0, item 4INFO:root:Worker 2, item 5INFO:root:Worker 0, item 6INFO:root:Worker 2, item 7DEBUG:root:Starting 3INFO:root:Worker 1, item 2INFO:root:Worker 1, item 10INFO:root:Worker 1, item 12INFO:root:Worker 3, item 11INFO:root:Worker 1, item 13INFO:root:Worker 1, item 14INFO:root:Worker 3, item 15INFO:root:Worker 1, item 16INFO:root:Worker 1, item 17INFO:root:Worker 3, item 18INFO:root:Worker 1, item 19DEBUG:root:Exiting 3DEBUG:root:Exiting 1INFO:root:Worker 2, item 8INFO:root:Worker 0, item 9DEBUG:root:Exiting 2DEBUG:root:Exiting 0DEBUG:root:Exiting Main Process

Multiprocessingq = Queue()

# Put some data into the queue

for item in range(total_data):

q.put(item)

# Put stop signal for workers

for _ in range(total_processes):

q.put(None)

processes = []

for _ in range(total_processes):

p = Process(target=worker, args=(q,))

p.start()

processes.append(p)

for p in processes:

p.join()

logging.debug("Exiting")

import logging

from multiprocessing import Process, Queue, cpu_count

total_processes = cpu_count()

total_data = 200

logging.basicConfig(

level=logging.DEBUG,

format='%(asctime)s [%(levelname)s] %(processName)s

%(message)s'

)

def worker(q):

logging.debug("Starting worker")

while True:

item = q.get()

if item is None:

logging.debug("Exiting worker")

Break

logging.debug("Processing item {}".format(item))

Testing

unittestUnit testing framework

import unittest

class TestStringMethods(unittest.TestCase):

def test_upper(self):

self.assertEqual('foo'.upper(), 'FOO')

def test_isupper(self):

self.assertTrue('FOO'.isupper())

self.assertFalse('Foo'.isupper())

def test_split(self):

s = 'hello world'

self.assertEqual(s.split(), ['hello', 'world'])

# check that s.split fails when the separator is not a string

with self.assertRaises(TypeError):

s.split(2)

if __name__ == '__main__':

unittest.main()

$ python3 strings.py ...----------------------Ran 3 tests in 0.000s

OK

Debugging

pdbThe Python Debugger

$ python3 -m pdb myscript.py

> /home/lvidarte/Test/python/pdb/myscript.py(1)<module>()

-> num_list = [1, 2, 3]

(Pdb) n

> /home/lvidarte/Test/python/pdb/myscript.py(2)<module>()

-> alpha_list = ['a', 'b', 'c']

(Pdb) j 12

> /home/lvidarte/Test/python/pdb/myscript.py(12)<module>()

-> foo()

(Pdb) s

--Call--

> /home/lvidarte/Test/python/pdb/myscript.py(4)foo()

-> def foo():

(Pdb) n

> /home/lvidarte/Test/python/pdb/myscript.py(5)foo()

-> while num_list:

(Pdb) n

> /home/lvidarte/Test/python/pdb/myscript.py(6)foo()

-> n = num_list.pop(0)

# myscript.py num_list = [1, 2, 3]alpha_list = ['a', 'b', 'c']

def foo(): while num_list: n = num_list.pop(0) print(n) for c in alpha_list: print(c)

if __name__ == '__main__': foo()

pdbThe Python Debugger (cont...)

(Pdb) n

> /home/lvidarte/Test/python/pdb/myscript.py(7)foo()

-> print(n)

(Pdb) p num_list

[2, 3]

(Pdb) p n

1

(Pdb) l

3

4 def foo():

5 while num_list:

6 n = num_list.pop(0)

7 -> print(n)

8 for c in alpha_list:

9 print(c)

10

11 if __name__ == '__main__':

12 foo()

13

(Pdb) exit

pdbThe Python Debugger (cont...)

También podés meter esta líneaen cualquier parte de tu programa:

import pdb; pdb.set_trace()

Ni bien tu programa llegue a esta línease ejecutará el debugger.

Raspberry Pi Python♥

https://www.raspberrypi.org/magpi/issues/

PiCamera

Activar la cámara durante 10 segundos

# 01_preview.py

from picamera import PiCamera

from time import sleep

camera = PiCamera()

camera.start_preview()

sleep(10)

camera.stop_preview()

sudo apt-get install python3-picamera

PiCamera - PhotosTomar 5 imágenes, una cada 5 segundos

# 02_take_photos.py

from picamera import PiCamera

from time import sleep

camera = PiCamera()

camera.start_preview()

for i in range(5):

sleep(5)

camera.capture('/home/pi/Desktop/image{}.jpg'.format(i))

camera.stop_preview()

PiCamera - GIF Tomar varias imágenes y generar un .gif

# 03_gif.py

from picamera import PiCamera

from time import sleep

from os import system

camera = PiCamera()

camera.start_preview()

for i in range(10):

camera.capture('image{0:04d}.jpg'.format(i))

camera.stop_preview()

system('convert -delay 10 -loop 0 image*.jpg

animation.gif')

PiCamera - Recording VideoGrabar 10 segundos de video

# 03_video.py

from picamera import PiCamera

from time import sleep

camera.start_preview()

camera.start_recording('/home/pi/Desktop/video.h264')

sleep(10)

camera.stop_recording()

camera.stop_preview()

omxplayer video.h264

PiCamera - Configcamera.resolution = (1280, 1024)

camera.sharpness = 0

camera.contrast = 0

camera.brightness = 50

camera.saturation = 0

camera.ISO = 0

camera.video_stabilization = False

camera.exposure_compensation = 0

camera.exposure_mode = 'auto'

camera.meter_mode = 'average'

camera.awb_mode = 'auto'

camera.image_effect = 'none'

camera.color_effects = None

camera.rotation = 0

camera.hflip = False

camera.vflip = False

camera.crop = (0.0, 0.0, 1.0, 1.0)

camera.annotate_text = None

camera_annotate_text_size = 10

camera.annotate_background = Color('blue')

camera.annotate_foreground = Color('yellow')

PiCamera - Effects Ciclar por todos los efectos disponibles

# 07_effects.py

from picamera import PiCamera, Color

from time import sleep

camera = PiCamera()

camera.annotate_background = Color('black')

camera.annotate_foreground = Color('white')

camera.start_preview()

for effect in camera.IMAGE_EFFECTS:

camera.image_effect = effect

camera.annotate_text = "Effect: %s" % effect

sleep(5)

camera.stop_preview()

negative, solarize, sketch, denoise, emboss, oilpaint, hatch, gpen, pastel, watercolor, film, blur, saturation, colorswap, washedout, posterise, colorpoint, colorbalance, cartoon, deinterlace1, and deinterlace2

GPIO ZERO

GPIO Zero - LEDs

from gpiozero import LED

from time import sleep

led = LED(17)

while True:

led.on()

sleep(1)

led.off()

sleep(1)

GPIO Zero - Buttons

from gpiozero import LED, Button

from signal import pause

led = LED(17)

button = Button(3)

button.when_pressed = led.on

button.when_released = led.off

pause()

GPIO ZeroCamera capture

from gpiozero import Button

from picamera import PiCamera

from datetime import datetime

from signal import pause

from time import sleep

button = Button(3)

camera = PiCamera()

def capture():

camera.start_preview()

sleep(2)

datetime = datetime.now().isoformat()

path = '/home/pi/Desktop/%s.jpg'

camera.capture(path % datetime)

camera.stop_preview()

sleep(5)

button.when_pressed = capture

pause()

Alarma consensor de movimiento

Alarma 1 from gpiozero import MotionSensor

2 from gpiozero import Buzzer

3 import time

4

5 pir = MotionSensor(22)

6 bz = Buzzer(17)

7

8 while True:

9 print("Ready")

10 pir.wait_for_motion()

11 print("Motion detected!")

12 bz.beep(0.5, 0.25, 8)

13 time.sleep(3) http://nerdlabs.com.ar/blog/2016/6/27/alarma-con-raspberry-pi/

http://nerdlabs.com.ar/blog/2014/12/4/arduino-snake/

Snak

eSe trata de dos proyectos,Arduino Matrix y Arduino Gamepad

En el ejemplo la matriz funciona sobre un Arduino UNO y el gamepad sobre un Nano, ambos conectados a una Raspberry Pi y controlados con Python

Video

Python en IoT

Lámpara WiFicon Python y NodeMCU

Y se hizo la luzwhile True:

conn, addr = s.accept()

method, url = get_url(conn)

path, query = parse_url(url)

if path == '/':

r = query.get('r', 0)

g = query.get('g', 0)

b = query.get('b', 0)

led.set(r, g, b)

response = template.html % (led.r, led.g, led.b)

conn_send(conn, response)

conn.close()http://nerdlabs.com.ar/blog/2016/8/26/micropython-lampara-rgb/

RobotArduino

Bluetooth

from minirobots import turtle

from random import randint

def star(self, n, side):

for _ in range(n):

turtle.forward(side)

turtle.right(360 / n)

turtle.forward(side)

turtle.left(720 / n)

for _ in range(5):

n = randint(5, 7)

side = randint(10, 25)

star(n, side)

Estrellas al azar

http://minirobots.com.ar

ESP-Car WiFi

Controlado remotamentecon Python

ESP-Car WiFidef move(direction, speed, ms=None): params = {'dir': direction, 'speed': speed} r = requests.get(ESP_CAR_URL, params=params) if ms is not None: time.sleep(ms / 1000.0)

def right(speed, ms=None): move(DIR_RIGHT, speed, ms)

def left(speed, ms=None): move(DIR_LEFT, speed, ms)

def forward(speed, ms=None): move(DIR_FORWARD, speed, ms)

def reverse(speed, ms=None): move(DIR_REVERSE, speed, ms)

def stop(): move(DIR_STOP, 0, None)

Código completo acá

Tkinter

Tk - Simple Hello world

>>> from Tkinter import *

>>> root = Tk() # main window

>>> w = Label(root, text="Hello, world!")

>>> w.pack()

>>> root.mainloop()

Tk - Ejemplo de grillaimport Tkinter as tk

from minirobots import turtle

root = tk.Tk()

root.title('Tortuga')

bt_forward = tk.Button(root, text='Adelante', command=lambda:turtle.forward(20))

bt_left = tk.Button(root, text='Izquierda', command=lambda:turtle.left(90))

bt_right = tk.Button(root, text='Derecha', command=lambda:turtle.right(90))

bt_backward = tk.Button(root, text='Atrás', command=lambda:turtle.backward(20))

options = dict(padx=10, pady=10, ipadx=20, ipady=20)

bt_forward.grid(row=0, columnspan=2, **options)

bt_left.grid(row=1, column=0, **options)

bt_right.grid(row=1, column=1, **options)

bt_backward.grid(row=2, columnspan=2, **options)

tk.mainloop()

Código completo: ui.py

TkEjemplocon clase

import tkinter as tk

class Application(tk.Frame):

def __init__(self, master):

super(Application, self).__init__(master)

self.pack()

self.create_widgets()

def create_widgets(self):

self.hello = tk.Button(self, text='Hello', command=self.say_hello)

self.hello.pack(side='top')

self.quit = tk.Button(self, text='Quit', command=self.master.destroy)

self.quit.pack(side='bottom')

def say_hello(self):

print('Hello')

if __name__ == '__main__':

master = tk.Tk()

app = Application(master)

app.mainloop()

Reglas del JuegoNace

3 vecinas

Permanece

2 ó 3 vecinas

Muereo sigue muerta2 o 3 vecinas

Las transiciones dependen del número de células vecinas vivas:

● Una célula muerta con exactamente 3 células vecinas vivas "nace" (al turno siguiente estará viva).

● Una célula viva con 2 ó 3 células vecinas vivas sigue viva.● En otro caso muere o permanece muerta

(por "soledad" o "superpoblación").

Podemos representar esto con un if/else así:

if (cell == DEAD and neigbords_alive == 3) or \ (cell == ALIVE and neigbords_alive in (2, 3)): return ALIVEelse: return DEAD

Game of Lifev0.1

Definimos el tablero y cómo imprimirlo.

En el tablero dibujamos un glider.

Finalmente imprimimos el tablero.

DEAD = 0ALIVE = 1

def print_board(board): for row in board: print(' '.join(['.' if col == DEAD else '#' for col in row]))

if __name__ == '__main__':

# Glider board = [ [0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 1, 0, 0, 0, 0], [0, 0, 0, 0, 1, 0, 0, 0], [0, 0, 1, 1, 1, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0], ]

print_board(board)

life-v0.1_board.py

Game of Lifev0.2

Debajo de la función print_board

agregamos una función para obtener

el tamaño del tablero.

Finalmente imprimimos su

resultado.

def print_board(board): for row in board: print(' '.join(['.' if col == DEAD else '#' for col in row]))

def get_board_size(board): width = len(board[0]) # explícito mejor que implícito height = len(board) return (width, height)

...

print_board(board)

width, height = get_board_size(board)

print("width {}, height {}".format(width, height))

life-v0.2_board_size.py

Game of Lifev0.3

Creamos una función para obtener todas las células vecinas.

Reemplazamos la impresión del tamaño

del tablero por las células vecinas para

cada célula del tablero.

def get_neighbors(cell_pos, board): x_center, y_center = cell_pos width, height = get_board_size(board)

x_left = x_center-1 if x_center-1 >= 0 else width-1 x_right = x_center+1 if x_center+1 < width else 0 y_up = y_center-1 if y_center-1 >= 0 else height-1 y_down = y_center+1 if y_center+1 < height else 0

return (board[y_up][x_left], board[y_up][x_center], board[y_up][x_right], board[y_center][x_left], board[y_center][x_right], board[y_down][x_left], board[y_down][x_center], board[y_down][x_right])...

for x in range(width): for y in range(height): cell_pos = (x, y) neighbors = get_neighbors(cell_pos, board) print(cell_pos, neighbors)

life-v0.3_neighbors.py

Game of Lifev0.4

Modificamos la función anterior para

que devuelva directamente el

total de células vecinas vivas.

def count_neighbors_alive(cell_pos, board): x_center, y_center = cell_pos width, height = get_board_size(board)

x_left = x_center-1 if x_center-1 >= 0 else width-1 x_right = x_center+1 if x_center+1 < width else 0 y_up = y_center-1 if y_center-1 >= 0 else height-1 y_down = y_center+1 if y_center+1 < height else 0

return (board[y_up][x_left], board[y_up][x_center], board[y_up][x_right], board[y_center][x_left], board[y_center][x_right], board[y_down][x_left], board[y_down][x_center], board[y_down][x_right]).count(ALIVE)...

for x in range(width): for y in range(height): cell_pos = (x, y) neighbors_alive = count_neighbors_alive(cell_pos, board) print(cell_pos, neighbors_alive)

life-v0.4_neighbors_alive.py

Game of Lifev0.5

Creamos una función que nos devuelva,

teniendo en cuenta el estado actual de la célula y el total de

vecinas vivas,cuál será el próximo estado de la misma:

viva o muerta.

def get_next_cell_state(cell_state, neighbors_alive): if (cell_state == DEAD and neighbors_alive == 3) or \ (cell_state == ALIVE and neighbors_alive in (2, 3)): return ALIVE else: return DEAD

...

for x in range(width): for y in range(height): cell_pos = (x, y) cell_state = board[y][x] # alive or dead neighbors_alive = count_neighbors_alive(cell_pos, board) cell_next_state = get_next_cell_state(cell_state, neighbors_alive) print(cell_pos, neighbors_alive, cell_state, cell_next_state)

life-v0.5_next_cell_state.py

Game of Lifev0.6

Juntando las dos últimas funciones

creamos otra (tick) que recorra todo el

tablero y genere otro con el nuevo estado.

Finalmente llamamos varias veces a la

función tick.

def tick(board): width, height = get_board_size(board) board_ = [[DEAD] * width for _ in range(height)] for x in range(width): for y in range(height): cell_pos = (x, y) cell_status = board[y][x] neighbords_alive = count_neighbors_alive(cell_pos, board) board_[y][x] = get_next_cell_state(cell_status, neighbords_alive) return board_

...

for i in range(33): width, _ = get_board_size(board) if i: print('-' * ((width * 2) - 1)) print_board(board) board = tick(board) sleep(.5)

life-v0.6_tick.py

Ya tenemos una versión funcional del Juego de la

Vida!

Game of Lifev0.7

Es hora de convertir a board en una clase.

Lo primero es crear el constructor y un par

de métodos para setear el tablero, que

seguirá siendo una lista anidada como el

anterior.

class Board:

DEAD = 0 ALIVE = 1

def __init__(self, width=8, height=8): self._width = width self._height = height self.set(self._get_empty_board_list())

def set(self, board_list): self._board_list = [row[:] for row in board_list] # deep copy self._width = len(board_list[0]) self._height = len(board_list)

def _get_empty_board_list(self): return [[self.DEAD] * self._width for _ in range(self._height)]

def get(self): return self._board_list

life-v0.7_class_board.py

Paso 1

Game of Lifev0.7

Movemos get_board_size a size y adaptamos un poco count_neighbors_alive para convertirlo en un

método de la clase.

def size(self): return (self._width, self._height)

def count_neighbors_alive(self, x, y): x_center, y_center = x, y

x_left = x_center-1 if x_center-1 >= 0 else self._width-1 x_right = x_center+1 if x_center+1 < self._width else 0 y_up = y_center-1 if y_center-1 >= 0 else self._height-1 y_down = y_center+1 if y_center+1 < self._height else 0

return (self._board_list[y_up][x_left], self._board_list[y_up][x_center], self._board_list[y_up][x_right], self._board_list[y_center][x_left], self._board_list[y_center][x_right], self._board_list[y_down][x_left], self._board_list[y_down][x_center], self._board_list[y_down][x_right]).count(self.ALIVE)

life-v0.7_class_board.py

Paso 2

Game of Lifev0.7

Hacemos lo mismo con el resto de las

funciones, simplemente las

adaptamos mínimamente para

convertirlas en métodos de la clase.

def get_next_cell_state(self, cell_state, neighbors_alive): if (cell_state == self.DEAD and neighbors_alive == 3) or \ (cell_state == self.ALIVE and neighbors_alive in (2, 3)): return self.ALIVE else: return self.DEAD

def tick(self): board_list = self._get_empty_board_list() for x in range(self._width): for y in range(self._height): cell_state = self._board_list[y][x] neighbors_alive = self.count_neighbors_alive(x, y) args = (cell_state, neighbors_alive) board_list[y][x] = self.get_next_cell_state(*args) self._board_list = board_list

life-v0.7_class_board.py

Paso 3

Game of Lifev0.7

La función print_board la

dejamos fuera de la clase haciéndole unas

correcciones menores.

Creamos una instancia de Board y

realizamos las últimas correcciones.

# Gliderboard_list = [ [0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 1, 0, 0, 0, 0],

...

[0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0],]

board = Board()board.set(board_list)

def print_board(board_list): for row in board_list: print(' '.join(['.' if col == Board.DEAD else '#' for col in row]))

for i in range(33): width, _ = board.size() if i: print('-' * ((width * 2) - 1)) print_board(board.get()) board.tick() sleep(.5)

life-v0.7_class_board.py

Paso 4

Game of Lifev0.8

Importamos tkinter y creamos la clase Life extendiendo la clase

Frame.

También creamos un widget de tipo Canvas

con un tamaño de cell_size por cada

célula.

import tkinter as tk

class Life(tk.Frame):

def __init__(self, board, cell_size=20): super(Life, self).__init__(tk.Tk()) self.master.title("Game of Life") self.grid() self.board = board self.cell_size = cell_size self.create_widgets() self.mainloop()

def create_widgets(self): width, height = self.board.size() kwargs = { 'width' : width * self.cell_size, 'height': height * self.cell_size, 'bg' : 'white', } self.canvas = tk.Canvas(self, **kwargs) self.canvas.grid()

life-v0.8_tkinter.py

Paso 1

Game of Lifev0.8

Finalmente reemplazamos todo lo

debajo delif __name__

por lo siguiente

if __name__ == '__main__':

board = Board(width=20, height=20) life = Life(board, cell_size=30)

life-v0.8_tkinter.py

Paso 2

Game of Lifev0.9

Creamos los métodos para generar la grilla base y agregamos la

llamada correspondiente en el

constructor.

self.cell_size = cell_size self.create_widgets() self.draw_grid() self.mainloop()

...

def draw_grid(self): self.draw_vertical_lines() self.draw_horizontal_lines()

def draw_vertical_lines(self, color='gray'): width, height = self.board.size() for i in range(width - 1): x = (self.cell_size * i) + self.cell_size y0 = 0 y1 = self.cell_size * height self.canvas.create_line(x, y0, x, y1, fill=color)

def draw_horizontal_lines(self, color='gray'): width, height = self.board.size() for i in range(height - 1): x0 = 0 x1 = self.cell_size * width y = (self.cell_size * i) + self.cell_size self.canvas.create_line(x0, y, x1, y, fill=color)

life-v0.9_grid.py

Game of Lifev0.10

Creamos en Life el método draw_cell, que nos permitirá dibujar cualquier

célula y guardar su ID en cells_alive.

También agregamos el método set_alive a

la clase Board.

self.draw_grid() self.cells_alive = {} self.mainloop()

...

def draw_cell(self, x, y, color='black'): x0 = x * self.cell_size y0 = y * self.cell_size x1 = x0 + self.cell_size y1 = y0 + self.cell_size args = (x0, y0, x1, y1) _id = self.canvas.create_rectangle(*args, width=0, fill=color) self.cells_alive[(x, y)] = _id self.board.set_alive(x, y)

...

def set_alive(self, x, y): self._board_list[y][x] = self.ALIVE

life-v0.10_draw_cell.py

Game of Lifev0.11

Creamos los métodos del_cell y toggle_cell y asociamos el evento

click a este último.

También agregamos el método set_dead en

Board.

self.cells_alive = {} self.create_events() self.mainloop()...

def del_cell(self, x, y): self.canvas.delete(self.cells_alive[(x, y)]) del self.cells_alive[(x, y)] self.board.set_dead(x, y)

def toggle_cell(self, event): x = event.x // self.cell_size y = event.y // self.cell_size if (x, y) in self.cells_alive: self.del_cell(x, y) else: self.draw_cell(x, y)

def create_events(self): self.canvas.bind_all('<Button 1>', self.toggle_cell)

...

def set_dead(self, x, y): self._board_list[y][x] = self.DEAD

life-v0.11_toggle_cell.py

Game of Lifev1.0

Creamos los métodos clear, draw y tick y

asociamos la tecla espacio a este último.

Agregamos el método get_cells_alive

en Board.

Listo, terminamos!

def create_events(self): self.canvas.bind_all('<Button 1>', self.toggle_cell) self.canvas.bind_all('<space>', self.tick)

def tick(self, event): self.board.tick() self.draw()

def draw(self): self.clear() for cell_pos in self.board.get_cells_alive(): self.draw_cell(*cell_pos)

def clear(self): for _id in self.cells_alive.values(): self.canvas.delete(_id) self.cells_alive = {}...

def get_cells_alive(self): cells = [] for x in range(self._width): for y in range(self._height): if self._board_list[y][x] == self.ALIVE: cells.append((x, y)) return cells

life-v1.0.py

Cosas que hice

Mi blog con Django

https://github.com/lvidarte/django-nerdlabs

Lai Sistema Cliente - Servidor para sincronizar anotaciones entre múltiples computadoras, lo uso principalmente para snippets o comandos complejos.

El servidor funciona con Tornado.

Usa encriptación y funciona con clave pública/privada.

$> lai sync

$> lai search "config"240: update-alternatives --config editor762: git config --global user.email "<mail>"843: dpkg-reconfigure locales

https://github.com/lvidarte/lai-clienthttps://github.com/lvidarte/lai-server

Visor de imágenes GTK Usagevimg # view all images in current dir

vimg image.png # view just image.png

vimg -r dir # view recursively all images in dir

Normal Mode

Space,j next image

Backspace,k previous image

i show/hide info

m add/remove image from memory list

o next image in memory list

p previous image in memory list

q quit

f enter/exit fullscreen mode

: enter to command mode

Command Mode

:cp <target> copy current image to directory

:mcp <target> copy all images in memory to dir

:q quit

Esc return to normal mode

https://github.com/lvidarte/vimg

Juegos con Python

Tetris - 450 líneashttps://github.com/lvidarte/tetris

Rompecabezas150 líneas

https://github.com/lvidarte/sliding-puzzle

Juegode la Vida280 líneas https://github.com/lvidarte/game-of-life

El juego de la vida es un autómata celular diseñado por el matemático británico John Horton Conway en 1970.

11, un clon del 2048 con Python y curses

Es un clon del 2048 para la consola en 150 líneas.Lo llamé 11 porque utiliza (muestra) los exponentes en lugar del resultado, por lo tanto 1 + 1 = 2,pero 2 + 2 = 3, y así. Entonces se gana llegando al 11, ya que 2 ** 11 = 2048

Se juega con las teclas de Vim (hjkl)

https://gist.github.com/lvidarte/68644f0cda09c9476682

Laberintos con Python

Es una implementación de un sencillo algoritmo de generación de laberintos llamado Depth-first search.

El programa permite generar laberintos de cualquier tamaño. El juego consiste en mover el cuadrado rojo hasta el verde en la menor cantidad de movidas posibles.

https://github.com/lvidarte/maze

Gracias !

top related