implementing descriptors

D

dippim

I am new to Python and I have a question about descriptors. If I have
a class as written below, is there a way to use descriptors to be
certain that the datetime in start is always before the one in end?

class foo(object):
def __init__(self,a = None,b = None)
self.start = a
self.end = b

from datetime import datetime
c = datetime(2009,8,13,6,15,0)
d = datetime(2009,8,14,12,0,0)
afoo = foo(c,d)

For instance, if the following code were run, I would like to instance
of foo to switch the start and end times.

afoo.start = datetime(2010,8,13,6,15,0)

I was thinking of using the __set__ descriptor to catch the assignment
and reverse the values if necessary, but I can't figure out how to
determine which values is being set.

Thanks for the help!

David
 
M

Mark Lawrence

dippim said:
I am new to Python and I have a question about descriptors. If I have
a class as written below, is there a way to use descriptors to be
certain that the datetime in start is always before the one in end?

class foo(object):
def __init__(self,a = None,b = None)
self.start = a
self.end = b

from datetime import datetime
c = datetime(2009,8,13,6,15,0)
d = datetime(2009,8,14,12,0,0)
afoo = foo(c,d)

For instance, if the following code were run, I would like to instance
of foo to switch the start and end times.

afoo.start = datetime(2010,8,13,6,15,0)

I was thinking of using the __set__ descriptor to catch the assignment
and reverse the values if necessary, but I can't figure out how to
determine which values is being set.

Thanks for the help!

David
Take a look at the example given here.
http://users.rcn.com/python/download/Descriptor.htm
 
E

Emile van Sebille

On 8/13/2009 3:17 PM dippim said...
I am new to Python and I have a question about descriptors. If I have
a class as written below, is there a way to use descriptors to be
certain that the datetime in start is always before the one in end?

class foo(object):
def __init__(self,a = None,b = None)
self.start = a
self.end = b

from datetime import datetime
c = datetime(2009,8,13,6,15,0)
d = datetime(2009,8,14,12,0,0)
afoo = foo(c,d)

For instance, if the following code were run, I would like to instance
of foo to switch the start and end times.

afoo.start = datetime(2010,8,13,6,15,0)

I was thinking of using the __set__ descriptor to catch the assignment
and reverse the values if necessary,

why not...


class foo(object):
def __init__(self,a = None,b = None)
self.start = min(a,b)
self.end = max(a,b)


Emile
 
R

Raymond Hettinger

[David]
I am new to Python and I have a question about descriptors.  If I have
a class as written below, is there a way to use descriptors to be
certain that the datetime in start is always before the one in end?

class foo(object):
   def __init__(self,a = None,b = None)
      self.start = a
      self.end = b

from datetime import datetime
c = datetime(2009,8,13,6,15,0)
d = datetime(2009,8,14,12,0,0)
afoo = foo(c,d)

For instance, if the following code were run, I would like to instance
of foo to switch the start and end times.

afoo.start = datetime(2010,8,13,6,15,0)

I was thinking of using the __set__ descriptor to catch the assignment
and reverse the values if necessary, but I can't figure out how to
determine which values is being set.

You're on the right track, but it is easier to use property() than to
write your own custom descriptor with __get__ and __set__.

class foo(object):
def __init__(self,a = None,b = None):
self._start = a
self._end = b
def get_start(self):
return self._start
def set_start(self, value):
if self._end is None or value < self._end:
self._start = value
else:
self._end = value
start = property(get_start, set_start)
def get_end(self):
return self._end
def set_end(self, value):
if self._start is None or value > self._start:
self._end = value
else:
self._start = value
end = property(get_end, set_end)



Raymond
 
J

Jean-Michel Pichavant

Emile said:
On 8/13/2009 3:17 PM dippim said...

why not...


class foo(object):
def __init__(self,a = None,b = None)
self.start = min(a,b)
self.end = max(a,b)


Emile
or

class foo(object):
def __init__(self, start, end)
self.start = start
self.end = end


Problem solved by design :eek:)

JM
 
D

dippim

or

class foo(object):
    def __init__(self, start, end)
       self.start = start
       self.end = end

Problem solved by design :eek:)

JM

Emile and JM,

Thanks for the response. However, these solution only work at
instantiation. If I change the value of start or end after
instantiation, then I can make start or end whatever I like without
regard to order.
 
D

Diez B. Roggisch

dippim said:
Emile and JM,

Thanks for the response. However, these solution only work at
instantiation. If I change the value of start or end after
instantiation, then I can make start or end whatever I like without
regard to order.

But for that, you don't need descriptors. All you need is a property:

class Foo(object):

def __init__(self, start, end):
self._start = start
self._end = end


@property
def start(self):
return start


@start.setter
def start(self, value):
if value >= self._end:
raise ValueError, "Tried to set a value greater than end!"
self._start = value

# repeat for end, switch relational op


Diez
 
D

dippim

[David]


I am new to Python and I have a question about descriptors.  If I have
a class as written below, is there a way to use descriptors to be
certain that the datetime in start is always before the one in end?
class foo(object):
   def __init__(self,a = None,b = None)
      self.start = a
      self.end = b
from datetime import datetime
c = datetime(2009,8,13,6,15,0)
d = datetime(2009,8,14,12,0,0)
afoo = foo(c,d)
For instance, if the following code were run, I would like to instance
of foo to switch the start and end times.
afoo.start = datetime(2010,8,13,6,15,0)
I was thinking of using the __set__ descriptor to catch the assignment
and reverse the values if necessary, but I can't figure out how to
determine which values is being set.

You're on the right track, but it is easier to use property() than to
write your own custom descriptor with __get__ and __set__.

class foo(object):
    def __init__(self,a = None,b = None):
        self._start = a
        self._end = b
    def get_start(self):
        return self._start
    def set_start(self, value):
        if self._end is None or value < self._end:
            self._start = value
        else:
            self._end = value
    start = property(get_start, set_start)
    def get_end(self):
        return self._end
    def set_end(self, value):
        if self._start is None or value > self._start:
            self._end = value
        else:
            self._start = value
    end = property(get_end, set_end)

Raymond

Raymond,
This functionality is exactly what I was looking for. Thanks! I'll
be using this to solve my problem.

Now that I'm on the right track, I'm still a bit confused about how
__get__ and __set__ are useful. Admittedly, I don't need to
understand them to solve this problem, but perhaps they may be useful
in the future. If I wanted to solve this problem using __get__ and
__set__ could it be done?

Thanks Again!
 
D

Dave Angel

dippim said:
[David]



I am new to Python and I have a question about descriptors. If I have
a class as written below, is there a way to use descriptors to be
certain that the datetime in start is always before the one in end?

class foo(object):
def __init__(self,a =one,b = None)
self.start =
self.end =

from datetime import datetime
c =atetime(2009,8,13,6,15,0)
d =atetime(2009,8,14,12,0,0)
afoo =oo(c,d)

For instance, if the following code were run, I would like to instance
of foo to switch the start and end times.

afoo.start =atetime(2010,8,13,6,15,0)

I was thinking of using the __set__ descriptor to catch the assignment
and reverse the values if necessary, but I can't figure out how to
determine which values is being set.
You're on the right track, but it is easier to use property() than to
write your own custom descriptor with __get__ and __set__.

class foo(object):
def __init__(self,a =one,b = None):
self._start =
self._end =
def get_start(self):
return self._start
def set_start(self, value):
if self._end is None or value < self._end:
self._start =alue
else:
self._end =alue
start =roperty(get_start, set_start)
def get_end(self):
return self._end
def set_end(self, value):
if self._start is None or value > self._start:
self._end =alue
else:
self._start =alue
end =roperty(get_end, set_end)

Raymond

Raymond,
This functionality is exactly what I was looking for. Thanks! I'll
be using this to solve my problem.

Now that I'm on the right track, I'm still a bit confused about how
__get__ and __set__ are useful. Admittedly, I don't need to
understand them to solve this problem, but perhaps they may be useful
in the future. If I wanted to solve this problem using __get__ and
__set__ could it be done?

Thanks Again!
DANGER- WILL ROBINSON!

Don't use this code as-is. There is a nasty surprise waiting for the
caller when he sets start and end, and discovers that one of them gets
thrown out, and an old value still remains.

obj= foo(3, 5)
obj.start = 8
obj.end = 12
print obj.start, obj.end

will print out 3, 12. Not what the caller expected.

Four fixes, in order of preference:
0) Trust your user to read and obey your docstrings. This was what JM
was implying, by changing the names of the formal parameters.
1) make a new method that sets both values, making these two properties
readonly. That new method would make sure the two parameters are
self-consistent. Making the actual values readonly can be done with a
descriptor as well, or even a decorator.
2) Raise an exception in the getter methods if they're out of order
3) do the min/max logic on the getter methods, but I don't like that one
at all.

DaveA
 
D

dippim

dippim said:
[David]
I am new to Python and I have a question about descriptors.  If I have
a class as written below, is there a way to use descriptors to be
certain that the datetime in start is always before the one in end?
class foo(object):
   def __init__(self,a =one,b = None)
      self.start =
      self.end =
from datetime import datetime
c =atetime(2009,8,13,6,15,0)
d =atetime(2009,8,14,12,0,0)
afoo =oo(c,d)
For instance, if the following code were run, I would like to instance
of foo to switch the start and end times.
afoo.start =atetime(2010,8,13,6,15,0)
I was thinking of using the __set__ descriptor to catch the assignment
and reverse the values if necessary, but I can't figure out how to
determine which values is being set.
You're on the right track, but it is easier to use property() than to
write your own custom descriptor with __get__ and __set__.
class foo(object):
    def __init__(self,a =one,b = None):
        self._start =
        self._end =
    def get_start(self):
        return self._start
    def set_start(self, value):
        if self._end is None or value < self._end:
            self._start =alue
        else:
            self._end =alue
    start =roperty(get_start, set_start)
    def get_end(self):
        return self._end
    def set_end(self, value):
        if self._start is None or value > self._start:
            self._end =alue
        else:
            self._start =alue
    end =roperty(get_end, set_end)
Raymond
Raymond,
   This functionality is exactly what I was looking for. Thanks!  I'll
be using this to solve my problem.
   Now that I'm on the right track, I'm still a bit confused about how
__get__ and __set__ are useful.  Admittedly, I don't need to
understand them to solve this problem, but perhaps they may be useful
in the future.  If I wanted to solve this problem using __get__ and
__set__ could it be done?
Thanks Again!

DANGER- WILL ROBINSON!

Don't use this code as-is.  There is a nasty surprise waiting for the
caller when he sets start and end, and discovers that one of them gets
thrown out, and an old value still remains.

obj= foo(3, 5)
obj.start = 8
obj.end = 12
print obj.start, obj.end

will print out  3, 12.    Not what the caller expected.

You're right about this and I appreciate the warning, but I think what
Raymond was going for was directional accuracy without a great deal of
his time wasted on details. The explanation served the purpose of
moving me forward using property() and I'm thankful for it.
Four fixes, in order of preference:
0) Trust your user to read and obey your docstrings.  This was what JM
was implying, by changing the names of the formal parameters.

Considering I am not the sharpest knife in the drawer, I don't wish to
start a philosophical discussion about the relative aptitude and
capabilities of the people who might use a class I build. However, I
will say that as this particular requirement is imposed on this class
by the writer, shouldn't it be the writer's responsibility to enforce
it, especially, when the cost of enforcement is so low?
 
D

dippim

dippim said:
[David]
I am new to Python and I have a question about descriptors.  If I have
a class as written below, is there a way to use descriptors to be
certain that the datetime in start is always before the one in end?
class foo(object):
   def __init__(self,a =one,b = None)
      self.start =
      self.end =
from datetime import datetime
c =atetime(2009,8,13,6,15,0)
d =atetime(2009,8,14,12,0,0)
afoo =oo(c,d)
For instance, if the following code were run, I would like to instance
of foo to switch the start and end times.
afoo.start =atetime(2010,8,13,6,15,0)
I was thinking of using the __set__ descriptor to catch the assignment
and reverse the values if necessary, but I can't figure out how to
determine which values is being set.
You're on the right track, but it is easier to use property() than to
write your own custom descriptor with __get__ and __set__.
class foo(object):
    def __init__(self,a =one,b = None):
        self._start =
        self._end =
    def get_start(self):
        return self._start
    def set_start(self, value):
        if self._end is None or value < self._end:
            self._start =alue
        else:
            self._end =alue
    start =roperty(get_start, set_start)
    def get_end(self):
        return self._end
    def set_end(self, value):
        if self._start is None or value > self._start:
            self._end =alue
        else:
            self._start =alue
    end =roperty(get_end, set_end)
Raymond
Raymond,
   This functionality is exactly what I was looking for. Thanks!  I'll
be using this to solve my problem.
   Now that I'm on the right track, I'm still a bit confused about how
__get__ and __set__ are useful.  Admittedly, I don't need to
understand them to solve this problem, but perhaps they may be useful
in the future.  If I wanted to solve this problem using __get__ and
__set__ could it be done?
Thanks Again!

DANGER- WILL ROBINSON!

Don't use this code as-is.  There is a nasty surprise waiting for the
caller when he sets start and end, and discovers that one of them gets
thrown out, and an old value still remains.

obj= foo(3, 5)
obj.start = 8
obj.end = 12
print obj.start, obj.end

will print out  3, 12.    Not what the caller expected.

You're right about this and I appreciate the warning, but I think what
Raymond was going for was directional accuracy without a great deal of
his time wasted on details. The explanation served the purpose of
moving me forward using property() and I'm thankful for it.
Four fixes, in order of preference:
0) Trust your user to read and obey your docstrings.  This was what JM
was implying, by changing the names of the formal parameters.

Considering I am not the sharpest knife in the drawer, I don't wish to
start a philosophical discussion about the relative aptitude and
capabilities of the people who might use a class I build. However, I
will say that as this particular requirement is imposed on this class
by the writer, shouldn't it be the writer's responsibility to enforce
it, especially, when the cost of enforcement is so low?
 
D

Dave Angel

dippim said:
dippim said:
[David]

I am new to Python and I have a question about descriptors. If I have
a class as written below, is there a way to use descriptors to be
certain that the datetime in start is always before the one in end?

class foo(object):
def __init__(self,a =e,b = None)
self.start > >>> self.end >
from datetime import datetime
c =etime(2009,8,13,6,15,0)
d =etime(2009,8,14,12,0,0)
afoo =(c,d)

For instance, if the following code were run, I would like to instance
of foo to switch the start and end times.

afoo.start =etime(2010,8,13,6,15,0)

I was thinking of using the __set__ descriptor to catch the assignment
and reverse the values if necessary, but I can't figure out how to
determine which values is being set.

You're on the right track, but it is easier to use property() than to
write your own custom descriptor with __get__ and __set__.

class foo(object):
def __init__(self,a =e,b = None):
self._start > >> self._end > >> def get_start(self):
return self._start
def set_start(self, value):
if self._end is None or value < self._end:
self._start =ue
else:
self._end =ue
start =perty(get_start, set_start)
def get_end(self):
return self._end
def set_end(self, value):
if self._start is None or value > self._start:
self._end =ue
else:
self._start =ue
end =perty(get_end, set_end)

Raymond

Raymond,
This functionality is exactly what I was looking for. Thanks! I'll
be using this to solve my problem.

Now that I'm on the right track, I'm still a bit confused about how
__get__ and __set__ are useful. Admittedly, I don't need to
understand them to solve this problem, but perhaps they may be useful
in the future. If I wanted to solve this problem using __get__ and
__set__ could it be done?

Thanks Again!
DANGER- WILL ROBINSON!

Don't use this code as-is. There is a nasty surprise waiting for the
caller when he sets start and end, and discovers that one of them gets
thrown out, and an old value still remains.

obj=oo(3, 5)
obj.start = 8
obj.end = 12
print obj.start, obj.end

will print out 3, 12. Not what the caller expected.

You're right about this and I appreciate the warning, but I think what
Raymond was going for was directional accuracy without a great deal of
his time wasted on details. The explanation served the purpose of
moving me forward using property() and I'm thankful for it.

Four fixes, in order of preference:
0) Trust your user to read and obey your docstrings. This was what JM
was implying, by changing the names of the formal parameters.

Considering I am not the sharpest knife in the drawer, I don't wish to
start a philosophical discussion about the relative aptitude and
capabilities of the people who might use a class I build. However, I
will say that as this particular requirement is imposed on this class
by the writer, shouldn't it be the writer's responsibility to enforce
it, especially, when the cost of enforcement is so low?

1) make a new method that sets both values, making these two properties
readonly. That new method would make sure the two parameters are
self-consistent. Making the actual values readonly can be done with a
descriptor as well, or even a decorator.


2) Raise an exception in the getter methods if they're out of order
3) do the min/max logic on the getter methods, but I don't like that one
at all.

DaveA
I'm going to assume by "writer" you mean yourself? In other words, the
interface isn't being forced upon you by someone else? So I'll continue
to push for a cleaner/clearer interface.

I'm not going to make any judgment about the relative abilities of you
and the caller of your class. You make the final judgment call on how
much you want to bulletproof your interface.

But occasionally the "automatic fix" causes more problems than it
solves. Now, this time you use the word "enforce," which is much
different than the original implied "fix up." So I'll assume you mean
it literally, that you want to detect when the caller has broken the
rules, and raise an exception in that case. In either case, you need to
define what "the rules" are. And normally this is done by specifying
invariants.

I think you're declaring an invariant that, at all times, the object has
two datetime attributes, and that obj.start is no greater than obj.end.
If the attributes are individually writable, and you do the swap at that
time, you get into the trouble I described above. On the other hand, if
you just do a raise, then your caller would still get into trouble for
the same sequence of calls.

So the cure is to either make a new method that explicitly changes both,
or don't do the actual check for order till he tries to read the
values. In that latter case, you're changing the invariant subtly, to
say that if the object is temporarily in an invalid state, one cannot
call any other methods or fetch values till the state has been
corrected. A real pain to describe, but occasionally useful.

This problem of an object temporarily being in an invalid state is not
an uncommon one. But most of us have fallen into one of the pitfalls at
one time or another.

DaveA
 
E

Ethan Furman

dippim said:
dippim said:

I am new to Python and I have a question about descriptors. If I have
a class as written below, is there a way to use descriptors to be
certain that the datetime in start is always before the one in end?
class foo(object):
def __init__(self,a =one,b = None)
self.start =
self.end =
from datetime import datetime
c =atetime(2009,8,13,6,15,0)
d =atetime(2009,8,14,12,0,0)
afoo =oo(c,d)
For instance, if the following code were run, I would like to instance
of foo to switch the start and end times.
afoo.start =atetime(2010,8,13,6,15,0)
I was thinking of using the __set__ descriptor to catch the assignment
and reverse the values if necessary, but I can't figure out how to
determine which values is being set.
You're on the right track, but it is easier to use property() than to
write your own custom descriptor with __get__ and __set__.
class foo(object):
def __init__(self,a =one,b = None):
self._start =
self._end =
def get_start(self):
return self._start
def set_start(self, value):
if self._end is None or value < self._end:
self._start =alue
else:
self._end =alue
start =roperty(get_start, set_start)
def get_end(self):
return self._end
def set_end(self, value):
if self._start is None or value > self._start:
self._end =alue
else:
self._start =alue
end =roperty(get_end, set_end)

Raymond,
This functionality is exactly what I was looking for. Thanks! I'll
be using this to solve my problem.
Now that I'm on the right track, I'm still a bit confused about how
__get__ and __set__ are useful. Admittedly, I don't need to
understand them to solve this problem, but perhaps they may be useful
in the future. If I wanted to solve this problem using __get__ and
__set__ could it be done?
Thanks Again!

DANGER- WILL ROBINSON!

Don't use this code as-is. There is a nasty surprise waiting for the
caller when he sets start and end, and discovers that one of them gets
thrown out, and an old value still remains.

obj= foo(3, 5)
obj.start = 8
obj.end = 12
print obj.start, obj.end

will print out 3, 12. Not what the caller expected.


You're right about this and I appreciate the warning, but I think what
Raymond was going for was directional accuracy without a great deal of
his time wasted on details. The explanation served the purpose of
moving me forward using property() and I'm thankful for it.

Four fixes, in order of preference:
0) Trust your user to read and obey your docstrings. This was what JM
was implying, by changing the names of the formal parameters.


Considering I am not the sharpest knife in the drawer, I don't wish to
start a philosophical discussion about the relative aptitude and
capabilities of the people who might use a class I build. However, I
will say that as this particular requirement is imposed on this class
by the writer, shouldn't it be the writer's responsibility to enforce
it, especially, when the cost of enforcement is so low?

1) make a new method that sets both values, making these two properties
readonly. That new method would make sure the two parameters are
self-consistent. Making the actual values readonly can be done with a
descriptor as well, or even a decorator.

2) Raise an exception in the getter methods if they're out of order
3) do the min/max logic on the getter methods, but I don't like that one
at all.

DaveA

I am in complete agreement with DaveA on this issue -- if you want the
invariant, then have a routine that set's both at once.

The head-aches it would cause me, at least, to have to gyrate around
whether I was making the object later or earlier in time (having to swap
the order I assign stand and end, assuming I even know whether I'm going
forward or backward without further checking... argh!) would quite
possibly cause me to choose some other routine instead.

How about something like (untested):
def set(self, start=None, end=None):
if start is None:
start = self.start
if end is None:
end = self.end
if start > end:
raise ValueError('blah blah')
self.start = start
self.end = end

This way you could still set only one, but have bounds checking in place.

~Ethan~
 
T

Terry Reedy

dippim said:
will say that as this particular requirement is imposed on this class
by the writer, shouldn't it be the writer's responsibility to enforce
it, especially, when the cost of enforcement is so low?

I would say that it is the writer's responsibility to set the
requirement and be clear as to what it is and define the contract with
respect to the requirement. The latter could either be user
responsibility -- behavior explicitly undefined if violated -- or writer
responsibility -- with either fix or raise exception upon violation
detection.

Python's duck-typing style tends to put responsibility on callers. Consider

def minmidmax(seq):
seq.sort()
n = len(seq)
return seq[0], seq[n//2], seq[n-1]

This requires that seq have sane .sort, .__len__, and .__getitem__
methods. Checks could be added, but most Python programs would simply
say the the input must be an in-place sortable sequence.

Terry Jan Reedy
 
R

Raymond Hettinger

Raymond,
   This functionality is exactly what I was looking for. Thanks!  I'll
be using this to solve my problem.

   Now that I'm on the right track, I'm still a bit confused about how
__get__ and __set__ are useful.  Admittedly, I don't need to
understand them to solve this problem, but perhaps they may be useful
in the future.  If I wanted to solve this problem using __get__ and
__set__ could it be done?

The __get__ and __set__ methods are used to implement property()
itself.
So, if you didn't have property, you could roll your own version:

class MyProperty(object):

def __init__(self, fget, fset):
self.fget = fget
self.fset = fset

def __get__(self, obj, objtype=None):
return self.fget(obj)

def __set__(self, obj, value):
self.fset(obj, value)


class foo(object):
def __init__(self,a = None,b = None):
self._start = a
self._end = b
def get_start(self):
return self._start
def set_start(self, value):
if self._end is None or value < self._end:
self._start = value
else:
self._end = value
start = MyProperty(get_start, set_start)
def get_end(self):
return self._end
def set_end(self, value):
if self._start is None or value > self._start:
self._end = value
else:
self._start = value
end = MyProperty(get_end, set_end)


Raymond
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,770
Messages
2,569,584
Members
45,075
Latest member
MakersCBDBloodSupport

Latest Threads

Top