~~stoggle_buttons~~ ====== pip freeze ====== Según este [[https://github.com/pypa/pip/issues/8174|issue]], ''pip freeze'' ya no vale como método para volcar dependencias, hay que usar: * ''%%pip list --format=freeze%%'' ====== links ====== ===== Libros ===== * [[https://jakevdp.github.io/WhirlwindTourOfPython/]] * [[https://jakevdp.github.io/PythonDataScienceHandbook/index.html]] * [[https://www.slitherintopython.com/|Slither into Python]] Aprender Python a la vez que te enseñan los fundamentos de más bajo nivel * [[https://docs.python-guide.org/]] ===== Patrones de diseño ===== * [[https://python-patterns.guide/]] * [[https://docs.quantifiedcode.com/python-anti-patterns/]] * [[https://github.com/RafeKettler/magicmethods/raw/master/magicmethods.pdf|Magic methods]] ''%%__XXXX__%%'' * [[https://towardsdatascience.com/6-new-features-in-python-3-8-for-python-newbies-dc2e7b804acc|Python 3.8]] ===== Docs generales ===== * [[https://treyhunner.com/2019/05/python-builtins-worth-learning/]] Ordenados de más conocidos/usados a menos * [[https://docs.python.org/3/library/pdb.html]] * [[https://pythonspeed.com/]] ===== Cursos ===== * [[https://www.kaggle.com/learn/overview|kaggle]] Cursos gratuitos como introducción para Data Science con Python * [[https://github.com/huangsam/ultimate-python|ultimate-python]] Curso completo para aprender Python que también vale para gente que ya sabe. Recopilación de un montón de recursos para aprender interactivamente, ideas para proyectos y code katas ==== Benchmarking ==== * [[https://levelup.gitconnected.com/faster-lists-in-python-4c4287502f0a|Faster lists in python]] Scripts para ver cómo escalan las distintas estructuras de datos de python * [[https://switowski.com/tag/writing-faster-python/|Optimización en python]] del estilo: "es más rápido hacer X o Y?" ====== trucos ====== ===== Python.h: No such file or directory ===== hdbscan/_hdbscan_tree.c:4:10: fatal error: Python.h: No such file or directory 4 | #include "Python.h" * ''sudo apt install python3-dev # o la versión necesaria'' ===== Conversión rápida de casi-json espaciado con pprint a json ===== import json from pprint import pprint # j = json.loads(...) with open('Almost.json', 'w') as f: pprint(j, f) cat Almost.json | sed -e "s/'/\"/g" | sed -Ez 's/"\n[ ]*\"//g' | sed "s/None/null/g" | sed "s/False/false/g" | sed "s/True/true/g" > Full.json ===== Recorrer un directorio recursivamente ===== * [[https://docs.python.org/3/library/glob.html|glob]] is your friend from glob import glob files = glob("/path/to/files/**/*.ext", recursive=True) # Con recursive=True matchea también /path/to/files/*.ext ===== Recargas dinámicas con pdb ===== Cuando pones un pdb a una web o lo que sea, hay que ejecutar `stty sane` después, porque si guardas y se recarga flask/django (ambas tienen un fswatcher que notifica de cambios en los archivos) se queda pillada la terminal y no coje los RET bien ===== Conversiones de fecha con pytz ===== local_tz = pytz.timezone("Europe/Madrid") # WHYYYYYY???? date_utc = local_tz.localize(date_naive_in_local_tz).astimezone(pytz.utc).replace(tzinfo=None) date_local = date_naive_in_utc.replace(tzinfo=pytz.utc).astimezone(local_tz).replace(tzinfo=None) ===== async debugging ===== # Cuando esto da el siguiente error: (Pdb) node.read_value() RuntimeWarning: coroutine 'Node.read_value' was never awaited # Hay que ejecutarlo así: (Pdb) async def a(node): return await node.read_value() asyncio.run(a(node)) (Pdb) asyncio.run(a(node)) ===== python debugger (pdb) con múltiples líneas ===== * ''interact'' en una consola de Pdb * https://docs.python.org/3/library/pdb.html#pdbcommand-interact ===== Trabajando con warnings ===== ==== Ignorar warnings ==== ''python -W ignore::DeprecationWarning -m module'' [[https://docs.python.org/3/library/warnings.html#the-warnings-filter]] para control más fino de los warnings ==== Subir una warning a error ==== Si hay que importar el error de otra librería, hay que poner en el código: import warnings warnings.filterwarnings(action="error", category=np.ComplexWarning) Así, cuando se lance un warning, podemos ver el Traceback y ver la línea de nuestro código que causó el warning ===== debugging ===== from pdb import set_trace from IPython import embed # Sin colores, deja la terminal bien from IPython import start_ipython # Con colores, deja la terminal mal al salir . . . breakpoint(header=embed()) # Python 3.7+ en vez de set_trace # https://docs.python.org/3/library/functions.html#breakpoint ===== Sacar la traza cuando no hay excepción ===== import traceback print("".join(traceback.format_stack())) ====== memoryview ====== * [[https://docs.python.org/3/library/stdtypes.html#memoryview|memoryview]] copias más rápidas de memoria a bajo nivel ===== listas ====== ==== operaciones matriciales sin numpy ==== def transpose(M): return list(map(list, zip(*M))) def dot(M, v): return [sum([m*x for m, x in zip(row, v)]) for row in M] ==== list comprehension ==== In : [i for i in range(20) if i % 3 > 0] # Condicionales Out: [1, 2, 4, 5, 7, 8, 10, 11, 13, 14, 16, 17, 19] In : [(i, j) for i in range(2) for j in range(3)] # Dobles índices Out: [1, 2, 4, 5, 7, 8, 10, 11, 13, 14, 16, 17, 19] In : [val if val % 2 else -val for val in range(20) if val % 3] Out: [1, -2, -4, 5, 7, -8, -10, 11, 13, -14, -16, 17, 19] In : {a % 3 for a in range(1000)} # set filtra valores únicos Out: {0, 1, 2} In : {n:n**2 for n in range(6)} # diccionario Out: {0: 0, 1: 1, 2: 4, 3: 9, 4: 16, 5: 25} In : (n**2 for n in range(12)) # crea un generador (iterador) Out: at 0x7f92cc317138> === where sin un where === list(filter(None, (i if ((dt.date(2000, 4, 13)+i*dt.timedelta(days=1)) < dt.date(2020, 4, 13)) and (dt.date(2000, 4, 13)+(i+1)*dt.timedelta(days=1)) >= dt.date(2020, 4, 13) else None for i in range(10000)))) # Sólo devuelve un índice ==== for con índice o varios objetos ==== # Iterar sobre lista y con índice al mismo tiempo: for index, element in enumerate(object[, start=0]): # Iterar sobre varias listas a la vez: for x1, x2, ..., xN in zip(list1, list2, ..., listN): # Combinando las dos: for index, elementlist in enumerate(zip(list1, list2, ... listN)): ===== collections ===== ==== namedtuple ==== from collections import namedtuple Features = namedtuple('Features', ['age', 'gender', 'name']) row = Features(age=22, gender='male', name='Alex') # row.age, row.gender, row.name ==== Counter ==== from collections import Counter ages = [22, 22, 25, 25, 30, 24, 26, 24, 35, 45, 52, 22, 22, 22, 25, 16, 11, 15, 40, 30] value_counts = Counter(ages) import numpy as np np.histogram(ages, bins=np.max(ages)-np.min(ages)) # Es más eficiente para len(ages) >~ 1000 ===== slices ===== Para dar un nombre más user-friendly cuando estamos haciendo un slice, podemos hacer: TIME = slice(0, 10) RAIN = slice(20, 40) df[TIME] df[RAIN] así evitamos harcodear slices con números mágicos: df[0:10], df[20:40] ===== print, f-strings, formato ===== [[https://pyformat.info/]] "Hello, {}. You are {}.".format(name, age), "Hello, {1}. You are {0}.".format(age, name) "Hello, {name}. You are {age}.".format(name="Julian", age=22) # También funciona con diccionarios: person = {'name': 'Eric', 'age': 74} "Hello, {name}. You are {age}.".format(**person) "{}{}{}{}".format(numbers=*nlist) # También se pueden desempacar listas # Desde python 3.6, f-strings. Se interpreta como código lo de dentro de {} f"Hello, {name}. You are {age}." f"{2 * 37}" ==== Formato printf dentro de f-strings ==== f'{a:1.2f}+{b:1.2f}j' f'{a=}' # En python 3.8, es equivalente a poner f'a={a}' ''print (blablabla, end="\r")'' vuelve al principio de la línea y sobrescribe lo anterior ===== map, filter, reduce ===== * Antes de usar [[python:python#list_comprehension|list comprehension]], preguntarse si se puede hacer lo mismo con ''map'' ==== map ==== Usar map con métodos de una clase from operator import methodcaller map(methodcaller('some_method'), some_object)) ===== lambda ===== ==== lambdas multilínea ==== **PELIGRO USAR CON MODERACIÓN** hello_world = lambda x: ( [print("Hello world!"), globals().update({'var':3+2}), # Para crear variables print("Your name is "+x)] )[-1] hello_world("Julian") # Nuevo en python 3.8 func = lambda x: ( # Se puede asignar con := # Las variables creadas tienen ámbito local y := 0, ( y := y + i for i in range(0, x)), y)[-1] y = func(101) print(y) # 5050 ===== Diccionarios ===== * Los diccionarios permiten //cualquier objeto// como clave. Internamente se generan las claves con ''hash(key)'', con lo cual cualquier cosa que acepte ''hash'' es una clave válida. ==== Diccionarios con valor por defecto ==== mydict.get('key', default_value) # Devuelve mydict['key'] si existe, y si no, default_value mydict.setdefault('key', default_value) # Si 'key' está vacía, crea una nueva entrada con default_value ==== Darle la vuelta (reverse) un dict ==== mydict_inv = dict(map(reversed, mydict.items())) ==== Diccionarios como switch ==== value = { 'a': 1, 'b': 2, 'c': 3, . . . }.get(x, default_value) Como los diccionarios se puede modificar, podemos modificar el "switch" en tiempo de ejecución, a diferencia de un switch sintáctico. # De nuevo en python 3.8 podemos hacer asignaciones value = {'a': 7, 'b': 6 if True else 0, 'c': [y :=0, [y := y+x for x in range(0, 101)]][-1][-1] }.get('c', 0) print(value) # 5050 ==== unir diccionarios ==== ab = {**a, **b} # Si tienen una clave común, prevalece el valor de b ==== crear un dict a partir de listas ==== Si tenemos dos listas, ''a'' y ''b'' de la misma longitud y queremos hacer diccionario con el siguiente formato: # {a[0]:b[0], a[1]:b[1], a[2]:b[2], ..., a[n]:b[n]} dict(zip(a, b)) ===== funciones ===== ==== *args, **kwargs y unpacking ==== * Antes de usar [[python:python#list_comprehension|list comprehension]], preguntarse si se puede hacer lo mismo desempacando def myfunc(x, y, z): print(x, y, z) tuple_vec = (1, 0, 1) dict_vec = {'x': 1, 'y': 0, 'z': 1} >>> myfunc(*tuple_vec) 1, 0, 1 >>> myfunc(**dict_vec) 1, 0, 1 [[https://www.agiliq.com/blog/2012/06/understanding-args-and-kwargs/]] ==== funciones como argumentos ==== # Functions are first-class citizens in Python: # Functions can be passed as arguments to other functions, # returned as values from other functions, and # assigned to variables and stored in data structures. ==== Recargar módulos y funciones ==== import sys from importlib import reload if sys.modules.get('module') is not None: reload(sys.modules['module']) from module import function ===== Clases ===== [[https://www.programiz.com/python-programming/property|Setters y getters en Python]] ==== magic methods ==== === excluding operators === ^Category^ Method names^ | String/bytes representation| ''%%__repr__, __str__, __format__, __bytes__%%''| | Conversion to number| ''%%__abs__, __bool__, __complex__, __int__, __float__, __hash__, __index__%%''| | Emulating collections| ''%%__len__, __getitem__, __setitem__, __delitem__, __contains__%%''| | Iteration| ''%%__iter__, __reversed__, __next__%%''| | Emulating callables| ''%%__call__%%''| | Context management| ''%%__enter__, __exit__%%''| | Instance creation and destruction | ''%%__new__, __init__, __del__%%''| | Attribute management| ''%%__getattr__, __getattribute__, __setattr__, __delattr__, __dir__%%''| | Attribute descriptors| ''%%__get__, __set__, __delete__%%''| | Class services| ''%%__prepare__, __instancecheck__, __subclasscheck__%%''| === operators === ^Category^ Method names^ |Unary numeric operators| ''%%__neg__ -, __pos__ +, __abs__ abs()%%''| |Rich comparison operators| ''%%__lt__ >, __le__ <=, __eq__ ==, __ne__ !=, __gt__ >, __ge__ >=%%''| |Arithmetic operators| ''%%__add__ +, __sub__ -, __mul__ *, __truediv__ /, __floordiv__ //, __mod__ %%%,__divmod__%%'' divmod() , __pow__ ** or pow(), __round__ round()%%'' | |Reversed arithmetic operators| ''%%__radd__, __rsub__, __rmul__, __rtruediv__, __rfloordiv__, __rmod__,__rdivmod__, __rpow__%%'' | |Augmented assignment arithmetic operators|''%%__iadd__, __isub__, __imul__, __itruediv__, __ifloordiv__, __imod__, __ipow__%%''| |Bitwise operators | ''%%__invert__ ~, __lshift__ <<, __rshift__ >>, __and__ &, __or__ |, __xor__ ^%%''| |Reversed bitwise operators| ''%%__rlshift__, __rrshift__, __rand__, __rxor__, __ror__%%'' | |Augmented assignment bitwise operators| ''%%__ilshift__, __irshift__, __iand__, __ixor__, __ior__%%''| === __getattr__ vs __getattribute__ === * [[https://docs.python.org/3/reference/datamodel.html#object.__getattr__|__getattr__]] sólo se llama cuando no se encuentra el atributo. El método que se llama siempre si se encuentra el atributo o no es [[https://docs.python.org/3/reference/datamodel.html#object.__getattribute__|__getattribute__]] ===== sequences ===== left to right direction class Container { {method} //__contains__// } class Iterable { {method} //__iter__// } class Sized { {method} //__len__// } class Sequence { {method} //__getitem__// {method} __contains__ {method} __iter__ {method} __reversed__ {method} index {method} count } class MutableSequence { {method} //__setitem__// {method} //__delitem__// {method} _//insert// {method} append {method} reverse {method} extend {method} pop {method} remove {method} __iadd__ } Container <|-- Sequence Iterable <|-- Sequence Sized <|-- Sequence Sequence <|-- MutableSequence ==== sort, sorted ==== mylist.sort() # Modifica inplace mysortedlist = sorted(mylist) # Devuelve una copia ordenada El algoritmo de ordenación de Python es [[https://en.wikipedia.org/wiki/Timsort|Timsort]], un algoritmo que es estable (los elementos que ya estaban ordenados mantienen su orden relativo) ==== bisect ==== Búsqueda binaria en una lista ordenada para buscar o insertar en la posición correcta. ===== resumen ===== [[python:resumen]] ====== IDE ====== * VSCodium [[https://gitlab.com/paulcarroty/vscodium-deb-rpm-repo]] [[https://github.com/lytex/vscode-setup]]