Una guía de mejores prácticas para la ingeniería de proyectos de Python

No existe una especificación unificada y un plan de gestión de proyectos para la ingeniería de Python, quizás porque el ascenso repentino de Python es relativamente corto y hay menos empresas para desarrollar proyectos a gran escala. Entonces, para ayudar a los estudiantes internos a resolver mejor los problemas de ingeniería de Python y compartir hábitos de desarrollo personal e ideas de administración de código, escribí este artículo.

gestión de dependencia

Antes de que se introdujeran PEP 518 y pyproject.toml. Un proyecto no puede decirle a una herramienta como pip qué herramientas de construcción necesita construir. Ahora setuptools tiene un parámetro setup_require para especificar lo que se requiere para construir el proyecto, pero no puede leer esa configuración a menos que tenga instalado setuptools, lo que significa que no puede declarar que necesita setuptools para leer la configuración en setuptools. La pregunta del huevo y la gallina es por qué herramientas como virtualenv instalan setuptools de forma predeterminada, y por qué pip siempre inyecta setuptools y wheel cuando ejecuta un setup.py, ya sea que lo instale explícitamente o no. Ni siquiera debería tratar de depender de una versión específica de las herramientas de configuración para construir su proyecto, porque no tiene forma de especificar una versión; tendrá que conformarse con lo que el usuario haya instalado.

Después de PEP 518, puede declarar sus herramientas de compilación y las versiones requeridas.

En el pasado, solíamos usar archivos como requirements.txt para guardar las dependencias requeridas por nuestro proyecto, pero no tiene una buena forma de distinguir las dependencias que necesitamos en el entorno de producción, el entorno de desarrollo y el entorno de prueba. debe dividirse en varios archivos, indique por separado que con algunas herramientas de compilación nuevas podemos resolver nuestro problema. Y el uso de requirements.txt solo no puede declarar la versión de Python, el entorno del sistema, etc. que necesitamos.

En el desarrollo de este proyecto interno, elegí PDM , que pretende ser la herramienta de administración de paquetes de Python de próxima generación. Nació originalmente como un interés personal. Si cree pipenvque o lo poetryusa es realmente bueno y no desea introducir un nuevo administrador de paquetes, entonces continúe y utilícelo; pero si encuentra algo que estas herramientas no admiten, entonces probablemente pueda encontrarlo en pdm.

Poetry también parece una buena opción. Al igual que Pipenv y PDM, Poetry es un entorno virtual de Python y una herramienta de gestión de dependencias. Además, también proporciona funciones de gestión de paquetes, como empaquetado y publicación. Puedes pensar en ello como un superconjunto de herramientas como Pipenv y Flit. Le permite usar Poetry para administrar tanto las bibliotecas de Python como los programas de Python.

Si está utilizando PDM o Poetry, cree una carpeta llamada .venv o similar en el directorio del proyecto a través de virtualenv. ¿Por qué recomiendo virtualenv en lugar de PEP582? Muchos sistemas se basan en una versión de python predeterminada. Si se usa PEP582, la versión de python que viene con el sistema se usará de manera predeterminada. De lo contrario, debemos generar adicionalmente un entorno virtual correspondiente a la versión de python requerida; seguido de Actualmente, vscode sí lo hace. no apoyarlo.

Podemos usar estas herramientas de compilación para separar las dependencias de nuestro entorno de desarrollo, entorno de prueba y entorno de producción.

También podemos agregar comandos adicionales para crear herramientas en pyproject.toml, como el comando de inicio para iniciar el servicio, el comando de prueba para probar, etc. Similar a lo que describen los scripts de PDM . Iniciar el servicio a través de la herramienta de compilación puede resolver efectivamente el problema de dónde se encuentra el paquete y obligar a que el directorio en ejecución de todos los paquetes sea el directorio raíz del proyecto.

estructura del proyecto

La estructura de proyecto recomendada es la siguiente:

Dockerfile

Este archivo se usa principalmente para crear imágenes para Docker. Se recomienda implementar a través de Docker cuando se implementa en un entorno de producción.

documentos

Una carpeta dedicada a guardar documentos.

LICENCIA

Si se trata de un proyecto de código abierto, este archivo generalmente se utiliza para colocar el protocolo de código abierto utilizado.

pyproject.toml

El archivo de configuración basado en la especificación PEP518 guarda la introducción del proyecto, la información de contacto del autor, los paquetes dependientes, las herramientas de construcción usadas, etc.

LÉAME.md

Se utiliza principalmente para documentos MarkDown de introducción de proyectos e instrucciones de uso.

{nombre del proyecto}

La carpeta donde se coloca el código del proyecto real, puede elegir cualquier nombre, pero debe asegurarse de que no duplique el nombre de otros paquetes de terceros de los que depende. La razón por la que no usa src es que será más conveniente cuando escriba casos de prueba o lo use como un paquete de python para otros proyectos.

{project_name}-stubs

El nombre del proyecto en {project_name}-stubs debe ser el mismo que el nombre de la carpeta anterior y luego empalmar -stubs. Por ejemplo, si el proyecto se llama andy, entonces esta carpeta se llama andy-stubs. Si su proyecto es una biblioteca de python, puede almacenar los archivos de descripción de tipo generados por mypy en esta carpeta. mypy leerá automáticamente la descripción del tipo en este proyecto, lo cual es conveniente para la evaluación del tipo. Consulte la documentación de mypy para obtener más información .

pruebas

Carpeta para pruebas unitarias, etc.

toxicología ini

archivo de configuración tox.

.gitignore

Se usa para decirle a git qué archivos deben ignorarse.

referencia del módulo

Los módulos de Python son una de las principales capas de abstracción, y posiblemente la más natural. La capa de abstracción permite dividir el código en diferentes partes, cada parte contiene datos y funciones relacionadas.

Por ejemplo, en un proyecto, una capa controla las interfaces relacionadas con la operación del usuario y la otra capa maneja las operaciones de datos subyacentes. La forma más natural de separar estas dos capas es reorganizar todas las interfaces funcionales en un archivo y encapsular todas las operaciones de bajo nivel en otro archivo. En este caso, el archivo de interfaz debe importar el archivo que encapsula las operaciones subyacentes, que se pueden realizar con instrucciones importy from ... import. Una vez que use la declaración de importación, puede usar este módulo. Puede ser un módulo integrado que incluya os y sys, o un módulo de terceros que se haya instalado, o un módulo dentro del proyecto.

Para cumplir con la guía de estilo, mantenga los nombres de los módulos cortos, use minúsculas y evite símbolos especiales como el punto (.) y el signo de interrogación (?). ¡ Tal my.spam.pynombre no debe usarse! Nombrarlo de esta manera interferirá con la funcionalidad de búsqueda de módulos de Python. En el caso de my.spam.py, Python espera myencontrar el archivo en la carpeta spam.py, lo cual no es el caso. Puede nombrar su módulo si lo desea my_spam.py, pero no se recomienda usar guiones bajos en los nombres de los módulos. Sin embargo, el uso de otros caracteres (espacios o guiones) en los nombres de los módulos evitará las importaciones (- es el operador de resta), así que trate de mantener los nombres de los módulos simples para que no tenga que separar las palabras. Lo más importante, no use el espacio de nombres de guión bajo, use submódulos en su lugar.

# OK
import library.plugin.foo
# not OK
import library.foo_plugin
复制代码

Además de las restricciones de nombres anteriores, no existen otros requisitos especiales para que los archivos de Python se conviertan en módulos, pero para usar este concepto de manera razonable y evitar problemas, debe comprender el mecanismo principal de importación. Específicamente, import modula declaración busca el archivo apropiado, es decir, modu.pyel archivo en el directorio de llamada, si existe. Si no se encuentra el archivo, el intérprete de Python busca recursivamente el archivo en la variable de entorno "PYTHONPATH" y, si aún no se encuentra, se lanza una excepción ImportError.

Una vez encontrado modu.py, el intérprete de Python ejecutará el módulo en un ámbito aislado. Se ejecutan todas las declaraciones de nivel superior, incluidas otras referencias. Las definiciones de métodos y clases se almacenarán en el diccionario del módulo. Las variables, los métodos y las clases de este módulo luego se exponen a las personas que llaman a través de espacios de nombres, un concepto central particularmente útil y poderoso en Python.

En muchos otros lenguajes, include fileel preprocesador usa directivas para tomar todo el código en un archivo y 'copiarlo' en el código de la persona que llama. Python es diferente: el código de inclusión se coloca de forma independiente en el espacio de nombres del módulo, lo que significa que, por lo general, no debe preocuparse de que el código incluido pueda causar efectos negativos, como la sobrecarga de métodos con el mismo nombre.

También es posible from modu import *emular un comportamiento más estándar utilizando formas especiales de la declaración de importación. Pero import *generalmente se considera una mala práctica. El código que se utiliza from modu import *es más difícil de leer y tiene una independencia de dependencia insuficiente. Úselo from modu import funcpara identificar el método que desea importar y colocarlo en el espacio de nombres global. es from modu import *mejor que porque indica explícitamente qué métodos se importan en el espacio de nombres global, y su import moduúnica ventaja es que requiere escribir menos cuando se usa el método más adelante.

import modu
[...]
x = modu.sqrt(4)
复制代码

En segundo lugar, si hace referencia al módulo de su propio proyecto, agregue su proyecto llamado my y el módulo llamado modu, entonces no se recomienda usarlo como from my import modureferencia, y se recomienda encarecidamente usarlo from . import modu.

En segundo lugar, se sugiere que si necesita referirse a algunas clases para otros módulos, __init__.pyexpóngalas en este módulo y agregue como para reemplazar la exportación en algunos idiomas.

from .config import Config as Config
复制代码

Y no se recomienda __init__.py  colocar una gran cantidad de código, solo se recomienda reemplazar la exportación.

Se agrega demasiado código __init__.pyy, a medida que crece la complejidad del proyecto, la estructura de directorios se vuelve más y más profunda y pueden aparecer subpaquetes y subpaquetes anidados más profundos. En este caso, la importación de un componente en un subpaquete anidado de varios niveles requiere la ejecución de todos los __init__.pyarchivos que se encuentran en la ruta. Si los módulos y subpaquetes del paquete no tienen requisitos de código compartido, es normal o incluso una buena práctica usar __init__.pyarchivos en blanco.

comprobación de tipo

Python es un lenguaje dinámico. Muchas veces es posible que no conozcamos el tipo de parámetro de la función o el tipo de valor devuelto. Es muy probable que algunos tipos no tengan un método específico. Después de escribir el código por un tiempo y mirar hacia atrás en el código, es probable que hayamos olvidado lo que escribimos.Que parametros necesita pasar la funcion y que tipo de resultado devolver, hay que leer el contenido especifico del codigo, lo que reduce la velocidad de lectura.El modulo de tipeo puede solucionar este problema muy bien.

Desde python3.5, PEP484 introdujo anotaciones de tipo (sugerencias de tipo) para python.

Mypy es un verificador de tipo estático en Python. Mypy tiene un sistema de tipos potente y fácil de usar con muchas características interesantes, como inferencia de tipos, genéricos, tipos invocables, tipos de tuplas, tipos de unión y subtipos estructurales. Se recomienda utilizar mypy como herramienta de verificación de tipos y cada método debe declarar claramente los parámetros, los tipos de parámetros y los tipos de valores devueltos.

def register(
    self, factory: Optional[PooledObjectFactory] = None, name: Optional[str] = None
) -> None:

	pass
复制代码

Si el parámetro o valor de retorno puede estar vacío, debe marcarse como Opcional o usar el tipo de sintaxis de 3.11 | Ninguno. Como PooledObjectFactory | Ninguno.

En vscode, puede instalar el complemento mypy, de modo que la verificación de tipos se pueda realizar directamente en vscode.

Comprobación de formato y estilo de código

Para ayudar a los desarrolladores a unificar el estilo del código, la comunidad de Python propuso el estilo de codificación del código PEP8, que no requiere que todos lo sigan.Python lanzó oficialmente una herramienta para verificar si el estilo del código se ajusta a PEP8, también llamado pep8.

Black se llama a sí mismo "El formateador de código intransigente".

Black afirma ser una herramienta de formateo de código Python intransigente. La razón por la que se vuelve "intransigente" es porque detecta estilos de código que no se ajustan a la especificación y formatea todo directamente por usted. No necesita confirmarlo en absoluto y toma decisiones por usted directamente. Y a cambio, Black ofrece velocidades rápidas. Black permite revisiones de código más rápidas al producir diferencias mínimas. El uso de Black es muy simple. Después de una instalación exitosa, se puede usar como otros comandos del sistema, solo especifique el archivo o directorio que desea formatear después del comando black.

En cierto sentido, una herramienta de inspección y formato de código de muy baja configurabilidad es mejor en el equipo que una que se puede personalizar en gran medida. Los IDE modernos generalmente brindan soporte para Black.

gestión de la configuración

Se recomienda colocar la configuración en la carpeta {project_name}/{project_name} y guardarla en formato yaml. La razón por la que no se usan formatos como toml es que si usa funciones de mapeo de configuración como k8s, no estará disponible y yaml puede ser compatible con otros sistemas.

Puede leer el archivo yaml donde se encuentra la configuración y deserializarlo en un objeto de configuración. Este objeto de configuración puede ser una clase de datos en python o una clase ordinaria, y cada campo de la configuración se declara arriba.

La configuración es algo que a menudo puede agregar y eliminar campos, y no deberíamos hacerlo de una manera similar a un dictado.

gestión de excepciones

Existen excepciones en casi todos los lenguajes de programación. La excepción puede señalar rápidamente los problemas en el programa, lo cual es conveniente para la resolución de problemas. Los desarrolladores también pueden generar excepciones personalizadas según la situación para indicar que el contenido esperado no coincide con el contenido real. Un buen diseño de excepciones y buenos hábitos de uso pueden mejorar la calidad del programa.

En la lógica, puede haber lógica que no cumpla con las expectativas y se generarán excepciones relacionadas. En este momento, al codificar, para el funcionamiento normal de la lógica, es necesario procesar la lógica y detectar excepciones.

Para capturar excepciones, use try...exceptbloques de código para envolver el código que necesita manejar las excepciones. expectCaptura el tipo de excepción especificado e ingresa la lógica del código correspondiente si ocurre. Para algunos no quieren manejar, lanzando raiseuna excepción.

Al realizar la captura, intente no capturar clases base de excepciones amplias, como Exception, sino excepciones específicas, como ValueError.

Cuando se manejan excepciones, si no se siguen lanzando excepciones, se debe ingresar la información de registro. A menos que sepa que no generar ninguna información no causará dificultad para cometer errores. Las excepciones de elementos deben ERRORterminar con . Similar a la denominación de excepción estándar.

prueba

Además del marco de prueba integrado en Python, hay muchos marcos de prueba de terceros y algunos marcos que no son de prueba también tienen marcos de prueba integrados. El propósito es agregar algunas características sobre la base del marco de prueba incorporado para que la escritura de pruebas sea más conveniente y el proceso de prueba sea más fluido.

Para facilitar que el marco de prueba encuentre casos de prueba, se deben seguir ciertas especificaciones al escribir pruebas:

  • Los módulos de prueba test_comienzan con
  • Los métodos de prueba deben test_comenzar con
  • El nombre de la clase de prueba debe Testcomenzar con

Las pruebas se colocan en la carpeta de pruebas.

Pytest agrega mucho azúcar sintáctico sobre la base de unittest para hacer que las pruebas sean más fáciles y flexibles. Y tiene una función de complemento para facilitar la integración de otras funciones.

Dado que Pytest es compatible con la mayoría de los otros marcos de prueba y también tiene funciones poderosas, se recomienda usar Pytest como el marco de prueba principal.

tox es una herramienta de línea de comandos de prueba y gestión de entornos virtuales de propósito general. tox nos permite personalizar múltiples entornos de python independientes y aislados en el mismo Host. Si su proyecto necesita ser compatible con múltiples versiones de python, se recomienda encarecidamente usarlo.

Supongo que te gusta

Origin blog.csdn.net/weixin_73136678/article/details/128794036
Recomendado
Clasificación