oop con - ¿Cuándo debería usar clases en Python?




constructor una (5)

He estado programando en python durante aproximadamente dos años; principalmente material de datos (pandas, mpl, numpy), pero también scripts de automatización y pequeñas aplicaciones web. Intento convertirme en un mejor programador e incrementar mi conocimiento de Python, y una de las cosas que me molesta es que nunca he usado una clase (aparte de copiar código de mazo aleatorio para pequeñas aplicaciones web). En general, entiendo lo que son, pero no puedo entender por qué los necesitaría sobre una función simple.

Para agregar especificidad a mi pregunta: escribo toneladas de informes automatizados que siempre implican extraer datos de múltiples fuentes de datos (mongo, sql, postgres, apis), realizar mucho o un poco de munging y formateo de datos, escribir los datos en csv / excel / html, enviarlo en un correo electrónico. Los scripts van desde ~ 250 líneas a ~ 600 líneas. ¿Hubo alguna razón para que use clases para hacer esto y por qué?


Answers

Las clases son el pilar de la Programación Orientada a Objetos . OOP está muy preocupado con la organización del código, la reutilización y la encapsulación.

Primero, un descargo de responsabilidad: OOP está parcialmente en contraste con la Programación Funcional , que es un paradigma diferente que se usa mucho en Python. No todos los que programan en Python (o seguramente en la mayoría de los idiomas) usan OOP. Puedes hacer mucho en Java 8 que no está muy orientado a objetos. Si no quieres usar OOP, entonces no lo hagas. Si solo está escribiendo scripts únicos para procesar datos que nunca volverá a usar, siga escribiendo como está.

Sin embargo, hay muchas razones para usar OOP.

Algunas razones:

  • Organización: OOP define formas conocidas y estándar de describir y definir tanto los datos como el procedimiento en el código. Tanto los datos como el procedimiento pueden almacenarse en diferentes niveles de definición (en diferentes clases), y existen formas estándar de hablar sobre estas definiciones. Es decir, si usa OOP de forma estándar, ayudará a su yo posterior y a otros a comprender, editar y usar su código. Además, en lugar de utilizar un mecanismo de almacenamiento de datos complejo y arbitrario (dicts de dicts o listas o dicts o listas de dicts de sets, o lo que sea), puede nombrar piezas de estructuras de datos y referirse a ellas convenientemente.

  • Estado: OOP lo ayuda a definir y realizar un seguimiento del estado. Por ejemplo, en un ejemplo clásico, si está creando un programa que procesa estudiantes (por ejemplo, un programa de grado), puede mantener toda la información que necesita sobre ellos en un solo lugar (nombre, edad, sexo, nivel de grado, cursos, calificaciones, maestros, compañeros, dieta, necesidades especiales, etc.), y esta información se conserva mientras el objeto esté vivo y sea de fácil acceso.

  • Encapsulation : con encapsulación, el procedimiento y los datos se almacenan juntos. Los métodos (un término OOP para las funciones) se definen junto con los datos que operan y producen. En un lenguaje como Java que permite el control de acceso , o en Python, dependiendo de cómo describa su API pública, esto significa que los métodos y los datos pueden ocultarse al usuario. Lo que esto significa es que si necesita o quiere cambiar el código, puede hacer lo que quiera con la implementación del código, pero mantener las API públicas iguales.

  • Inheritance : la herencia le permite definir datos y procedimientos en un solo lugar (en una clase), y luego anular o ampliar esa funcionalidad más adelante. Por ejemplo, en Python, a menudo veo personas creando subclases de la clase dict para agregar funcionalidad adicional. Un cambio común está anulando el método que arroja una excepción cuando se solicita una clave de un diccionario que no existe para dar un valor predeterminado basado en una clave desconocida. Esto le permite extender su propio código ahora o más tarde, permitir que otros amplíen su código y le permite extender el código de otras personas.

  • Reusabilidad: todas estas razones y otras permiten una mayor reutilización del código. El código orientado a objetos le permite escribir código sólido (probado) una vez, y luego reutilizarlo una y otra vez. Si necesita retocar algo para su caso de uso específico, puede heredar de una clase existente y sobrescribir el comportamiento existente. Si necesita cambiar algo, puede cambiarlo todo manteniendo las firmas de métodos públicos existentes, y nadie es más inteligente (con suerte).

De nuevo, hay varias razones para no usar OOP, y no es necesario. Pero, afortunadamente, con un lenguaje como Python, puedes usar solo un poco o mucho, depende de ti.

Un ejemplo del caso de uso del estudiante (sin garantía de calidad del código, solo un ejemplo):

Orientado a objetos

class Student(object):
    def __init__(self, name, age, gender, level, grades=None):
        self.name = name
        self.age = age
        self.gender = gender
        self.level = level
        self.grades = grades or {}

    def setGrade(self, course, grade):
        self.grades[course] = grade

    def getGrade(self, course):
        return self.grades[course]

    def getGPA(self):
        return sum(self.grades.values())/len(self.grades)

# Define some students
john = Student("John", 12, "male", 6, {"math":3.3})
jane = Student("Jane", 12, "female", 6, {"math":3.5})

# Now we can get to the grades easily
print(john.getGPA())
print(jane.getGPA())

Dict estándar

def calculateGPA(gradeDict):
    return sum(gradeDict.values())/len(gradeDict)

students = {}
# We can set the keys to variables so we might minimize typos
name, age, gender, level, grades = "name", "age", "gender", "level", "grades"
john, jane = "john", "jane"
math = "math"
students[john] = {}
students[john][age] = 12
students[john][gender] = "male"
students[john][level] = 6
students[john][grades] = {math:3.3}

students[jane] = {}
students[jane][age] = 12
students[jane][gender] = "female"
students[jane][level] = 6
students[jane][grades] = {math:3.5}

# At this point, we need to remember who the students are and where the grades are stored. Not a huge deal, but avoided by OOP.
print(calculateGPA(students[john][grades]))
print(calculateGPA(students[jane][grades]))

Creo que lo haces bien. Las clases son razonables cuando necesita simular alguna lógica comercial o procesos difíciles de la vida real con relaciones difíciles. Como ejemplo:

  • Varias funciones con estado compartido
  • Más de una copia de las mismas variables de estado
  • Para extender el comportamiento de una funcionalidad existente

También te sugiero que veas este clásico video


Siempre que necesite mantener un estado de sus funciones y no se pueda lograr con generadores (funciones que ceden en lugar de regresar). Los generadores mantienen su propio estado.

Si desea anular cualquiera de los operadores estándar , necesita una clase.

Siempre que tenga un uso para un patrón de visitante, necesitará clases. Cualquier otro patrón de diseño se puede lograr de manera más efectiva y limpia con generadores, administradores de contexto (que también se implementan mejor como generadores que como clases) y tipos de POD (diccionarios, listas y tuplas, etc.).

Si desea escribir código "pythonic", debería preferir los gestores y generadores de contexto sobre las clases. Estará más limpio.

Si desea ampliar la funcionalidad, casi siempre podrá lograrlo con contención en lugar de herencia.

Como cada regla, esto tiene una excepción. Si desea encapsular funcionalidad rápidamente (es decir, escribir código de prueba en lugar de código reutilizable a nivel de biblioteca), puede encapsular el estado en una clase. Será simple y no necesitará ser reutilizable.

Si necesita un destructor de estilo C ++ (RIIA), definitivamente NO quiere usar clases. Quieres administradores de contexto


Una clase define una entidad del mundo real. Si está trabajando en algo que existe individualmente y tiene su propia lógica separada de las demás, debe crear una clase para ella. Por ejemplo, una clase que encapsula la conectividad de la base de datos.

Si este no es el caso, no es necesario crear una clase


Trataré de explicar la diferencia básica usando un ejemplo.

class A(object):
    x = 0

    def say_hi(self):
        pass

    @staticmethod
    def say_hi_static():
        pass

    @classmethod
    def say_hi_class(cls):
        pass

    def run_self(self):
        self.x += 1
        print self.x # outputs 1
        self.say_hi()
        self.say_hi_static()
        self.say_hi_class()

    @staticmethod
    def run_static():
        print A.x  # outputs 0
        # A.say_hi() #  wrong
        A.say_hi_static()
        A.say_hi_class()

    @classmethod
    def run_class(cls):
        print cls.x # outputs 0
        # cls.say_hi() #  wrong
        cls.say_hi_static()
        cls.say_hi_class()

1 - podemos llamar directamente static y classmethods sin inicializar

# A.run_self() #  wrong
A.run_static()
A.run_class()

2- El método estático no puede llamarse método propio, pero puede llamar a otro método estático y de clase

3- El método estático pertenece a la clase y no usará el objeto en absoluto.

4- El método de clase no está vinculado a un objeto sino a una clase.





python oop