~~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]]