Follow Techotopia on Twitter

On-line Guides
All Guides
eBook Store
iOS / Android
Linux for Beginners
Office Productivity
Linux Installation
Linux Security
Linux Utilities
Linux Virtualization
Linux Kernel
System/Network Admin
Programming
Scripting Languages
Development Tools
Web Development
GUI Toolkits/Desktop
Databases
Mail Systems
openSolaris
Eclipse Documentation
Techotopia.com
Virtuatopia.com
Answertopia.com

How To Guides
Virtualization
General System Admin
Linux Security
Linux Filesystems
Web Servers
Graphics & Desktop
PC Hardware
Windows
Problem Solutions
Privacy Policy

  




 

 

Descriptors

A Descriptor is a class which provides detailed get, set and delete control over an attribute of another object. This allows you to define attributes which are fairly complex objects in their own right. The idea is that we can use simple attribute references in a program, but those simple references are actually method functions of a descriptor object.

This allows us to create programs that look like the following example.

>>> 
oven= Temperature()

>>> 
oven.farenheit= 450

>>> 
oven.celsius

232.22222222222223
>>> 
oven.celsius= 175

>>> 
oven.farenheit

347.0

In this example, we set one attribute and the value of another attribute changes to mirror it precisely.

A common use for descriptors is in an object-oriented database (or an object-relational mapping). In a database context, getting an attribute value may require fetching data objects from the file system; which may involve creating and executing a query in a database.

Descriptor Design Pattern. The Descriptor design pattern has two parts: the Owner and the attribute Descriptor . The Owner is usually a relatively complex object that uses one or more Descriptors for it's attributes. Each Descriptor class defines get, set and delete methods for a specific attribute of the Owner.

Note that Desciptors can easily be written as reusable, generic types of attributes. The Owner can have multiple instances of each Descriptor. Each use of a Desciptor class is a unique instance of a Descriptor object, bound to an attribute name when the Owner class is defined.

To be recognized as a Descriptor, a class must implement some combination of the following three methods.

__get__ ( self , instance , owner )

The instance argument is the self variable of the owning class; the owner argument is the owning class object. This method of the descriptor must return this attribute's value. If this descriptor implements a class level variable, the instance parameter can be ignored.

__set__ ( self , instance , value )

The instance argument is the self variable of the owning class. This method of the descriptor must set this attribute's value.

__delete__ ( self , instance )

The instance argument is the self variable of the owning class. This method of the descriptor must delete this attribute's value.

Sometimes, a descriptor class will also need an __init__ method function to initialize the descriptor's internal state. Less commonly, the descriptor may also need __str__ or __repr__ method functions to display the instance variable correctly.

You must also make a design decision when defining a descriptor. You must determine where the underlying instance variable is contained. You have two choices.

  • The Descriptor object has the instance variable.

  • The Owner object contains the instance variable. In this case, the descriptor class must use the instance parameter to reference values in the owning object.

Descriptor Example. Here's a simple example of an object with two attributes defined by descriptors. One descriptor (Celsius) contains it's own value. The other desriptor (Farenheit), depends on the Celsius value, showing how attributes can be "linked" so that a change to one directly changes the other.

Example 25.1. descriptor.py

class Celsius( object ):
    def __init__( self, value=0.0 ):
        self.value= float(value)
    def __get__( self, instance, owner ):
        return self.value
    def __set__( self, instance, value ):
        self.value= float(value)
        
class Farenheit( object ):
    def __get__( self, instance, owner ):
        return instance.celsius * 9 / 5 + 32
    def __set__( self, instance, value ):
        instance.celsius= (float(value)-32) * 5 / 9

class Temperature( object ):
    celsius= Celsius()
    farenheit= Farenheit()
1

We've defined a Celsius descriptor. The Celsius descriptor has an __init__ method which defines the attribute's value. The Celsius descriptor implements the __get__ method to return the current value of the attribute, and a __set__ method to change the value of this attribute.

2

The Farenheit descriptor implements a number of conversions based on the value of the celsius attribute. The __get__ method converts the internal value from Celsius to Farenheit. The __set__ method converts the supplied value (in Farenheit) to Celsius.

3

The owner class, Temperature has two attributes, both of which are managed by descriptors. One attribute, celsius, uses an instance of the Celsius descriptor. The other attribute, farenheit, uses an instance of the Fareheit descriptor. When we use one of these attributes in an assignment statement, the descriptor's __set__ method is used. When we use one of these attributes in an expression, the descriptor's __get__ method is used. We didn't show a __delete__ method; this would be used when the attribute is used in a del statement.

Let's look at what happens when we set an attribute value, for example, using oven.farenheit= 450. In this case, the farenheit attribute is a Descriptor with a __set__ method. This __set__ method is evaluated with instance set to the object which is being modified (the oven variable) and owner set to the Temperature class. The __set__ method computes the celsius value, and provides that to the celsius attribute of the instance. The Celsius descriptor simply saves the value.

When we get an attribute value, for example, using oven.celsius, the following happens. Since celsius is a Descriptor with a __get__ method, this method is evaluated, and returns the celsius temperature.


 
 
  Published under the terms of the Open Publication License Design by Interspire