Python provides some advanced control over an object's attributes.
In the section called “Attribute Handling Special Method Names” we explicitly separated
attribute and instance variable. The default rule is that an attribute
name gives us access to an instance variable.
An attribute is a name that appears after an object name. For
example, someObj.name.
An instance variable is a name in the
__dict__ of an object.
When we say someObj.name, the default behavior is
effectively someObj.__dict__['name']. However, we can alter
this by making the name into a property or associating the name with a
descriptor object.
There are several ways that we can tap into Python's internal
mechanisms for getting and setting attribute values.
The most accessible technique is to use the
property function to define get, set and delete
methods associated with an attribute name. The
property function builds descriptors for you.
We'll look at this in the section called “Properties”.
A slightly less accessible, but more extensible and reusable
technique is to define descriptor classes
yourself. This allows you considerable flexibility. You do this by
creating a class which defines get, set and delete methods, and you
associate your descriptor class with an attribute name. We'll look at
this in the section called “Descriptors”.
Fundamentally, an object encapsulates data and processing via it's
instance variables and method functions. Because of this encapsulation,
we can think of a class definition as providing a interface definition
and an implementation that fits the defined interface. The method names
and public instance variables which begin with _ are
treated as part of the private implementation of the class; the remaming
elements form the public interface.
In Python, this distinction between interface and implementation
is not heavily emphasized in the syntax, since it can often lead to
wordy, complex programs. Most will-designed classes, however, tend to
have a set of interface methods that form the interface for
collaborating with objects of that class.
There are several commonly-used design patterns for an object's
interface.
Getters and Setters. We can encapsulate each instance variable with method
functions that get and set the value of that instance variable. To
be sure that the instace variables aren't accessed except via the
method functions, we make each instance variable private, using
_ as the first character of the variable name. When
we do this, each access to an attribute of the object is via an
explicit function: anObject.setPrice( someValue );
anObject.getValue().
Properties. We can bind getter, setter (and deleter) functions with an
attribute name, using the built-in property
function. When we do this, each reference to an attribute looks
like simple, direct access, but invokes the appropriate function
of the object. For example, anObject.price= someValue;
anObject.value.
Descriptors. We can bind getter, setter (and deleter) functions into a
separate class. We then assign an object of this class to the
attribute name. When we do this, each reference to an attribute
looks like simple, direct access, but invokes an appropriate
function of the Descriptor object. For example,
anObject.price= someValue; anObject.value.
The Getter and Setter design
pattern is does not have a Pythonic look. This design is common in C++;
it is required in Java. Python programmers find the use of getter and
setter functions to be wordy and prefer to access attributes
directly.
Published under the terms of the Open Publication License