Innovate anywhere, anytime withruncode.io Your cloud-based dev studio.
Python

Programming with python Descriptors (_get_, _set_, _delete_) - MicroPyramid

2022-07-19

Python descriptors are object attributes that are only invoked for new style of classes. Python descriptors comes under the category of meta programming(code that manipulates code). The descriptors are accessed with special classes that allows to: -- manage properties of another class and -- implement an interface for get, set and delete We can also say that the descriptors are object attributes and the attribute access is overridden by one of the methods in descriptor protocol. Those methods are __get__(), __set__() and __delete__(). Now the question will be like why to use descriptors?? Descriptors are considered as a most powerful and interesting because: -- They provide you to handle the inputs in ordered way -- They are reusable properties which makes to avoid repetitive code. -- They provide you with a powerful validation tool that allows only particular type of data as requested. Creating descriptors:- There are three ways of creating a descriptor:

Type 1:- If you want a descriptor to be used for many different classes and attributes. eg: for type checking The structure of a descriptor class is as follows:

class descriptor(object):
  def __get__(self, instance owner):
    pass
  def __set__(self, instance, self):
    pass
  def __del__(self, instance)
    pass

-- The descriptor allows to send some special properties. self is the instance if the descriptor, instance is the instance of class or the instance of whole descriptors, owner is the type or the class that instance is a part of. Eg:

class Descriptor(object):
  def __init__(self):
    self._name = ";"
  def __get__(self, instance, owner):
    print "Getting: %s" % self._name return self._name
  def __set__(self, instance, name):
    print "Setting: %s" % name self._name = name.title()
  def __delete__(self, instance):
    print "Deleting: %s" %self._name del self._name

class Person(object):
  name = Descriptor()

Lets see the output:

>>> user = Person()
>>> user.name = 'john smith'
Setting: john smith
>>> user.name
Getting: John Smith 'John Smith'
>>> del user.name
Deleting: John Smith

Calling descriptor class is overriding the methods get,set and delete.

Type 2 :- The most simplest way of creating descriptors is to use a property type. By using a property type we are able to create descriptors for attributes only. Syntax for creating property() is: property(fget=None, fset=None, fdel=None, doc=None) Where fget – attribute get method fset – attribute set method fdel – attribute delete method doc – docstring Eg:-

class Person(object):
  def __init__(self):
    self._name = ';'
  def fget(self):
    print "Getting: %s" % self._name
    return self._name
  def fset(self, value):
    print "Setting: %s" % value
    self._name = value.title()
  def fdel(self):
    print "Deleting: %s" %self._name
  del self._name
    name = property(fget, fset, fdel, "I'm the property.")

Note:- -- The output will be same as the output for Type 1. -- The difference is that the property() attribute is used to define read-only attributes in the system. -- fset, fget and fdel methods are optional -- If we try to assign a value to the attribute it shows error like

name = property(fget, None, fdel, "I'm the property")
user.name = 'john smith'
Traceback (most recent call last):
File stdin, line 21, in mоdule
user.name = 'john smith'
AttributeError: can't set attribute

Type 3 :- Creating descriptors using property decorators which are a combination of property type method and Python decorators. what are python decorators? A decorator is a callable object in python that takes a function/method/class definition as an argument and returns a replacement of its type ie.. it is used to modify a function/method/class definition. eg:-

>>> def outer(some_func):
... def inner():
... print "before some_func"
... ret = some_func() # 1
... return ret + 1
... return inner >>> def foo():
... return 1
>>> decorated = outer(foo) # 2
>>> decorated() before some_func
2

We can replace the decorated class with foo itself so that no new class to be created. This will re-assign the old class with decorated class:

Now foo is the decorated version of the original foo The symbol @: This symbol is used to assign a decorater to a function. For the above example we can write @outer >>> foo This will assign the decorator outer to the function foo Now creating descriptor using decorator: eg:

The below is an example for decorator.
class Person(object):
  def __init__(self):
    self._name = ';'
  @property
  def name(self):
    print "Getting: %s" % self._name
    return self._name
  @name.setter
  def name(self, value):
    print "Setting: %s" % value
    self._name = value.title()
  @name.deleter
  def name(self):
    print "Deleting: %s" % self._name
    del self._name

The code is so complex and it has lot of code. To resolve this issue the concept of descriptors came. Type 1 will resolve this issue.