Upload
others
View
1
Download
0
Embed Size (px)
Citation preview
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
Python en la Web
Tornado
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()
Game of Lifehttps://es.wikipedia.org/wiki/Juego_de_la_vida
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 !