Descriptors and side effects
Bruno Desthuilliers
bruno.42.desthuilliers at wtf.websiteburo.oops.com
Mon Nov 5 04:07:38 EST 2007
mrkafk at gmail.com a écrit :
> Hello everyone,
>
> I'm trying to do seemingly trivial thing with descriptors: have
> another attribute updated on dot access in object defined using
> descriptors.
>
> For example, let's take a simple example where you set an attribute s
> to a string and have another attribute l set automatically to its
> length.
>
>>>> class Desc(str):
> def __init__(self,val):
> self.s=val
> self.l=len(val)
> print "creating value: ", self.s
> print "id(self.l)", id(self.l)
> def __set__(self, obj, val):
> self.s=val
> self.l=len(val)
> print "setting value:", self.s, "length:", self.l
> def __get__(self, obj, type=None):
> print "getting value:", self.s, "length:", self.l
> return self.l
>
>
>>>> class some(str):
> m=Desc('abc')
> l=m.l
First point : I don't get why Desc and some derive from str. Second
point: I don't get why you're storing the value and it's length in the
descriptor itself - obviously, you can't expect one descriptor instance
to be mapped to 2 distinct attributes. Third point: you understand that,
the way you wrote it, your descriptor will behave as a class (ie:shared)
attribute, don't you. Fourth point: if you hope some.l to be rebound
when m.l is, then you should learn Python basics before trying to jump
into descriptors.
The obvious, simple way to some your problem is to use a couple of
properties:
class Some(object):
@apply
def m():
def fget(self):
return self._m
def fset(self, val):
self._m = val
self._l = len(val)
return property(**locals())
@apply
def l():
def fget(self):
return self._l
def fset(self):
raise AttributeError("%s.l is readonly" % self)
def __init__(self, m):
self.m = m
Now if you absolutely insist on using custom descriptors, you'll need
two of them: one to manage access to s, and the second to manage access
to l (which btw is a very bad name):
class DescS(object):
def __init__(self, val):
self._default = val
def __set___(self, obj, val):
obj._s = val
obj._l = len(val)
def __get__(self, obj, cls):
if obj is None:
return self # or self._default, or whatever
try:
return obj._s
except AttributeError:
return self._default
class DescL(object):
def __init__(self, descS):
self._descS = descS
def __get__(self, obj, cls):
if obj is None:
return self # or self._default, or whatever
try:
return obj._l
except AttributeError:
return len(self._descS._default)
class Test(object):
m = DescS('abc')
l = DescL(m)
(nb : not tested)
More information about the Python-list
mailing list