Python Language
Las clases
Buscar..
Introducción
Python se ofrece a sí mismo no solo como un popular lenguaje de scripting, sino que también es compatible con el paradigma de programación orientado a objetos. Las clases describen datos y proporcionan métodos para manipular esos datos, todos incluidos en un solo objeto. Además, las clases permiten la abstracción al separar los detalles de implementación concretos de las representaciones abstractas de datos.
El código que utiliza clases es generalmente más fácil de leer, entender y mantener.
Herencia basica
La herencia en Python se basa en ideas similares utilizadas en otros lenguajes orientados a objetos como Java, C ++, etc. Una nueva clase puede derivarse de una clase existente de la siguiente manera.
class BaseClass(object):
pass
class DerivedClass(BaseClass):
pass
BaseClass
es la clase ya existente ( principal ), y DerivedClass
es la nueva clase ( secundaria ) que hereda (o subclases ) atributos de BaseClass
. Nota : a partir de Python 2.2, todas las clases heredan implícitamente de la clase de object
, que es la clase base para todos los tipos incorporados.
Definimos una clase de Rectangle
principal en el siguiente ejemplo, que se hereda implícitamente del object
:
class Rectangle():
def __init__(self, w, h):
self.w = w
self.h = h
def area(self):
return self.w * self.h
def perimeter(self):
return 2 * (self.w + self.h)
La clase Rectangle
se puede usar como una clase base para definir una clase Square
, ya que un cuadrado es un caso especial de rectángulo.
class Square(Rectangle):
def __init__(self, s):
# call parent constructor, w and h are both s
super(Square, self).__init__(s, s)
self.s = s
La clase Square
heredará automáticamente todos los atributos de la clase Rectangle
, así como la clase de objeto. super()
se utiliza para llamar al __init__()
de la clase Rectangle
, esencialmente llamando a cualquier método anulado de la clase base. Nota : en Python 3, super()
no requiere argumentos.
Los objetos de clase derivados pueden acceder y modificar los atributos de sus clases base:
r.area()
# Output: 12
r.perimeter()
# Output: 14
s.area()
# Output: 4
s.perimeter()
# Output: 8
Funciones incorporadas que funcionan con herencia.
issubclass(DerivedClass, BaseClass)
: devuelve True
si DerivedClass
es una subclase de BaseClass
isinstance(s, Class)
: devuelve True
si s es una instancia de Class
o cualquiera de las clases derivadas de Class
# subclass check
issubclass(Square, Rectangle)
# Output: True
# instantiate
r = Rectangle(3, 4)
s = Square(2)
isinstance(r, Rectangle)
# Output: True
isinstance(r, Square)
# Output: False
# A rectangle is not a square
isinstance(s, Rectangle)
# Output: True
# A square is a rectangle
isinstance(s, Square)
# Output: True
Variables de clase e instancia
Las variables de instancia son únicas para cada instancia, mientras que las variables de clase son compartidas por todas las instancias.
class C:
x = 2 # class variable
def __init__(self, y):
self.y = y # instance variable
C.x
# 2
C.y
# AttributeError: type object 'C' has no attribute 'y'
c1 = C(3)
c1.x
# 2
c1.y
# 3
c2 = C(4)
c2.x
# 2
c2.y
# 4
Se puede acceder a las variables de clase en las instancias de esta clase, pero la asignación al atributo de clase creará una variable de instancia que sombrea la variable de clase
c2.x = 4
c2.x
# 4
C.x
# 2
Tenga en cuenta que la mutación de variables de clase de instancias puede llevar a algunas consecuencias inesperadas.
class D:
x = []
def __init__(self, item):
self.x.append(item) # note that this is not an assigment!
d1 = D(1)
d2 = D(2)
d1.x
# [1, 2]
d2.x
# [1, 2]
D.x
# [1, 2]
Métodos enlazados, no enlazados y estáticos.
La idea de métodos enlazados y no enlazados se eliminó en Python 3 . En Python 3 cuando declara un método dentro de una clase, está usando una palabra clave def
, creando así un objeto de función. Esta es una función regular, y la clase circundante funciona como su espacio de nombres. En el siguiente ejemplo, declaramos el método f
dentro de la clase A
, y se convierte en una función Af
:
class A(object):
def f(self, x):
return 2 * x
A.f
# <function A.f at ...> (in Python 3.x)
En Python 2, el comportamiento fue diferente: los objetos de función dentro de la clase fueron reemplazados implícitamente por objetos de tipo instancemethod
, que se denominaron métodos no vinculados porque no estaban vinculados a ninguna instancia de clase en particular. Fue posible acceder a la función subyacente utilizando la propiedad .__func__
.
A.f
# <unbound method A.f> (in Python 2.x)
A.f.__class__
# <type 'instancemethod'>
A.f.__func__
# <function f at ...>
Los últimos comportamientos se confirman mediante inspección: los métodos se reconocen como funciones en Python 3, mientras que la distinción se mantiene en Python 2.
import inspect
inspect.isfunction(A.f)
# True
inspect.ismethod(A.f)
# False
import inspect
inspect.isfunction(A.f)
# False
inspect.ismethod(A.f)
# True
En ambas versiones de Python function / method Af
se puede llamar directamente, siempre que pase una instancia de clase A
como primer argumento.
A.f(1, 7)
# Python 2: TypeError: unbound method f() must be called with
# A instance as first argument (got int instance instead)
# Python 3: 14
a = A()
A.f(a, 20)
# Python 2 & 3: 40
Supongamos ahora que a
es una instancia de la clase A
, lo que es af
entonces? Bueno, intuitivamente este debe ser el mismo método f
de clase A
, sólo que debe de alguna manera "saber" que se aplica al objeto a
- método en Python esto se llama unida a a
.
Los detalles esenciales son los siguientes: writing af
invoca el método magic __getattribute__
de a
, que primero verifica si a
tiene un atributo llamado f
(no lo hace), luego verifica la clase A
si contiene un método con ese nombre (lo hace), y crea un nuevo objeto m
del method
de tipo que tiene la referencia al Af
original en m.__func__
, y una referencia al objeto a
en m.__self__
. Cuando este objeto se llama como una función, simplemente hace lo siguiente: m(...) => m.__func__(m.__self__, ...)
. Por lo tanto, este objeto se denomina método enlazado porque, cuando se invoca, sabe que debe proporcionar el objeto al que estaba vinculado como primer argumento. (Estas cosas funcionan de la misma manera en Python 2 y 3).
a = A()
a.f
# <bound method A.f of <__main__.A object at ...>>
a.f(2)
# 4
# Note: the bound method object a.f is recreated *every time* you call it:
a.f is a.f # False
# As a performance optimization you can store the bound method in the object's
# __dict__, in which case the method object will remain fixed:
a.f = a.f
a.f is a.f # True
Finalmente, Python tiene métodos de clase y métodos estáticos , tipos especiales de métodos. Los métodos de clase funcionan de la misma manera que los métodos regulares, excepto que cuando se invocan en un objeto, se unen a la clase del objeto en lugar de al objeto. Así m.__self__ = type(a)
. Cuando llama a dicho método enlazado, pasa la clase de a
como primer argumento. Los métodos estáticos son incluso más simples: no vinculan nada en absoluto, y simplemente devuelven la función subyacente sin ninguna transformación.
class D(object):
multiplier = 2
@classmethod
def f(cls, x):
return cls.multiplier * x
@staticmethod
def g(name):
print("Hello, %s" % name)
D.f
# <bound method type.f of <class '__main__.D'>>
D.f(12)
# 24
D.g
# <function D.g at ...>
D.g("world")
# Hello, world
Tenga en cuenta que los métodos de clase están vinculados a la clase incluso cuando se accede a ellos en la instancia:
d = D()
d.multiplier = 1337
(D.multiplier, d.multiplier)
# (2, 1337)
d.f
# <bound method D.f of <class '__main__.D'>>
d.f(10)
# 20
Vale la pena señalar que en el nivel más bajo, las funciones, los métodos, los métodos estáticos, etc., son en realidad descriptores que invocan los métodos especiales __get__
, __set
__ y opcionalmente __del__
. Para más detalles sobre métodos de clase y métodos estáticos:
- ¿Cuál es la diferencia entre @staticmethod y @classmethod en Python?
- ¿Significado de @classmethod y @staticmethod para principiante?
Clases de estilo nuevo vs. estilo antiguo
Las clases de nuevo estilo se introdujeron en Python 2.2 para unificar clases y tipos . Heredan del tipo de object
nivel superior. Una clase de nuevo estilo es un tipo definido por el usuario y es muy similar a los tipos incorporados.
# new-style class
class New(object):
pass
# new-style instance
new = New()
new.__class__
# <class '__main__.New'>
type(new)
# <class '__main__.New'>
issubclass(New, object)
# True
Las clases de estilo antiguo no heredan de object
. Las instancias de estilo antiguo siempre se implementan con un tipo de instance
incorporado.
# old-style class
class Old:
pass
# old-style instance
old = Old()
old.__class__
# <class __main__.Old at ...>
type(old)
# <type 'instance'>
issubclass(Old, object)
# False
En Python 3, se eliminaron las clases de estilo antiguo.
Las clases de nuevo estilo en Python 3 heredan implícitamente del object
, por lo que ya no es necesario especificar MyClass(object)
.
class MyClass:
pass
my_inst = MyClass()
type(my_inst)
# <class '__main__.MyClass'>
my_inst.__class__
# <class '__main__.MyClass'>
issubclass(MyClass, object)
# True
Valores por defecto para variables de instancia
Si la variable contiene un valor de un tipo inmutable (por ejemplo, una cadena), está bien asignar un valor predeterminado como este
class Rectangle(object):
def __init__(self, width, height, color='blue'):
self.width = width
self.height = height
self.color = color
def area(self):
return self.width * self.height
# Create some instances of the class
default_rectangle = Rectangle(2, 3)
print(default_rectangle.color) # blue
red_rectangle = Rectangle(2, 3, 'red')
print(red_rectangle.color) # red
Hay que tener cuidado al inicializar objetos mutables como listas en el constructor. Considere el siguiente ejemplo:
class Rectangle2D(object):
def __init__(self, width, height, pos=[0,0], color='blue'):
self.width = width
self.height = height
self.pos = pos
self.color = color
r1 = Rectangle2D(5,3)
r2 = Rectangle2D(7,8)
r1.pos[0] = 4
r1.pos # [4, 0]
r2.pos # [4, 0] r2's pos has changed as well
Este comportamiento se debe al hecho de que en Python los parámetros predeterminados están vinculados en la ejecución de la función y no en la declaración de la función. Para obtener una variable de instancia predeterminada que no se comparte entre las instancias, se debe usar una construcción como esta:
class Rectangle2D(object):
def __init__(self, width, height, pos=None, color='blue'):
self.width = width
self.height = height
self.pos = pos or [0, 0] # default value is [0, 0]
self.color = color
r1 = Rectangle2D(5,3)
r2 = Rectangle2D(7,8)
r1.pos[0] = 4
r1.pos # [4, 0]
r2.pos # [0, 0] r2's pos hasn't changed
Consulte también Argumentos predeterminados mutables y “Menos asombro” y el Argumento predeterminado mutable .
Herencia múltiple
Python utiliza el algoritmo de linealización C3 para determinar el orden en el que resolver los atributos de clase, incluidos los métodos. Esto se conoce como Orden de resolución de métodos (MRO).
Aquí hay un ejemplo simple:
class Foo(object):
foo = 'attr foo of Foo'
class Bar(object):
foo = 'attr foo of Bar' # we won't see this.
bar = 'attr bar of Bar'
class FooBar(Foo, Bar):
foobar = 'attr foobar of FooBar'
Ahora si creamos una instancia de FooBar, si buscamos el atributo foo, veremos que el atributo de Foo se encuentra primero
fb = FooBar()
y
>>> fb.foo
'attr foo of Foo'
Aquí está el MRO de FooBar:
>>> FooBar.mro()
[<class '__main__.FooBar'>, <class '__main__.Foo'>, <class '__main__.Bar'>, <type 'object'>]
Se puede decir simplemente que el algoritmo MRO de Python es
- Profundidad primero (por ejemplo,
FooBar
luegoFoo
) a menos que - un padre compartido (
object
) está bloqueado por un hijo (Bar
) y - No se permiten relaciones circulares.
Es decir, por ejemplo, Bar no puede heredar de FooBar, mientras que FooBar hereda de Bar.
Para un ejemplo completo en Python, vea la entrada de wikipedia .
Otra característica poderosa en la herencia es super
. super puede obtener características de clases para padres.
class Foo(object):
def foo_method(self):
print "foo Method"
class Bar(object):
def bar_method(self):
print "bar Method"
class FooBar(Foo, Bar):
def foo_method(self):
super(FooBar, self).foo_method()
Herencia múltiple con el método init de clase, cuando cada clase tiene su propio método init, entonces intentamos obtener una inercia múltiple, luego solo se llama al método init de la clase que se hereda primero.
para el siguiente ejemplo, solo se llama al método de inicio de clase Foo que no se llama a la clase de barra de inicio
class Foo(object):
def __init__(self):
print "foo init"
class Bar(object):
def __init__(self):
print "bar init"
class FooBar(Foo, Bar):
def __init__(self):
print "foobar init"
super(FooBar, self).__init__()
a = FooBar()
Salida:
foobar init
foo init
Pero eso no significa que la clase Bar no sea hereditaria. La instancia de la clase FooBar final también es una instancia de la clase Bar y la clase Foo .
print isinstance(a,FooBar)
print isinstance(a,Foo)
print isinstance(a,Bar)
Salida:
True
True
True
Descriptores y búsquedas punteadas
Los descriptores son objetos que son (generalmente) atributos de clases y que tienen cualquiera de los métodos especiales __get__
, __set__
o __delete__
.
Los descriptores de datos tienen cualquiera de __set__
, o __delete__
Estos pueden controlar la búsqueda de puntos en una instancia y se utilizan para implementar funciones, staticmethod
, classmethod
y property
. Una búsqueda de puntos (por ejemplo, la instancia foo
de la clase Foo
busca la bar
atributos, es decir, foo.bar
) utiliza el siguiente algoritmo:
bar
se mira en la clase,Foo
. Si está allí y es un descriptor de datos , entonces se usa el descriptor de datos. Así es como laproperty
puede controlar el acceso a los datos en una instancia, y las instancias no pueden anular esto. Si un descriptor de datos no está allí, entoncesbar
se mira en la instancia__dict__
. Es por esto que podemos anular o bloquear los métodos que se invocan desde una instancia con una búsqueda de puntos. Si existe unabar
en la instancia, se utiliza. Si no, entonces nosotrosBusca en la clase
Foo
parabar
. Si es un Descriptor , entonces se usa el protocolo del descriptor. Así es como se implementan las funciones (en este contexto, los métodosclassmethod
), elclassmethod
y elstaticmethod
. De lo contrario, simplemente devuelve el objeto allí, o hay unAttributeError
Métodos de clase: inicializadores alternos
Los métodos de clase presentan formas alternativas para construir instancias de clases. Para ilustrar, veamos un ejemplo.
Supongamos que tenemos una clase de Person
relativamente simple:
class Person(object):
def __init__(self, first_name, last_name, age):
self.first_name = first_name
self.last_name = last_name
self.age = age
self.full_name = first_name + " " + last_name
def greet(self):
print("Hello, my name is " + self.full_name + ".")
Podría ser útil tener una forma de crear instancias de esta clase especificando un nombre completo en lugar del nombre y apellido por separado. Una forma de hacer esto sería tener el last_name
como un parámetro opcional, y suponiendo que si no se da, pasamos el nombre completo en:
class Person(object):
def __init__(self, first_name, age, last_name=None):
if last_name is None:
self.first_name, self.last_name = first_name.split(" ", 2)
else:
self.first_name = first_name
self.last_name = last_name
self.full_name = self.first_name + " " + self.last_name
self.age = age
def greet(self):
print("Hello, my name is " + self.full_name + ".")
Sin embargo, hay dos problemas principales con este bit de código:
Los parámetros
first_name
ylast_name
ahora sonlast_name
, ya que puede ingresar un nombre completo parafirst_name
. Además, si hay más casos y / o más parámetros que tienen este tipo de flexibilidad, la ramificación if / elif / else puede ser molesta rápidamente.No es tan importante, pero aún así vale la pena señalar: ¿y si
last_name
esNone
, perofirst_name
no se divide en dos o más cosas a través de los espacios? Tenemos otra capa de validación de entrada y / o manejo de excepciones ...
Introduzca los métodos de clase. En lugar de tener un solo inicializador, crearemos un inicializador separado, llamado from_full_name
, y lo classmethod
decorador de classmethod
(incorporado).
class Person(object):
def __init__(self, first_name, last_name, age):
self.first_name = first_name
self.last_name = last_name
self.age = age
self.full_name = first_name + " " + last_name
@classmethod
def from_full_name(cls, name, age):
if " " not in name:
raise ValueError
first_name, last_name = name.split(" ", 2)
return cls(first_name, last_name, age)
def greet(self):
print("Hello, my name is " + self.full_name + ".")
Note cls
lugar de self
como el primer argumento de from_full_name
. Los métodos de clase se aplican a la clase general, no a una instancia de una clase dada (que es lo que el self
generalmente denota). Por lo tanto, si cls
es nuestra Person
de clase, entonces el valor devuelto por la from_full_name
método de clase es Person(first_name, last_name, age)
, que utiliza la Person
's __init__
para crear una instancia de la Person
de clase. En particular, si tuviéramos que hacer una subclase Employee
of Person
, from_full_name
también funcionaría en la clase Employee
.
Para mostrar que esto funciona como se esperaba, __init__
instancias de Person
de más de una manera sin la bifurcación en __init__
:
In [2]: bob = Person("Bob", "Bobberson", 42)
In [3]: alice = Person.from_full_name("Alice Henderson", 31)
In [4]: bob.greet()
Hello, my name is Bob Bobberson.
In [5]: alice.greet()
Hello, my name is Alice Henderson.
Otras referencias:
https://docs.python.org/2/library/functions.html#classmethod
https://docs.python.org/3.5/library/functions.html#classmethod
Composición de la clase
La composición de clase permite relaciones explícitas entre objetos. En este ejemplo, las personas viven en ciudades que pertenecen a países. La composición permite a las personas acceder al número de todas las personas que viven en su país:
class Country(object):
def __init__(self):
self.cities=[]
def addCity(self,city):
self.cities.append(city)
class City(object):
def __init__(self, numPeople):
self.people = []
self.numPeople = numPeople
def addPerson(self, person):
self.people.append(person)
def join_country(self,country):
self.country = country
country.addCity(self)
for i in range(self.numPeople):
person(i).join_city(self)
class Person(object):
def __init__(self, ID):
self.ID=ID
def join_city(self, city):
self.city = city
city.addPerson(self)
def people_in_my_country(self):
x= sum([len(c.people) for c in self.city.country.cities])
return x
US=Country()
NYC=City(10).join_country(US)
SF=City(5).join_country(US)
print(US.cities[0].people[0].people_in_my_country())
# 15
Parche de mono
En este caso, "parche de mono" significa agregar una nueva variable o método a una clase después de que se haya definido. Por ejemplo, digamos que definimos la clase A
como
class A(object):
def __init__(self, num):
self.num = num
def __add__(self, other):
return A(self.num + other.num)
Pero ahora queremos agregar otra función más adelante en el código. Supongamos que esta función es la siguiente.
def get_num(self):
return self.num
Pero, ¿cómo añadimos esto como un método en A
? Eso es simple, simplemente colocamos esa función en A
con una declaración de asignación.
A.get_num = get_num
¿Por qué funciona esto? Porque las funciones son objetos como cualquier otro objeto, y los métodos son funciones que pertenecen a la clase.
La función get_num
estará disponible para todos los existentes (ya creados) así como para las nuevas instancias de A
Estas adiciones están disponibles en todas las instancias de esa clase (o sus subclases) automáticamente. Por ejemplo:
foo = A(42)
A.get_num = get_num
bar = A(6);
foo.get_num() # 42
bar.get_num() # 6
Tenga en cuenta que, a diferencia de otros idiomas, esta técnica no funciona para ciertos tipos integrados y no se considera un buen estilo.
Listado de todos los miembros de la clase
La función dir()
se puede usar para obtener una lista de los miembros de una clase:
dir(Class)
Por ejemplo:
>>> dir(list)
['__add__', '__class__', '__contains__', '__delattr__', '__delitem__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '__iadd__', '__imul__', '__init__', '__iter__', '__le__', '__len__', '__lt__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__reversed__', '__rmul__', '__setattr__', '__setitem__', '__sizeof__', '__str__', '__subclasshook__', 'append', 'clear', 'copy', 'count', 'extend', 'index', 'insert', 'pop', 'remove', 'reverse', 'sort']
Es común buscar solo miembros "no mágicos". Esto se puede hacer usando una comprensión simple que enumera los miembros con nombres que no comienzan con __
:
>>> [m for m in dir(list) if not m.startswith('__')]
['append', 'clear', 'copy', 'count', 'extend', 'index', 'insert', 'pop', 'remove', 'reverse', 'sort']
Advertencias:
Las clases pueden definir un __dir__()
. Si ese método existe, se llama a dir()
a __dir__()
, de lo contrario, Python intentará crear una lista de miembros de la clase. Esto significa que la función dir puede tener resultados inesperados. Dos citas de importancia de la documentación oficial de python :
Si el objeto no proporciona dir (), la función intenta recopilar información del atributo dict del objeto, si está definido, y de su tipo de objeto. La lista resultante no está necesariamente completa, y puede ser inexacta cuando el objeto tiene un getattr () personalizado.
Nota: dado que dir () se proporciona principalmente como una conveniencia para su uso en una solicitud interactiva, intenta proporcionar un conjunto interesante de nombres más de lo que trata de proporcionar un conjunto de nombres definido de manera rigurosa o consistente, y su comportamiento detallado puede cambiar lanzamientos Por ejemplo, los atributos de metaclase no están en la lista de resultados cuando el argumento es una clase.
Introducción a las clases
Una clase, funciona como una plantilla que define las características básicas de un objeto en particular. Aquí hay un ejemplo:
class Person(object):
"""A simple class.""" # docstring
species = "Homo Sapiens" # class attribute
def __init__(self, name): # special method
"""This is the initializer. It's a special
method (see below).
"""
self.name = name # instance attribute
def __str__(self): # special method
"""This method is run when Python tries
to cast the object to a string. Return
this string when using print(), etc.
"""
return self.name
def rename(self, renamed): # regular method
"""Reassign and print the name attribute."""
self.name = renamed
print("Now my name is {}".format(self.name))
Hay algunas cosas que se deben tener en cuenta al observar el ejemplo anterior.
- La clase se compone de atributos (datos) y métodos (funciones).
- Los atributos y métodos se definen simplemente como variables y funciones normales.
- Como se indica en la cadena de documentación correspondiente, el
__init__()
se llama inicializador . Es equivalente al constructor en otros lenguajes orientados a objetos, y es el método que se ejecuta por primera vez cuando crea un nuevo objeto o una nueva instancia de la clase. - Los atributos que se aplican a toda la clase se definen primero y se denominan atributos de clase .
- Los atributos que se aplican a una instancia específica de una clase (un objeto) se denominan atributos de instancia . Generalmente se definen dentro de
__init__()
; esto no es necesario, pero se recomienda (ya que los atributos definidos fuera de__init__()
corren el riesgo de ser accedidos antes de que se definan). - Cada método, incluido en la definición de clase, pasa el objeto en cuestión como su primer parámetro. La palabra
self
se usa para este parámetro (el uso deself
es en realidad por convención, ya que la palabraself
no tiene un significado inherente en Python, pero esta es una de las convenciones más respetadas de Python, y siempre se debe seguir). - Aquellos que están acostumbrados a la programación orientada a objetos en otros lenguajes pueden sorprenderse por algunas cosas. Una es que Python no tiene un concepto real de elementos
private
, por lo que todo, de manera predeterminada, imita el comportamiento de la palabra clavepublic
C ++ / Java. Para obtener más información, consulte el ejemplo "Miembros de la clase privada" en esta página. - Algunos de los métodos de la clase tienen la siguiente forma:
__functionname__(self, other_stuff)
. Todos estos métodos se denominan "métodos mágicos" y son una parte importante de las clases en Python. Por ejemplo, la sobrecarga de operadores en Python se implementa con métodos mágicos. Para más información, consulte la documentación relevante .
Ahora vamos a hacer algunos ejemplos de nuestra clase de Person
!
>>> # Instances
>>> kelly = Person("Kelly")
>>> joseph = Person("Joseph")
>>> john_doe = Person("John Doe")
Actualmente tenemos tres objetos Person
, kelly
, joseph
y john_doe
.
Podemos acceder a los atributos de la clase desde cada instancia utilizando el operador de punto .
Note nuevamente la diferencia entre los atributos de clase e instancia:
>>> # Attributes
>>> kelly.species
'Homo Sapiens'
>>> john_doe.species
'Homo Sapiens'
>>> joseph.species
'Homo Sapiens'
>>> kelly.name
'Kelly'
>>> joseph.name
'Joseph'
Podemos ejecutar los métodos de la clase usando el mismo operador de punto .
:
>>> # Methods
>>> john_doe.__str__()
'John Doe'
>>> print(john_doe)
'John Doe'
>>> john_doe.rename("John")
'Now my name is John'
Propiedades
Las clases de Python admiten propiedades , que parecen variables de objetos normales, pero con la posibilidad de adjuntar comportamiento y documentación personalizados.
class MyClass(object):
def __init__(self):
self._my_string = ""
@property
def string(self):
"""A profoundly important string."""
return self._my_string
@string.setter
def string(self, new_value):
assert isinstance(new_value, str), \
"Give me a string, not a %r!" % type(new_value)
self._my_string = new_value
@string.deleter
def x(self):
self._my_string = None
De la clase de objeto MyClass
parecerá tener tiene una propiedad .string
, sin embargo su comportamiento está ahora estrictamente controlado:
mc = MyClass()
mc.string = "String!"
print(mc.string)
del mc.string
Además de la sintaxis útil que se mencionó anteriormente, la sintaxis de la propiedad permite la validación u otros aumentos a esos atributos. Esto podría ser especialmente útil con las API públicas, donde se debe brindar un nivel de ayuda al usuario.
Otro uso común de las propiedades es permitir que la clase presente "atributos virtuales", atributos que no se almacenan en realidad, sino que se calculan solo cuando se solicitan.
class Character(object):
def __init__(name, max_hp):
self._name = name
self._hp = max_hp
self._max_hp = max_hp
# Make hp read only by not providing a set method
@property
def hp(self):
return self._hp
# Make name read only by not providing a set method
@property
def name(self):
return self.name
def take_damage(self, damage):
self.hp -= damage
self.hp = 0 if self.hp <0 else self.hp
@property
def is_alive(self):
return self.hp != 0
@property
def is_wounded(self):
return self.hp < self.max_hp if self.hp > 0 else False
@property
def is_dead(self):
return not self.is_alive
bilbo = Character('Bilbo Baggins', 100)
bilbo.hp
# out : 100
bilbo.hp = 200
# out : AttributeError: can't set attribute
# hp attribute is read only.
bilbo.is_alive
# out : True
bilbo.is_wounded
# out : False
bilbo.is_dead
# out : False
bilbo.take_damage( 50 )
bilbo.hp
# out : 50
bilbo.is_alive
# out : True
bilbo.is_wounded
# out : True
bilbo.is_dead
# out : False
bilbo.take_damage( 50 )
bilbo.hp
# out : 0
bilbo.is_alive
# out : False
bilbo.is_wounded
# out : False
bilbo.is_dead
# out : True
Clase de singleton
Un singleton es un patrón que restringe la creación de instancias de una clase a una instancia / objeto. Para más información sobre los patrones de diseño singleton de python, consulte aquí .
class Singleton:
def __new__(cls):
try:
it = cls.__it__
except AttributeError:
it = cls.__it__ = object.__new__(cls)
return it
def __repr__(self):
return '<{}>'.format(self.__class__.__name__.upper())
def __eq__(self, other):
return other is self
Otro método es decorar tu clase. Siguiendo el ejemplo de esta respuesta, crea una clase Singleton:
class Singleton:
"""
A non-thread-safe helper class to ease implementing singletons.
This should be used as a decorator -- not a metaclass -- to the
class that should be a singleton.
The decorated class can define one `__init__` function that
takes only the `self` argument. Other than that, there are
no restrictions that apply to the decorated class.
To get the singleton instance, use the `Instance` method. Trying
to use `__call__` will result in a `TypeError` being raised.
Limitations: The decorated class cannot be inherited from.
"""
def __init__(self, decorated):
self._decorated = decorated
def Instance(self):
"""
Returns the singleton instance. Upon its first call, it creates a
new instance of the decorated class and calls its `__init__` method.
On all subsequent calls, the already created instance is returned.
"""
try:
return self._instance
except AttributeError:
self._instance = self._decorated()
return self._instance
def __call__(self):
raise TypeError('Singletons must be accessed through `Instance()`.')
def __instancecheck__(self, inst):
return isinstance(inst, self._decorated)
Para usar puedes usar el método de Instance
@Singleton
class Single:
def __init__(self):
self.name=None
self.val=0
def getName(self):
print(self.name)
x=Single.Instance()
y=Single.Instance()
x.name='I\'m single'
x.getName() # outputs I'm single
y.getName() # outputs I'm single