Controlling Attribute Access


Sometimes, instead of denying access to an attribute, you may want only to limit access to it. For example, you might have an attribute that you want client code to be able to read, but not change. Python provides a few tools to accomplish this kind of thing, including properties. Properties allow you to manage exactly how an attribute is accessed or changed.

Introducing the Property Critter

The Property Critter program allows client code to read a Critter object's attribute that refers to its name, but imposes restrictions when client code attempts to change the attribute's value. If client code tries to assign the attribute the empty string, the program complains and does not allow the change. Figure 8.9 shows the results of the program.

click to expand
Figure 8.9: A property controls access to the Critter object's attribute for its name.

Using Get Methods

One way to control access to an attribute is to create access methods—methods that allow indirect access to attributes and often impose some sort of restriction on that access. One type of access method is a get method, which gets the value of an attribute. By convention, a get method's name always starts with the word "get." I wrote the simplest form of a get method for the private attribute __name, called get_name(). The method simply returns the value of the private attribute, which represents a critter's name.

 # Property Critter # Demonstrates get and set methods and properties # Michael Dawson - 3/26/03 class Critter(object):     """A virtual pet"""     def __init__(self, name):         print "A new critter has been born!"         self.__name = name     def get_name(self):         return self.__name 

Now, it's easy to get the value of the private attribute through the get method as you can see in this interactive session:

 >>> crit = Critter("Poochie") >>> print crit.get_name() Poochie 

By creating a get method, you can provide read access to a private attribute.

Using Set Methods

Since I want to allow controlled changes to the name of a critter, I created another type of access method, called a set method, which sets an attribute to a value. By convention, a set method's name always starts with the word "set." This new method, set_name(), allows a value to be assigned to the private variable __name; however, it imposes the restriction that the value cannot be the empty string.

     def set_name(self, new_name):         if new_name == "":             print "A critter's name can't be the empty string."         else:             self.__name = new_name             print "Name change successful." 

If I try to change the name of my critter to the empty string, set_name() won't let me:

 >>> crit.set_name("") A critter's name can't be the empty string. 

However, the method will allow me to set the name to anything else:

 >>> crit.set_name("Randolph") Name change successful. >>> print crit.get_name() Randolph 

Using Properties

Properties allow you to harness the power of access methods while hiding the implementation from the client. A property essentially wraps access methods around the consistent and familiar dot notation.

TRAP

Properties only work as intended with new-style classes. If you must work with old-style classes, you can control attribute access with the special methods __getattr__() and __setattr__(). You can find out about these methods through the online Python documentation at http://www.python.org/doc.

I use the property() function to create a property in the next line of the program:

     name = property(get_name, set_name) 

This code creates a property called name that allows indirect access to the private attribute __name through the get_name() and set_name() methods. Notice that the arguments of the property() function are the names of the methods, not calls to the methods, so they don't include parentheses.

To create a property, follow my example. Supply the property() function with get and set methods to allow controlled access to a private attribute. (You can supply just a get method to create a read-only property.) Finally, make sure to assign the resulting property to an attribute name which client code will use to access the property.

By using the new name property, I can get the name of my critter through the familiar dot notation as you can see in the beginning of this interactive session:

 >>> print crit.name Randolph 

This line of code invokes the get_name() method. It has the same effect as the line print get_name(), but it maintains the consistent dot notation format.

I can also set the name of my critter through dot notation:

 >>> crit.name = "Sammy" Name change successful. >>> print crit.name Sammy 

This first line of code indirectly invokes the set_name() method. It has the same effect as the line set_name("Sammy"), but it maintains the consistent dot notation format.

As before, if I try to make my critter's name the empty string, I can't:

 >>> crit.name = "" A critter's name can't be the empty string. 

The rest of the Property Critter program uses the name property to indirectly access the private __name attribute:

     def talk(self):         print "\nHi, I'm", self.name # main crit = Critter("Poochie") crit.talk() print "\nMy critter's name is:", print crit.name print "\nAttempting to change my critter's name." crit.name = "" print "\nAttempting to change my critter's name again." crit.name = "Randolph" crit.talk() raw_input("\n\nPress the enter key to exit.") 

As you can see, I access the name property in the talk() method of the Critter class the same way I access it in the main part of the program, through dot notation. You access a property the same way, whether you're in the class definition of the property or in some other part of the program.




Python Programming for the Absolute Beginner
Python Programming for the Absolute Beginner, 3rd Edition
ISBN: 1435455002
EAN: 2147483647
Year: 2003
Pages: 194

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net