una programacion poo otra orientada objetos metodo llamar listas lista importar con como clases clase python oop class-design

programacion - ¿Cómo diseño una clase en Python?



poo python 3 (6)

He tenido una ayuda realmente increíble en mis preguntas anteriores para detectar patas y dedos dentro de una pata , pero todas estas soluciones solo funcionan para una medición a la vez.

Ahora tengo datos que consisten en:

  • unos 30 perros;
  • cada uno tiene 24 medidas (divididas en varios subgrupos);
  • cada medición tiene al menos 4 contactos (uno para cada pata) y
    • cada contacto se divide en 5 partes y
    • tiene varios parámetros, como tiempo de contacto, ubicación, fuerza total, etc.

Obviamente, pegar todo en un objeto grande no lo va a cortar, así que pensé que necesitaba usar clases en lugar de las funciones actuales. Pero aunque he leído el capítulo de Learning Python sobre clases, no puedo aplicarlo a mi propio código ( enlace GitHub )

También siento que es bastante extraño procesar todos los datos cada vez que quiero obtener algo de información. Una vez que conozco la ubicación de cada pata, no hay ninguna razón para que vuelva a calcular esto. Además, quiero comparar todas las patas del mismo perro para determinar qué contacto pertenece a cada pata (frontal / posterior, izquierda / derecha). Esto se convertiría en un desastre si continúo usando solo funciones.

Así que ahora estoy buscando consejos sobre cómo crear clases que me permitan procesar mis datos ( enlace a los datos comprimidos de un perro ) de manera sensata.


Cómo diseñar una clase.

  1. Escribe las palabras. Usted comenzó a hacer esto. Algunas personas no lo hacen y se preguntan por qué tienen problemas.

  2. Expande tu conjunto de palabras en declaraciones simples sobre lo que harán estos objetos. Es decir, anote los diversos cálculos que hará sobre estas cosas. Su lista corta de 30 perros, 24 medidas, 4 contactos y varios "parámetros" por contacto es interesante, pero solo una parte de la historia. Sus "ubicaciones de cada pata" y "comparar todas las patas del mismo perro para determinar qué contacto pertenece a cada pata" son el siguiente paso en el diseño de objetos.

  3. Subraye los sustantivos. Seriamente. Algunas personas debaten el valor de esto, pero me parece que para los desarrolladores primerizos de OO ayuda. Subraye los sustantivos.

  4. Revisa los sustantivos. Los sustantivos genéricos como "parámetro" y "medición" deben reemplazarse por sustantivos específicos y concretos que se apliquen a su problema en el dominio de su problema. Los detalles ayudan a aclarar el problema. Los genéricos simplemente eluden los detalles.

  5. Para cada sustantivo ("contacto", "pata", "perro", etc.) anote los atributos de ese sustantivo y las acciones en las que ese objeto se engancha. No atajos esto. Cada atributo "El conjunto de datos contiene 30 perros", por ejemplo, es importante.

  6. Para cada atributo, identifique si esto es una relación con un sustantivo definido, o algún otro tipo de datos "primitivos" o "atómicos" como una cadena o un flotador o algo irreductible.

  7. Para cada acción u operación, debe identificar qué sustantivo tiene la responsabilidad y qué sustantivos simplemente participan. Es una cuestión de "mutabilidad". Algunos objetos se actualizan, otros no. Los objetos mutables deben ser los únicos responsables de sus mutaciones.

  8. En este punto, puede comenzar a transformar sustantivos en definiciones de clase. Algunos sustantivos colectivos son listas, diccionarios, tuplas, conjuntos o miniaturas con nombre, y no es necesario que trabajes mucho. Otras clases son más complejas, ya sea debido a datos derivados complejos o debido a alguna actualización / mutación que se realiza.

No olvides probar cada clase en aislamiento usando unittest.

Además, no hay ninguna ley que diga que las clases deben ser mutables. En su caso, por ejemplo, casi no tiene datos mutables. Lo que tiene son datos derivados, creados por funciones de transformación del conjunto de datos de origen.


Después de rozar su código vinculado, me parece que es mejor que no diseñe una clase Dog en este punto. Por el contrario, debe usar Pandas y dataframes . Un marco de datos es una tabla con columnas. Tu dataframe tendría columnas como: dog_id , contact_part , contact_time , contact_location , etc. Pandas utiliza Numpy arrays detrás de escena, y tiene muchos métodos de conveniencia para ti:

  • Seleccione un perro por ejemplo: my_measurements[''dog_id'']==''Charly''
  • guardar los datos: my_measurements.save(''filename.pickle'')
  • Considere usar pandas.read_csv() lugar de leer manualmente los archivos de texto.

Escribir los sustantivos, verbos, adjetivos es un gran enfoque, pero prefiero pensar en el diseño de clase como preguntando qué datos deberían ocultarse .

Imagine que tiene un objeto Query y un objeto Database :

El objeto Query te ayudará a crear y almacenar una consulta - almacenar, aquí está la clave, ya que una función podría ayudarte a crear una igual de fácil. Tal vez podrías quedarte: Query().select(''Country'').from_table(''User'').where(''Country == "Brazil"'') . No importa exactamente la sintaxis: ¡ese es su trabajo! - La clave es el objeto que te ayuda a ocultar algo , en este caso los datos necesarios para almacenar y generar una consulta. El poder del objeto proviene de la sintaxis de su uso (en este caso, un encadenamiento inteligente) y no necesita saber qué almacena para que funcione. Si se hace bien, el objeto Query podría generar consultas para más de una base de datos. Internamente almacenaría un formato específico, pero podría convertirlo fácilmente a otros formatos al generar resultados (Postgres, MySQL, MongoDB).

Ahora pensemos en el objeto Database . ¿Qué esconde y almacena esto? ¡Claramente no puede almacenar el contenido completo de la base de datos, ya que es por eso que tenemos una base de datos! Entonces, ¿cuál es el punto? El objetivo es ocultar cómo funciona la base de datos de las personas que usan el objeto de la Database . Las buenas clases simplificarán el razonamiento al manipular el estado interno. Para este objeto de Database , puede ocultar cómo funcionan las llamadas de red, o realizar consultas o actualizaciones por lotes, o proporcionar una capa de almacenamiento en caché.

El problema es que este objeto de Database es ENORME. Representa cómo acceder a una base de datos, por lo que bajo las coberturas podría hacer cualquier cosa y todo. Evidentemente, la conexión en red, el almacenamiento en caché y el procesamiento por lotes son bastante difíciles de manejar dependiendo de su sistema, por lo que ocultarlos sería muy útil. Pero, como muchas personas notarán, una base de datos es increíblemente compleja, y cuanto más lejos de las llamadas de base de datos que obtienes, más difícil es ajustar el rendimiento y comprender cómo funcionan las cosas.

Esta es la compensación fundamental de OOP. Si elige la abstracción correcta, simplifica la codificación (Cadena, Matriz, Diccionario), si selecciona una abstracción que es demasiado grande (Base de datos, Administrador de correo electrónico, Administrador de redes), puede ser demasiado compleja para comprender realmente cómo funciona, o qué esperar. El objetivo es ocultar la complejidad , pero es necesaria cierta complejidad. Una buena regla empírica es comenzar evitando los objetos Manager , y en su lugar crear clases que sean como structs ; todo lo que hacen es contener datos, con algunos métodos auxiliares para crear / manipular los datos para facilitarle la vida. Por ejemplo, en el caso de EmailManager comience con una función llamada sendEmail que toma un objeto Email . Este es un punto de partida simple y el código es muy fácil de entender.

En cuanto a su ejemplo, piense qué datos deben estar juntos para calcular lo que está buscando. Si quisieras saber qué tan lejos está caminando un animal, por ejemplo, podrías tener las AnimalStep y AnimalTrip (colección de AnimalSteps). Ahora que cada Viaje tiene todos los datos del Paso, entonces debería ser capaz de descubrir cosas al respecto, quizás AnimalTrip.calculateDistance() tenga sentido.


La idea general del diseño OO es hacer que su código se relacione con su problema, de modo que cuando, por ejemplo, quiere los primeros pasos de un perro, haga algo como:

dog.footstep(0)

Ahora, puede ser que para su caso necesite leer en su archivo de datos brutos y calcular las ubicaciones de los pasos. Todo esto podría ocultarse en la función footstep () para que solo ocurra una vez. Algo como:

class Dog: def __init__(self): self._footsteps=None def footstep(self,n): if not self._footsteps: self.readInFootsteps(...) return self._footsteps[n]

[Esto es ahora una especie de patrón de almacenamiento en caché. La primera vez que va y lee los datos de las pisadas, las veces subsiguientes solo lo obtiene de self._footsteps.]

Pero sí, obtener el diseño OO correcto puede ser complicado. Piense más sobre las cosas que quiere hacer con sus datos, y eso le informará qué métodos necesitará para aplicar a qué clases.


Los siguientes consejos (similar al consejo de @ S.Lott) provienen del libro, Beginning Python: de principiante a profesional

  1. Escriba una descripción de su problema (¿qué debería hacer el problema?). Subraye todos los sustantivos, verbos y adjetivos.

  2. Revisa los nombres, busca clases potenciales.

  3. Repase los verbos, buscando posibles métodos.

  4. Ir a través de los adjetivos, en busca de posibles atributos

  5. Asigna métodos y atributos a tus clases

Para refinar la clase, el libro también informa que podemos hacer lo siguiente:

  1. Escriba (o invente) un conjunto de casos de uso: escenarios de cómo se puede usar su programa. Intenta cubrir todo lo funcionalmente.

  2. Analice cada caso de uso paso a paso, asegurándose de que todo lo que necesitamos esté cubierto.


Me gusta el enfoque TDD ... Comience por escribir pruebas para lo que quiere que sea el comportamiento. Y escribe el código que pasa. En este punto, no se preocupe demasiado por el diseño, solo obtenga un paquete de prueba y software que pase. No te preocupes si terminas con una sola gran clase fea, con métodos complejos.

A veces, durante este proceso inicial, encontrará un comportamiento que es difícil de probar y necesita descomponerse, solo para probarlo. Esto puede ser una pista de que una clase separada está garantizada.

Entonces la parte divertida ... refactorización. Después de tener un software en funcionamiento, puede ver las piezas complejas. A menudo, pequeños focos de comportamiento se harán evidentes, sugiriendo una nueva clase, pero si no, solo busque formas de simplificar el código. Extrae objetos de servicio y objetos de valor. Simplifica tus métodos.

Si estás usando git correctamente (estás usando git, ¿no?), Puedes experimentar rápidamente con alguna descomposición particular durante la refactorización, y luego abandonarlo y volver atrás si no simplifica las cosas.

Al escribir primero el código de trabajo probado, debe obtener una visión íntima del dominio del problema que no podría obtener fácilmente con el enfoque de diseñar primero. Las pruebas escritas y el código lo empujan más allá de la parálisis de "dónde empiezo".