python hidden-features

Características ocultas de Python



hidden-features (30)

Creando nuevos tipos de forma totalmente dinámica.

>>> NewType = type("NewType", (object,), {"x": "hello"}) >>> n = NewType() >>> n.x "hello"

que es exactamente lo mismo que

>>> class NewType(object): >>> x = "hello" >>> n = NewType() >>> n.x "hello"

Probablemente no sea lo más útil, pero es bueno saberlo.

Edición : el nombre fijo del nuevo tipo, debe ser NewType para ser exactamente lo mismo que con class declaración de class .

Editar : Ajustó el título para describir con mayor precisión la característica.

¿Cuáles son las características menos conocidas pero útiles del lenguaje de programación Python?

  • Intenta limitar las respuestas al núcleo de Python.
  • Una característica por respuesta.
  • Dé un ejemplo y una breve descripción de la función, no solo un enlace a la documentación.
  • Etiqueta la característica usando un título como la primera línea.

Enlaces rápidos a las respuestas:


Descriptores

Son la magia detrás de un montón de características principales de Python.

Cuando usa el acceso de puntos para buscar un miembro (por ejemplo, xy), Python primero busca el miembro en el diccionario de instancia. Si no se encuentra, lo busca en el diccionario de clase. Si lo encuentra en el diccionario de clase y el objeto implementa el protocolo descriptor, en lugar de devolverlo, Python lo ejecuta. Un descriptor es cualquier clase que implementa la __get__, __set__o __delete__métodos.

Así es como implementaría su propia versión de solo lectura con descriptores:

class Property(object): def __init__(self, fget): self.fget = fget def __get__(self, obj, type): if obj is None: return self return self.fget(obj)

y lo usarías como la propiedad incorporada ():

class MyClass(object): @Property def foo(self): return "Foo!"

Los descriptores se utilizan en Python para implementar propiedades, métodos enlazados, métodos estáticos, métodos de clase y ranuras, entre otras cosas. Comprenderlos hace que sea fácil ver por qué muchas de las cosas que antes se parecían a las "peculiaridades" de Python son como son.

Raymond Hettinger tiene un excelente tutorial que hace un mejor trabajo para describirlos que yo.


Encadenamiento de operadores de comparación:

>>> x = 5 >>> 1 < x < 10 True >>> 10 < x < 20 False >>> x < 10 < x*10 < 100 True >>> 10 > x <= 9 True >>> 5 == x > 4 True

En caso de que piense que está haciendo 1 < x , que aparece como True , y luego comparando True < 10 , que también es True , entonces no, eso no es realmente lo que sucede (vea el último ejemplo). Realmente se traduce a 1 < x and x < 10 , y x < 10 and 10 < x * 10 and x*10 < 100 , pero con menos escritura y cada término solo se evalúa una vez.


Los diccionarios tienen un método get ()

Los diccionarios tienen un método ''get ()''. Si lo hace d [''clave''] y la clave no está allí, obtendrá una excepción. Si lo hace d.get (''clave''), obtendrá de nuevo Ninguno si ''clave'' no está allí. Puede agregar un segundo argumento para recuperar ese elemento en lugar de Ninguno, por ejemplo: d.get (''clave'', 0).

Es genial para cosas como sumar números:

sum[value] = sum.get(value, 0) + 1


Doctest : documentación y pruebas unitarias al mismo tiempo.

Ejemplo extraído de la documentación de Python:

def factorial(n): """Return the factorial of n, an exact integer >= 0. If the result is small enough to fit in an int, return an int. Else return a long. >>> [factorial(n) for n in range(6)] [1, 1, 2, 6, 24, 120] >>> factorial(-1) Traceback (most recent call last): ... ValueError: n must be >= 0 Factorials of floats are OK, but the float must be an exact integer: """ import math if not n >= 0: raise ValueError("n must be >= 0") if math.floor(n) != n: raise ValueError("n must be exact integer") if n+1 == n: # catch a value like 1e300 raise OverflowError("n too large") result = 1 factor = 2 while factor <= n: result *= factor factor += 1 return result def _test(): import doctest doctest.testmod() if __name__ == "__main__": _test()


Formato de nombre

El formato% toma un diccionario (también aplica la validación de% i /% s, etc.).

>>> print "The %(foo)s is %(bar)i." % {''foo'': ''answer'', ''bar'':42} The answer is 42. >>> foo, bar = ''question'', 123 >>> print "The %(foo)s is %(bar)i." % locals() The question is 123.

Y dado que locals () también es un diccionario, simplemente puede pasar eso como un dict y tener% -substitions de sus variables locales. Creo que esto está mal visto, pero simplifica las cosas.

Nuevo formato de estilo

>>> print("The {foo} is {bar}".format(foo=''answer'', bar=42))


A partir de 2.5, los dicts tienen un método especial __missing__ que se invoca para los elementos que faltan:

>>> class MyDict(dict): ... def __missing__(self, key): ... self[key] = rv = [] ... return rv ... >>> m = MyDict() >>> m["foo"].append(1) >>> m["foo"].append(2) >>> dict(m) {''foo'': [1, 2]}

También hay una subclase dict en collections llamadas defaultdict que hace prácticamente lo mismo pero llama a una función sin argumentos para elementos no existentes:

>>> from collections import defaultdict >>> m = defaultdict(list) >>> m["foo"].append(1) >>> m["foo"].append(2) >>> dict(m) {''foo'': [1, 2]}

Recomiendo convertir dichos dicts a dicts regulares antes de pasarlos a funciones que no esperan tales subclases. Una gran cantidad de código utiliza d[a_key] y captura KeyErrors para verificar si existe un elemento que agregaría un nuevo elemento al dict.


El argumento paso en operadores de corte. Por ejemplo:

a = [1,2,3,4,5] >>> a[::2] # iterate over the whole list in 2-increments [1,3,5]

El caso especial x[::-1] es un modismo útil para ''x invertido''.

>>> a[::-1] [5,4,3,2,1]


La sintaxis de for ... else (ver http://docs.python.org/ref/for.html )

for i in foo: if i == 0: break else: print("i was never 0")

El bloque "else" se ejecutará normalmente al final del bucle for, a menos que se llame a break.

El código anterior podría ser emulado de la siguiente manera:

found = False for i in foo: if i == 0: found = True break if not found: print("i was never 0")


ROT13 es una codificación válida para el código fuente, cuando utiliza la declaración de codificación correcta en la parte superior del archivo de código:

#!/usr/bin/env python # -*- coding: rot13 -*- cevag "Uryyb fgnpxbiresybj!".rapbqr("rot13")


Si no le gusta usar espacios en blanco para denotar ámbitos, puede usar el estilo C {} emitiendo:

from __future__ import braces


Envío de valores a funciones de generador . Por ejemplo teniendo esta función:

def mygen(): """Yield 5 until something else is passed back via send()""" a = 5 while True: f = (yield a) #yield a and possibly get f in return if f is not None: a = f #store the new value

Usted puede:

>>> g = mygen() >>> g.next() 5 >>> g.next() 5 >>> g.send(7) #we send this back to the generator 7 >>> g.next() #now it will yield 7 until we send something else 7


Asignación condicional

x = 3 if (y == 1) else 2

Hace exactamente lo que suena: "asigna 3 a x si y es 1, de lo contrario asigna 2 a x". Tenga en cuenta que los parens no son necesarios, pero me gustan por su legibilidad. También puedes encadenarlo si tienes algo más complicado:

x = 3 if (y == 1) else 2 if (y == -1) else 1

Aunque en un cierto punto, va un poco demasiado lejos.

Tenga en cuenta que puede usar if ... else en cualquier expresión. Por ejemplo:

(func1 if y == 1 else func2)(arg1, arg2)

Aquí se llamará func1 si y es 1 y func2, de lo contrario. En ambos casos se llamará a la función correspondiente con los argumentos arg1 y arg2.

Análogamente, lo siguiente también es válido:

x = (class1 if y == 1 else class2)(arg1, arg2)

donde class1 y class2 son dos clases.


Completar la ficha de intérprete interactivo

try: import readline except ImportError: print "Unable to load readline module." else: import rlcompleter readline.parse_and_bind("tab: complete") >>> class myclass: ... def function(self): ... print "my function" ... >>> class_instance = myclass() >>> class_instance.<TAB> class_instance.__class__ class_instance.__module__ class_instance.__doc__ class_instance.function >>> class_instance.f<TAB>unction()

También deberá establecer una variable de entorno PYTHONSTARTUP.


Creando objetos generadores.

Si tú escribes

x=(n for n in foo if bar(n))

Puedes sacar el generador y asignarlo a x. Ahora significa que puedes hacer

for n in x:

La ventaja de esto es que no necesita almacenamiento intermedio, que necesitaría si lo hiciera

x = [n for n in foo if bar(n)]

En algunos casos, esto puede llevar a una aceleración significativa.

Puede adjuntar muchas declaraciones if al final del generador, básicamente replicando anidados para bucles:

>>> n = ((a,b) for a in range(0,2) for b in range(4,6)) >>> for i in n: ... print i (0, 4) (0, 5) (1, 4) (1, 5)


Decoradores

Decorators permiten ajustar una función o método en otra función que puede agregar funcionalidad, modificar argumentos o resultados, etc. Usted escribe a los decoradores una línea sobre la definición de la función, comenzando con un signo "at" (@).

El ejemplo muestra un decorador print_args que imprime los argumentos de la función decorada antes de llamarla:

>>> def print_args(function): >>> def wrapper(*args, **kwargs): >>> print ''Arguments:'', args, kwargs >>> return function(*args, **kwargs) >>> return wrapper >>> @print_args >>> def write(text): >>> print text >>> write(''foo'') Arguments: (''foo'',) {} foo


Expresiones regulares legibles

En Python, puede dividir una expresión regular en varias líneas, nombrar sus coincidencias e insertar comentarios.

Ejemplo de sintaxis detallada (desde Dive into Python ):

>>> pattern = """ ... ^ # beginning of string ... M{0,4} # thousands - 0 to 4 M''s ... (CM|CD|D?C{0,3}) # hundreds - 900 (CM), 400 (CD), 0-300 (0 to 3 C''s), ... # or 500-800 (D, followed by 0 to 3 C''s) ... (XC|XL|L?X{0,3}) # tens - 90 (XC), 40 (XL), 0-30 (0 to 3 X''s), ... # or 50-80 (L, followed by 0 to 3 X''s) ... (IX|IV|V?I{0,3}) # ones - 9 (IX), 4 (IV), 0-3 (0 to 3 I''s), ... # or 5-8 (V, followed by 0 to 3 I''s) ... $ # end of string ... """ >>> re.search(pattern, ''M'', re.VERBOSE)

Ejemplo de denominación de coincidencias (del CÓMO de expresiones regulares )

>>> p = re.compile(r''(?P<word>/b/w+/b)'') >>> m = p.search( ''(((( Lots of punctuation )))'' ) >>> m.group(''word'') ''Lots''

También puede escribir verbalmente una expresión regular sin usar re.VERBOSE gracias a la concatenación de cadenas literales.

>>> pattern = ( ... "^" # beginning of string ... "M{0,4}" # thousands - 0 to 4 M''s ... "(CM|CD|D?C{0,3})" # hundreds - 900 (CM), 400 (CD), 0-300 (0 to 3 C''s), ... # or 500-800 (D, followed by 0 to 3 C''s) ... "(XC|XL|L?X{0,3})" # tens - 90 (XC), 40 (XL), 0-30 (0 to 3 X''s), ... # or 50-80 (L, followed by 0 to 3 X''s) ... "(IX|IV|V?I{0,3})" # ones - 9 (IX), 4 (IV), 0-3 (0 to 3 I''s), ... # or 5-8 (V, followed by 0 to 3 I''s) ... "$" # end of string ... ) >>> print pattern "^M{0,4}(CM|CD|D?C{0,3})(XC|XL|L?X{0,3})(IX|IV|V?I{0,3})$"


Función desempaquetando argumento

Puede descomprimir una lista o un diccionario como argumentos de función utilizando * y ** .

Por ejemplo:

def draw_point(x, y): # do some magic point_foo = (3, 4) point_bar = {''y'': 3, ''x'': 2} draw_point(*point_foo) draw_point(**point_bar)

Atajo muy útil ya que las listas, tuplas y dados son ampliamente utilizados como contenedores.


Gestores de contexto y la declaración " with "

Introducido en PEP 343 , un administrador de contexto es un objeto que actúa como un contexto de tiempo de ejecución para un conjunto de declaraciones.

Dado que la función utiliza nuevas palabras clave, se presenta gradualmente: está disponible en Python 2.5 a través de la directiva __future__ . Python 2.6 y superior (incluido Python 3) lo tiene disponible de forma predeterminada.

He usado mucho la declaración "con" porque creo que es una construcción muy útil, aquí hay una demostración rápida:

from __future__ import with_statement with open(''foo.txt'', ''w'') as f: f.write(''hello!'')

Lo que está sucediendo aquí detrás de la escena, es que la declaración "with" llama a los __enter__ especiales __enter__ y __exit__ en el objeto de archivo. Los detalles de la excepción también se pasan a __exit__ si se __exit__ una excepción desde el cuerpo de la declaración, lo que permite que la gestión de excepciones ocurra allí.

Lo que esto hace por usted en este caso particular es que garantiza que el archivo se cierra cuando la ejecución queda fuera del alcance del conjunto de aplicaciones, independientemente de si esto ocurre normalmente o si se produjo una excepción. Es básicamente una forma de abstraer el código común de manejo de excepciones.

Otros casos de uso comunes para esto incluyen el bloqueo con subprocesos y transacciones de base de datos.


Intercambio de valor en el lugar

>>> a = 10 >>> b = 5 >>> a, b (10, 5) >>> a, b = b, a >>> a, b (5, 10)

El lado derecho de la asignación es una expresión que crea una tupla nueva. El lado izquierdo de la asignación desempaqueta de inmediato esa tupla (sin referencia) a los nombres a y b .

Después de la asignación, la nueva tupla no tiene referencia y está marcada para la recolección de basura, y los valores vinculados a y b se han intercambiado.

Como se señaló en la sección de tutoriales de Python sobre estructuras de datos ,

Tenga en cuenta que la asignación múltiple es solo una combinación de empaquetamiento de tuplas y desempaquetado de secuencias.


Mensajes principales :)

import this # btw look at this module''s source :)

De-cyphered :

El zen de Python, por Tim Peters

Lo bello es mejor que lo feo.
Explícito es mejor que implícito.
Lo simple es mejor que lo complejo.
Complejo es mejor que complicado.
Plano es mejor que anidado.
Lo escaso es mejor que lo denso.
La legibilidad cuenta.
Los casos especiales no son lo suficientemente especiales para romper las reglas.
Aunque la practicidad supera la pureza.
Los errores nunca deben pasar en silencio.
A menos que sea silenciado explícitamente.
Ante la ambigüedad, rechace la tentación de adivinar. Debe haber una, y preferiblemente solo una, obvia forma de hacerlo.
Aunque esa forma puede no ser obvia al principio a menos que seas holandés.
Ahora es mejor que nunca.
Aunque nunca es mejor que el derecho.ahora.
Si la implementación es difícil de explicar, es una mala idea.
Si la implementación es fácil de explicar, puede ser una buena idea.
Los espacios de nombres son una gran idea, ¡hagamos más de ellos!


Obtenga el árbol de análisis de expresiones regulares de python para depurar su expresión regular.

Las expresiones regulares son una gran característica de python, pero depurarlas puede ser una molestia, y es muy fácil equivocarse con una expresión regular.

Afortunadamente, python puede imprimir el árbol de análisis de expresiones regulares, pasando la bandera no documentada, experimental y oculta re.DEBUG (en realidad, 128) a re.compile .

>>> re.compile("^/[font(?:=(?P<size>[-+][0-9]{1,2}))?/](.*?)[/font]", re.DEBUG) at at_beginning literal 91 literal 102 literal 111 literal 110 literal 116 max_repeat 0 1 subpattern None literal 61 subpattern 1 in literal 45 literal 43 max_repeat 1 2 in range (48, 57) literal 93 subpattern 2 min_repeat 0 65535 any None in literal 47 literal 102 literal 111 literal 110 literal 116

Una vez que entienda la sintaxis, puede detectar sus errores. Allí podemos ver que olvidé escapar de [] en [/font] .

Por supuesto, puede combinarlo con las banderas que desee, como las expresiones regulares comentadas:

>>> re.compile(""" ^ # start of a line /[font # the font tag (?:=(?P<size> # optional [font=+size] [-+][0-9]{1,2} # size specification ))? /] # end of tag (.*?) # text between the tags /[/font/] # end of the tag """, re.DEBUG|re.VERBOSE|re.DOTALL)


Re-aumento de excepciones :

# Python 2 syntax try: some_operation() except SomeError, e: if is_fatal(e): raise handle_nonfatal(e) # Python 3 syntax try: some_operation() except SomeError as e: if is_fatal(e): raise handle_nonfatal(e)

La declaración de "aumento" sin argumentos dentro de un controlador de errores le dice a Python que vuelva a elevar la excepción con el rastreo original intacto , permitiéndole decir "oh, lo siento, lo siento, no quise captar eso, lo siento, lo siento. "

Si desea imprimir, almacenar o jugar con el rastreo original, puede obtenerlo con sys.exc_info (), e imprimirlo como lo haría Python con el módulo ''rastreo''.


Sobrecarga del operador para el setbuiltin:

>>> a = set([1,2,3,4]) >>> b = set([3,4,5,6]) >>> a | b # Union {1, 2, 3, 4, 5, 6} >>> a & b # Intersection {3, 4} >>> a < b # Subset False >>> a - b # Difference {1, 2} >>> a ^ b # Symmetric Difference {1, 2, 5, 6}

Más detalles de la referencia de la biblioteca estándar: Establecer tipos


Tenga cuidado con los argumentos por defecto mutables

>>> def foo(x=[]): ... x.append(1) ... print x ... >>> foo() [1] >>> foo() [1, 1] >>> foo() [1, 1, 1]

En su lugar, debe usar un valor de centinela que indique "no dado" y reemplazarlo con el mutable que desee como predeterminado:

>>> def foo(x=None): ... if x is None: ... x = [] ... x.append(1) ... print x >>> foo() [1] >>> foo() [1]



iter () puede tomar un argumento llamable

Por ejemplo:

def seek_next_line(f): for c in iter(lambda: f.read(1),''/n''): pass

La función iter(callable, until_value) llama repetidamente a callable y produce su resultado hasta que se devuelve el valor until_value .


Comprensión de listas anidadas y expresiones generadoras:

[(i,j) for i in range(3) for j in range(i) ] ((i,j) for i in range(4) for j in range(i) )

Estos pueden reemplazar enormes trozos de código de bucle anidado.


Excepción de la cláusula else :

try: put_4000000000_volts_through_it(parrot) except Voom: print "''E''s pining!" else: print "This parrot is no more!" finally: end_sketch()

El uso de la cláusula else es mejor que agregar código adicional a la cláusula try porque evita la captura accidental de una excepción que no fue generada por el código protegido por la declaración try ... except.

Consulte http://docs.python.org/tut/node10.html


Para agregar más módulos de Python (especialmente los de terceros), la mayoría de las personas parece usar las variables de entorno PYTHONPATH o agregan enlaces simbólicos o directorios en los directorios de sus paquetes de sitio. Otra forma, es usar archivos * .pth. Aquí está la explicación oficial del documento de python:

"La forma más conveniente [para modificar la ruta de búsqueda de python] es agregar un archivo de configuración de ruta a un directorio que ya esté en la ruta de Python, generalmente al directorio ... / site-packages /. Los archivos de configuración de ruta tienen una extensión de .pth , y cada línea debe contener una única ruta que se agregará a sys.path. (Debido a que las nuevas rutas se agregan a sys.path, los módulos de los directorios agregados no anularán los módulos estándar. Esto significa que no puede usar este mecanismo para instalar versiones fijas de módulos estándar.) "