24 feb. 2017

De vuelta a lo básico, como manejar colas en python.

Las estructuras de datos tipo cola son tipo FIFO (primero que entra, primero que sale).

El artículo se basa de la documentación oficial de Python en inglés.
Se puede usar las listas como una cola.

Las listas soportan las siguientes métodos:

  • list.append(x): agrega un elemento al final de la lista, equivalente a a[len(a):] =[x].
  • list.extend(L): extiende una lista al agregar todos los elementos de una lista, equivalente a a[len(a):] = L.
  • list.insert(i,x): Inserta un elemento en la lista en una posición dada. 
  • list.remove(x): Elimina el primer item x de la lista.
  • list.pop([i]):Elimina el item en la posición dada de la lista, y lo devuelve.
  • list.clear(): Elimina todos los elementos de la lista.
  • list.count(x): Retorna el número de veces que x aparece en la lista.
  • list.sort(key=None,reverse=False): Ordena los elementos de la lista.
  • list.reverse(): Invierte el orden de la lista.
  • list.index(x): Devuelve el indice del primer elemento de la lista que sea x.
  • list.copy(): Devuelve una copia de la lista.

Para emular una cola con una lista se tiene el siguiente ejemplo:

>>> cola = [3,5,6]


>>> cola.append(8)

>>> cola

[3, 5, 6, 8]

>>> cola.pop()

8

>>> cola.append(8)

>>> cola.pop(0)

3

>>> cola.append(9)

>>> cola.pop(0)

5

>>> cola.pop(0)

6

>>> cola = [3,5,6]

>>> cola

[3, 5, 6]

>>> cola.append(9)

>>> cola

[3, 5, 6, 9]

>>> cola.append(19)

>>> cola

[3, 5, 6, 9, 19]

>>> cola.pop(0)

3

>>> cola.pop(0)

5

>>> cola

[6, 9, 19]

>>> cola.pop(0)

6

>>> cola

[9, 19]

>>> cola.pop(0)

9

>>> cola

[19]

>>> cola.pop(0)

19

>>> cola

[]


Se usa el método pop diciendo que tome siempre el primer elemento de la lista.

Esto no es eficiente así que la mejor opción es implementar una cola con collection.deque:

>>> from collections import deque
>>> cola = deque([1,2,3,4,5])
>>> cola
deque([1, 2, 3, 4, 5])
>>> cola.append(19) #Llega 19
>>> cola
deque([1, 2, 3, 4, 5, 19])
>>> cola.append(39) #Llega 39
>>> cola
deque([1, 2, 3, 4, 5, 19, 39])
>>> cola.popleft() #Sale 1
1
>>> cola.popleft() #Sale 2
2
>>> cola
deque([3, 4, 5, 19, 39])
>>> cola.append(51) #Llega 51
>>> cola
deque([3, 4, 5, 19, 39, 51])
>>> cola.popleft() #Sale 3
3
>>> cola
deque([4, 5, 19, 39, 51])



En este artículo se demostró como manejar colas sólo con listas o con collections.deque.

5 feb. 2017

Volviendo a lo básico, POO en Python ( diferencia entre __init__ y __new__) (parte 10)

Continuando con la serie de artículos volviendo a lo básico POO, en este artículo se toca el tema de las diferencias entre __init__ y __new__.

Este artículo se baja en un artículo en inglés entendiendo new e init.

Muchos que han programado orientado a objetos en Python nunca han usado el método __new__, el que usan como constructor es el __init__.

La realidad es que el método __init__ crea el objeto y luego lo inicializa, no es el constructor como tal, en cambio el método __new__ sólo construye el objeto.

Para este artículo se trabajará con Python 3.


Se tiene la siguiente clase:


class A(object):

    pass



Así que la Clase A hereda de object en Python 3.


Ahora se crea una clase que use __new__ e __init__:


class A(object):
    def __new__(cls):
        print "A.__new__ es llamado"
        return super(A, cls).__new__(cls)
    def __init__(self):
        print "A.__init__ es llamado"
A()

La salida es la siguiente:

A.__new__ es llamado
A.__init__ es llamado


Ahora vamos con un ejemplo donde __init__ no será llamado:



class A(object): def __new__(cls): print "A.__new__ es llamado" def __init__(self): print "A.__init__ es llamado" print ("Solo llamando el objeto A() o instanciandolo") A() a = A() print ("*"*40) print ("Ejecutando un print con el objeto A()") print (A())


Como se puede ver, en el método __init__ se está ejecutando un print y en el método __new__ también.

La salida es la siguiente:

Solo llamando el objeto A() o instanciandolo
A.__new__ es llamado
A.__new__ es llamado
****************************************
Ejecutando un print con el objeto A()
A.__new__ es llamado
None


Como se puede ver, al llamar a A() o al instanciarlo, sólo se muestra la salida del  método __new__ , y el de __init__ no, sólo cuando se ejecuta el print(A()) es que devuelve la salida del método __new__ y devuelve None el método __init__.


Ahora se mostrará la clase con sólo el método __new__ donde se ejecuta un print y retorna un número:



class A(object):
    def __new__(cls):
        print "A.__new__ es llamado"
        return 29
print ("Solo llamando al objeto")
A()
print ("*"*30)
print ("Llamando al objeto desde un print")
print A()

La salida es la siguiente:

Solo llamando al objeto
A.__new__ es llamado
******************************
Llamando al objeto desde un print
A.__new__ es llamado
29


Al sólo llamar al objeto sólo muestra el print, pero al ejecutar el print muestra el print del método y el entero que retorna.

Ahora se hará lo mismo pero con el método __init__:



class A(object):



    def __init__(self):

        print "A.__init__ es llamado"

        return 33





try:

    print ("Solo llamando al objeto")

    A()

except (TypeError):

    print ("Error de tipo")


La salida es la siguiente:

Solo llamando al objeto
A.__init__ es llamado
Error de tipo


En este caso el return devuelve error de tipo. La única forma que en el método __init__ devuelva algo es que sea del tipo None.

Ahora se muestra el uso de una clase alternativa llamada Ejemplo:

class Ejemplo(object):
    def __str__(self):
        return "Ejemplo"
class A(object):
    def __new__(cls):
        return Ejemplo()
class B(object):
    def __new__(cls):
        return super(B, cls).__new__(Ejemplo)
print ("Prueba con A")
print A()
print ("Prueba con B")
print B()


La salida devuelve lo siguiente:

Prueba con A
Ejemplo
Prueba con B
Ejemplo


Como puede verse, hay casos donde es útil utilizar __new__ y otros donde se pueden seguir usando __init__.

El método __new__ es usado en patrones de diseño con python, que será la siguiente serie de artículos que vendrán en este blog.



AddThis