property() usage - is this as good as it gets?

D

David Moss

Hi,

I want to manage and control access to several important attributes in
a class and override the behaviour of some of them in various
subclasses.

Below is a stripped version of how I've implemented this in my current
bit of work.

It works well enough, but I can't help feeling there a cleaner more
readable way of doing this (with less duplication, etc).

Is this as good as it gets or can this be refined and improved
especially if I was to add in a couple more attributes some fairly
complex over-ride logic?

#!/usr/bin/env python

class A(object):
def __init__(self):
self._x = None
self._y = None

def _set_x(self, value):
self._x = value

def _get_x(self):
return self._x

x = property(_get_x, _set_x)

def _set_y(self, value):
self._y = value

def _get_y(self):
return self._y

y = property(_get_y, _set_y)

class B(A):
def __init__(self):
self._z = None
super(B, self).__init__()

def _set_x(self, x):
# An example subclass 'set' override.
if x == 10:
raise Exception('%r is invalid!' % x)
self._x = x

x = property(A._get_x, _set_x)

def _set_z(self, value):
self._z = value

def _get_z(self):
return self._z

z = property(_get_z, _set_z)

del A._get_x, A._set_x, A._get_y, A._set_y
del B._set_x, B._get_z, B._set_z
 
C

castironpi

Hi,

I want to manage and control access to several important attributes in
a class and override the behaviour of some of them in various
subclasses.

Below is a stripped version of how I've implemented this in my current
bit of work.

It works well enough, but I can't help feeling there a cleaner more
readable way of doing this (with less duplication, etc).

Is this as good as it gets or can this be refined and improved
especially if I was to add in a couple more attributes some fairly
complex over-ride logic?

#!/usr/bin/env python

class A(object):
    def __init__(self):
        self._x = None
        self._y = None

    def _set_x(self, value):
        self._x = value

    def _get_x(self):
        return self._x

    x = property(_get_x, _set_x)

    def _set_y(self, value):
        self._y = value

    def _get_y(self):
        return self._y

    y = property(_get_y, _set_y)

Within the bounds of Python, that's it.

In the cases where you're not executing code on 'get' or 'set', it
might be possible to have a default getter/setter, that just sets the
value of a variable name. This keeps your interface the same across
inheritance and changes. Possible to have it assign a default value
too.

def _set_z(self, value):
self._z = value

#def _get_z(self):
# return self._z

z = property( True, _set_z ) #still gets self._z

Unless, you are looking at something 'periodic' or regular. From
something I'm working on:

def _getbalance( self ):
return self._tree.geti(
self.where+ self.lookup[ 'balance' ] )

def _getparent( self ):
return self._tree.getI(
self.where+ self.lookup[ 'parent' ] )

Where 'balance' and 'parent' could both come from something generic:

balance= property( tree_lookup( 'balance' ) )
parent= property( tree_lookup( 'parent' ) )

They would have to take care to define 'self' properly and everything.
 
M

Miles

Hi,

I want to manage and control access to several important attributes in
a class and override the behaviour of some of them in various
subclasses.

Below is a stripped version of how I've implemented this in my current
bit of work.

It works well enough, but I can't help feeling there a cleaner more
readable way of doing this (with less duplication, etc).

It could certainly be made more concise, without changing the meaning:

###

from operator import attrgetter
class attrsetter(object):
def __init__(self, attr):
self._attr = attr
def __call__(self, object, value):
setattr(object, self._attr, value)
def defaultproperty(attr):
return property(attrgetter(attr), attrsetter(attr))

class A(object):
def __init__(self):
self._x = None
self._y = None

x = defaultproperty('_x')
y = defaultproperty('_y')

class B(A):
def __init__(self):
super(B, self).__init__()
self._z = None

def _set_x(self, x):
# An example subclass 'set' override.
if x == 10:
raise Exception('%r is invalid!' % x)
self._x = x

x = property(A.x.fget, _set_x)
z = defaultproperty('_z')
# You really don't need to do this, but you can if you want
del _set_x

###

But, given the example you gave, you could also write things this way
(aside from no longer forbidding deleting the properties, which really
shouldn't be necessary):

###

class A(object):
def __init__(self):
self.x = None
self.y = None

class B(A):
def __init__(self):
super(B, self).__init__()
self._x = self.x
self.z = None

def _set_x(self, x):
# An example subclass 'set' override.
if x == 10:
raise Exception('%r is invalid!' % x)
self._x = x

x = property(attrgetter('_x'), _set_x)

###

It's difficult to know exactly what would work best for you without a
better idea of how your properties override each other.

-Miles
 
G

George Sakkis

Hi,

I want to manage and control access to several important attributes in
a class and override the behaviour of some of them in various
subclasses.

Below is a stripped version of how I've implemented this in my current
bit of work.

It works well enough, but I can't help feeling there a cleaner more
readable way of doing this (with less duplication, etc).

Is this as good as it gets or can this be refined and improved
especially if I was to add in a couple more attributes some fairly
complex over-ride logic?

A small improvement as far as overriding goes is the OProperty recipe:
http://infinitesque.net/articles/2005/enhancing Python's property.xhtml

George
 
M

Medardo Rodriguez

from operator import attrgetter
class attrsetter(object):
def __init__(self, attr):
self._attr = attr
def __call__(self, object, value):
setattr(object, self._attr, value)

This solution is very nice, but in programming is a good practice to be uniform:
The interface of "attrgetter" is "def attrgetter(*names): ..."
So, it will be nice the same for attrsetter: "def attrsetter(*names):"

I will send an example, but extending it a little bit and also using
some nice python structures of functional programming:

#<code>
from operator import attrgetter

def attrsetter(*names):
def closure(obj, *values):
for i in xrange(len(names)):
setattr(obj, names, values)
return closure

attrmanager = lambda name: (attrgetter(name), attrsetter(name))


class Test(object):
x = property(*attrmanager('_x'))
y = property(*attrmanager('_y'))

setvalues = attrsetter('x', 'y')


test = Test()

test.x = 1
print 'test.x:', test.x

test.y = 'Merchise'
print 'test.y:', test.y


# Just another test

test.setvalues(3, 'med')

print 'test.x:', test.x
print 'test.y:', test.y
#</code>

Regards
 
C

castironpi

Hi,

I want to manage and control access to several important attributes in
a class and override the behaviour of some of them in various
subclasses.

Below is a stripped version of how I've implemented this in my current
bit of work.

It works well enough, but I can't help feeling there a cleaner more
readable way of doing this (with less duplication, etc).

Is this as good as it gets or can this be refined and improved
especially if I was to add in a couple more attributes some fairly
complex over-ride logic?

#!/usr/bin/env python

class A(object):
def __init__(self):
self._x = None
self._y = None

def _set_x(self, value):
self._x = value

def _get_x(self):
return self._x

x = property(_get_x, _set_x)

def _set_y(self, value):
self._y = value

def _get_y(self):
return self._y

y = property(_get_y, _set_y)

To the OP: you have a unique procedure executed for every attribute of
an instance, for each of set, get, and del. That's a lot of code. If
you don't want unique procedures, what patterns should they follow?
What is wrong with the way you overrode 'get_x' above? When
brainstorming, don't restrict yourself to Python syntax-- make
something up, and we'll write Python.
 
A

alex23

castironpi said:
and we'll write Python.

I haven't seen anything you've contributed to this group that would so
far be considered well-written Python.

Confusing and border-line insane, yes. Pythonic? Not at all.

Here's a tip: try actually developing some real-world application in
Python for a while, rather than the god-knows-what-you're-doing
attempts that you keep hijacking various threads with. Your code isn't
anywhere near as clever as you seem to think.
 
R

Raymond Hettinger

It works well enough, but I can't help feeling there a cleaner more
readable way of doing this (with less duplication, etc).

Is this as good as it gets or can this be refined and improved
especially if I was to add in a couple more attributes some fairly
complex over-ride logic?

#!/usr/bin/env python

class A(object):
    def __init__(self):
        self._x = None
        self._y = None

    def _set_x(self, value):
        self._x = value

    def _get_x(self):
        return self._x

    x = property(_get_x, _set_x)

    def _set_y(self, value):
        self._y = value

    def _get_y(self):
        return self._y

    y = property(_get_y, _set_y)


FWIW, there is a variant of property() that allows you to re-use the
same getter/setter code for multiple attributes:

http://code.activestate.com/recipes/205126/


Raymond Hettinger
 
C

castironpi

I haven't seen anything you've contributed to this group that would so
far be considered well-written Python.

Confusing and border-line insane, yes. Pythonic? Not at all.

Here's a tip: try actually developing some real-world application in
Python for a while, rather than the god-knows-what-you're-doing
attempts that you keep hijacking various threads with. Your code isn't
anywhere near as clever as you seem to think.

Python isn't as clever as you think. It's a language. Do you want a
link to clever code? I like to encourage creativity.
 
A

alex23

castironpi said:
Python isn't as clever as you think.  It's a language.  

Yet another non-sequitur response from you. At which point in my post
did I make any such claims about Python's 'cleverness'?
Do you want a
link to clever code?  

Not if you wrote it or find it clever, no.

To quote Kernighan: "Debugging is twice as hard as writing the code in
the first place. Therefore, if you write the code as cleverly as
possible, you are, by definition, not smart enough to debug it."

When the code in question is one of the abstract monstrosities you
regularly post here - often in response to _new python users_ asking
_basic_ questions that _don't require metaclass solutions_ - I've
repeatedly found the cost in interpreting it is just never worth the
time and effort required.
I like to encourage creativity.

Then why do you seem to stymie it? Care to go back through the
responses to your posts and tally up the "thanks, that makes sense!"
replies to the "I don't get what you mean here" ones?

If you're seriously attempting to educate, you're failing. If you're
trying to show how blazingly clever you are, you're failing at that,
too.

When people seem to generally consider you a markov chainer, that -
should- be a sign to work on the clarity of your communication.
 
C

castironpi

Yet another non-sequitur response from you. At which point in my post
did I make any such claims about Python's 'cleverness'?


Not if you wrote it or find it clever, no.

To quote Kernighan: "Debugging is twice as hard as writing the code in
the first place. Therefore, if you write the code as cleverly as
possible, you are, by definition, not smart enough to debug it."

When the code in question is one of the abstract monstrosities you
regularly post here - often in response to _new python users_ asking
_basic_ questions that _don't require metaclass solutions_ - I've
repeatedly found the cost in interpreting it is just never worth the
time and effort required.


Then why do you seem to stymie it? Care to go back through the
responses to your posts and tally up the "thanks, that makes sense!"
replies to the "I don't get what you mean here" ones?

If you're seriously attempting to educate, you're failing. If you're
trying to show how blazingly clever you are, you're failing at that,
too.

When people seem to generally consider you a markov chainer, that -
should- be a sign to work on the clarity of your communication.

I'm baffled. I don't understand what you write. I think someone in
my shoes would be justified in dismissing it as either malicious or a
miscommunication. My typical response is, take something I've said in
context, and show how it fulfills the criteria you describe.

For instance, "I won't like it if you do" and "I won't think it's
clever if you do" aren't consistent belief structures, they're fight
structures. You have made such claims.

In the post above, I make two claims, ask two questions, and encourage
a brainstorm. I don't see how what you say applies to it.

I am willing to change the way I communicate. I want to communicate
effectively. But, until someone takes something I want to say, and
says it the right way, I can't learn how. So I wait and write Python,
and share my frustration with my peers.

Lastly, in the syllogism you propose, "not smart enough to debug it",
you equate maximally clever writing with maximally clever debugging,
which is clearly wrong. They are distinct; someone could be better at
either one than the other. And besides, your quoted premise "twice as
hard" is assumed, not proven.
 
A

alex23

I'm baffled.  I don't understand what you write.  

Which is pretty much how I feel about -all- of your posts.
I think someone in
my shoes would be justified in dismissing it as either malicious or a
miscommunication.  

No, it's frustration at the sharp decrease in signal:noise that this
newsgroup has undertaken since your arrival.
My typical response is, take something I've said in
context, and show how it fulfills the criteria you describe.

Generally, it's -extremely difficult- understanding your context, for
starters. But how about this, your code snippet from this very thread,
your -first- of two seemingly independent replies to the original
post:
def _getbalance( self ):
return self._tree.geti(
self.where+ self.lookup[ 'balance' ] )

def _getparent( self ):
return self._tree.getI(
self.where+ self.lookup[ 'parent' ] )

Where 'balance' and 'parent' could both come from something generic:

balance= property( tree_lookup( 'balance' ) )
parent= property( tree_lookup( 'parent' ) )

In your 'example', you reference one marked-as-implementation
attribute, two unspecified attributes, one unspecified method and one
unspecified function. That you refer to one function as both 'geti'
and 'getI' leads me to believe this -isn't- workable code and you've
just re-keyed this in without testing.

I've also -no- idea what you're trying to demonstrate here. It's not
clear nor obvious, and I can't see how your _getwhatever methods tie
in with the property definition.
For instance, "I won't like it if you do" and "I won't think it's
clever if you do" aren't consistent belief structures, they're fight
structures.  You have made such claims.

No, what I've stated is I've yet to see -any- code samples that you've
provided be of any real, clear use to -anyone-. This isn't a 'fight
structure', this is an opinion formed by the overwhelming amount of
evidence you keep contributing to this group.
In the post above, I make two claims, ask two questions, and encourage
a brainstorm.  I don't see how what you say applies to it.

What you said was: "When brainstorming, don't restrict yourself to
Python syntax-- make
something up, and we'll write Python."

What I'm contesting is your ability to write Python.

Furthermore, people -aren't- posting questions to the "castironpi
hour", they're asking questions to help further their understanding of
Python, not to give you further opportunity to emphasise your
misunderstanding of it. Suggesting they 'brainstorm' without any
understanding of the language itself and that you'll somehow magically
generate Python code for them is, based on what you've written here,
laughable.
I am willing to change the way I communicate.  I want to communicate
effectively.  But, until someone takes something I want to say, and
says it the right way, I can't learn how.  So I wait and write Python,
and share my frustration with my peers.

Given how many people have publicly killfiled you on this group, I'm
not sure who you consider to be your 'peers'. There was a -lot- of
initial frustration at your post, along with even more dismissal of it
as the output of a poorly written AI experiment.

You're -failing- the Turing test. If that isn't telling you something
is wrong with your communication style, I don't really know what will.
Lastly, in the syllogism you propose, "not smart enough to debug it",
you equate maximally clever writing with maximally clever debugging,
which is clearly wrong.  They are distinct; someone could be better at
either one than the other.  And besides, your quoted premise "twice as
hard" is assumed, not proven.

Assumed by someone whose programming knowledge I have a -lot- more
respect for than your's, mostly because Kernighan's is -more than
amply demonstrated-.

Which leads me to reiterate my original statement, only framing it now
as a question: what -real world- applications or systems have you
worked on...in -any- language? I find it difficult to believe you've
ever worked on any, or even more pointedly, had to work on -someone
else's real world code-. Because the sample code fragments you post,
when not broken, at best seem to relate to pointless wankery on your
behalf, without any practical application whatsoever, and at worst
carry deep-seated misunderstandings about how Python works.
 
A

alex23

May I ask both of you to continue this in private?

Certainly. But be warned, I -will- continue posting publicly if I
believe someone is misinforming others here, whether it's willful or
not.
 
C

castironpi

Which is pretty much how I feel about -all- of your posts.

Alright. You're articulate. I'm getting a better picture of what
your problem with me is. What we have is a misunderstanding, *not a
war*. If you think I leave the grounds of civil argument, be sure to
say so. Otherwise, I'll expect observation, premise, conclusion.
Just like a good A.I. would. (Computers are good at something, and
that's logic.)
No, it's frustration at the sharp decrease in signal:noise that this
newsgroup has undertaken since your arrival.

You also want to assign causality and/or blame as a result. The
evidence is circumstantial. I'm sorry you're frustrated. If I've had
a negative influence, which is hard to prove, that would mean that I'm
influential (in the bad way). I'm not sure I can accept that at this
point in my life. It's a problem. But I also feel that I've been
plonked without hearing me out, which only escalates the frustration.
(Google search castironpi+plonk. Ha.)
But how about this, your code snippet from this very thread,
your -first- of two seemingly independent replies to the original
post:
 def _getbalance( self ):
   return self._tree.geti(
     self.where+ self.lookup[ 'balance' ] )
 def _getparent( self ):
   return self._tree.getI(
     self.where+ self.lookup[ 'parent' ] )
Where 'balance' and 'parent' could both come from something generic:
balance= property( tree_lookup( 'balance' ) )
parent= property( tree_lookup( 'parent' ) )

In your 'example', you reference one marked-as-implementation
attribute, two unspecified attributes, one unspecified method and one
unspecified function. That you refer to one function as both 'geti'
and 'getI' leads me to believe this -isn't- workable code and you've
just re-keyed this in without testing.

It is from real code. If you want the link say so. It's an in-place
memory manager that I think is quite nifty... *and* functional. *And*
pure Python.

Others earlier proposed similar things; mine was just more
specialized.

Miles:
x = defaultproperty('_x')
y = defaultproperty('_y')

Medardo:
x = property(*attrmanager('_x'))
y = property(*attrmanager('_y'))

I see a similarity between theirs and mine, but you only criticize
mine. Do you agree that mine was similar?

If I would have said, "Where 'balance' and 'parent' both share
something generic", instead of "could both come from something
generic", would that have made more sense? That would definitely be
my fault. Word choice is definitely a weakness and I am not perfect.
I've also -no- idea what you're trying to demonstrate here. It's not
clear nor obvious, and I can't see how your _getwhatever methods tie
in with the property definition.

My point was, "Here's one snippet you could clear up with some
abstraction." It answers the OP's question directly:

"It works well enough, but I can't help feeling there a cleaner more
readable way of doing this (with less duplication, etc). Is this as
good as it gets?"

But I did not organize my post well, so I can see your confusion.
No, what I've stated is I've yet to see -any- code samples that you've
provided be of any real, clear use to -anyone-. This isn't a 'fight
structure', this is an opinion formed by the overwhelming amount of
evidence you keep contributing to this group.

I'm lost. Do you want to see some clever code, even if I wrote it,
and even if I think it is clever too? I am wanting a code review.
What you said was: "When brainstorming, don't restrict yourself to
Python syntax-- make
something up, and we'll write Python."

What I'm contesting is your ability to write Python.

Furthermore, people -aren't- posting questions to the "castironpi
hour", they're asking questions to help further their understanding of
Python, not to give you further opportunity to emphasise your
misunderstanding of it. Suggesting they 'brainstorm' without any
understanding of the language itself and that you'll somehow magically
generate Python code for them is, based on what you've written here,
laughable.

No, and this is not my editorial, blog, or advice column, no matter
how much I write. Brainstorming is healthy and productive. The OP
was looking for a way to improve something he'd written and was
already working. I was urging him to think outside the box, then get
help getting back "in to the box," metaphorically. This is different
than inviting someone to brainstorm who is asking for help with the
'range' function, you know. Python makes a lot of things possible
that other languages don't, and I don't believe that anyone knows the
extent of that.
Given how many people have publicly killfiled you on this group, I'm
not sure who you consider to be your 'peers'. There was a -lot- of
initial frustration at your post, along with even more dismissal of it
as the output of a poorly written AI experiment.

YES, no kidding. I'm amazed myself, but I just chalk it up to my
being new on Usenet and bad with people. I'll work on it but Rome
wasn't built in a day. My peer is someone who is bad at
communicating. How am I supposed to establish communication with him
or her?
You're -failing- the Turing test. If that isn't telling you something
is wrong with your communication style, I don't really know what will.

I am aware, thanks to the group, that something is wrong. But I don't
know what it is.
Assumed by someone...
[snip, ok?]
Which leads me to reiterate my original statement, only framing it now
as a question: what -real world- applications or systems have you
worked on...in -any- language? I find it difficult to believe you've
ever worked on any, or even more pointedly, had to work on -someone
else's real world code-. Because the sample code fragments you post,
when not broken, at best seem to relate to pointless wankery on your
behalf, without any practical application whatsoever, and at worst
carry deep-seated misunderstandings about how Python works.

Most of my programming is either academic research, academic study, or
hobbyist. If you're asking if I have any experience, yes I do. If
you're asking whether I've been forced to write awful code under
someone else's deadline, no I haven't. That's important. I don't
know what trait that gives my code, maybe abstractness, but no my
experience is not typical. And the academic research wasn't for the
business colleges either. If you want my credentials, e-mail me. I
went to a 4-year Research 1, but I believe that people's work should
speak for itself.

My offer stands to show you my code. Probably not my resume or work
history though. Fair?

Lastly, if I spend too much time on the newsgroup (300 in February,
wow), that is, on this one newsgroup, it's important to bring that to
my attention. That is not something that anyone can judge for
themselves. (A famous philosopher contends that friends are the
harshest critics.) It is Ok to have that opinion and it's ok if you
notice it; that doesn't automatically mean you do too. I could use a
kick in the rear to branch out, and perhaps diversify my time. I
think I would find it most productive to point-blank say so.
 
B

Bruno Desthuilliers

David Moss a écrit :
Hi,

I want to manage and control access to several important attributes in
a class and override the behaviour of some of them in various
subclasses.

Below is a stripped version of how I've implemented this in my current
bit of work.

It works well enough, but I can't help feeling there a cleaner more
readable way of doing this (with less duplication, etc).

Is this as good as it gets or can this be refined and improved
especially if I was to add in a couple more attributes some fairly
complex over-ride logic?

(snip code)


Remember that properties have nothing magical - they're just one
possible (and very handy) way to use the descriptor protocol to
implement computed attributes. If you have to resort to any non-obvious
trick or just too much boilerplate code with properties, then it's
probably time to implementent your own custom descriptor object.
 
D

David Moss

Venerable Pythonistas,

Looks like a pretty unanimous vote in favour of custom descriptor
usage rather than the more generic property() BIF for my purposes.

This is going to tidy and firm up my library code very nicely ;-)

Thanks very much for your responses. Your assistance is greatly
appreciated!

P.S. How did my post manage to ignite a mini flame war?!
 
D

Diez B. Roggisch

P.S. How did my post manage to ignite a mini flame war?!

Because of the high resident troll quota in replies - which is created by
just one rather annoying member of this community...

Diez
 

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,743
Messages
2,569,478
Members
44,898
Latest member
BlairH7607

Latest Threads

Top