BASIC PYTHON

General resources

BASIC PYTHON

OPERATIONS

Basic operations: + - * /

20 // 6 # floor division
1.25 % 0.5 # modulus
20 ** 3 # exponentiation
x += 1 # same as x=x+1
print(2+"2") # error
print(2+int("2")) # 4

COMPLEX NUMBERS

complex_var = 2+3j

# imaginary or real components
print("Complex: ", complex_var, "Real: ", complex_var.real, "Imag: ", complex_var.imag)

STRINGS

print("Brian\'s mother")
# Print special characters: use \ before
print("Brian\'s\n house")
Brian's mother
Brian's
 house

Strings are really char lists:

s = 'Hello world'
print(len(s))
print(s.replace('world', 'test'))
print(s[1:7:1])
11
Hello test
ello w

STRING METHODS

Search on strings:

USER INPUT

input("Write something: ")

STRING FORMATTING

Print style:

print('str1','str2') #  with space
print('str1'+'str2') # without space
print('price = %.2f' % 3.14) # string format
print('price = {0}'.format(3.14)) # string format2
str1 str2
str1str2
price = 3.14
price = 3.14

Old way for string formatting: {}.format().

nums = [4,5,6]
print("Numbers: %i, %i, %i" % (nums[0], nums[1], nums[2])) # old, don't use
print("Numbers: {0}, {1}, {2}".format(nums[0], nums[1], nums[2]))
	# acceptable, numbers needed only if repeated; otherwise in order

New way f-strings using f before string (after python 3.6):

print(f"Numbers: {nums[0]}, {nums[1]}, {nums[2]}")

Es preferible usar double_quotes para que no afecte a las variables que haya dentro:

f"My name is {person['name']}"

VARIABLES

name = "santi"
money = 13.753453
print("Hello, %s! You've got %3.1f new dollars" % (name, money)) # old C-type formatting
Hello, santi! You've got 13.8 new dollars

More advanced topics: see variable scope

CONDITIONALS

BOOLEAN OPERATORS

In python, better use words rather than symbols. Parenthesis use as in math.

ITERATION

IF-ELSE-ELIF

Requires indentation (!= C++)

n = 3
if n <2:
  print("small") # indentation
elif n < 4:
  print("medium")
else:
  print("big")
medium
# one-line short-if
print("small") if n<4 else print("big")
small

WHILE

i = 0
while True:
   i = i +1
   if i == 2:
      print("Skipping 2")
      continue
   if i == 5:
      print("Breaking")
      break
   print(i)
1
Skipping 2
3
4
Breaking

FOR

Iterates in a range(from, to, step). From = 0 to = n-1

# long
for x in range(0,5):
    x**2
# compact 
l1 = [x**2 for x in range(0,5)]

With enumerate we can get the index value of the list iteration (use start to set the start id).

for index, value in enumerate(list, start=1):
  print(index, value) # prints id from 1

DATA STRUCTURES

Types: ‘List []’, ‘Tuples ()’ (and namedTuples), ‘Sets {}’ and ‘Dictionary {}’

Usage:

LISTS

New list: l = [].

List Indexing:

# Behaviour of copying or not copying
# Copying a list
l1 = ["We", "should", "use", "[:]", "to", "copy", "the", "whole", "list"]
l2 = l1[:]
l2.append(". Using [:] ensures the two lists are different")
print(l2)
print(l1)
# RESULT = both are diferent
l2 = l1
l2.append(". Using [:] ensures the two lists are different")
print(l2)
print(l1)
# RESULT = both point to the same list

Useful functions:

Exception: list.insert(-1, item) inserts in the PENULTIMATE position, not the last. Use append instead.

# INSERT
my_list = ['A', 'B', 'C', 'D']
my_list.insert (-1, 'E')
print (my_list)
# result = ['A', 'B', 'C', 'E', 'D']

# POP: pop(n) -> Removes the element at index 'n' and returns it
l1 = ['A', 'B', 'C', 'D', 'E']

# Removes the element at 0 position and returns it
c = l1.pop(0)
print (l1)
print (c)
# Works as expected with negetive indexes
c = l1.pop(-1)
print (l1)
print (c)

Sort objects:

Podemos usar sorted() que crea nueva variable o el método list.sort() para una ya creada.

    l1 = ['E', 'D', 'C', 'B', 'A']
    print(sorted(l1))
    print(l1)
    l1.sort()
    print (l1)

Check pertenence:

words = ["spam", "eggs"]
print(not "spam" in words)

For range creation, need to convert to list. Otherwise returns range object. range(first, last, interval). Remember: starts from 0 to n-1

# range(first, last, interval), REMEMBER STARTS ON 0
print(list(range(10)))
print(range(10))
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
range(0, 10)

TUPLES

point = (10, 20) # same as 10,20 without parenthesis
point[0] = 40 # give error, ca't change
---------------------------------------------------------------------------

TypeError                                 Traceback (most recent call last)

<ipython-input-2-83e607a374fc> in <module>
      1 point = (10, 20) # con parentesis
----> 2 point[0] = 40 # dará error


TypeError: 'tuple' object does not support item assignment

namedTuples

Enhanced and more comprehensive tuples. Same advantages (non-mutable and faster than dict).

Eg. RGB-colored tuples (to identify each color)

from collections import namedtuple
# namedtuple('name', ['args'])

Color = namedtuple('Color', ['red','green', 'blue'])
new_color = Color(55,155,255)
new_color.blue # we can access individually

SETS

VERY USEFUL: we can use several sets and play with IoU:

DICTIONARY

Create with dict = {}. Access one key with dict["keyname"].

params = {'name': 'Will', 'surname':'Smith'} # creation
params['address'] = 'Major st.' # adds one element

Iteration over dictionaries:

1) Iterating over key-value pair:

for key, value in dict.items():
    print(key + " = " + str(value))

# same as
for key in dict.keys():
    print(key = dict[key])

2) Iterating with temporary ID with enumerate (uses iteration index and value):

Usage:

for idx, x in enumerate(dict):
    print(idx, x) 
    # we get iteration index (0,1,2,3,...) and the value of iteration.

Eg:

monsters = ['Kraken', 'Leviathan', 'Uroborus', 'Hydra']

for i, monster in enumerate(monsters):
  print(i, monster)
0 Kraken
1 Leviathan
2 Uroborus
3 Hydra

defaultDict

crea un valor por defecto para cuando inicializas un diccionario sin key por primera vez.

COMPREHENSIONS

Generalmente se usan listas: list comprehensions

[n^2 for n in nums if n%2 == 0]
[(letter,num) for letter in 'abcd' for num in range(4)]

Tambien se pueden hacer con diccionarios y sets:

{name:hero for name, hero in zip(names, heros)if name != 'Peter}

Tambien con sets que tienen valores únicos:

{n for n in nums}

Los generadores son muy similares a las comprehensions.

Recuerda que zip crea tuplas con el primer indice de cada lista, luego el segundo, y así hasta el final.

FUNCTIONS

Recommended to write ‘docstring’ with """ at the beginning. To call functions you need () at the end function().

def square(x, debug=False):
    """
    Returns de square of a number
    """
    # debug has a default value
    return x**2
    pass # don't do anything

Blank function = use pass.

* in args means to make a tuple with arguments:

BE CAREFUL: you can’t pack args/kwargs because will handle them as args. You need to SPECIFY

courses = ['math', 'science']
info = {'name': 'John'; 'age': 28}
student_info(courses, info) # INCORRENT, will both as ARGS
student_info(*courses,**info) # THIS IS CORRECT

Recursion means calling a function inside itself. Eg: factorial

Lambda functions:

Short functions

f1 = lambda x: x**2

# same as

def f2(x):
    return x**2

Map/Filter/Zip

We can use map to apply a function to many iterables (apply the same function to each object, eg: list):

map(function, object)

map(lambda x: x**2, range(-3,4))
# Devuelve [9, 4, 1, 0, 1, 4, 9]

Filter removes items that don’t match a boolean predicate:

filter(predicate, object)

filter(lambda x: x%2==0, nums)

To print result convert to list() first.

zip packs two lists to iterate simultaneously:

for x, y in zip(first, second):
  print(x+y)

Counter

Sirve para contar cosas (por ejemplo listas, y te da una tupla con cada valor y su conteo.

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)
print(value_counts.most_common())

ERRORS AND EXCEPTIONS

try:
  print("Hello")
  print(1/0)
except ZeroDivisionError:
  print("Divided by zero")
finally:
  print("Final code")
  assert 2+2==4
  raise ValueError

Raise exception:

raise Exception("Error Description")

Create custom ErrorType:

class myError(Exception):
	def __init__(self, *args, **kwargs):
		Exception.__init__(self, *args, **kwargs)

FILE PROCESSING

First open(file) and then file.read() / file.write().

file = open("filename.txt", "w") #w = write mode, r = read , "wb" = binary mode
file.read(20) # reads 20 lines
file.close() # always need to close the file

Good practise when working with files, to close always at the end to free mem. Even better, read in the else block.

try:
  f = open("filename.txt")
except:
    pass
else:
  print(f.read())
finally:
  f.close()

Better way: with keeps file open as long as needed (closes automatically):

with open("filename.txt") as f:
  print(f.read())

CLASSES - OOP

Utilidad: nos permite reutilizar más eficientemente el código.

class Employee:
	pass

RESUMEN

A la hora de acceder:

VARIABLES (ATRIBUTES)

Tipos de variables:

object.__dict__ = te da un diccionario con todos los atributos de un objeto.

Instance variables

Contienen datos únicos de cada instancia.
Ej: employee1.name, employee1.email

Se pueden modificar.

Class variables (variables de clase)

Son variables que pertenecen a la clase y no a los objetos (son conjuntas y tienen el mismo valor para todos los objetos).
Se ponen antes del init de la clase (al principio). Ej: pi en la clase circulo. Se podrá acceder con Circulo.pi y con circulo_uno.pi.

No necesitan instanciar el objeto antes.

En python las variables de clase NO SON INMUTABLES; por ello se le suele poner un guion bajo delante. _pi (por convencion).

#### (Avanzado) Acceso a variables de clase No se pueden acceder directamente desde un método, sino que habría que acceder a través de la clase o a través del objeto (Employee.company o self.company, nunca solo con company).

La forma de acceder es importante porque:

Esto es importante al programar un método o una subclase, ya que si accedemos con self.raise_amount nos referiremos a un valor que se asigna automáticamente sólo al objeto (sobreescribe al de la clase para ese objeto) y si accedemos con Employee.raise_amountnos referimos a la clase.
Es interesante usar una variable de clase para valores inmutables por los objetos como Employee.num_of_employes).

Private atributes (Atributos PRIVADOS)

Weakly-private atributes:

Strongly-private atributes:

Los atributos privados solo pueden ser modificados por la clase y no por fuera (ni siquiera por la instancia de clase). Esto se hace haciendo __ delante del atributo al crearlo. Ej self.__password = password

Para poder acceder desce fuera de la clase debemos usar object.Class.__privatemethod.

Eg: audrey.Dog.__isanimal

 Limitar atributos de una clase

object.__dict__ = te da un diccionario con todos los atributos de un objeto.

En una clase o en una instancia de clase podemos añadir los atributos que queramos (aunque no estén definidos). Pero a veces estos atributos provocan errores; para ello podemos definir que una clase tenga permitido usar unos atributos definidos.

Para ello pondremos esto al principio de la clase: __slots__ = ['atributo1', 'atributo2']

Esto además provoca que no podamos usar __dict__. El atributo __slots__ no se hereda (las subclases podrán establecer atributos que quieran).

Property decorators (decoradores)

Los property decorators permiten definir metodos y acceder como si fueran atributos. @property.

Al definir como property ya no podemos cambiar el valor de los atributos desde fuera como si fueran normales, es necesario modificarlos con otros decoradores (setters y getters).

Por ello principalmente sirven para trabajar con los atributos privados de una clase (ej self.__password) para no manipularla directamente:

@property
def password(self):
	return self.__password
	
@password.setter
def password(self, valor):
	self.__password = self.__generar_password(valor)

@password.getter
def get_password(self, valor):
	print(self.__password)
	
@password.deleter
def delete_password(self):
	self.__password = None
	print("Password deleted")

VARIABLE SCOPE

Es el alcance de cada variable en python

LEGB es en orden en que python busca:

Enclosing-scope se refiere a que todo lo interior incluye a lo exterior, pero no al revés.

Podemos especificar que dentro de una funcion se use un valor global y no un valor local de dentro de la funcion:

x = 'outside x'

def test():
	global x
	x = 'inside x'

print(x) # = 'inside x'

Lo mismo podemos hacer en funciones anidadas, cambiar un valor más externo pero sin afectar al valor local

def outer():
	x = 'outer x'
	def inner():
		nonlocal x
		x = 'inner x'
	inner()
outer() # dará 'inner x'

Built-in variables

Built-in se refiere a nombres globales de python. Tenemos que tener cuidado porque podemos sobreescribirlas con nuestras propias funciones y no da error.

import builtins
print(dir(builtins))

METHODS

__ init __ method (constructor)

Se ejecuta automáticamente al instanciar una clase. Args: self (por convención) + demás + *args + **kwargs.

def __init__(self, first, last):
	self.first = first
	self.last = last

Regular methods (métodos de instancia)

Cada metodo dentro de una clase tiene que tomar la instancia como primer argumento (self).

def fullname(self):
	return f'The name is {self.name} {self.surname}'

Explicación: es como si llamaramos a la clase con el argumento como objeto Employee.fullname(employee1) == employee1.fullname()

Class methods (métodos de clase)

@classmethod

Utilizan la clase como argumento, no el objeto; por ello hay que usar cls (no self).

Pertenecen a la clase, y pueden usar los atributos y métodos públicos de las clases padre (a diferencia de los métodos de clase, que solo pertenecen a la clase y nada más).

No se suelen llamar metodos de clase desde un objeto, pero hace lo mismo que llamarlo desde una clase. Employee.set_raise_amount(1.05) == employee1.set_raise_amount(1.05) Ambas nos lo cambian para todos (ya que cambian la variable de clase).

Constructores con classmethods

Mucha gente usa métodos de clase para crear constructores con funciones. Ej.

@classmethod
def from_string(cls, string):
	name, surname = string.split('-')
	return cls(name, surname)

La cls del final se refiere a la clase, y es lo mismo que si pusieramos Employee. De esta forma estamos creando la clase con un método:
employee1 = Employee.from_string('Santi-Bacat')

Static methods (métodos estáticos)

@staticmethod

No pasan ningun argumento al método (ni self ni cls) a diferencia de los anteriores. Son como funciones normales pero con alguna conexión con la clase.

No se pueden atribuir a un objeto real (ni a la clase), pero son útiles para la clase. Le pertenecen a la clase, no a la instancia.

Ej: un método que calcula si es festivo o no para los empleados (no usa nada de la clase).


MAGIC METHODS

Son __methods__ y sirven para establecer acciones por defecto.

Representación del objeto:

__repr__: unambiguous representation (solo para que la vean otros desarrollaodres). __str__: readable representation (para el usuario final).

Por defecto se usa str>repr cuando hacemos un print

Operaciones: __add +, sub -, mul *, matmul, truediv /, floordiv //, mod %, pow **__ = para hacer cálculos con clases Para el cálculo se hace usando other para el otro argumento:

def __add__(self, other):
	return (self.pay + other.pay)

# same as x.__add__(y)

Tambien se pueden hacer con booleanos: __and &, xor ^, or |__.

y comparaciones: __lt <, le <=, eq ==, ne !=, gt >, ge >=__

y conversión de objetos: __int__= para convertir a entero

NOTAS:

Otros sirven para hacer que las clases sean como contenedores:


INHERITANCE (HERENCIA)

Nos permite obtener las características de la clase padre, superponerlas o añadir nuevas.

Primero busca el init en la subclase y luego va a la clase padre.

Podemos usar la funcion help para ver todo sobre la clase (la subherencia, atributos heredados…). Podemos usar isinstance() para ver un objeto es instancia de una clase: isinstance(manager1, Developer) Podemos usar issubclass() para ver si una clase es subclase de otra (<–): issubclass(Manager, Employee) es True.

NO se puede usar herencia circular en python

Herencia multiple:

class Subclase(Clase1, Clase 2):
	return

Si un método es privado (__cazar) no puede ser accedido por las claes hijas El orden de la herencia múltiple es de izquierda a dercha (para override de métodos)


iPYTHON SHELL

Basic Commands

help(len)
len?
len??
len.TAB
*Warning?

Magic Functions

To get all magic functions>

Available magic functions:

Others:

%cpaste
def donothing(x):
... return x
%run myscript.py
%timeit L = [n ** 2 for n in range(1000)]
%time L = [n ** 2 for n in range(1000)]
%%timeit
L = []
for n in range(100):
  L.append(n**2)

To use history

%history
print(_)
%prun print(range(10))

pip install line_profiler
%lprun -f print(range(10))

pip install memory_profiler

Terminal

If you save terminal output > python special list (SList) where you can use add functs (grep, fields, s, n, p)

For error debugging:


ADVANCED PYTHON

PACKAGES

IMPORTING

Documentation and Useful tips when importing

import math # imports everything
math.sqrt(2)
from math import * # imports everything as local
sqrt(2)
from math import sqrt as square_root # imports everything as other name local
square_root(2)

Al importar con import * se importan todos los objetos excepto los que empiezan con __ como __name__. Pero nn los paquetes no ocurre así, y habría que crear además en __init__.py una lista para __all__ con los nombres de los modulos a importar.

IMPORTAR DE OTRO DIRECTORIO

Si tenemos un paquete que queremos importar en otra localización:

# Add directory to path
import sys; sys.path.insert(0, '/path')

if name == main

if __name__ == `__main__`:
	main()

Sirve para verificar si un archivo está siendo ejecutado directamente (llamando a ese archivo) o es importado.

argument parser

Un ejemplo de parser cuando se ejecuta un archivo python:

import argparse

# Construct the argument parser
ap = argparse.ArgumentParser()

# Add the arguments to the parser
ap.add_argument("-a", "--foperand", required=True,
   help="first operand")
ap.add_argument("-b", "--soperand", required=True,
   help="second operand")
   
args = vars(ap.parse_args())

# Calculate the sum
print("Sum is {}".format(int(args['foperand']) + int(args['soperand'])))

PACKAGING

To make packages, place all files in the same directory and create a file __init__.py, setup.py and README-LICENSE.txt

In __init__.py hay que poner los subpaquetes para que se importen automaticamente al importar:

# Ejemplo de __init__.py de un paquete llamado utils
from utils import utils_basic, utils_advanced
# para importar todos, lo mejor es hacer:
from utils import *

In setup.py you need necesary info for package assembly with pip:

from distutils.core import setup

setup(
  name='PackageName',
  version='1.0dev',
  packages=['package1',],
  license='MIT',
  long_description=open('README.txt').read(),
)

After that: OR upload to PyPI, OR create binary with `python setup.py bdist` and after `register` and then `upload`; later `install`.

To create exe: PyInstaller (win-osx) or py2exe (win).


BE PYTHONIC

Duck typing

Se refiere a no tener en cuenta el tipo de objeto si puede hacer lo que se le permite.

Si anda como un pato y vuela como un pato, se le trata como a un pato.

EAFP (easier to ask forgiveness than permission)

Se refiere a que en otros lenguajes vamos anidando condicionales para hacer comprobaciones antes de ejecutar (LBYL = look before you leak), y en python es mejor dejar ejecutar y usar try/except para ver los errores.

PEP8 (Python enhancement proposals) = style guide:

Enlaces: PEP-8 python, Best guilelines medium


DECORATORS

Modify functions using other functions (useful for extending functionallity without modifying a function).

Primero habla de los closures que yo no habia oido hablar; creo que son funciones anidadas en la que se guarda la ejecución pero no se ejecuta:

def outer_function(msg):
	def inner_function():
		print(msg)
	return inner_function

hi_func = outer_function('Hi')

De esta forma se crea la función con los parametros pero aun no se ejecuta.

Esta es la base de funcionamiento de los decoradores, que modifican la ejecución de una función (wrapped):

def decorator_function(original_function):
	def wrapper_function(*args, **kwargs):
		-loquequeramosquehagaeldecorador-
		return original_function(*args, **kwargs)
	return wrapper_function

def decorator_class(object):
	def __init__(self, original_function):
		self.original_function = original_function

	def __call__(self, *args, **kwargs):
	-loquequeramosquehaga-
	return self.original_function(*args,**kwargs)

La forma tipica de los decoradores es @. Estas dos cosas son lo mismo: @decorator_function de display == decorator_function(display)

Para preservar la información de las variables, cuando vamos a usar varios decoradores lo mejor es usar:

from functools import wraps

def decorador(orig_func):
	...
	@wraps(orig_func)
	def wrapper(...):
		...
	return wrapper

@decorador
orig_func()

Decorators with args

Se hace añadiendole otra función anidada “prefijo” que irá por encima del decorador:

def prefix_decorator(prefix):
	def decorator_function(original_function):
			def wrapper_function(*args, **kwargs):
					...
		return wrapper_function
	return decorator_function

@prefix_decorator('LOG: )
def function():
	pass

Pocas veces se usará pero es interesante saberlo.


GENERATORS

Cuando hacemos un for con una list comprehension en el fondo estamos haciendo un generador; para ello debemos usar () paréntesis. (x*x for x in [1,2,3,4,5])

ITERTOOLS

Functions useful in functional programming:


RANDOM

import random

random.random()= valor entre 0 y 1 random.uniform(inicio, final) = devuelve float random.randint(inicio, final) = devuelve enteros random.choice(list) = devuelve un valor aleatorio de entre una lista random.choices(list, k=veces) = devuelve una lista de k valores aleatorios de entre una lista.

Valores unicos (evitar repeticion), no se usa choices sino random.sample(list, k=).

REGULAR EXPRESSIONS

import re

Generalmente comunes para todos los lenguajes de programación.

Los raw strings se utilizan con r'' e interpretan en crudo los strings.

Lo primero que tenemos que hacer es crear un patrón: pattern = re.compile(r'')

finditer busca en el texto y devuelve indices si es igual.

Los metacarácteres deben llevar \ delante para diferenciarlos de los caracteres reales. Son: . ^$ * + ? {} [] \ | ()

Las minusculas son un valor y las mayusculas el contrario | Digit | Value | |——-|——-| \d | Digit 0-9 \D | Not Digit (0-9) \w | Word chars (a-z, A-Z, 0-9, _) \W | Not word chars \s | Whitespace (space, tab, newline)

\b Word Boundary
\B Not word boundary
^ Beginning of a String
$ End of a String
[] Matches only chars in brackets
[^] NOT matches inside brackets
. Matches anything

Cuantificadores:

Podemos especificar rangos entre guiones: [a-e0-5] o [a-zA-Z0-9] pero solo buscaria UN DIGITO cada vez.

Ej: r'\d\d\d[-.]\d\d\d[-.]\d\d\d\d' Esto buscaría números de teléfono tipo 111-222-3333 o 111.222.3333

Con los cuantificadores podemos evitar repeticiones. Se colocan DETRÁS: r'\d{3}.\d{3}.\d{3}'

Tambien sirven para condiciones (? = tanto si se cumple como si no): r'Mr\.?\s[A-Z]\w+' = buscaría tanto Mr como Mr. seguido de espacio y el nombre en mayuscula (ej: Mr. Bacat o Mr Bacat), pero no si solo fuera la inicial (no buscaría Mr. B)

Los grupos nos permiten buscar patrones alternativos pero parecidos. Se usan entre parentesis y con :
r'M(r|s|rs)' buscaría Mr/Ms/Mrs  

Cuando encontramos un patrón con matches = pattern.finditer(text) podemos acceder a los distintos grupos que hemos buscado en el texto:

Además de finditertambien podemos usar:

sub que sustituye el patrón encontrado por un string que le pasemos: pattern.sub(sub_pattern, text) Ej: r'(\2\3', urls) –> esto reemplaza por el segundo y el tercer grupo

findall es similar a finditer pero devuelve una lista en vez de un iterable

match te dice si lo que hemos buscado está al principio del string (solo devuelve el primer resultado, no un iterable). No se usará mucho.

search busca en todo el string pero devuelve solo el primer resultado tambien.

Tambien se pueden añadir FLAGS a nuestros patrones: re.IGNORECASE ignora mayusculas y minusculas (es lo mismo que [a-zA-Z])


DATES

import datetime

DATE (fecha): AAAA-MM-DD

Obtiene solo la fecha

TIME (tiempo): HH:MM:SS.mmss

Se usa poco porque no tiene fecha

DATETIME (fecha y hora): AAAA-MM-DD HH:MM:SS.mmss

Obtiene ambos atributos, es el que usaremos

Uso:

datetime.date(y,m,d)= para crear una fecha
datetime.date.today = fecha de hoy

Si queremos solo un dato en concreto:

*.today.year() = solo el año
*.today.weekday() = dia de la semana si Lunes es 0. Con isoweekday() Lunes es 1.

Los timedeltas son intervalos de tiempo en relación a una fecha.
Con ello podemos:

Ahora esto se lo podemos sumar por ejemplo a la fecha de hoy y ver de dentro de una semana.

Para la fecha actual hemos de tener en cuenta las zonas horarias. datetime.datetime.today() o datetime.datetime.now() no tienen bien implementadas las zonas horarias.

Lo mejor es usar el paquete pytz: datetime.datetime.now(tz=pytz.UTC)

Si queremos convertirla: hora.astimezone(pytz.timezone('Spain'))

Para formatear horas en otro formato (ej: 23 de Julio de 2020)

hora.strftime('formato') pueder ver los distintos formatos en la documentación. al reves (de string a hora) con strptime()


TIME measure

import time
start_time = time.time()
do_something()
print(%s seconds % (time.time()  start_time)) 

tambien se puede usar el magic method %timeit al inicio

OS Module


REFERENCE

== != is

== checks equality dos latas de cocacola son iguales

is checks identity dos latas de cocacola no son la misma lata (no son el mismo objeto que ocupa espacio en memoria


Subprocess

import subprocess
p1 = subprocess.run()
	p1.args # te dice los argumentos que has dado
	p1.stdout # te da la salida si la has guardado
		# con capture_output = True
	p1.stderr # te da los errores (tambien .errorcode)

str vs repr

Vamos, que hay objetos que pueden no ser un string pero si tener una representación leible (que es la que buscamos con str), y con repr vemos lo que realmente es.

ej: “2016-10-22 12:13:43” vs datetime.datetime(2016,10,22,12,13,43,tz=UTC)

partition vs split

partition rompe solo una vez en el caracter dado y devuelve una tupla (previo, caracter, posterior) split parte cada vez que se vea ese caracter

Walrus-operator

Sirve para asignar (:=) y evaluar un booleano al mismo tiempo (asi nos ahorramos repetir una expresion)

my_list = [1,2,3,4,5]
if (n := len(my_list)) > 3:    print(f"The list is too long with {n} elements")

BIBLIOGRAFÍA

Scientific python lectures