Tipos de datos y algoritmos aplicados
Números
1. Tipos
a. Enteros
Un número entero es de tipo int:
>>> type(1)
<class 'int'>
He aquí la lista de métodos y atributos que integra:
>>> dir(int)
['__abs__', '__add__', '__and__', '__bool__', '__ceil__',
'__class__', '__delattr__', '__divmod__', '__doc__', '__eq__',
'__float__', '__floor__', '__floordiv__', '__format__', '__ge__',
'__getattribute__', '__getnewargs__', '__gt__', '__hash__',
'__index__', '__init__', '__int__', '__invert__', '__le__',
'__lshift__', '__lt__', '__mod__', '__mul__', '__ne__', '__neg__',
'__new__', '__or__', '__pos__', '__pow__', '__radd__', '__rand__',
'__rdivmod__', '__reduce__', '__reduce_ex__', '__repr__',
'__rfloordiv__', '__rlshift__', '__rmod__', '__rmul__', '__ror__',
'__round__', '__rpow__', '__rrshift__', '__rshift__', '__rsub__',
'__rtruediv__', '__rxor__', '__setattr__', '__sizeof__',
'__str__', '__sub__', '__subclasshook__', '__truediv__',
'__trunc__', '__xor__', 'as_integer_ratio','bit_length', 'conjugate',
'denominator', 'imag', 'numerator', 'real']
Estos métodos se aplican a los objetos en el marco de lo que está permitido por la gramática de Python. De este modo, el punto se considera como una coma, en el sentido matemático, y no como el acceso al objeto.
Para utilizar dicho acceso se utilizan los paréntesis:
>>> 1+2
3
>>> 1.__add__(2)
File "<stdin>", line 1
1.__add__(2)
^
SyntaxError: invalid syntax
>>> (1).__add__(2)
3
Por el contrario, no es posible modificar un literal...
Secuencias
1. Presentación de los distintos tipos de secuencias
a. Generalidades
Una secuencia es un contenedor de objetos (que no son, necesariamente, únicos) que disponen de una relación de orden. Esto significa que los objetos pueden ser de cualquier tipo, y que se almacenan en un orden preciso. Varios objetos pueden, de este modo, estar incluidos varias veces en la misma secuencia, en posiciones diferentes.
Se distinguen dos tipos de secuencias: aquellas modificables o mutables, como las listas, y aquellas no modificables o inmutables, las n-tuplas o tuple en inglés.
Las primeras se utilizan para gestionar una secuencia de objetos que está destinada a «vivir», es decir, a modificarse de manera regular; las segundas se utilizan para agrupar datos, cuando los valores tienen un sentido más amplio que, simplemente, una sucesión de objetos ordenados, o también por motivos de rendimiento. La conservación de la semántica requiere que el objeto no pueda modificarse.
De este modo, cuando una función o un método devuelven varios valores, devuelven, en realidad, una n-tupla de valores:
>>> def test():
... return 1, 2, 3
...
>>> test()
(1, 2, 3)
>>> a, b, c = test()
>>> a
1
>>> b
2
>>> c
3
Para expresar, por ejemplo, las coordenadas de un punto en el plano o en el espacio, la n-tupla se utiliza con mayor frecuencia que la lista, pues tiene un sentido más fuerte:
>>> o=(0,0)
>>> p=(1,6)
Es posible trabajar con listas desde la programación orientada a objetos, aunque también utilizando programación funcional. Son una herramienta particularmente potente. Trabajar con las n-tuplas permite responder a otros objetivos, y se realiza a menudo sin darse cuenta, pues es un elemento indispensable del lenguaje.
No obstante, ambos tipos de datos tienen mucho en común, y los puntos para pasar de uno a otro permiten resolver todas las problemáticas.
El desarrollador debe tener precaución de no utilizar siempre el mismo tipo, sino buscar y contextualizar sus desarrollos para sacar el mejor provecho.
b. Las listas
Una lista es un conjunto modificable ordenado no desplegado de objetos Python. Dicho de otro modo...
Conjuntos
1. Presentación
a. Definición de un conjunto
Un conjunto es una colección no ordenada de objetos únicos. No existe, por tanto, una relación de orden y resulta imposible encontrar dos elementos idénticos. Se trata, simple y llanamente, de un conjunto, en el sentido matemático del término.
Un conjunto se diferencia de una secuencia (lista o n-tupla) en que su uso es radicalmente distinto, mientras que las diferencias entre una lista y una n-tupla son de orden semántico y técnico.
Existen dos tipos de conjuntos, los conjuntos modificables (set) y los conjuntos no modificables (frozenset). La diferencia a nivel técnico es exactamente del mismo tipo que entre las listas y las n-tuplas.
Por el contrario, a nivel semántico, no existen diferencias entre un set y un frozenset -mientras que sí las hay entre una lista y una n-tupla-, dado que los dos se utilizan para representar el mismo tipo de datos, con el mismo significado.
Los puentes que hemos visto entre las n-tuplas, las listas y los iteradores funcionan también con los conjuntos. Es posible construir uno a partir de una lista, una tupla u otras secuencias:
>>> set([1, 2, 3])
{1, 2, 3}
Se comprueba, por otra parte, en la respuesta de la consola la nueva representación de un conjunto:
>>> {1, 2, 3}
{1, 2, 3}
No debe confundirse con un diccionario:
>>> {1:1, 2:2, 3:3}
{1: 1, 2: 2, 3: 3}
La representación de un conjunto vacío supone utilizar el constructor, pues el uso de llaves sin valores devuelve un diccionario:
>>> type({})
<class 'dict'>
La representación que da Python a un conjunto vacío creado artificialmente muestra con claridad que es preciso utilizar el constructor cuando se trata de un conjunto vacío:
>>> {0}-{0}
set()
En la rama 2.x de Python esta semántica que utiliza llaves no está presente y la representación se realiza siempre mediante el constructor:
>>> set([1, 2, 3])
set([1, 2, 3])
Existe una restricción de uso importante: los objetos contenidos en el conjunto deben poderse hashear, es decir, debe poderse determinar una clave de hash:
>>> {1, 2, 3}
{1, 2, 3}
>>> {1, 2, [3]} ...
Cadenas de caracteres
1. Presentación
a. Definición
Una cadena de caracteres es una colección ordenada y modificable de caracteres. No existe, necesariamente, una noción de duplicados, puesto que esta noción no tiene sentido para una cadena de caracteres. El orden es importante, pues se trata del orden de lectura, sin el cual no tiene sentido.
En Python 3, los métodos disponibles son:
>>> dir(str)
['__add__', '__class__', '__contains__', '__delattr__', '__doc__',
'__eq__', '__format__', '__ge__', '__getattribute__',
'__getitem__', '__getnewargs__', '__gt__', '__hash__', '__init__',
'__iter__', '__le__', '__len__', '__lt__', '__mod__', '__mul__',
'__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__',
'__rmod__', '__rmul__', '__setattr__', '__sizeof__', '__str__',
'__subclasshook__', 'capitalize', 'center', 'count', 'encode',
'endswith', 'expandtabs', 'find', 'format', 'format_map', 'index',
'isalnum', 'isalpha', 'isdecimal', 'isdigit', 'isidentifier',
'islower', 'isnumeric', 'isprintable', 'isspace', 'istitle',
'isupper', 'join', 'ljust', 'lower', 'lstrip', 'maketrans',
'partition', 'replace', 'rfind', 'rindex', 'rjust', 'rpartition',
'rsplit', 'rstrip', 'split', 'splitlines', 'startswith', 'strip',
'swapcase', 'title', 'translate', 'upper', 'zfill']
Podemos considerar que una cadena de caracteres es una secuencia que contiene únicamente caracteres. Las cadenas de caracteres son, así, comparables entre sí, direccionables mediante índices y también mediante tramos:
>>> list(sorted(set(dir(str))&set(dir(list))))
['__add__'...
Diccionarios
1. Presentación
a. Definición
Un diccionario es una colección no ordenada de relaciones entre claves y valores. La semántica de Python 3.x aproxima la notación de los conjuntos a la de los diccionarios; existen efectivamente similitudes entre ambas colecciones, empezando por el hecho de que una clave de un diccionario debe poderse hashear.
Veamos la lista de métodos de un diccionario:
>>> dir(dict)
['__class__', '__contains__', '__delattr__', '__delitem__',
'__doc__', '__eq__', '__format__', '__ge__', '__getattribute__',
'__getitem__', '__gt__', '__hash__', '__init__', '__iter__',
'__le__', '__len__', '__lt__', '__ne__', '__new__', '__reduce__',
'__reduce_ex__', '__repr__', '__setattr__', '__setitem__',
'__sizeof__', '__str__', '__subclasshook__', 'clear', 'copy',
'fromkeys', 'get', 'items', 'keys', 'pop', 'popitem',
'setdefault', 'update', 'values']
Los métodos que posee una lista y no posee un diccionario son:
>>> list(sorted(set(dir(list))-set(dir(dict))))
['__add__', '__iadd__', '__imul__', '__mul__', '__reversed__',
'__rmul__', 'append', 'count', 'extend', 'index', 'insert',
'remove', 'reverse', 'sort']
En efecto, el diccionario no implementa más operadores que los de comparación. No existe ninguna noción de índices, aunque posee claves que son únicas, y no existe una relación de orden. No existe, tampoco, la noción de tramos, pues si bien es posible crearlos a partir de índices, no lo es a partir de claves.
>>> list(sorted(set(dir(dict))-set(dir(list))))
['clear', 'copy', 'fromkeys', 'get', 'items', 'keys', 'popitem',
'setdefault', 'update', 'values']
Dado que no existen más operadores, son los métodos keys...
Booleanos
1. El tipo booleano
a. Clase bool
Un booleano es un valor entero:
>>> type.mro(bool)
[<class 'bool'>, <class 'int'>, <class 'object'>]
Los métodos y atributos son idénticos:
>>> list(set(dir(bool))-set(dir(int)))
[]
>>> list(set(dir(int))-set(dir(bool)))
[]
Todo lo que hemos visto en la sección relativa a los enteros puede aplicarse, por tanto, a las instancias de bool, incluidos los operadores e incluso las conversiones a bytes que se han visto en la sección relativa a las cadenas de caracteres:
>>> bool(42)*1
1
>>> bool(0)*1
0
>>> True.to_bytes(2, 'little')
b'\x01\x00'
>>> True.to_bytes(2, 'big')
b'\x00\x01'
>>> False.to_bytes(2, 'little')
b'\x00\x00'
>>> False.to_bytes(2, 'big')
b'\x00\x00'
El interés de estas operaciones es hacer que el booleano se parezca a un valor binario 1 o 0, que se asemeja a un valor entero.
b. Los dos objetos True y False
Esta clase es muy particular, puesto que no posee más que dos instancias, que son True y False, y que se asemejan, respectivamente, a los valores enteros 1 y 0:
>>> bool(42)
True
>>> bool(0) ...
Datos temporales
1. Gestionar una fecha del calendario
a. Noción de fecha del calendario
Una fecha es, simplemente, la combinación de un día, un mes y un año. Los tres elementos son obligatorios. No existe la noción de instante, de segundos, de minutos o de horas.
No hay nada más sencillo que gestionar una fecha, basta con crear un objeto datetime.date asignándole el año, el mes y el día.
>>> import datetime
>>> d=datetime.date(2009, 7, 22)
>>> d
datetime.date(2009, 7, 22)
En caso de existir algún error en los parámetros, se genera una excepción muy clara:
>>> d2=datetime.date(2009, 2, 30)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: day is out of range for month
Dicho objeto proporciona:
>>> dir(datetime.date)
['__add__', '__class__', '__delattr__', '__doc__', '__eq__',
'__format__', '__ge__', '__getattribute__', '__gt__', '__hash__',
'__init__', '__le__', '__lt__', '__ne__', '__new__', '__radd__',
'__reduce__', '__reduce_ex__', '__repr__', '__rsub__',
'__setattr__', '__sizeof__', '__str__', '__sub__',
'__subclasshook__', 'ctime', 'day', 'fromordinal',
'fromtimestamp', 'isocalendar', 'isoformat', 'isoweekday', 'max',
'min', 'month', 'replace', 'resolution', 'strftime', 'timetuple',
'today', 'toordinal', 'weekday', 'year']
Dicho objeto posee tres propiedades que se pasan al constructor:
>>> d.day
22
>>> d.month
7
>>> d.year
2009
b. Trabajar con una fecha
Es posible recuperar el día de la semana según la norma española (el lunes es 0, el domingo es 6) o según la norma ISO (0 para el domingo hasta 6 para el sábado, según la norma inglesa):
>>> d.weekday() ...