He leído ultimamente:
Nota: Este documento está basado en los distintos documentos que han ido surgiendo
detallando los cambios de cada versión de Python. Tanto la traducción como la adaptación
son bastante libres. Los autores de los textos originales son Moshe Zadka, Andrew Kuchling
print >> sys.stderr, "Esto es un error muy chungo"
Esto mandaría esa cadena al error estándar en lugar de a la salida
estándar.
print >> miarchivo, "Esto no sale por pantalla; se escribe en un archivo"
Escribe esa cadena en el objeto de tipo archivo miarchivo (que debe
estar abierto para escritura, obviamente).
from SimpleXMLRPCServer import (SimpleXMLRPCServer,
SimpleXMLRPCRequestHandler,
CGIXMLRPCRequestHandler,
resolve_dotted_attribute)
for i in reversed(miLista): print t
import string as nomeusesquetoyobsoleto.
int('123',10) -> 123
int('123',16) -> 291
Además, como veremos más adelante, los ints ahora se convierten
automaticamente a longs cuando es necesario sin provocar excepciones
de desbordamiento.
if dict.has_key(key): return dict[key]
else:
dict[key] = pordefecto
return dict[key]
a:
return dict.setdefault(key, pordefecto)
Este es sin duda el mayor cambio que ha experimentado Python en este
periodo. Los cambios (de momento) son compatibles hacia atrás asi que la
mayoría de los programadores no deberían tener problemas en mantener sus
programas bajo el modelo antiguo, pero sería recomendable cambiar con el
tiempo (por ejemplo, cuando estemos casi seguros de que la inmensa
mayoría de la gente esta utilizando, al menos, Python2.2).
En resumen:
Python 2.2 tiene dos tipos de clases, las de estilo viejo y las de
estilo nuevo conviviendo. Las de estilo viejo siguen exactamente el
mismo modelo que cualquier programador de cualquier versión anterior
debería conocer. Todas las características que se explicarán a
continuación se aplican sólo a las clases de estilo nuevo. Eventualmente
se dejarán de soportar las clases de estilo viejo, probablemente en
Python 3.0.
¿Cómo se define una clase de estilo nuevo?
Se hace heredando de una clase existente. La mayoría de los tipos
internos de Python, como enteros, listas, diccionarios, e incluso
archivos son ahora clases de estilo nuevo. Hay además una clase de
estilo nuevo llamada 'object' que se convierte en la clase base para
todos los tipos internos, de modo que si no queremos heredar de un nuevo
tipo interno se puede heredar de este:
class MiClase(object):
def __init__(self):
...
De esto se deduce que todas las clases que no hereden de ninguna serán
de estilo viejo.
Ahora las funciones internas 'int()', 'float()' y 'str()' ya no son
funciones sino objetos clases que se comportan como fábricas cuando se
las llama.
>>> int
<type 'int'>
>>> int('123')
123
Para añadir simetría, se han añadido las 'fábricas' para el resto de
tipos como 'dict' y 'file'. Por ejemplo, para añadir un método lock a
una clase heredada de los objetos fichero (file) tán sólo tendríamos que
hacer:
class FicheroLockable(file):
def lock(self, operation, length=0, start=0, whence=0):
import fcntl
return fcntl.lockf(self.fileno(), operation, length, start, whence)
En versiones anteriores no existía una forma consistente de descubrir
que atributos y métodos tenía un objeto. Existían algunas convenciones
informales como definir __members__ y __methods__ que eran listas de
nombres, pero en muchas ocasiones los autores de una clase añadida no se
preocupaban por definirlos. Se podía inspeccionar el __dict__ de un
objeto pero en algunas ocasiones esto también podía ser poco apropiado.
Los descriptores especifican el valor de un atributo, empezando por si
es un método o un campo. Con el API para descriptores, pueden hacerse
los métodos de clase y los métodos estáticos además de contrucciones más
exóticas.
Los descriptores de atributos son objetos que viven dentro de los
objetos de clase y tienen unos pocos atributos por si mismos:
Por ejemplo, cuando se escribe obj.x, los pasos que Python realiza
realmente son:
descriptor = obj.__class__.x descriptor.__get__(obj)
Para los métodos get devuelve un objeto temporal al que se le puede
llamar, y agrupa la instancia y el método en ese objeto.
Se definen de forma similar al siguiente ejemplo:
class Miclase(object):
@staticmethod
def f(arg1,arg2):
...
@classmethod
def g(cls,arg1,arg2):
...
A los métodos estáticos no se les pasa la instancia de la clase, por lo
que son muy parecidos a las funciones normales (aunque se sigue
accediendo a ellos con la notación Miclase.f()). Son similares a los
métodos estáticos de C++. A los métodos de clase se les pasa como primer
argumento la clase, pero no la instancia. Por decirlo de alguna forma,
son como métodos de 'la fábrica' más que de los objetos fabricados.
En el apartado anterior hemos visto que se ha usado una arroba antes de una palabra clave
para especificar que el método de la siguiente linea era de un tipo especial. Estos símbolos
antepuestos de una arroba se llaman "Decoradores" y no son más que funciones que toman como parámetro
una función, realizan operaciones sobre la misma y devuelven otra función (o la misma, modificada).
Por ejemplo en el apartado anterior el:
@staticmethod def f(arg1, arg2): ...
es equivalente a:
def f(arg1, arg2): ... f = staticmethod(f)
Podemos definir nuestros propios decoradores:
def miDecorador(funcion_a_decorar): func.propiedad = "decorada" return func @deco def funcion_decorable(): pass # f.attr vale "decorada"
super() proporciona una forma nueva de acceder a cualquier de las
superclases de una clase, y es la forma recomendada de acceder a ellas.
El uso es sencillo:
super(superclase, self).metodo()
o:
super(superclase, self).variable
Con versiones anteriores de Python se suele utilizar __getattr__ para
definir 'ganchos' que se ejecuten cuando se acceda a un atributo. Por
ejemplo, muchas clases utilizan __getattr__ para definir que método se
utilizará al acceder a determinada variable (por ejemplo, para convertir
obj.cosa en una llamada al método get_cosa() de forma transparente al
usuario de la clase).
Ahora se han añadido nuevas formas de controlar el acceso a atributos,
aunque __getattr__ se mantiene intacto.
__getattribute__: Si __getattr__ se utilizaba cuando se accede a un
atributo que no existe, __getattribute__ se utiliza siempre que se
accede a un atributo.
'Propiedades': Las propiedades (properties) son un nuevo tipo de datos
interno que empaqueta tres funciones para leer, escribir y borrar un
atributo, respectivamente, y una cadena de documentación. Es como llevar
al nivel del lenguaje lo que siempre se ha hecho en programación
orientada a objetos, es decir, los métodos de acceso 'get' y 'set' para
las variables de instancia.
El uso es bien sencillo, y se ve mejor con un ejemplo:
class C_nueva(object):
def get_tamanio(self):
print 'Retornamos el valor de tamanio!'
return self.tamanio
def set_tamanio(self,tamanio):
... calcular algo en relación con el parámetro tamanio ...
... y poner el estado interno
print 'Escribimos el valor de tamanio!'
tamanio = properties(get_tamanio, set_tamanio, None, "Cadena de docuemntacion")
El equivalente en una clase 'vieja' sería:
class C_vieja:
def __init__(self):
self.tamanio = 0
def get_tamanio(self):
print 'Retornamos el valor de tamanio!'
return self.tamanio
def set_tamanio(self,tamanio):
... bla bla bla..
print 'Escribimos el valor de tamanio!'
Ahora examinemos la misma secuencia de comandos para las dos clases y su
resultado:
cv = C_vieja(); # No imprime nada, pone tamanio a 0 cv.tamanio = 5 # No imprime nada, pone tamanio a 5 tmp = cv.tamanio # No imprime nada, asigna tamanio a tmp cv.set_tamanio(5) # Imprime 'Escribimos...' y pone tamanio a 5 cv.get_tamanio() # Imprime 'Retornamos...' y devuelve 5
Con la clase nueva sería:
cv = C_nueva(); cv.tamanio = 5 # Escribe 'Escribimos...' y pone tamanio a 5 tmp = cv.tamanio # Escribe 'Retornamos...' y asigna tamanio a tmp cv.set_tamanio(5) # Escribe 'Escribimos...' y pone tamanio a 5 cv.get_tamanio(5) # Escribe 'Retornames...' y devuelve 5
Como se puede observar, la diferencia en usar properties en lugar de
atributos 'normales' es que con las properties las funciones registradas
para leer, escribir y borrar el atributo son llamadas automáticamente
aunque accedamos directamente al nombre de la variable. Esto permite
hacer nuestro código mucho más robusto, añadiendo las comprobaciones
necesarias a la hora de poner o escribir el valor del atributo (o hacer
lo que queramos, no estamos limitados a leer y escribir el valor, aunque
será lo que normalmente se haga), y queda un código más claro (menos
inundado de llamadas a métodos get y set) y legible.
Los conjuntos son un nuevo tipo de datos, mutables e internos al lenguaje (desde la 2.4)sobre el que, además de las operaciones de añadir y eliminar
elementos, se pueden realizar las operaciones algebraicas de resta, or exclusivo, unión e intersección con otros conjuntos
(mediante el signo "-", "^", "|","&", respectivamente). También permiten añadir elementos (método add() para uno y update() para varios) o eliminarlos (con remove.)
Los conjuntos _no_ son tipos secuenciales (de hecho están
construidos a partir de un diccionario). También soportan las operaciones de comprobación de
sub (issubset()) o superconjunto (issuperset()).
Existe un segundo tipo de conjunto declarado como "frozenset" que es inmutable.
Ejemplo del documento de cambios a 2.4:
>>> a = set('abracadabra') # form a set from a string
>>> 'z' in a # fast membership testing
False
>>> a # unique letters in a
set(['a', 'r', 'b', 'c', 'd'])
>>> ''.join(a) # convert back into a string
'arbcd'
>>> b = set('alacazam') # form a second set
>>> a - b # letters in a but not in b
set(['r', 'd', 'b'])
>>> a | b # letters in either a or b
set(['a', 'c', 'r', 'd', 'b', 'm', 'z', 'l'])
>>> a & b # letters in both a and b
set(['a', 'c'])
>>> a ^ b # letters in a or b but not both
set(['r', 'd', 'b', 'm', 'z', 'l'])
>>> a.add('z') # add a new element
>>> a.update('wxy') # add multiple new elements
>>> a
set(['a', 'c', 'b', 'd', 'r', 'w', 'y', 'x', 'z'])
>>> a.remove('x') # take one element out
>>> a
set(['a', 'c', 'b', 'd', 'r', 'w', 'y', 'z'])
Los iteradores son objetos que tienen un método llamado next() que
devuelve un elemento. Cada vez que se llame a next() un iterador debe
devolver el siguiente elemento de la estructura de datos o objeto sobre la que
itera hasta que ya no hay más elementos momento en el cual se lanza una
excepción de tipo StopIteration. Los objetos iterados se crean mediante
la nueva función interna iter(objeto) siendo el parámetro objeto el
objeto sobre el que el iterador devuelto va a iterar.
Ejemplo:
L = [1,2,3] # Creamos una sencilla lista i = iter(L) # i es un objeto iterador que iterará sobre L i.next() # Devuelve 1 i.next() # Devuelve 2 i.next() # Devuelve 3 i.next() # Lanza la siguiente excepción Traceback (most recent call last): File "<stdin>", line 1, in ? StopIteration
En general, son iterables cualquier colección (listas, tuplas, cadenas,
diccionarios) y objetos que tengan el método __iter__ redefinido.
iter() también puede otra forma con dos parámetros iter(C,centinela)
siendo el primer parámetro un objeto invocable (una función, un método,
o un objeto con el método __call__ redefinido) y centinela un valor que
hará que se lance StopIteration cuando C devuelve ese valor, por
ejemplo:
fich = open('fichero', 'r')
fich_it = iter(fich.readline, '')
fich_it.next()
....
Cada llamada a fich_it.next() leera una nueva línea hasta que se
llegue al final del archivo (fich.readline devuelve '') momento en el
que se lanzará la StopIteration.
Otro ejemplo:
i = iter( [1,2,3,None,4,5,6] ) j = iter( i.next, None )
Tendrá como salida:
1 2 3
Los iteradores tienen una serie de consecuencias internas:
Como también podemos llamar a iter() con un diccionario, podemos
hacer:
for clave in dicc: print clave, dicc[clave]
Para escribir todas las claves y valores de un diccionario. Pero
además si se quiere iterar especificamente sobre algún componente del
diccionario puede utilizarse:
iterkeys(dicc): Devuelve un iterador que itera sobre las claves del
diccionario argumento.
itervalues(dicc): Devuelve un iterador que itera sobre los valores del
diccionario argumento.
iteritems(dicc): Devuelve un iterador que itera sobre los conjuntos
clave/valor del diccionario argumento.
for linea in fichero:
... hacer algo con cada línea ...
L = [1,2,3] i = iter(L) a,b,c = i a,b,c # Escribirá:1,2,3
Es importante destacar que sólo se puede ir hacia adelante con un
iterador, no puede ni irse hacía atrás, reiniciar el operador o hacer
una copia del mismo. Un objeto iterador creado podría proporcionar esas
capacidades adicionales pero el protocolo iterador sólo requiere un
método next().
Los generadores son otra característica nueva, y depende de los
iteradores. Un generador modifica sustancialmente la forma en la que una
función o método trabaja.
Normalmente se llama a una función, y en algún punto la función
devuelve un valor o ninguno en absoluto. La siguiente vez que se llama a
la función, se vuelve a ejecutar desde el principio de la misma forma y
de nuevo devolverá (o no) un valor dependiendo de sus argumentos (o
falta de ellos). Esto creo que lo sabemos todos.
Bien, cuando en una función normal llegamos al 'return' las variables
locales se destruyen y el valor resultante es devuelto al invocador. Una
llamada posterior a la misma función tendrá un conjunto creado de nuevo
de variables locales. Pero...
¿Qué pasaría si quisiéramos que las variables locales de la función no
se destruyeran al final de cada llamada? ¿Qué pasaría si en la siguiente
llamada quisiéramos continuar la ejecución de la función donde se quedo
la última vez?
Pues la respuesta a estas preguntas es: Utilizar generadores. Puede
pensarse en los generadores (o funciones generadores) como en funciones
continuables.
Para convertir una función en una función generadora, tan sólo tenemos
que utilizar la palabra clave yield en lugar de return. yield
devuelve, al igual que return, el valor de la expresión que tenga a la
derecha (o ninguno, si no tiene ninguno), pero la próxima vez que
llamemos a la función la ejecución continuará con las mismas variables
locales y a partir de donde se salió la última vez (es decir, a partir
del último yield que provocará el retorno de la función generador).
El ejemplo más sencillo es:
def generar_enteros(N):
for i in range(N):
yield i
¿Qué hace exactamente esta función? Veámoslo continuando el ejemplo, y
si hemos leido el apartado sobre iteradores lo entenderemos a la
perfección:
gen = generar_enteros(3) gen.next() # Devuelve 0 gen.next() # Devuelve 1 gen.next() # Devuelve 2 gen.next() # Genera la excepción StopIteration
También podría escribirse:
for i in generar_enteros(3):
... etc ...
ó:
a,b,c,d = generar_enteros(4) # a=0 b=1 c=2 d=3
Dentro de una función generadora, el 'return' sólo puede utilizarse sin
ninguna expresión a la derecha y marca el final del ciclo de proceso;
utilizar el return con alguna expresión es un error dentro de una
función generador. El final del ciclo de proceso también puede marcarse
lanzando StopIteration manualmente o simplemente dejando que el flujo de
ejecución del generador llega al final de la función.
Un ejemplo más elaborado de un generador que implementa el recorrido de
un arbol en orden in-orden (primero todos los nodos de la izquierda y
luego los de la derecha) de forma recursiva:
def inorder(t):
if t:
for x in inorder(t,left):
yield x
yield x.label
for x in inorder(t.right):
yield x
Como los generadores introducen una palabra clave nueva, si queremos
utilizarlos desde Python 2.2 es necesario incluir al principio del archivo .py:
from __future__ import generator
Desde Python 2.3 no es necesario incluir este comando.
enumerate(secuencia), siendo secuencia un tipo de datos secuencia (lista, tupla, cadena, etc) nos
devolverá un iterador (ver más arriba) que en cada llamada nos irá devolviendo una tupla (posición, elemento)
en la que 'posición' será la posición ordinal que 'elemento' ocupará dentro de la secuencia. Por ejemplo:
lista = ['perro', 'gato', 'hamster'] listaEnumGen = enumerate(lista) listaEnumGen.next() (0, 'perro') listaEnumGen.next() (1, 'gato') listaEnumGen.next() (2, 'hamster')
Por supuesto el iterador creado también podrá utilizarse en un bucle 'for', por ejemplo para sustitúir:
for posicion in range(len(Lista)):
elemento = L[posicion]
#...hacer algo con el elemento...
L[posicion] = resultado
Por:
for posicion,elemento in enumerate(Lista):
#... hacer algo con el elemento...
L[posicion] = resultado
Python 2.3 añade un nuevo tipo de datos interno a los ya existentes: El lógico o
booleano. Este tipo sólo puede tener dos valores, que se incorporan como palabras clave
al lenguaje, y son True o False (si teníamos código existente utilizando esas palabras para
nombres de símbolo debemos cambiarlas si queremos que funcione correctamente en Python 2.3). Como
seguramente ya haya adivinado el lector, True significa 'Cierto, Verdadero' y False 'Falso'. Estos
tipos tienen un valor numérico que es '1' y '0' respectivamente, por compatibilidad.
Python 2.2 (y posteriores) convertirá los enteros en enteros largos
automáticamente cuando sea necesario, por lo que ya no se necesita el
sufijo 'L' para indicar el tipo. Muchas operaciones que antes lanzaban
la excepción OverflowError ahora devolverán un entero largo como su
resultado, por ejemplo:
>>> 1234567890123 # Devuelve 1234567890123L >>> 2 ** 64 # Devuelve 18446744073709551616L
Actúalmente el operador de división / de Python (y en versiones
anteriores) se comporta como el operador de división de C cuando se le
utiliza con dos valores enteros: devuelve un resultado entero que trunca
la parte fraccionaria. Por ejemplo 3/2 es 1, no 1.5. Esto tiene la
consecuencia de que los resultados de una división pueden variar de
forma inesperada dependiendo del tipo de los dos operandos (enteros o
reales) y como Python es dinámicamente tipado, puede ser dificil
determinar los tipos de los operarandos.
Ahora se ha dividido la operación de división en dos operaciones, por un
lado la 'división real' proporciona los resultados que la mayoría de los
no-programadores esperarían, es decir, con parte fraccionaria si es
necesario aunque los dos operadores sean enteros, y por otro lado está
la 'división clásica' que es lo que el operador / actúal realiza.
También está la 'división con redondeo' que devuelve un entero sin
importar los tipos de los operandos (aunque seán flotantes).
Python 2.2 introduce el operador // para la 'divisón con redondeo', y
se cambia el
operador / que a partir de ahora realizará la división real.
Simétricamente las clases ahora pueden redefinir los métodos __truediv__
para la división real y __floordiv__ para la división con redondeo,
correspondientes a los operadores del mismo nombre.
Para utilizar el nuevo operador / como división real es necesario
incluir al principio de los archivos .py la sentencia:
from __future__ import division
En Python 2.3 no será necesario (el operador / actuará como división
real por defecto).
Si queremos comprobar si nuestros programas se pueden ver afectados por
este cambio, podemos ejecutar el intérprete con el parámetro -Q warn que
causará que se imprima una advertencia cada vez que se utilice una
divisón con dos enteros, de modo que podamos arreglar el código afectado
por este cambio.
Utilizan 16 bits por caracter en lugar de 8. Se crean anteponiendo una 'u'
a las comillas (u"esto es una cadena unicode en python"). Puede obtenerse
cualquier carácter unicode con \uHHHH siendo HHHH un número en hexadecimal
de 0000 a FFFF.
Tienen un método encode() que devuelve una cadena normal de 8 bits en la
codificación deseada (como 'ascii', 'utf-8', 'iso-8859-1' u otras).
Combinar cadenas de 8 bits y unicode fuerza la conversión a unicode.
Hay funciones para tratarlas:
Todos los comandos internos que aceptaban cadenas normales ahora también
aceptan cadenas unicode.
Esa directiva se utiliza cuando en una versión de Python se introducen
cambios que podrían romper la compatibilidad de programas existentes.
Por ello se ha añadido esta palabra clave que permite tener
opcionalmente determinadas características que serán obligatorias en
versiones siguientes.
Esta directiva, __future__, se utiliza de la siguiente manera:
from __future__ import característica
Deben ponerse al principio de los módulos, antes de cualquier otro
import o código.
Hasta ahora las clases que tenían un método __cmp__ para sobrecargar la
comparación entre dos clases sólo tenían que preocuparse de devolver 0
si las clases eran iguales o +1 o -1 si no lo eran. No podían lanzarse
excepciones o devolver cualquier otro valor que no fuera booleano.
Ahora las comparaciones se ha dividido en varios métodos para cada tipo,
siendo estos y los nombres de los métodos correspondientes:
Además estos métodos no sólo pueden devolver un valor booleano sino
cualquier cosa (listas, matrices, etc.)
La función interna cmp() acepta también como tercer argumento opcional
el tipo de comparación a realizar como una cadena (como '<' '>=', etc.)
Si le le sigue llamando sin ese tercer argumento el funcionamiento es
igual que el antiguo, devolviendo un valor booleano.
Cuando se importa un módulo que va a dejar de desarrollarse por
cualquier motivo en versiones futuras se producirán advertencias
('DeprecationWarning'). Podemos producir advertencias en nuestro código
utilizando el módulo warning: warning.warning("No uses esto!").
Se ha incluido un nuevo tipo de dato llamado 'referencia debil'
(weakref). Este tipo mantiene una referencia hacia un objeto, pero deja
de funcionar cuando el objeto sólo tiene referencias débiles apuntando
hacia él. Es símilar al concepto de 'enlace simbólico' en los sistemas de
arhivos Unix.
Por ejemplo, si tuviéramos una caché que almacenara los valores de
retorno de una función para optimizar el código:
_cache = {}
def memoize(x):
if _cache.has_key(x):
return _cache[x]
retval = f(x)
_cache[x] = retval
return retval
Esto tiene el problema de que aunque no usemos nunca más el valor de
retorno, el objeto almacenado en _cache seguirá allí. Esto no es un gran
problema si el objeto es un entero o otro tipo pequeño, pero para
objetos muy grandes puede suponer un gasto de memoria innecesario.
Reescribámoslo con referencias débiles:
_cache = {}
def momoize(x):
if _cache.has_key(x):
obj = _cache[x]()
# Si la referencia débil aún existe la devolvemos:
if obj is not None: return obj
retval = f(x)
#Cacheamos la referencia débil
_cache[x] = weakref.ref(retval)
return retval
Como puede observarse las referencias débiles se crean llamando a
weakref.ref, y para objeterlas se llama al nombre creado con la
referencia como si fuera una función, esto podemos verlo en un ejemplo
más sencillo:
obj = funcion_q_crea_objeto() refobj = weakref.ref(obj) #Hemos creado la referencia debil 'refobj' obj2 = refobj() # obj2 es una referencia (fuerte) a obj. del obj del obj2 #Eliminamos las referencias fuertes a obj print refobj() # Escribirá 'None'
Aquí (y en el ejemplo anterior) también observamos que cuando un objeto
sólo tiene referencias débiles ya no existe, y la llamada a la
referencia débil devuelve 'None'.
También podemos crear un tipo de referencias débiles llamadas 'proxis'
que pueden actúar como alias del objeto:
proxy = weakref.proxy(obj)
proxy.attr #Equivalente a obj.attr
proxy.meth() # Equivalente a obj.meth()
del obj # Borramos el objeto referenciado
proxy.attr # Lanzará una excepción weakref.ReferenceError porque el
# objeto ya no tiene ninguna referencia fuerte apuntando
# hacia el.
Las funciones ahora pueden tener información de cualquier tipo unida a
ellas. Anteriormente se utilizaba mucho el atributo __doc__ para incluir
información diversa sobre una función porque era la única forma de
hacerlo. Por ejemplo en el servidor de aplicaciones Zope las funciones
se marcaban como 'seguras para acceso público' mediante una cadena en
__doc__.
Ahora para añadir información y atributos de cualquier tipo a una
función se puede hacer utilizando la síntaxis habitúal de Python:
def f(): pass f.publicable = 1 f.segura = 1 f.gramatica = "A :: = B (C D) *"
Como se ahora las funciones se parecen un poquito más a los objetos,
objetos con un sólo método. De hecho esto anteriormente se podría
similar creando objetos cuyo método a llamar fuera __call__, pero esto
es mucho más eficiente.
Sirven para crear listas a partir de los elementos de una secuencia o
conjunto de secuencias. Tiene la síntaxis:
[ expresión for expr in secuencia1
for expr2 in secuencia2
for expr3 in secuencia3
if condicion]
El if es opcional, se ve más claro con un ejemplo:
sec1 = 'abc' sec2 = (1,2,3) [ (x,y) for x in sec1 for y in sec2 ]
Generará:
[('a', 1), ('a', 2), ('a', 3), ('b', 1), ('b', 2), ('b', 3), ('c', 1),
('c', 2), ('c', 3)]
Se añaden los operadores +=, -=, *=, /=, %=, **=, &=, |=, ^=, >>= y <<=.
Son iguales que los de C, para el que no lo entienda lo verá con un
ejemplo:
x = x + 2 equivale a: x+=2 x = x /3 equivale a: x/=3
Las clases de python pueden sobrecargar estos operadores mediante los
métodos __iadd__, __isub__, etc.
NO se han añadido los operadores de post/pre incremento/decremento (a++,
++a).
Anteriormente sólo había tres tipos de alcance en Python, el global, el
del módulo y en interno. Esto podría producir errores en el caso de
algunas funciones anidadas y recursivas como:
def f():
def g(valor):
return g(valor-1) + 1
Porque g no existe en ninguno de los tres alcances (ni local, ni módulo
ni interno).
Ahora ya no es así; cada agrupación de instrucciones tiene su propio
alcance anidado que incluye el del nivel superior.
Como hemos visto antes, ahora todo es un objeto (de tipo clásico o
nuevo, pero un objeto). Las cadenas no son una excepción.
La manipulación que antes se hacía desde el módulo string ahora puede
hacerse como métodos (aunque sigue vigente el módulo string por
compatibilidad):
'juanjo'.capitalize()
'Juanjo'
'windows mola'.replace('windows', 'linux)
'linux mola'
'juanjo'.find('o')
6
Sin embargo las cadenas siguen siendo totalmente inmutables, por lo que
los métodos de cadena devuelven nuevas cadenas sin modificar la cadena
sobre la que operan.
Hay dos métodos nuevos, startswith() y endswith() que añaden un carácter
al principio o al final de la cadena.
El método join recibe un parámetro que es una secuencia de cadenas y las
une con la cadena a la que pertenece el método:
' '.join(('uno','dos','tres'))
'uno dos tres'
Además, desde Python 2.3, el operador 'in' puede utilizarse con cadenas para evaluar si una es
subcadena de otra (antes sólo podía utilizarse con carácteres), por ejemplo:
cadena = "el veloz murciélago hindú..."
subcadena = "veloz"
if subcadena in cadena:
print "Si!"
Desde Python 2.3 los diccionarios tienen un nuevo método llamado pop(clave) que
devuelve el valor correspondiente a la clave especificada en el argumento y sacan
el par clave/valor del diccionario o lanza un KeyError si el par no existe.
Antes ya lo tenía, pero el tipo de recolector que tenía (contador de
referencias) podía llegar a situaciones en la que un objeto inaccesible
nunca seríe borrado, por ejemplo:
instancia = AlgunaClase() instancia.yomismo = instancia
Ahora el objeto tiene dos referencias; una definida por el propio nombre
'instancia' y otra dentro de si misma. Si ahora hacemos:
del instancia
Decrementamos el contador en un, pero como tenía 2 se queda en uno y
tenemos un objeto al que no podemos acceder (porque hemos borrado la
referencia que era el nombre 'instancia') pero que sigue en la memoria.
Por eso ahora periódicamente se ejecuta un sencillo recolector de basura
basado en un algoritmo para detectar estos ciclos, que borrará los
objetos en caso de hacerlo.
socket.ssl(socket,fich_de_clave,fich_de_certificado) genera un objeto
socket SSL.
Otro cambio que se ha introducido es que los sockets ahora tienen
soporte para sockets 'raw' (si tenemos permisos para ellos en el sistema
operativo en el que estemos). Por último, desde Python 2.3 podemos especificar
un timeout que haga que los sockets lancen la excepción socket.error cuando este 'salte'.
Para poner un tiemout a un socket llamamos a su método settimeout(tiempo) indicando el tiempo
(en segundos) como argumento.
time.asctime(time.localtime(time.time())) Ahora basta con time.asctime()
Ejemplo: Para obtener el tamaño de un fichero utilizando las viejas
tuplas había que terminar escribiendo algo como:
tamanio_fich = os.stat(nombre_fich)[stat.ST_SIZE]
pero ahora pueden ser escritas con más claridad de la forma:
tamanio_fich = os.stat(nombre_fich).st_size