Python descriptors are not transparent when they're first used
Cole Tuininga
colet-KCgK2vT7wad/90uGnh1m2w at public.gmane.org
Mon Jul 30 21:06:35 EDT 2007
On Mon, 2007-07-30 at 15:46 -0400, Seth Gordon wrote:
> Then I set up a client class like this:
>
> > class Foo(object):
> > def __init__(self):
> > for attr in ('bar', 'baz', 'quux'):
> > self.__dict__[attr] = WriteOnly(attr)
> >
> > foo = Foo()
> > foo.bar = 4
> > foo.baz = None
> > print `foo.bar`
> > print `foo.baz`
> > print `foo.quux`
>
> Running that program (with Python 2.4.4) gives me this output:
>
> > 4
> > None
> > <WriteOnly.WriteOnly object at 0xf7daa28c>
>
> Why don't I get "None" on the third line? What subtle (or
> not-so-subtle) detail of Python descriptors am I missing?
What you've done is to set the attributes Foo.bar, Foo.baz, and Foo.quux
to instances of the WriteOnly class. However, because Foo.bar is a
reference (just like everything in Python), doing this:
foo.bar = 4
overrides the 'bar' attribute of the instance known as foo. It is no
longer a 'WriteOnly' instance. If you did a print str(type(foo.bar)),
it would report that it is now an instance of an Integer object.
Similarly, foo.baz = None is replacing the value of foo.baz (which used
to be a 'WriteOnly' instance) with the None object.
At this point, foo.quux is an instance of a 'WriteOnly' object and
because you're not overriding the __repr__ method, the "'"s are
defaulting to the normal behavior.
Oh, just as an FYI, I believe the "`" approach is deprecated in
deference to calling repr(whatever).
What I think you're really trying to to do is to override the
__setattr__ and __getattribute__ calls. Warning, code below is
untested, but I think you want something like this:
class WriteOnly(object):
def __init__(self, write_only_attrs):
self.__write_only = {}
for attr_name in write_only_attrs:
self.__write_only[attr_name] = 1
self.__write_once = {}
def __setitem__(self, key, value):
if self.__write_only.get(key) and self.__write_once.get(key):
oldval = self.__write_once.get(key)
msg = "Can't reset %s from %s to %s" % (key, oldval, value)
raise Attribute Error, msg
elif self.__write_only.get(key):
return self.__write_once[key] = value
else:
return self.__dict__[key] = value
def __getitem__(self, key):
if self.__write_only.get(key):
# Note this will throw an exception if you haven't set
# it yet
return self.__write_once[key]
else:
# Ditto
return self.__dict__[key]
class Foo(WriteOnly):
def __init__(self):
super(Foo, self).__init__(['bar', 'baz', 'quux'])
Feel free to ask questions.
--
A: Yes.
> Q: Are you sure?
>> A: Because it reverses the logical flow of conversation.
>>> Q: Why is top posting annoying in email?
Cole Tuininga
colet-KCgK2vT7wad/90uGnh1m2w at public.gmane.org
http://www.code-energy.com/
--
This message has been scanned for viruses and
dangerous content by MailScanner, and is
believed to be clean.
More information about the Discuss
mailing list