Python Orientado a Objetos

Python ha sido un lenguaje orientado a objetos desde su diseño. Por eso, crear una clase y un objeto en Python es muy fácil. En este capítulo, presentaremos en detalle la programación orientada a objetos de Python.

Si no has tenido contacto previo con lenguajes de programación orientados a objetos, quizás necesites conocer primero algunas características básicas de estos lenguajes y formar un concepto básico de orientación a objetos en tu mente, lo que te ayudará a aprender más fácilmente la programación orientada a objetos de Python.

A continuación, primero veamos brevemente algunas características básicas de la orientación a objetos.


Introducción a la tecnología orientada a objetos

  • Clase:Se usa para describir una colección de objetos que tienen los mismos atributos y métodos. Define los atributos y métodos comunes a cada objeto de esa colección. Un objeto es una instancia de la clase.
  • Variable de clase:La variable de clase es compartida por todos los objetos instanciados. Se define dentro de la clase y fuera del cuerpo de las funciones. Normalmente no se usa como variable de instancia.
  • Miembro de datos:Variable de clase o variable de instancia, utilizada para manejar datos relacionados con la clase y sus objetos de instancia.
  • Sobrescritura de métodos:Si el método heredado de la clase padre no satisface las necesidades de la subclase, se puede reescribir. Este proceso se llama anulación (override) de método, también conocido como sobrescritura de método.
  • Variable local:Variable definida en un método, que solo actúa sobre la instancia actual de la clase.
  • Variable de instancia:En la declaración de la clase, los atributos se representan con variables. Esta variable se denomina variable de instancia, y se declara dentro de la declaración de la clase pero fuera de los demás métodos miembros de la clase.
  • Herencia:Es decir, una clase derivada hereda los campos y métodos de la clase base. La herencia también permite tratar un objeto de una clase derivada como un objeto de la clase base. Por ejemplo, hay un diseño así: un objeto de tipo Dog deriva de la clase Animal, lo que simula una relación "es un" (is-a) (ejemplo en la figura: Dog es un Animal).
  • Instanciación:Crear una instancia de una clase, es decir, el objeto concreto de la clase.
  • Método:Función definida en la clase.
  • Objeto:Instancia de una estructura de datos definida por la clase. Un objeto incluye dos miembros de datos (variable de clase y variable de instancia) y métodos.

Crear una clase

Usa la declaración `class` para crear una nueva clase. Después de `class` va el nombre de la clase y termina con dos puntos:

class ClassName:
   '类的帮助信息'   #类文档字符串
   class_suite  #类体

La información de ayuda de la clase se puede consultar mediante `ClassName.__doc__`.

`class_suite` se compone de miembros de la clase, métodos y atributos de datos.

Ejemplo

El siguiente es un ejemplo simple de una clase en Python:

Ejemplo

#!/usr/bin/python # -*- coding: UTF-8 -*- class Employee: 'Clase base para todos los empleados' empCount = 0 def __init__(self, name, salary): self.name = name self.salary = salary Employee.empCount += 1 def displayCount(self): print "Total Employee %d" % Employee.empCount def displayEmployee(self): print "Name : ", self.name, ", Salary: ", self.salary
  • La variable `empCount` es una variable de clase; su valor se compartirá entre todas las instancias de esta clase. Puedes acceder a ella usando `Employee.empCount` dentro de la clase o desde fuera de la clase.

  • El primer método, `__init__()`, es un método especial, llamado constructor o método de inicialización de la clase. Se llama cuando se crea una instancia de esta clase.

  • `self` representa la instancia de la clase. `self` es obligatorio al definir métodos de la clase, aunque no es necesario pasar el parámetro correspondiente al llamar al método.

`self` representa la instancia de la clase, no la clase.

Los métodos de una clase solo tienen una diferencia especial con las funciones ordinarias: deben tener un parámetro adicionalcomo nombre del primer parámetro,según la convención, su nombre es `self`.

class Test: def prt(self): print(self) print(self.__class__) t = Test() t.prt()

El resultado de ejecutar el ejemplo anterior es:

<__main__.Test instance at 0x10d066878>
__main__.Test

Del resultado de ejecución se puede ver claramente que `self` representa la instancia de la clase, representa la dirección del objeto actual, mientras queself.__class__apunta a la clase.

`self` no es una palabra clave de Python; si lo reemplazamos por `ejemplo`, también se ejecutará correctamente:

Ejemplo

class Test: def prt(ejemplo): print(ejemplo) print(ejemplo.__class__) t = Test() t.prt()

El resultado de ejecutar el ejemplo anterior es:

<__main__.Test instance at 0x10d066878>
__main__.Test

Crear objeto de instancia

Al instanciar una clase, otros lenguajes de programación generalmente usan la palabra clave `new`, pero en Python no existe esta palabra clave. La instanciación de una clase es similar a una llamada de función.

A continuación se usa el nombre de la clase `Employee` para instanciar, y se reciben parámetros a través del método `__init__`.

"创建 Employee 类的第一个对象"
emp1 = Employee("Zara", 2000)
"创建 Employee 类的第二个对象"
emp2 = Employee("Manni", 5000)

Acceder a atributos

Puedes usar el punto.para acceder a los atributos del objeto. Usa el nombre de la clase de la siguiente manera para acceder a la variable de clase:

emp1.displayEmployee()
emp2.displayEmployee()
print "Total Employee %d" % Employee.empCount

Ejemplo completo:

Ejemplo

#!/usr/bin/python # -*- coding: UTF-8 -*- class Employee: 'Clase base para todos los empleados' empCount = 0 def __init__(self, name, salary): self.name = name self.salary = salary Employee.empCount += 1 def displayCount(self): print "Total Employee %d" % Employee.empCount def displayEmployee(self): print "Name : ", self.name, ", Salary: ", self.salary "Crear el primer objeto de la clase Employee" emp1 = Employee("Zara", 2000) "Crear el segundo objeto de la clase Employee" emp2 = Employee("Manni", 5000) emp1.displayEmployee() emp2.displayEmployee() print "Total Employee %d" % Employee.empCount

Al ejecutar el código anterior, el resultado es el siguiente:

Name :  Zara ,Salary:  2000
Name :  Manni ,Salary:  5000
Total Employee 2

Puedes agregar, eliminar y modificar atributos de la clase, como se muestra a continuación:

emp1.age = 7  # 添加一个 'age' 属性
emp1.age = 8  # 修改 'age' 属性
del emp1.age  # 删除 'age' 属性

También puedes usar las siguientes funciones para acceder a los atributos:

  • `getattr(obj, name[, default])`: accede al atributo del objeto.
  • `hasattr(obj, name)`: verifica si existe un atributo.
  • `setattr(obj, name, value)`: establece un atributo. Si el atributo no existe, crea uno nuevo.
  • `delattr(obj, name)`: elimina un atributo.
hasattr(emp1, 'age') # Devuelve True si existe el atributo 'age'. getattr(emp1, 'age') # Devuelve el valor del atributo 'age' setattr(emp1, 'age', 8) # Agrega el atributo 'age' con valor 8 delattr(emp1, 'age') # Elimina el atributo 'age'

Atributos de clase integrados en Python

  • `__dict__`: atributos de la clase (contiene un diccionario formado por los atributos de datos de la clase)
  • `__doc__`: cadena de documentación de la clase
  • `__name__`: nombre de la clase
  • `__module__`: módulo donde se encuentra la definición de la clase (el nombre completo de la clase es '__main__.className'; si la clase está en un módulo importado `mymod`, entonces `className.__module__` es igual a `mymod`)
  • `__bases__`: todos los elementos de las clases base de la clase (incluye una tupla compuesta por todas las clases base)

A continuación se muestra un ejemplo de invocación de los atributos de clase integrados en Python:

Ejemplo

#!/usr/bin/python # -*- coding: UTF-8 -*- class Employee: 'Clase base para todos los empleados' empCount = 0 def __init__(self, name, salary): self.name = name self.salary = salary Employee.empCount += 1 def displayCount(self): print "Total Employee %d" % Employee.empCount def displayEmployee(self): print "Name : ", self.name, ", Salary: ", self.salary print "Employee.__doc__:", Employee.__doc__ print "Employee.__name__:", Employee.__name__ print "Employee.__module__:", Employee.__module__ print "Employee.__bases__:", Employee.__bases__ print "Employee.__dict__:", Employee.__dict__

Al ejecutar el código anterior, el resultado es el siguiente:

Employee.__doc__: 所有员工的基类
Employee.__name__: Employee
Employee.__module__: __main__
Employee.__bases__: ()
Employee.__dict__: {'__module__': '__main__', 'displayCount': <function displayCount at 0x10a939c80>, 'empCount': 0, 'displayEmployee': <function displayEmployee at 0x10a93caa0>, '__doc__': '\xe6\x89\x80\xe6\x9c\x89\xe5\x91\x98\xe5\xb7\xa5\xe7\x9a\x84\xe5\x9f\xba\xe7\xb1\xbb', '__init__': <function __init__ at 0x10a939578>}

Destrucción de objetos en Python (recolección de basura)

Python utiliza la técnica simple de recuento de referencias para rastrear y recolectar la basura.

Internamente, Python registra cuántas referencias tiene cada objeto en uso.

Una variable de seguimiento interna, llamada contador de referencias.

Cuando se crea un objeto, se crea un contador de referencias. Cuando el objeto ya no es necesario, es decir, cuando su contador de referencias llega a 0, el objeto es recolectado como basura. Sin embargo, la recolección no es "inmediata"; el intérprete recupera el espacio de memoria ocupado por los objetos basura en el momento adecuado.

a = 40      # 创建对象  <40>
b = a       # 增加引用, <40> 的计数
c = [b]     # 增加引用.  <40> 的计数

del a       # 减少引用 <40> 的计数
b = 100     # 减少引用 <40> 的计数
c[0] = -1   # 减少引用 <40> 的计数

El mecanismo de recolección de basura no solo se dirige a objetos con contador de referencias 0, sino que también puede manejar el caso de referencias cíclicas. Una referencia cíclica se refiere a que dos objetos se referencian mutuamente, pero ninguna otra variable los referencia. En este caso, no basta con usar solo el recuento de referencias. El recolector de basura de Python es en realidad un contador de referencias y un recolector de ciclos. Como complemento al recuento de referencias, el recolector de basura también presta atención a los objetos cuya cantidad total asignada es grande (es decir, aquellos que no se destruyen mediante el recuento de referencias). En este caso, el intérprete se detiene e intenta limpiar todos los ciclos sin referencias.

Ejemplo

El destructor `__del__` se llama cuando el objeto se destruye. Cuando el objeto ya no se usa, el método `__del__` se ejecuta:

Ejemplo

#!/usr/bin/python # -*- coding: UTF-8 -*- class Point: def __init__( self, x=0, y=0): self.x = x self.y = y def __del__(self): class_name = self.__class__.__name__ print class_name, "Destruir" pt1 = Point() pt2 = pt1 pt3 = pt1 print id(pt1), id(pt2), id(pt3) # Imprime el id del objeto del pt1 del pt2 del pt3

El resultado de ejecutar el ejemplo anterior es el siguiente:

3083401324 3083401324 3083401324
Point 销毁

Nota:Normalmente necesitas definir una clase en un archivo separado,

Herencia de clases

Uno de los principales beneficios de la programación orientada a objetos es la reutilización de código, y una de las formas de lograr esta reutilización es mediante el mecanismo de herencia.

Las nuevas clases creadas mediante herencia se denominansubclasesoclases derivadas, y las clases heredadas se denominanclases base、clases padreosuperclases。

Sintaxis de herencia

class 派生类名(基类名)
    ...

Algunas características de la herencia en Python:

  • 1. Si en la subclase se necesita el método constructor de la clase padre, se debe llamar explícitamente al constructor de la clase padre, o no reescribir el constructor de la clase padre. Para más detalles, consulte:Explicación sobre la subclase de Python que hereda el constructor de la clase padre。
  • 2. Al llamar a un método de la clase base, es necesario añadir el prefijo del nombre de la clase base y se debe incluir el parámetro self. La diferencia es que al llamar a una función normal dentro de la clase no se necesita incluir el parámetro self.
  • 3. Python siempre busca primero el método del tipo correspondiente; si no puede encontrar el método correspondiente en la clase derivada, entonces comienza a buscar en la clase base una por una. (Primero busca el método llamado en la clase actual, y solo si no lo encuentra va a la clase base).

Si se lista más de una clase en la tupla de herencia, se denomina "herencia múltiple".

Sintaxis:

La declaración de las clases derivadas es similar a la de sus clases padre; la lista de clases base heredadas sigue al nombre de la clase, como se muestra a continuación:

class SubClassName (ParentClass1[, ParentClass2, ...]):
    ...

Ejemplo

#!/usr/bin/python # -*- coding: UTF-8 -*- class Parent: # Definir la clase padre parentAttr = 100 def __init__(self): print "Llamar al constructor de la clase padre" def parentMethod(self): print 'Llamar al método de la clase padre' def setAttr(self, attr): Parent.parentAttr = attr def getAttr(self): print "Atributo de la clase padre:", Parent.parentAttr class Child(Parent): # Definir la subclase def __init__(self): print "Llamar al constructor de la subclase" def childMethod(self): print 'Llamar al método de la subclase' c = Child() # Instanciar la subclase c.childMethod() # Llamar al método de la subclase c.parentMethod() # Llamar al método de la clase padre c.setAttr(200) # Llamar nuevamente al método de la clase padre - establecer el valor del atributo c.getAttr() # Llamar nuevamente al método de la clase padre - obtener el valor del atributo

El resultado de ejecución del código anterior es el siguiente:

调用子类构造方法
调用子类方法
调用父类方法
父类属性 : 200

Puedes heredar de múltiples clases

class A:        # 定义类 A
.....

class B:         # 定义类 B
.....

class C(A, B):   # 继承类 A 和 B
.....

Puedes usar los métodos issubclass() o isinstance() para comprobarlo.

  • issubclass() - función booleana que determina si una clase es subclase o clase descendiente de otra clase, sintaxis: issubclass(sub, sup)
  • isinstance(obj, Class) función booleana que devuelve true si obj es un objeto instancia de la clase Class o un objeto instancia de una subclase de Class.

Sobrescritura de métodos

Si la funcionalidad del método de tu clase padre no satisface tus necesidades, puedes anular el método de tu clase padre en la subclase:

Ejemplo:

Ejemplo

#!/usr/bin/python # -*- coding: UTF-8 -*- class Parent: # Definir la clase padre def myMethod(self): print 'Llamar al método de la clase padre' class Child(Parent): # Definir la subclase def myMethod(self): print 'Llamar al método de la subclase' c = Child() # Instancia de la subclase c.myMethod() # La subclase llama al método anulado

El resultado de ejecutar el código anterior es el siguiente:

Llamar a métodos de la subclase

Métodos de sobrecarga básicos

La siguiente tabla enumera algunas funciones comunes que puedes anular en tus propias clases:

NúmeroMétodo, descripción y llamada simple
1__init__ ( self [,args...] )
Constructor
Método de llamada simple:obj = className(args)
2__del__( self )
Método destructor, elimina un objeto
Método de llamada simple:del obj
3__repr__( self )
Convertir a una forma legible por el intérprete
Método de llamada simple:repr(obj)
4__str__( self )
Se utiliza para convertir valores a una forma legible para las personas
Método de llamada simple:str(obj)
5__cmp__ ( self, x )
Comparación de objetos
Método de llamada simple:cmp(obj, x)

Sobrecarga de operadores

Python también admite la sobrecarga de operadores, ejemplo a continuación:

Ejemplo

#!/usr/bin/python class Vector: def __init__(self, a, b): self.a = a self.b = b def __str__(self): return 'Vector (%d, %d)' % (self.a, self.b) def __add__(self,other): return Vector(self.a + other.a, self.b + other.b) v1 = Vector(2,10) v2 = Vector(5,-2) print v1 + v2

El resultado de ejecución del código anterior es el siguiente:

Vector(7,8)

Atributos y métodos de clase

Atributos privados de la clase

__private_attrs: comienzan con dos guiones bajos, declaran el atributo como privado, no se puede usar ni acceder directamente fuera de la clase. Cuando se usa en métodos dentro de la claseself.__private_attrs。

Métodos de la clase

Dentro de la clase, usando ladefpalabra clave se puede definir un método para la clase. A diferencia de la definición de funciones generales, los métodos de clase deben incluir el parámetro self, y debe ser el primer parámetro.

Métodos privados de la clase

__private_method: comienzan con dos guiones bajos, declaran el método como privado, no se puede llamar fuera de la clase. Se llama dentro de la claseself.__private_methods

Ejemplo

#!/usr/bin/python # -*- coding: UTF-8 -*- class JustCounter: __secretCount = 0 # Variable privada publicCount = 0 # Variable pública def count(self): self.__secretCount += 1 self.publicCount += 1 print self.__secretCount counter = JustCounter() counter.count() counter.count() print counter.publicCount print counter.__secretCount # Error, la instancia no puede acceder a la variable privada

Python cambia el nombre para incluir el nombre de la clase:

1
2
2
Traceback (most recent call last):
  File "test.py", line 17, in <module>
    print counter.__secretCount  # 报错,实例不能访问私有变量
AttributeError: JustCounter instance has no attribute '__secretCount'

Python no permite que las clases instanciadas accedan a datos privados, pero puedes usarobject._className__attrName( nombre_del_objeto._nombre_de_la_clase__nombre_del_atributo_privado) para acceder a los atributos, consulta el siguiente ejemplo:

#!/usr/bin/python
# -*- coding: UTF-8 -*-

class Ejemplo:
    __site = "www.example.com"

ejemplo = Ejemplo()
print ejemplo._Ejemplo__site

Ejecuta el código anterior, el resultado de la ejecución es el siguiente:

www.example.com

Explicación de guion bajo simple, guion bajo doble y guion bajo doble al inicio y al final:

  • __foo__: define métodos especiales, generalmente nombres definidos por el sistema, similares a__init__()etc.

  • _foo: los que comienzan con un solo guion bajo indican variables de tipo protected, es decir, el tipo protegido solo permite el acceso a sí mismo y a sus subclases, no se puede utilizar parafrom module import *

  • __foo: los de doble guion bajo indican variables de tipo privado (private), solo se permite el acceso a la propia clase.

Otras extensiones