Unidad 3 Python Avanzado
Unidad 3 Python Avanzado
DIPLOMATURA EN PYTHON
Centro de e
e-Learning SCEU UTN - BA.
Medrano 951 2do piso (1179) // Tel. +54 11 4867 7589 / Fax +54 11 4032 0148
www.sceu.frba.utn.edu.ar/e
www.sceu.frba.utn.edu.ar/e-learning
p. 2
Presentación:
Los atributos son nombres accesibles por los objetos de una clase que podemos definir
dentro de la clase, heredarlos o crearlos dinámicamente. La forma de utilizarlos por un
objeto es mediante la notación de punto, por ejemplo podríamos hacer que un objeto
acceda al atributo “color” de la clase a la cual pertenece así: objeto.color.
Objetivos:
Que los participantes:
Bloques temáticos:
1.- Introducción – Manipulación de atributos.
Es importante que todos los participantes realicen algunas de las actividades sugeridas y
compartan en los foros los resultados obtenidos.
El carácter constructivista y colaborativo del MEC nos exige que todas las actividades
realizadas por los participantes sean compartidas en los foros.
Tomen nota
Las actividades son opcionales y pueden realizarse en forma individual, pero siempre es
deseable que se las realice en equipo, con la finalidad de estimular y favorecer el trabajo
colaborativo y el aprendizaje entre pares. Tenga en cuenta que, si bien las actividades
son opcionales, su realización es de vital importancia para el logro de los objetivos de
aprendizaje de esta instancia de formación. Si su tiempo no le permite realizar todas las
actividades, por lo menos realice alguna, es fundamental que lo hhaga.
aga. Si cada uno de los
participantes realiza alguna, el foro, que es una instancia clave en este tipo de cursos,
tendrá una actividad muy enriquecedora.
Asimismo, también tengan en cuenta cuando trabajen en la Web, que en ella hay de todo,
cosas excelentes,
entes, muy buenas, buenas, regulares, malas y muy malas. Por eso, es
necesario aplicar filtros críticos para que las investigaciones y búsquedas se encaminen a
la excelencia. Si tienen dudas con alguno de los datos recolectados, no dejen de consultar
al profesor-tutor.
tutor. También aprovechen en el foro proactivo las opiniones de sus
compañeros de curso y colegas.
La propiedad es creada mediante la asignación del resultado de una función del built
built-in a
un atributo de clase:
propiedades1.py
1 class Cliente:
2
3 def __init__(self, usuario):
4 self._usuario = usuario
5
6 def get_usuario(self):
7 print('Recupera el usuario...')
8 return self._usuario
9
10 def set_usuario(self,
(self, valor):
11 print('Modifica el usuario...')
12 self._usuario = valor
13
14 def del_usuario(self):
15 print('Remueve el usuario...')
16 del self._usuario
17 usuario = property(get
get_usuario, set_usuario, del_usuario,, "Datos adicionales")
18
19 cliente1 = Cliente('Juan')
20 print(cliente1.usuario)
21 cliente1.usuario = 'Pedro'
22 print(cliente1.usuario)
23 del cliente1.usuario
24 print(Cliente.usuario.__doc__)
Al ejecutarlo obtenemos:
Recupera el usuario...
Juan
Modifica el usuario...
Recupera el usuario...
Pedro
Remueve el usuario...
Datos adicionales
Herencia de propiedades
Como los atributos, las propiedades pueden ser heredadas. Notar que al atrapar el
usuario podemos actuar sobre el valor ob
obtenido,
tenido, en este caso podemos ver como en la
línea 8 transformamos el nombre a mayúscula.
propiedades2.py
1 class MiEmpresa:
2
3 def __init__(self, usuario):
4 self._usuario = usuario
5
6 def get_usuario(self):
7 print('Recupera el usuario...')
8 return self._usuario.upper()
9
10 def set_usuario(self,
(self, valor):
11 print('Modifica el usuario...')
12 self._usuario = valor
13
14 def del_usuario(self):
15 print('Remueve
int('Remueve el usuario...')
16 del self._usuario
17 usuario = property(get
get_usuario, set_usuario, del_usuario,, "Datos adicionales")
18
19 class Cliente(MiEmpresa): pass
20
21 cliente1 = Cliente('Juan')
22 print(cliente1.usuario)
23 cliente1.usuario = 'Pedro'
24 print(cliente1.usuario)
25 del cliente1.usuario
26 print(Cliente.usuario.__doc__)
Al ejecutarlo obtenemos:
Recupera el usuario...
JUAN
Modifica el usuario...
Recupera el usuario...
PEDRO
Remueve el usuario...
Datos adicionales
propiedades2.py
1 class Cliente:
2
3 def __init__(self, usuario):
4 self._usuario = usuario
5
6 @property
7 def usuario(self):
8 "Datos adicionales"
9 print('Recupera el usuario...')
10 return self._usuario.upper()
11
12 @usuario.setter
13 def usuario(self,
f, valor):
14 print('Modifica el usuario...')
15 self._usuario = valor
16
17 @usuario.deleter
18 def usuario(self):
19 print('Remueve el usuario...')
20 del self._usuario
21
22 cliente1 = Cliente('Juan')
23 print(cliente1.usuario)
24 cliente1.usuario = 'Pedro'
25 print(cliente1.usuario)
26 del cliente1.usuario
27 print(Cliente.usuario.__doc__)
Al ejecutarlo obtenemos:
Recupera el usuario...
JUAN
Modifica el usuario...
Recupera el usuario...
PEDRO
Remueve el usuario...
Datos adicionales
class Descriptor:
def __get__(self, instance, owner): ...
def __set__(self, instance, value): ...
def __delete__(self,
delete__(self, instance): ...
En este caso __get__ recibe un parámetro extra “owner”, el que indica la clase a la cual la
instancia del descriptor
escriptor se encuentra asociada, p
por lo que:
descriptor1.py
1 class DescriptorUsuario:
2
3 def __get__(self, instance, owner):
4 print(self, instance, owner, sep='
sep='\n')
5
6 class Cliente:
7
8 usuario = DescriptorUsuario()
9
10 cliente1 = Cliente()
11 cliente1.usuario
12 print('-'*15)
13 Cliente.usuario
Al ejecutarlo obtenemos:
Centro de e-Learning SCEU UTN - BA.
Medrano 951 2do piso (1179) // Tel. +54 11 4867 7589 / Fax +54 11 4032 0148
www.sceu.frba.utn.edu.ar/e
www.sceu.frba.utn.edu.ar/e-learning
p. 14
<__main__.DescriptorUsuario
__main__.DescriptorUsuario object at 0x008FF6B0>
<__main__.Cliente object at 0x0095E810>
<class '__main__.Cliente'>
---------------
<__main__.DescriptorUsuario object at 0x008FF6B0>
None
<class '__main__.Cliente'>
Asignación
ón en la instancia.
A diferencia de las propiedades, si omitimos __set__ le permite al nombre de atributo del
descriptor ser asignado o redefinido en la instancia. En el siguiente ejemplo en donde se
omite __set__ el atributo asignado a ““cliente1.usuario” almacena “usuario
usuario” en la instancia
del objeto “cliente1”,
”, ocultando de este modo el descriptor almacenado en la clase C Cliente.
Esta es la forma en la cual toda asignación de un atributo de instancia trabaja en python y
le permite a las clases sobreescribir de forma selectiva los niveles por defecto de clases
en sus instancias.
descriptor2.py
1 class DescriptorUsuario:
2
3 def __get__(*args):
4 print('get')
5
6 class Cliente:
7
8 usuario = DescriptorUsuario()
9
10 cliente1 = Cliente()
11 cliente1.usuario
12 Cliente.usuario
13 cliente1.usuario = 'Juan'
14 print(cliente1.usuario)
15 print(list(cliente1.__dict__.keys()))
Al ejecutarlo obtenemos:
Centro de e-Learning SCEU UTN - BA.
Medrano 951 2do piso (1179) // Tel. +54 11 4867 7589 / Fax +54 11 4032 0148
www.sceu.frba.utn.edu.ar/e
www.sceu.frba.utn.edu.ar/e-learning
p. 15
get
get
Juan
['usuario']
descriptor3.py
1 class DescriptorUsuario:
2
3 def __get__(*args):
4 print('get')
5
6 def __set__(*args):
7 raise AttributeError('No se puede realizar un set')
8
9 class Cliente:
10
11 usuario = DescriptorUsuario()
12
13 cliente1 = Cliente()
14 cliente1.usuario
15 Cliente.usuario
16 cliente1.usuario = 'Juan'
Al ejecutarlo obtenemos:
get
get
Traceback (most recent call last):
File "C:\Users\juanb\descriptor3.py",
descriptor3.py", line 16, in <module>
cliente1.usuario = 'Juan'
File "C:\Users\juanb\\descriptor3.py
descriptor3.py ", line 7, in __set__
raise AttributeError('No se puede realizar un set')
AttributeError:r: No se puede realizar un set
descriptor4.py
1 class DescriptorUsuario:
2
3 "Documentación de descriptor de nombre"
4 def __get__(self, instance
instance, owner):
5 print('Atrapa valor... ')
6 return instance._usuario.upper()
7
8 def __set__(self, instance, valor):
9 print('Cambia el valor ...')
10 instance._usuario = valor
11
12 def __delete__(self, instance):
13 print('Remover el atributo ...')
14 del instance._usuario
15
16 class Cliente:
17 def __init__(self, usuario):
18 self._usuario = usuario
19
20 usuario = DescriptorUsuario()
21
22 cliente1 = Cliente('Juan')
23 print(cliente1.usuario)
24 cliente1.usuario = 'Pedro'
25 print(cliente1.usuario)
26 del cliente1.usuario
27 print(cliente1.usuario)
Al ejecutarlo obtenemos:
Atrapa valor...
JUAN
Cambia el valor ...
Atrapa valor...
PEDRO
Remover el atributo ...
Atrapa valor...
Traceback (most recent call last):
File "C:\Users\juanb\\descriptor4.py",
descriptor4.py", line 27, in <module>
print(cliente1.usuario)
File "C:\Users\juanb\descriptor4.py",
descriptor4.py", line 6, in __get__
return instance._usuario.upper()
AttributeError: 'Cliente' object has no attribute '_usuario'
descriptor5.py
1 class Cliente:
2 def __init__(self, usuario):
3 self._usuario = usuario
4
5 class DescriptorUsuario:
6
7 "Documentación de descriptor de nombre"
8 def __get__(self, instance, owner):
9 print('Atrapa
a valor... ')
10 return instance._usuario.upper()
11
12 def __set__(self, instance, valor):
13 print('Cambia el valor ...')
14 instance._usuario = valor
15
16 def __delete__(self, instance):
17 print('Remover el atributo ...')
18 del instance._usuario
19
20 usuario = DescriptorUsuario()
21
22 cliente1 = Cliente('Juan')
23 print(cliente1.usuario)
24 cliente1.usuario = 'Pedro'
25 print(cliente1.usuario)
26 del cliente1.usuario
27 try:
28 print(cliente1.usuario)
29 except:
30 print('El usuario ya no existe')
31 print(Cliente.DescriptorUsuario.__doc__)
Al ejecutarlo obtenemos:
Atrapa valor...
JUAN
Cambia el valor ...
Atrapa valor...
PEDRO
Remover el atributo ...
Atrapa valor...
El usuario ya no existe
Documentación de descriptor de nombre
Dos comentarios:
Descriptor
escriptor y atributos de clientes.
También es posible para un Descriptor almacenar o usar atributos asociados a la
instancia de la clase cliente (atributos de instancia de la clase cliente)
descriptor6.py
1 class AccederInstanciaMail:
2 def __get__(self, instance, owner):
3 print('Obtener Estado de Instancia')
4 return instance._mail + '.ar'
5 def __set__(self, instance, valor):
6 print('Seteo de Estado de Instancia')
7 instance._mail = valor
8
9
10 class Cliente:
11 def __init__(self, usuario, _mail):
12 self._usuario = usuario
13 self._mail = _mail
14
15 class DescriptorUsuario:
16
17 "Documentación de descriptor de nombre"
18 def __get__(self, instance, owner):
19 print('Atrapa valor... ')
20 return instance._usuario.upper()
21
22 def __set__(self, instance, valor):
23 print('Cambia
t('Cambia el valor ...')
24 instance._usuario = valor
25
26 def __delete__(self, instance):
27 print('Remover el atributo ...')
28 del instance._usuario
29
30 usuario = DescriptorUsuario()
31 mail = AccederInstanci
AccederInstanciaMail()
32
33
34 cliente1 = Cliente('Juan', '[email protected]')
35 print(cliente1.usuario, cliente1._mail, cliente1.mail)
36 cliente1.usuario = 'Ana'
37 print(cliente1.usuario)
38 cliente1.mail = '[email protected]'
39 print(cliente1.usuario, cliente1._mail, cliente1.mail)
Al ejecutarlo obtenemos:
Atrapa valor...
Obtener Estado de Instancia
JUAN [email protected] [email protected]
Cambia el valor ...
Atrapa valor...
ANA
Seteo de Estado de Instancia
Atrapa valor...
Obtener Estado de Instancia
ANA [email protected] [email protected]
print(cliente1.__dict__)
descriptor7.py
1 class AccederInstanciaMail:
2
3 def __init__(self, mail):
4 self.mail = mail
5 def __get__(self, instance, owner):
6 return '%s, %s' % (self.mail, instance.mail)
7 def __set__(self, instance, valor):
8 instance.mail = valor
9
10 class Cliente:
11
12 def __init__(self, mail):
13 self.mail = mail
14 administradormail = AccederInstanciaMail('[email protected]')
15
16 cliente1 = Cliente('[email protected]')
liente('[email protected]')
17 print(cliente1.administradormail)
18 cliente1.administradormail = '[email protected]'
19 print(cliente1.administradormail)
20 print('-'*10)
21 print(cliente1.__dict__)
Al ejecutarlo obtenemos:
[email protected], [email protected]
[email protected], [email protected]
----------
{'mail': '[email protected]'}
__set_name__(self,owner,name)
Con este nuevo método, cada vez que se crea una instancia de un descriptor, se llama a
este método y el parámetro de nombre se establece automáticamente. Esto hace posible
crear su descriptor sin especificar el nombre del atributo interno que se necesita usar para
almacenar el valor.
descriptor36_o_mayor.py
class Descriptor():
def __set_name__(self, owner, nombre):
self.nombre = nombre
class Clase():
atributo_descriptor
escriptor = Descriptor()
objeto1 = Clase()
objeto2 = Clase()
objeto1.atributo_descriptor
escriptor = 3
print(objeto1.atributo_descriptor)
escriptor)
print(objeto2.atributo_descriptor)
escriptor)
4.__getattr__
__getattr__ y __getatribute__, __
__setatrr__,
__delattr__
Estos métodos también se pueden utilizar para interceptar atributos.
__getattribute__ : Es ejecutado para cada atributo. Hay que tener cuidado cuando
utilizamos este método para no sufrir un problema de loops de recursividad mediante
acceso de atributos a una superclase. (Requiere utilizar el nuevo estilo)(Seestilo puede
utilizar con todos los atributos)) ((Hay
Hay que tener cuidado con la generación de loops)
loops
Ejemplo
Estos métodos permiten atrapar, mostrar y setear atrib
atributos,, vamos a crear una clase que
en el caso de que creemos un atributo “color” lo capture con __getattr__ y retorne un color
aleatorio rgb para la web. En el caso de que el atributo no sea “color” solamente
guardamos el valor en el diccionario de la insta
instancia:
descriptor7.py
1 from random import randint
2
3 class Auto:
4
5 def __getattr__(self, dato):
6 if dato == 'color':
7 rojo = randint(0,
(0, 255)
8 verde = randint(0, 255)
9 azul = randint(0, 255)
10 return 'rgb('+str(rojo)+','+str(verde)+','+str(azul)+')'
('+str(rojo)+','+str(verde)+','+str(azul)+')'
11 else:
12 raise AttributeError(dato)
13
14 def __setattr__(self, dato, valor):
15 print('set:
('set: %s %s' % (dato, valor))
16 if dato == 'color':
17 self.__dict__['_color'] = valor
18
19 else:
20 self.__dict__[dato] = valor
21
22 auto1 = Auto()
23 print(auto1.color)
24 auto1.color = '(f,0,0)'
25 print('-------------------')
26 print(auto1._color)
27 print('-------------------')
28 print(auto1.color)
29 print(auto1.color)
30 print(auto1.color)
31 print('-------------------')
32 print(auto1._color)
33 print('-------------------')
34
35 auto1.altura = '1.6 metros'
36 print(auto1.altura)
37 print('-------------------')
38 print(auto1.__dict__)
Al ejecutarlo obtenemos:
rgb(33,188,80)
set: color (f,0,0)
-------------------
(f,0,0)
-------------------
rgb(221,31,104)
rgb(32,38,252)
rgb(29,5,101)
-------------------
(f,0,0)
-------------------
set: altura 1.6 metros
1.6 metros
-------------------
{'_color': '(f,0,0)', 'altura': '1.6 metros'}
metros'}1.6 metros
-------------------
{'_color': '(f,0,0)', 'altura': '1.6 metros'}
Problemas
roblemas con loops en __getattribute__
Al utilizar __getattribute__ o __setattr__ podemos crear un loop indefinido que puede
generar la saturación
ón de la memoria. Veamos cómo solucionarlo.
CASO DE __getattribute__
getattr_setattr_loop.py
1 class Auto:
2 def __init__(self):
3 self.color = 'Rojo'
4
5 def __getattribute__(self, color):
6 return self.color
7
8 auto1 = Auto()
9 print(auto1.color)
Al ejecutarlo obtenemos:
La forma de evitarlo es pasar el objeto atrapado o a una clase superior, como puede ser
object, para evitar de esta forma la recursividad
getattr_setattr_loop.py
1 class Auto:
2 def __init__(self):
3 self.color = 'Rojo'
4
5 def __getattribute__(self, color):
6 return object.__getattribute__(self, color)
7
8 auto1 = Auto()
9 print(auto1.color)
Al ejecutarlo obtenemos:
Rojo
por:
getattr_setattr_loop2.py
1 class Auto:
2 def __setattr__(self, atributo, valor):
3 if atributo == 'precio':
4 self.__dict__[atributo] = valor + 202000
5 #self.atributo = valor + 202000
6
7 else:
8 raise Auto(atributo + ' No permitido')
9 auto1 = Auto()
10 auto1.precio = 1000000
11 print(auto1.precio)
Al ejecutarlo obtenemos:
1202000
Al ejecutarlo obtenemos:
set: _nombre
get: nombre
Juan
set: nombre
del: nombre
Centro de e-Learning SCEU UTN - BA.
Medrano 951 2do piso (1179) // Tel. +54 11 4867 7589 / Fax +54 11 4032 0148
www.sceu.frba.utn.edu.ar/e
www.sceu.frba.utn.edu.ar/e-learning
p. 28
Manual online
https://docs.python.org/3.7/tutorial/
https://docs.python.org/3.7/library/index.html
https://docs.python.org/3/reference/datamodel.html
Lo que vimos
En esta unidad hemos trabajado sobre la manipulación de atributos..
Lo que viene:
En la siguiente unidad comenzaremos a trabajar con decoradores.