5 años de Python, 10 habilidades de Python resumidas

Hoy voy a compartir contigo 10 tips de desarrollo de Python que suelo organizar y son muy útiles. Los contenidos son los siguientes:

Vale la pena mencionar que estos 10 consejos están incluidos en la "Guía de Python Black Magic" escrita por mí.

1. ¿Cómo ver el código fuente en estado de ejecución?

Ver el código fuente de la función, usualmente usamos IDE para completar.

Por ejemplo, en PyCharm, puede Ctrl + clic del mouse para ingresar el código fuente de la función.

¿Qué pasa si no hay IDE?

Cuando queremos usar una función, ¿cómo sabemos qué parámetros necesita recibir la función?

Cuando tenemos un problema al usar una función, ¿cómo podemos solucionar el problema leyendo el código fuente?

En este momento, podemos usar inspeccionar en lugar de IDE para ayudarlo a lograr estas cosas

# demo.py
import inspect


def add(x, y):
    return x + y

print("===================")
print(inspect.getsource(add))

Los resultados son los siguientes

$ python demo.py
===================
def add(x, y):
    return x + y

2. ¿Cómo desactivar la correlación automática de contexto de excepciones?

Cuando maneja una excepción, debido a un manejo inadecuado u otros problemas, cuando se lanza otra excepción nuevamente, la excepción arrojada también llevará la información de la excepción original.

Así.

try:
    print(1 / 0)
except Exception as exc:
    raise RuntimeError("Something bad happened")

Se pueden ver dos mensajes de excepción en la salida

Traceback (most recent call last):
  File "demo.py", line 2, in <module>
    print(1 / 0)
ZeroDivisionError: division by zero

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "demo.py", line 4, in <module>
    raise RuntimeError("Something bad happened")
RuntimeError: Something bad happened

Si se lanza una excepción en el controlador de excepciones o en un bloque final, de forma predeterminada, el mecanismo de excepción implícitamente funcionará para las nuevas __context__propiedades anormales adicionales de la anomalía anterior . Este es el contexto de excepción de correlación automática que Python activa de forma predeterminada.

Si desea controlar este contexto usted mismo, puede agregar una palabra clave from (la fromsintaxis tendrá una restricción, es decir, la segunda expresión debe ser otra clase o instancia de excepción) para indicar qué excepción es causada directamente por su nueva excepción de.

try:
    print(1 / 0)
except Exception as exc:
    raise RuntimeError("Something bad happened") from exc

La salida es la siguiente

Traceback (most recent call last):
  File "demo.py", line 2, in <module>
    print(1 / 0)
ZeroDivisionError: division by zero

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "demo.py", line 4, in <module>
    raise RuntimeError("Something bad happened") from exc
RuntimeError: Something bad happened

Por supuesto, también puede with_traceback()establecer las __context__propiedades de contexto para la excepción a través de métodos , que también pueden tracebackmostrar mejor la información de la excepción.

try:
    print(1 / 0)
except Exception as exc:
    raise RuntimeError("bad thing").with_traceback(exc)

Finalmente, ¿qué pasa si quiero desactivar completamente este mecanismo para correlacionar automáticamente contextos de excepción? ¿qué más podemos hacer?

Se puede utilizar raise...from None. En el ejemplo siguiente, no hay ninguna excepción original.

$ cat demo.py
try:
    print(1 / 0)
except Exception as exc:
    raise RuntimeError("Something bad happened") from None
$
$ python demo.py
Traceback (most recent call last):
  File "demo.py", line 4, in <module>
    raise RuntimeError("Something bad happened") from None
RuntimeError: Something bad happened
(PythonCodingTime)

3. La forma más rápida de ver la ruta de búsqueda de paquetes

Cuando usa la importación para importar un paquete o módulo, Python buscará en algunos directorios, y estos directorios tienen un orden de prioridad, y la gente normal usará sys.path para verlo.

>>> import sys
>>> from pprint import pprint   
>>> pprint(sys.path)
['',
 '/usr/local/Python3.7/lib/python37.zip',
 '/usr/local/Python3.7/lib/python3.7',
 '/usr/local/Python3.7/lib/python3.7/lib-dynload',
 '/home/wangbm/.local/lib/python3.7/site-packages',
 '/usr/local/Python3.7/lib/python3.7/site-packages']
>>> 

¿Hay una manera mas rápida?

¿Hay alguna manera de que ni siquiera necesite ingresar al modo de consola?

Puede pensar en esto, pero es esencialmente lo mismo que el anterior.

[wangbm@localhost ~]$ python -c "print('\n'.join(__import__('sys').path))"

/usr/lib/python2.7/site-packages/pip-18.1-py2.7.egg
/usr/lib/python2.7/site-packages/redis-3.0.1-py2.7.egg
/usr/lib64/python27.zip
/usr/lib64/python2.7
/usr/lib64/python2.7/plat-linux2
/usr/lib64/python2.7/lib-tk
/usr/lib64/python2.7/lib-old
/usr/lib64/python2.7/lib-dynload
/home/wangbm/.local/lib/python2.7/site-packages
/usr/lib64/python2.7/site-packages
/usr/lib64/python2.7/site-packages/gtk-2.0
/usr/lib/python2.7/site-packages

Lo que quiero presentar aquí es más conveniente que los dos métodos anteriores, se puede resolver una línea de comando

[wangbm@localhost ~]$ python3 -m site
sys.path = [
    '/home/wangbm',
    '/usr/local/Python3.7/lib/python37.zip',
    '/usr/local/Python3.7/lib/python3.7',
    '/usr/local/Python3.7/lib/python3.7/lib-dynload',
    '/home/wangbm/.local/lib/python3.7/site-packages',
    '/usr/local/Python3.7/lib/python3.7/site-packages',
]
USER_BASE: '/home/wangbm/.local' (exists)
USER_SITE: '/home/wangbm/.local/lib/python3.7/site-packages' (exists)
ENABLE_USER_SITE: True

En el resultado, puede encontrar que la ruta en esta columna es más completa que sys.path, que contiene el directorio del entorno del usuario.

4. Escriba el bucle for anidado como una sola línea

A menudo usamos el siguiente código de bucle for anidado

list1 = range(1,3)
list2 = range(4,6)
list3 = range(7,9)
for item1 in list1:
    for item2 in list2:
          for item3 in list3:
              print(item1+item2+item3)

Aquí hay solo tres bucles for, en la codificación real, puede haber más capas.

Este código es muy poco legible, mucha gente no quiere escribirlo, pero no hay mejor manera de escribirlo.

Aquí hay una forma de escribir que uso a menudo, usando la biblioteca itertools para lograr un código más elegante y legible.

from itertools import product
list1 = range(1,3)
list2 = range(4,6)
list3 = range(7,9)
for item1,item2,item3 in product(list1, list2, list3):
    print(item1+item2+item3)

La salida es la siguiente

$ python demo.py
12
13
13
14
13
14
14
15

5. Cómo utilizar la impresión para imprimir el registro

A los principiantes les gusta usar la impresión para depurar el código y registrar el proceso en ejecución del programa.

Sin embargo, la impresión solo enviará el contenido al terminal y no se puede conservar en el archivo de registro, lo que no conduce a la resolución de problemas.

Si está interesado en utilizar la impresión para depurar el código (aunque esta no es la mejor práctica) y registrar el proceso en ejecución del programa, entonces el uso de la impresión que se describe a continuación puede resultarle útil.

Como función, imprimir en Python 3 puede recibir más parámetros, por lo que la función se vuelve más poderosa. Especifique algunos parámetros para generar el contenido de la impresión en el archivo de registro

el código se muestra a continuación:

>>> with open('test.log', mode='w') as f:
...     print('hello, python', file=f, flush=True)
>>> exit()

$ cat test.log
hello, python

6. Cómo calcular rápidamente el tiempo de ejecución de la función

Para calcular el tiempo de ejecución de una función, puede hacerlo así

import time

start = time.time()

# run the function

end = time.time()
print(end-start)

Verá, ha escrito algunas líneas de código para calcular el tiempo de ejecución de la función.

¿Hay alguna forma de calcular este tiempo de ejecución de manera más conveniente?

Tener.

Hay un módulo incorporado llamado timeit

Úselo con solo una línea de código

import time
import timeit

def run_sleep(second):
    print(second)
    time.sleep(second)

# 只用这一行
print(timeit.timeit(lambda :run_sleep(2), number=5))

Los resultados son los siguientes

2
2
2
2
2
10.020059824

7. Utilice el mecanismo de almacenamiento en caché integrado para mejorar la eficiencia

El almacenamiento en caché es un método de procesamiento que guarda datos cuantitativos para satisfacer las necesidades de adquisición posteriores y tiene como objetivo acelerar la adquisición de datos.

El proceso de generación de datos puede requerir operaciones como el cálculo, la regularización y la adquisición remota. Si la misma pieza de datos debe usarse varias veces, la regeneración cada vez será una pérdida de tiempo. Por lo tanto, si los datos obtenidos mediante operaciones como cálculos o solicitudes remotas se almacenan en caché, se acelerarán los requisitos de adquisición de datos posteriores.

Para cumplir con este requisito, Python 3.2 + nos proporciona un mecanismo que se puede implementar fácilmente sin necesidad de escribir dicho código lógico.

Este mecanismo se implementa en el decorador lru_cache del módulo functool.

@functools.lru_cache(maxsize=None, typed=False)

Interpretación de parámetros:

  • maxsize: cuántos resultados de llamadas de esta función se pueden almacenar en caché como máximo. Si es Ninguno, no hay límite. Cuando se establece en una potencia de 2, el rendimiento es mejor
  • typed: si es True, las llamadas con diferentes tipos de parámetros se almacenarán en caché por separado.

por ejemplo

from functools import lru_cache

@lru_cache(None)
def add(x, y):
    print("calculating: %s + %s" % (x, y))
    return x + y

print(add(1, 2))
print(add(1, 2))
print(add(2, 3))

El resultado es el siguiente, puede ver que la segunda llamada no ejecuta realmente el cuerpo de la función, sino que devuelve directamente el resultado en la caché

calculating: 1 + 2
3
3
calculating: 2 + 3
5

La siguiente es una secuencia clásica de Fibonacci: cuando especificas una n mayor, habrá muchos cálculos repetidos.

def fib(n):
    if n < 2:
        return n
    return fib(n - 2) + fib(n - 1)

El tiempo que se introdujo en el punto 6 ahora se puede utilizar para probar cuánta eficiencia se puede mejorar.

Sin lru_cache, el tiempo de ejecución es de 31 segundos

import timeit

def fib(n):
    if n < 2:
        return n
    return fib(n - 2) + fib(n - 1)



print(timeit.timeit(lambda :fib(40), number=1))
# output: 31.2725698948

Después de usar lru_cache, la velocidad de ejecución es demasiado rápida, por lo que ajusté el valor de n de 30 a 500, pero aún así, el tiempo de ejecución es de solo 0,0004 segundos. El aumento de velocidad es muy significativo.

import timeit
from functools import lru_cache

@lru_cache(None)
def fib(n):
    if n < 2:
        return n
    return fib(n - 2) + fib(n - 1)

print(timeit.timeit(lambda :fib(500), number=1))
# output: 0.0004921059880871326

8. Consejos para ejecutar código antes de salir del programa

Usando atexit este módulo incorporado, puede registrar y salir de funciones fácilmente.

No importa dónde provoque el bloqueo del programa, se ejecutarán las funciones que registró.

Los ejemplos son los siguientes

Si la clean()función tiene parámetros, puede llamarla directamente sin usar el decorador atexit.register(clean_1, 参数1, 参数2, 参数3='xxx').

Quizás tenga otras formas de lidiar con este tipo de demanda, pero definitivamente es más elegante que no usar atexit, es conveniente y fácil de expandir.

Pero el uso de atexit todavía tiene algunas limitaciones, como:

  • Si el programa es cancelado por una señal del sistema que no ha procesado, la función registrada no se puede ejecutar normalmente.
  • Si se produce un error interno grave de Python, la función que registró no se puede ejecutar normalmente.
  • Si lo llama manualmente os._exit(), la función que registró no se puede ejecutar normalmente.

9. Implementar una llamada diferida similar a un aplazamiento

En Golang, existe un mecanismo para retrasar la llamada, la palabra clave es aplazar, como en el siguiente ejemplo

import "fmt"

func myfunc() {
    fmt.Println("B")
}

func main() {
    defer myfunc()
    fmt.Println("A")
}

El resultado es el siguiente. La llamada de myfunc se completará un paso antes de que la función regrese. Incluso si escribe la llamada de myfunc en la primera línea de la función, esta es una llamada retrasada.

A
B

Entonces, ¿existe tal mecanismo en Python?

Por supuesto que los hay, pero no es tan simple como Golang.

Se puede usar en los administradores de contexto de Python para lograr este efecto

import contextlib

def callback():
    print('B')

with contextlib.ExitStack() as stack:
    stack.callback(callback)
    print('A')

La salida es la siguiente

A
B

10. Cómo leer gigabytes de archivos en flujo

Utilice con ... open ... para leer datos de un archivo, que es una operación con la que todos los desarrolladores de Python están muy familiarizados.

Pero si lo usa incorrectamente, también causará muchos problemas.

Por ejemplo, cuando usa la función de lectura, Python realmente cargará todo el contenido del archivo en la memoria a la vez. Si el archivo tiene 10 G o más, su computadora consumirá una gran cantidad de memoria.

# 一次性读取
with open("big_file.txt", "r") as fp:
    content = fp.read()

Para este problema, podría pensar en usar readline como generador para devolver línea por línea.

def read_from_file(filename):
    with open(filename, "r") as fp:
        yield fp.readline()

Pero si el contenido de este archivo es una línea, 10 G por línea, de hecho, aún leerá todo el contenido a la vez.

La solución más elegante es especificar que solo se lea un tamaño fijo de contenido cada vez que se utilice el método de lectura. Por ejemplo, en el siguiente código, solo se devuelven 8 kb cada vez.

def read_from_file(filename, block_size = 1024 * 8):
    with open(filename, "r") as fp:
        while True:
            chunk = fp.read(block_size)
            if not chunk:
                break

            yield chunk

El código anterior está funcionalmente bien, pero el código todavía parece inflado.

El código se puede optimizar con la ayuda de funciones parciales y funciones iter

from functools import partial

def read_from_file(filename, block_size = 1024 * 8):
    with open(filename, "r") as fp:
        for chunk in iter(partial(fp.read, block_size), ""):
            yield chunk

Supongo que te gusta

Origin blog.csdn.net/weixin_36338224/article/details/109341250
Recomendado
Clasificación