Orientación a objetos de Python (Parte 1)

Fundamentos de la Orientación a Objetos

Veamos primero una parte del código de aplicación orientado a objetos de Python más básico, no se asuste por su longitud.


class Document():
    def __init__(self, title, author, context):
        print('init function called')
        self.title = title
        self.author = author
        self.__context = context # __开头的属性是私有属性

    def get_context_length(self):
        return len(self.__context)

    def intercept_context(self, length):
        self.__context = self.__context[:length]

harry_potter_book = Document('Harry Potter', 'J. K. Rowling', '... Forever Do not believe any thing is capable of thinking independently ...')

print(harry_potter_book.title)
print(harry_potter_book.author)
print(harry_potter_book.get_context_length())

harry_potter_book.intercept_context(10)

print(harry_potter_book.get_context_length())

print(harry_potter_book.__context)

########## 输出 ##########

init function called
Harry Potter
J. K. Rowling
77
10

---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-5-b4d048d75003> in <module>()
     22 print(harry_potter_book.get_context_length())
     23 
---> 24 print(harry_potter_book.__context)

AttributeError: 'Document' object has no attribute '__context'

Con referencia a este código, primero permítanme explicar brevemente algunos conceptos.

  • Clase: Un grupo de cosas similares, aquí corresponde a la clase Python.
  • Objeto: un objeto de la colección, que corresponde a un objeto generado por la clase, como harry_potter_book en el código.
  • Atributo: una característica estática del objeto, como título, autor y __context en el código anterior.
  • Función: cierta capacidad dinámica del objeto, como la función intercept_context () en el código anterior.

Por supuesto, tales declaraciones no son ni rigurosas ni suficientes, pero si no comprende la programación orientada a objetos en absoluto, pueden brindarle una comprensión intuitiva rápidamente.

Clase, una colección de objetos con las mismas propiedades y funciones.

A continuación, interpretemos el código ahora.


class Document():
    def __init__(self, title, author, context):
        print('init function called')
        self.title = title
        self.author = author
        self.__context = context # __开头的属性是私有属性

    def get_context_length(self):
        return len(self.__context)

    def intercept_context(self, length):
        self.__context = self.__context[:length]

harry_potter_book = Document('Harry Potter', 'J. K. Rowling', '... Forever Do not believe any thing is capable of thinking independently ...')

print(harry_potter_book.title)
print(harry_potter_book.author)
print(harry_potter_book.get_context_length())

harry_potter_book.intercept_context(10)

print(harry_potter_book.get_context_length())

print(harry_potter_book.__context)

########## 输出 ##########

init function called
Harry Potter
J. K. Rowling
77
10

---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-5-b4d048d75003> in <module>()
     22 print(harry_potter_book.get_context_length())
     23 
---> 24 print(harry_potter_book.__context)

AttributeError: 'Document' object has no attribute '__context'

Como puede ver, la clase Documento define la clase Documento, y más abajo puede ver que tiene tres funciones Estas tres funciones son las tres funciones de la clase Documento.

Entre ellos, init significa constructor, lo que significa una función que se llamará automáticamente cuando se genere un objeto. Podemos ver que cuando se ejecuta la línea de código harry_potter_book = Document (...), se imprime la cadena 'función inicial llamada'. Y get_context_length () e intercept_context () son funciones ordinarias de la clase, y las llamamos para hacer algo con las propiedades del objeto.

La clase Documento también tiene tres atributos, título, autor y __contexto, que representan el título, el autor y el contenido, respectivamente, y se pasan a través del constructor. El código aquí es muy intuitivo, podemos ver que intercept_context puede modificar el atributo __context del objeto harry_potter_book.

El único punto que debe enfatizarse aquí es que si un atributo comienza con __ (tenga en cuenta que hay dos _s aquí), este atributo predeterminado es un atributo privado. Los atributos privados se refieren a atributos que no quieren ser accedidos y modificados fuera de las funciones de la clase. Entonces, como puede ver, el título y el autor se pueden imprimir libremente, pero imprimir (harry_potter_book .__ context) informará un error.

Orientado a objetos avanzado

En la práctica de la ingeniería, a medida que la complejidad sigue aumentando, puede pensar en algunos problemas:

  • ¿Cómo definir algunas constantes en una clase para que cada objeto pueda acceder fácilmente a estas constantes sin reestructurar?
  • Si una función no implica acceso para modificar las propiedades de esta clase, y es un poco inapropiado ponerla fuera de la clase, ¿cómo puede ser más elegante?
  • Dado que una clase es una colección de objetos similares, ¿puede ser una colección de clases similares?

Los dos primeros problemas son fáciles de resolver, pero involucran algunas especificaciones de código de uso común, aquí pongo un ejemplo de código.


class Document():
    
    WELCOME_STR = 'Welcome! The context for this book is {}.'
    
    def __init__(self, title, author, context):
        print('init function called')
        self.title = title
        self.author = author
        self.__context = context
    
    # 类函数
    @classmethod
    def create_empty_book(cls, title, author):
        return cls(title=title, author=author, context='nothing')
    
    # 成员函数
    def get_context_length(self):
        return len(self.__context)
    
    # 静态函数
    @staticmethod
    def get_welcome(context):
        return Document.WELCOME_STR.format(context)


empty_book = Document.create_empty_book('What Every Man Thinks About Apart from Sex', 'Professor Sheridan Simove')


print(empty_book.get_context_length())
print(empty_book.get_welcome('indeed nothing'))

########## 输出 ##########

init function called
7
Welcome! The context for this book is indeed nothing.

La primera pregunta es que en una clase de Python, solo necesitas declarar y asignar valores en paralelo con la función para lograr esto, como WELCOME_STR en este código. Una práctica muy común es usar todas las mayúsculas para representar constantes, por lo que podemos usar self.WELCOME_STR en la clase, o usar Entity.WELCOME_STR fuera de la clase para expresar esta cadena.

Para la segunda pregunta, presentamos tres conceptos de función de clase, función miembro y función estática. En realidad, se entienden bien. Los efectos de los dos primeros son dinámicos y pueden acceder o modificar las propiedades del objeto; mientras que las funciones estáticas no tienen nada que ver con la clase. La característica más obvia es que el primer parámetro de la función estática no no tiene ninguna particularidad.

Mire estas funciones en detalle. En términos generales, las funciones estáticas se pueden usar para realizar algunas tareas simples e independientes, que no solo son convenientes para las pruebas, sino que también pueden optimizar la estructura del código. Las funciones estáticas también se pueden representar agregando @staticmethod en la línea antes de la función, y hay ejemplos correspondientes en el código. En realidad, se utiliza el concepto de decoradores, que explicaremos en detalle en capítulos posteriores.

El primer parámetro de la función de clase es generalmente cls, lo que significa que se debe pasar una clase. La función más comúnmente utilizada de la función de clase es implementar diferentes constructores de inicio. Por ejemplo, en el código anterior, usamos la función de clase create_empty_book para crear un nuevo objeto de libro, y su contexto debe ser "nada". Tal código es más claro que su construcción directa. De manera similar, las funciones de clase deben declararse con el decorador @classmethod.

La función miembro es nuestra función de clase más normal. No requiere ninguna declaración de decorador. El primer parámetro self representa la referencia del objeto actual. Esta función se puede utilizar para lograr la consulta deseada / modificar atributos de clase y otras funciones.

heredar

A continuación, veamos la tercera pregunta: dado que una clase es una colección de objetos similares, ¿puede ser una colección de clases similares?

La respuesta es, por supuesto. Siempre que esté bien abstraído, una clase puede describirse como una colección de cualquier cosa.

La herencia de clase, como su nombre lo indica, se refiere al hecho de que una clase tiene las características de otra clase y también tiene características únicas que son diferentes de otra clase. La primera clase aquí se llama subclase, y la otra se llama clase padre. Las características son en realidad las propiedades y funciones de la clase.


class Entity():
    def __init__(self, object_type):
        print('parent class init called')
        self.object_type = object_type
    
    def get_context_length(self):
        raise Exception('get_context_length not implemented')
    
    def print_title(self):
        print(self.title)

class Document(Entity):
    def __init__(self, title, author, context):
        print('Document class init called')
        Entity.__init__(self, 'document')
        self.title = title
        self.author = author
        self.__context = context
    
    def get_context_length(self):
        return len(self.__context)
    
class Video(Entity):
    def __init__(self, title, author, video_length):
        print('Video class init called')
        Entity.__init__(self, 'video')
        self.title = title
        self.author = author
        self.__video_length = video_length
    
    def get_context_length(self):
        return self.__video_length

harry_potter_book = Document('Harry Potter(Book)', 'J. K. Rowling', '... Forever Do not believe any thing is capable of thinking independently ...')
harry_potter_movie = Video('Harry Potter(Movie)', 'J. K. Rowling', 120)

print(harry_potter_book.object_type)
print(harry_potter_movie.object_type)

harry_potter_book.print_title()
harry_potter_movie.print_title()

print(harry_potter_book.get_context_length())
print(harry_potter_movie.get_context_length())

########## 输出 ##########

Document class init called
parent class init called
Video class init called
parent class init called
document
video
Harry Potter(Book)
Harry Potter(Movie)
77
120

También combinamos código para aprender estos conceptos. En este código, Documento y Video son similares en que tienen atributos correspondientes como título, autor y contenido. Podemos abstraer una clase llamada Entidad de ella como la clase padre de las dos.

Lo primero a lo que hay que prestar atención es al constructor. Cada clase tiene un constructor. Cuando una clase heredada genera un objeto, no llamará automáticamente al constructor de la clase padre. Por lo tanto, debes llamar explícitamente al constructor de la clase padre en la función init (). Su orden de ejecución es el constructor de la clase secundaria -> el constructor de la clase principal.

En segundo lugar, debe prestar atención a la función get_context_length () de la clase principal. Si usa Entity para generar directamente un objeto y llama a la función get_context_length (), generará un error e interrumpirá la ejecución del programa. Esta es en realidad una muy buena forma de escribir, llamada reescritura de funciones, por lo que la subclase debe reescribir la función get_context_length () para sobrescribir la función original.

Finalmente, debe prestar atención a la función print_title (). Esta función está definida en la clase padre, pero el objeto de la subclase puede usarla para imprimir el título sin resistencia. Esto también refleja las ventajas de la herencia: reducir el código repetitivo y reducir la entropía del sistema (es decir, la complejidad).

En este punto, tiene una comprensión más detallada de la herencia y también se puede decir que la programación orientada a objetos ha comenzado. Por supuesto, si desea alcanzar un nivel superior, es indispensable practicar mucha programación y aprender más detalles.

Finalmente, quiero extender las funciones abstractas y las clases abstractas para usted. También usaré un fragmento de código para ayudar a explicar.


from abc import ABCMeta, abstractmethod

class Entity(metaclass=ABCMeta):
    @abstractmethod
    def get_title(self):
        pass

    @abstractmethod
    def set_title(self, title):
        pass

class Document(Entity):
    def get_title(self):
        return self.title
    
    def set_title(self, title):
        self.title = title

document = Document()
document.set_title('Harry Potter')
print(document.get_title())

entity = Entity()

########## 输出 ##########

Harry Potter

---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-7-266b2aa47bad> in <module>()
     21 print(document.get_title())
     22 
---> 23 entity = Entity()
     24 entity.set_title('Test')

TypeError: Can't instantiate abstract class Entity with abstract methods get_title, set_title

Debería haber descubierto que la Entidad en sí es inútil, solo úsela para definir algunos elementos básicos de Documento y Video. Sin embargo, ¿qué debe hacer si genera accidentalmente un objeto Entity? Para evitar este error manual, es necesario introducir clases abstractas.

Una clase abstracta es una clase especial, nace como una clase padre, una vez que se objetiva, reportará un error. De manera similar, las funciones abstractas se definen en clases abstractas y las subclases deben anular esta función antes de que puedan usarse. La función abstracta correspondiente está representada por el decorador @abstractmethod.

Podemos ver que entity = Entity () en el código informa directamente un error, y se puede usar normalmente solo heredando Entity a través de Document.

En realidad, este es un concepto muy importante en la ingeniería de software, la definición de interfaces. Los proyectos a gran escala a menudo requieren que muchas personas cooperen en el desarrollo. Por ejemplo, en Facebook, después de que se propone la idea, el equipo de desarrollo y el equipo de producto celebrarán primero una reunión de diseño de producto, PM (Product Manager, Product Manager) escribe documentos de requisitos de productos y luego itera; TL (líder de equipo, gerente de proyecto) escribe documentos de desarrollo. Los documentos de desarrollo definen las funciones generales e interfaces de diferentes módulos, cómo colaborar entre cada módulo, pruebas unitarias y pruebas de integración, pruebas en escala de grises en línea , seguimiento y registro, etc. Proceso de desarrollo.

La clase abstracta es un tipo de existencia, es un estilo de diseño de arriba hacia abajo, solo necesita usar una pequeña cantidad de código para describir claramente qué hacer, definir la interfaz y luego entregarla a diferentes desarrolladores para desarrollar y muelle.

Supongo que te gusta

Origin blog.csdn.net/qq_41485273/article/details/114098990
Recomendado
Clasificación