El decorador de propiedades de Python

    A menudo se considera una buena pr谩ctica crear captadores y definidores para las propiedades p煤blicas de una clase. Muchos lenguajes le permiten implementar esto de diferentes maneras, ya sea usando una funci贸n (como person.getName()), o mediante el uso de un idioma espec铆fico get o set construir. En Python, se hace usando @property.

    En este art铆culo, describir茅 el decorador de propiedades de Python, que es posible que haya visto que se usa con el @decorator sintaxis:

    class Person(object):
        def __init__(self, first_name, last_name):
            self.first_name = first_name
            self.last_name = last_name
    
        @property
        def full_name(self):
            return self.first_name + ' ' + self.last_name
    
        @full_name.setter
        def full_name(self, value):
            first_name, last_name = value.split(' ')
            self.first_name = first_name
            self.last_name = last_name
    
        @full_name.deleter
        def full_name(self):
            del self.first_name
            del self.last_name
    

    Esta es la forma en que Python crea captadores, definidores y eliminadores (o m茅todos mutantes) para una propiedad en una clase.

    En este caso, el @property decorador lo hace para que llames al full_name(self) m茅todo como si fuera una propiedad normal, cuando en realidad es un m茅todo que contiene c贸digo que se ejecutar谩 cuando se establezca la propiedad.

    El uso de un captador / definidor / eliminador como este nos proporciona bastantes ventajas, algunas de las cuales he enumerado aqu铆:

    • Validaci贸n: antes de configurar la propiedad interna, puede validar que el valor proporcionado cumple con algunos criterios y hacer que arroje un error si no lo hace.
    • Carga diferida: los recursos pueden cargado perezosamente aplazar el trabajo hasta que sea realmente necesario, ahorrando tiempo y recursos
    • Abstracci贸n: los captadores y definidores le permiten abstraer la representaci贸n interna de los datos. Como en nuestro ejemplo anterior, por ejemplo, los nombres y apellidos se almacenan por separado, pero los captadores y definidores contienen la l贸gica que usa el nombre y los apellidos para crear el nombre completo.
    • Depuraci贸n: dado que los m茅todos mutadores pueden encapsular cualquier c贸digo, se convierte en un gran lugar para la interceptaci贸n al depurar (o registrar) su c贸digo. Por ejemplo, puede registrar o inspeccionar cada vez que se cambia el valor de una propiedad.

    Python logra esta funcionalidad con decoradores, que son m茅todos especiales que se utilizan para cambiar el comportamiento de otra funci贸n o clase. Para describir c贸mo @property decorador funciona, echemos un vistazo a un decorador m谩s simple y c贸mo funciona internamente.

    Un decorador es simplemente una funci贸n que toma otra funci贸n como argumento y la agrega a su comportamiento envolvi茅ndola. He aqu铆 un ejemplo sencillo:

    # decorator.py
    
    def some_func():
        print 'Hey, you guys'
    
    def my_decorator(func):
        def inner():
            print 'Before func!'
            func()
            print 'After func!'
    
        return inner
    
    print 'some_func():'
    some_func()
    
    print ''
    
    some_func_decorated = my_decorator(some_func)
    
    print 'some_func() with decorator:'
    some_func_decorated()
    

    Ejecutar este c贸digo te da:

    $ python decorator.py
    some_func():
    Hey, you guys
    
    some_func() with decorator:
    Before func!
    Hey, you guys
    After func!
    

    Como puede ver, el my_decorator() function crea din谩micamente una nueva funci贸n para regresar usando la funci贸n de entrada, agregando c贸digo para ser ejecutado antes y despu茅s de que se ejecute la funci贸n original.

    los property El decorador se implementa con un patr贸n similar al my_decorator funci贸n. Usando Python @decorator sintaxis, recibe la funci贸n decorada como argumento, como en mi ejemplo: some_func_decorated = my_decorator(some_func).

    Entonces, volviendo a mi primer ejemplo, este c贸digo:

    @property
    def full_name_getter(self):
        return self.first_name + ' ' + self.last_name
    

    Es aproximadamente equivalente a esto:

    def full_name_getter(self):
        return self.first_name + ' ' + self.last_name
    
    full_name = property(full_name_getter)
    

    Tenga en cuenta que cambi茅 algunos nombres de funciones para mayor claridad.

    Luego, m谩s tarde, cuando quieras usar @full_name.setter como hacemos en el ejemplo, lo que realmente est谩s llamando es:

    def full_name_setter(self, value):
        first_name, last_name = value.split(' ')
        self.first_name = first_name
        self.last_name = last_name
    
    full_name = property(full_name_getter)
    full_name = full_name.setter(full_name_setter)
    

    Ahora esta nueva full_name objeto (una instancia del property object) tiene m茅todos getter y setter.

    Para usarlos con nuestra clase, Person, la property El objeto act煤a como un descriptor, lo que significa que tiene su propio __obtener__(), __conjunto__() y __Eliminar__() m茅todos. los __get__() y __set__() Los m茅todos se activan en un objeto cuando se recupera o establece una propiedad, y __delete__() se activa cuando se elimina una propiedad con del.

    Entonces person.full_name="Billy Bob" desencadena el __set__() m茅todo, que fue heredado de object. Esto nos lleva a un punto importante: su clase debe heredar de object para que esto funcione. Entonces una clase como esta no ser capaz de utilizar las propiedades del setter ya que no hereda de object:

    class Person:
        pass
    

    Gracias a property, estos m茅todos ahora corresponden a nuestro full_name_getter y full_name_setter m茅todos de arriba:

    full_name.fget is full_name_getter    # True
    full_name.fset is full_name_setter    # True
    

    fget y fset ahora est谩n envueltos por .__get__() y .__set__(), respectivamente.

    Y finalmente, se puede acceder a estos objetos descriptores pasando una referencia a nuestra clase, Person:

    >>> person = Person('Billy', 'Bob')
    >>> 
    >>> full_name.__get__(person)
    Billy Bob
    >>>
    >>> full_name.__set__(person, 'Timmy Thomas')
    >>>
    >>> person.first_name
    Timmy
    >>> person.last_name
    Thomas
    

    As铆 es esencialmente como funcionan las propiedades debajo de la superficie.

     

    Etiquetas:

    Deja una respuesta

    Tu direcci贸n de correo electr贸nico no ser谩 publicada. Los campos obligatorios est谩n marcados con *