On Thu, Mar 15, 2012 at 11:32 AM, Steven W. Orr <steveo at syslang.net> wrote:
> Question 1:
>> I have a class A with one attribute and I define __get__ and __set__ for
> that class. Then I create another class B that uses it.
>> Why does B require that the instance of A be a class variable in B and not
> created as an instance variable in __init__?
Because that's the way descriptors work -- you put them on a class,
and they modify the behavior of instances of that class w.r.t. the
attribute they're stored in.
If you store an object that implements the descriptor protocol in an
instance attribute, then it is treated as ordinary data, not as a
descriptor (which would often be undesirable). For example, this
behavior is the reason that you can store functions in instance
attributes without having them automagically turn into methods of the
instance:
def make_color(name):
if name == 'red':
return (255, 0, 0)
elif name == 'green':
return (0, 255, 0)
elif name == 'blue':
return (0, 0, 255)
else:
raise ValueError('Unsupported color %r' % name)
class FactoryUser(object):
def __init__(self, factory):
self.factory = factory
def show_blue(self):
color = self.factory('blue')
print(color)
FactoryUser(make_color).show_blue()
Here, if "self.factory" were treated as a descriptor, then it would
become a method of the FactoryUser instance instead of just an
arbitrary function, and the call "self.factory('blue')" would fail,
because it would try to pass "self" in as the first argument to
"make_color", which it is not expecting since it is not a member of
FactoryUser.
> Question2:
>> Is it the case that people only use descriptors for classes with single
> attributes? Or is it more frequent that descriptors are used with classes
> that have multiple attributes?
property is a convenient shorthand for a one-off descriptor. I use
descriptor classes instead when I have a generic behavior that I want
to use across multiple attributes, either on a single class or in
multiple classes. As a simple, real example, I have a wxPython app
with some classes for dialog windows, each of which contain one or
more text controls, and I want to be able to access the contents of
those text controls as attributes. I could create a whole bunch of
properties, each of which does basically the same thing, or I could
just use this descriptor class:
class TextDescriptor(object):
def __init__(self, control_name):
self._control_name = control_name
def __get__(self, instance, owner):
if instance is not None:
return getattr(instance, self._control_name).GetValue()
def __set__(self, instance, value):
getattr(instance, self._control_name).ChangeValue(value)
Then to create the individual properties, all I have to do is this:
class MyDialog(wx.Dialog):
name = TextDescriptor('_name')
description = TextDescriptor('_description')
address = TextDescriptor('_address')
def __init__(self, ...):
# Build the dialog along with the _name, _description, and
_address controls...
Cheers,
Ian