Podemos pensar en OOP como un modelo del comportamiento de un sistema. Tenga en cuenta que el sistema no tiene que existir en el "mundo real", aunque las metáforas del mundo real a veces pueden ser útiles (por ejemplo, "tuberías", "fábricas", etc.).
Si nuestro sistema deseado es demasiado complicado para modelarlo todo de una vez, podemos dividirlo en pedazos más pequeños y modelarlos (el "dominio del problema"), lo que puede implicar descomponerse aún más, y así sucesivamente hasta que lleguemos a pedazos cuyo comportamiento coincida (más o menos) el de algún objeto de lenguaje incorporado como un número, una cadena, una lista, etc.
Una vez que tenemos esas piezas simples, podemos combinarlas para describir el comportamiento de piezas más grandes, que podemos combinar en piezas aún más grandes, y así sucesivamente hasta que podamos describir todos los componentes del dominio que se necesitan para un todo sistema.
Es en esta fase de "combinación" donde podríamos escribir algunas clases. Escribimos clases cuando no hay un objeto existente que se comporte de la manera que queremos. Por ejemplo, nuestro dominio puede contener "foos", colecciones de foos llamados "bares" y colecciones de bares llamados "bazs". Podemos notar que los foos son lo suficientemente simples como para modelar con cadenas, por lo que hacemos eso. Encontramos que las barras requieren que sus contenidos obedezcan alguna restricción particular que no coincide con nada de lo que Python proporciona, en cuyo caso podríamos escribir una nueva clase para hacer cumplir esta restricción. Quizás los bazs no tienen tales peculiaridades, por lo que podemos representarlos con una lista.
Tenga en cuenta que podríamos escribir una nueva clase para cada uno de esos componentes (foos, bars y bazs), pero no necesitamos hacerlo si ya hay algo con el comportamiento correcto. En particular, para que una clase sea útil necesita 'proporcionar' algo (datos, métodos, constantes, subclases, etc.), por lo que incluso si tenemos muchas capas de clases personalizadas, debemos usar alguna característica incorporada; por ejemplo, si escribiéramos una nueva clase para foos, probablemente solo contendría una cadena, entonces ¿por qué no olvidar la clase foo y hacer que la clase bar contenga esas cadenas? Tenga en cuenta que las clases también son un objeto incorporado, solo son particularmente flexibles.
Una vez que tenemos nuestro modelo de dominio, podemos tomar algunas instancias particulares de esas piezas y organizarlas en una "simulación" del sistema particular que queremos modelar (por ejemplo, "un sistema de aprendizaje automático para ...").
Una vez que tenemos esta simulación, podemos ejecutarla y listo, tenemos un sistema de aprendizaje automático (simulación de) para ... (o cualquier otra cosa que estemos modelando).
Ahora, en su situación particular, está tratando de modelar el comportamiento de un componente "extractor de características". La pregunta es, ¿hay algún objeto incorporado que se comporte como un "extractor de características", o necesitarás dividirlo en cosas más simples? Parece que los extractores de características se comportan de manera muy parecida a los objetos de función, por lo que creo que estaría bien usarlos como modelo.
Una cosa a tener en cuenta al aprender sobre este tipo de conceptos es que diferentes lenguajes pueden proporcionar diferentes características y objetos integrados (y, por supuesto, ¡algunos ni siquiera usan terminología como "objetos"!). Por lo tanto, las soluciones que tienen sentido en un idioma pueden ser menos útiles en otro (¡esto incluso puede aplicarse a diferentes versiones del mismo idioma!).
Históricamente, gran parte de la literatura de OOP (especialmente "patrones de diseño") se ha centrado en Java, que es bastante diferente de Python. Por ejemplo, las clases de Java no son objetos, Java no tenía objetos de función hasta hace muy poco, Java tiene una verificación de tipo estricta (que fomenta las interfaces y subclases), mientras que Python fomenta la tipificación de pato, Java no tiene objetos de módulo, enteros de Java / flotadores / etc. no son objetos, la metaprogramación / introspección en Java requiere "reflexión", etc.
No estoy tratando de elegir Java (como otro ejemplo, mucha de la teoría OOP gira en torno a Smalltalk, que nuevamente es muy diferente de Python), solo estoy tratando de señalar que debemos pensar con mucho cuidado sobre el contexto y restricciones en las que se desarrollaron las soluciones y si eso coincide con la situación en la que nos encontramos.
En su caso, un objeto de función parece una buena opción. Si se pregunta por qué algunas pautas de "mejores prácticas" no mencionan los objetos de función como una posible solución, ¡podría ser simplemente porque esas pautas fueron escritas para versiones antiguas de Java!