Ethan Furman a écrit :

Let's head towards murkier waters (at least murkier to me -- hopefully they can be easily clarified): some of the attributes are read-only, such as record count; others are not directly exposed, but still settable, such as table version; and still others require a small amount of processing... at which point do I switch from simple attribute access to method access?

Short answer : you don't !-)

Long answer : well, in fact you do, but the client code doesn't have to be aware that it's in fact calling an accessor.

Before we go into more details, you have to know that Python has a pretty good support for computed attributes, with both a simple generic solution (the property type) and the full monty (custom types implementing the descriptor protocol). So from the "interface" POV, you should never have an explicit accessor method for what is semantically an attribute (wheter the attribute is a plain or a computed one being part of the implementation).

Let's start with your second point: "not directly exposed but still settable". I assume you mean "not part of the interface, only supposed to be accessed (rw) from the methods" - if not, please pardon my stupidity and provide better explanations !-). If yes: Python doesn't have "language inforced" access restrictions (private / protected / etc), but a *very strong* naming convention which is that names starting with a leading underscore are implementation details, not part of the official interface, and shouldn't be accessed directly. Kind of a "warranty voided if unsealed".

So if you have attributes you don't want to "expose" to the outside world, just add a single leading underscore to their names.

First and third points are solved by using computed attributes - usually a property. The property type takes a few accessor functions as arguments - typically, a getter and a setter, and eventually a "deleter". Used as a class attribute, a property instance will hook up into the attribute lookup / setup mechanism (__getattribute__ and __setattr__), and will call resp. it's getter or setter function, passing it the instance and (for the setter) value.

This directly solves the third point. For the first one, the obvious solution is to use a property with a setter that raises an exception - canonically, an AttributeError with a message explaining that the attribute is read-only.

And for something more hands-on:

class Person(object):
   def __init__(self, firstname, lastname, birthdate):
       self.firstname = firstname
       self.lastname = lastnale
       self.birthdate = birthdate
       self._foo = 42 # implementation only

   def _getfullname(self):
       return "%s %s" % (self.firstname, self.lastname)
   def _setfullname(self, value):
       raise AttributeError("%s.fullname is read-only" % type(self)
   fullname = property(fget=_getfullname, fset=_setfullname)

   def _getage(self):
       return some_computation_with(self.birthdate)
   def _setage(self, value):
       raise AttributeError("%s.age is read-only" % type(self)
   age = property(fget=_getage, fset=_setage)


For more on computed attributes, you may want to read about the "descriptor protocol" (google is your friend as usual). This and the attribute resolution mechanism are fundamental parts of Python's inner working. Learn how it works if you really want to leverage Python's power.

HTH
--
http://mail.python.org/mailman/listinfo/python-list

Reply via email to