Accessors in Python (getters and setters)

A

Ant

We seem to be flogging a dead horse now. Is the following a fair
summary:

Q. What is the Pythonic way of implementing getters and setters?

A. Use attributes.

Quote: "I put a lot more effort into choosing method and function
names"

Wisdom: Python is a different paradigm from (e.g.) Java w.r.t.
accessors: Put the effort you would have put into choosing accessor
names into choosing attribute names.

Anything else to add to this? Or can it be put to bed?
 
M

mystilleef

Ant said:
We seem to be flogging a dead horse now. Is the following a fair
summary:

Q. What is the Pythonic way of implementing getters and setters?

A. Use attributes.

Quote: "I put a lot more effort into choosing method and function
names"

Wisdom: Python is a different paradigm from (e.g.) Java w.r.t.
accessors: Put the effort you would have put into choosing accessor
names into choosing attribute names.

Anything else to add to this? Or can it be put to bed?

Here are the lessons I've learned (the hard way).

1) Make all attributes of a class private or protected. You can make
them public later if need be. If anyone tells you making attributes
private/protected is not Pythonic ignore them. You'll thank me when
your code base grows.
2) If a data attribute might likely be an API, think about controlling
access to via "properties". That's the pythonic way to implement
accessors and mutators

I guess people who've done medium to large scale programming in Python
already know this.
 
S

Simon Brunning

I decided to change the name of an attribute. Problem is I've used the
attribute in several places spanning thousands of lines of code. If I
had encapsulated the attribute via an accessor, I wouldn't need to do
an unreliable and tedious search and replace accross several source
code files to achieve my goal. I could simply change the name of the
attribute and move on. Well, I'm glad python has properties. It's a
feature that should be advertised more, especially for large scale
python development.

Something like this any use to you?

from warnings import warn
import sys

class MyClass(object):

def _get_bad_name(self):
caller = sys._getframe(1).f_code
warn("bad_name deprecated, but is referred to in function %s
(line %s in module %s). Please refer to good_name" % (caller.co_name,
caller.co_firstlineno, caller.co_filename))
return self.good_name

def _set_bad_name(self, bad_name):
caller = sys._getframe(1).f_code
warn("bad_name deprecated, but is referred to in function %s
(line %s in module %s). Please refer to good_name" % (caller.co_name,
caller.co_firstlineno, caller.co_filename))
self.good_name = bad_name

def _del_bad_name(self):
caller = sys._getframe(1).f_code
warn("bad_name deprecated, but is referred to in function %s
(line %s in module %s). Please refer to good_name" % (caller.co_name,
caller.co_firstlineno, caller.co_filename))
del self.good_name

bad_name = property(_get_bad_name, _set_bad_name, _del_bad_name,
"bad_name deprecated, please refer to good_name")

def test():
my_object = MyClass()
my_object.bad_name = "This should issue a warning"
print my_object.bad_name # This too
print my_object.good_name # But this should be fine

if __name__ == '__main__':
test()
 
A

Antoon Pardon

I did already. If I had used Java, Eiffel, Smalltalk or C++, I would
have easily changed tmp to temporary_buffer without having search and
replace or grep 27000 lines of code. The point of accessors in those
languages is encapsulation. Which means I can change any detail of
implementation, yes including names of attributes, without breaking
code.

Well the impression we have is that should you used Jave, Eiffel, ...
You would not only have had an attribute tmp but would have had
accessores like getTmp and setTmp.

Now if you find tmp do be a bad name, it seems this would
imply that getTmp and setTmp are equally bad names. So if you
are using Java, Eiffel, ... and you want to get rid of the
bad names, it seems you have to search and replave 27000 lines
of code. You will just be replacing getTmp into getTemporary_buffer
and setTmp into setTemporary_buffer instead of just tmp into
temporary_buffer.

You seem to argue that all you had to do was change the attribute
tmp and then only needed to change the code of the class. But
what we are wondering about is why you would find tmp such a bad
name that you want to change it all over the program but
would be willing to leave names like getTmp setTmp.

So why is tmp bad in code like:

obj.tmp = f()

But is setTmp ok in code like:

obj.setTmp(f())
 
B

Bruno Desthuilliers

mystilleef said:
This is a __baseless__ statement.

Please explain what's wrong ? The "bad naming" part is your own
statement, the "27000 lines" is your own statement too, and I don't see
what's wrong with the assumption that producing 27000 lines of Python
took some time.
For all you know I could have used
the attribute twice in those 27000 lines of code.

How does this make my statement wrong ? Did I imply that you used this
attribute in each and any of the 27000 lines ?
Even then searching
for and replacing the attribute is tedious and error prone.

Indeed, and whether you believe it or not, I do share your pain here. I
had to face similar problem, and without even find and grep (a dumb
'CASE Tool' that stored it's proprietary language code in a
non-documented binary format and was missing decent, reliable
search/replace utilities). And the project was actually more than
50KLOC, forms description excluded.
Yes, the same way many programmers suddenly realize their class,
function, method, project, etc is poorly designed even after using it
for months and proceed to fix the problem. Shit happens.

Agreed. I don't pretend to be better than anyone else at this.
I wouldn't have started the topic if I did. Apparently the only
solution is to rereference the attribute with a better name and pray
and hope third party developers will use the better name. Or search and
replace!

Is actually this code already published ? If yes, you have the option of
making the 'tmp' accessor a computed attribute pointing to the better
named one, have the hidden getter/setter code emit a warning (including
useful informations about where the attributes is accessed - this is
opened to inspection), and officialy deprecate the badly named attribute
so you can definitively get rid of it in a near release.
I did no such thing, you are making that up.

Sorry, that's what I understood. If it's me being guilty of
overreaction, please accept my apologies.
Do you really think I used the name 27000 times in 27000 lines of code?

Of course not. But since 27KLOC is far from trivial for a Python
application, one can think that you had time to notice something wrong
with the naming/use of this attribute.
Maybe I'm not making myself clear. Having to search 27000 lines of code
to replace an identifier name is tedious and error prone.

Yes. You can of course encapsulate access to the attribute in a property
and have that property either emit a warning, raise an exception, log
the access etc, so if you missed some use of it, chances are you'll find
out pretty soon. Unit tests may help too - they can be great for
reporting broken code. You may also want to look if lint-like tools
(pylint etc) can help you there.
You are quoting me out of context. I was speaking with respect to real
accessors, not Python's latent implementation mechanisms.

This I had understood. My point is that if you *see* a public attribute
name as really being an accessor with automagical default
getters/setters (FWIW, look at the __getattribute__ method and Python's
name lookup rules - you'll notice that it's really how it works), then
your "compare that to accessors" doesn't stand.
No I'm not. Naming attributes and naming methods are entirely different
situations.

Why ?

In Python, a "method" is nothing else than a descriptor (attribute)
returning a callable. I agree that there's a small difference between a
callable and a non-callable object, in that you can apply the __call__
operator only to callables, but this doesn't make such a difference
here. In Python, methods are attributes. And naming is naming.
(snip)

I never complained about either languages. You are making that up.

I may be misunderstanding your point about "Java having a clear
distinction between attributes and methods" and overinterpreting it as
implying "Python doesn't and it led me into a bad situation, hence Java
is Good(tm) and Python is Bad(tm)". If so, once again, I do apologize.
Yes, I fixed it then. Which was 27000 lines of code later.

Not exactly a garden party.
Can you point me to exactly where I blamed Python for anything?

Point addressed above.
There is a dichotomy between data and methods. Yes even in Python.

The only dichotomy, from a technical POV, is that methods are callable -
but so are classes, and any object that has a '__call__' attribute that
is itself a callable. The 'dichotomy' here is the same as the dichotomy
between iterable and non iterable objects. Of between file-like and
non-file like objects. IOW, the difference amounts to supported
interface, not to the very nature of the concerned objects.
That's just silly.

Yes, agreed. My fault. Let's take it back and not start fighting because
of a misunderstanding.

(snip)
I know. Many people still think the private/public classification is
unpythonic.

I'd rather say that many people thinks that language-enforced access
restrictors and the "public methods/private data" scheme are unpythonic.
And that if you're not sure wether a given name should be API or
implementation then make it part of the API - but then choose a
meaningful name for it !-)

Yes, a getter is a behavior. It instructs the object to perform an
action. All methods are message channels to an object.

Ok. Python's name lookup all go thru the __getattribute__ accessor. So
obj.value
is a synonym for
obj.__getattribute__('value')

which, according to your definition, is behaviour. Hence, there's *no*
"data attribute" in Python.
A data attribute
does not perform any action. It just holds data for an object.

The attribute itself may not perform any action, but looking up the name
is an action.
Even
though methods are attributes, attributes aren't necessarily methods.
Indeed.

This is basic OO, I don't see anything semantically complex about it.

It becomes complex when the languages sees functions as first-order
objects supporting the Callable interfaces and let you define your own
callable objects, and sees attribute-access as call to an hidden (well,
not that much hidden) getter/setter mechanism that one as all lattitude
to customize in many ways. Seeing that way really takes you out of the
Java semantic/mindset (well, at least it did to me, and I guess for at
least a few others).
This isn't an issue about object models and semantics, this is a
design/architecture issue with regards to eliminating the chances of
errors in large source code.

I was talking about the way you IMHO try to force-fit the common
"private data/public method" scheme (and it's associated dichotomy
between data and functions) into Python. Of course callable and
non-callable attributes usually have different usages, but we're really
far from the Java model where's the dichotomy between "data" and "code"
is irremediable.

Now wrt/ your naming problem, I think that you would not have done the
mistake if you had apply has much care to the naming of a non-callable
attribute as you assert (and I don't doubt you on this) you would have
for a callable one. You explain that you failed to do so because of you
viewing "data attributes" (ie non-callable attributes) as fundamentally
different from "methods" (ie callable attributes), so I try to share the
POV that Python is really different here, hoping this will help you
detect similar errors sooner.

The fact that I did, means I should have.

Then if you felt it finally had to be part of the API *and* was badly
named, you should have renamed it immediatly.

But granted, it's easier to say so afterward than to make the right
choice in the moment.
And how did you come up with that?

By understanding "evolved" as a process (implying a timeline), and
"important part of the system" as widely used.

(snip fighting part)
Good for you.

That doesn't mean I get it right.

(snip, idem)
 
S

Simon Brunning

Something like this any use to you?

Or this, about a squillion times cleaner:

class MyClass(object):

def _get_bad_name(self):
warn('"bad_name" deprecated. Please refer to "good_name"', stacklevel=2)
return self.good_name

def _set_bad_name(self, bad_name):
warn('"bad_name" deprecated. Please refer to "good_name"', stacklevel=2)
self.good_name = bad_name

def _del_bad_name(self):
warn('"bad_name" deprecated. Please refer to "good_name"', stacklevel=2)
del self.good_name

bad_name = property(_get_bad_name, _set_bad_name, _del_bad_name,
'"bad_name" deprecated. Please refer to "good_name"')
 
G

Gerhard Fiedler

[...] the underscore way to mark private entities. Maybe this doesn't
work as I think it does

If you think that single leading underscores have a special meaning for
the compiler/interpreter, then you got it wrong. It's a convention. Like
using ALL_CAPS for pseudo-constants. Nothing prevents you from accessing
implementation or modifying "constants" - but then you're on your own
and must not complain when something breaks.

I really did get that wrong. But this makes actually more sense,
considering the bigger picture (or what I saw from it, so far).

Thanks,
Gerhard
 
G

Gerhard Fiedler

I'm not sure about which languages you are talking (pretty much all that
allow public methods also allow public attributes), [...]

Smalltalk is a very well known object-oriented language that behaves
otherwise, just as one example.

I didn't know that Smalltalk allows any methods at all... I wrote about
languages that allow public methods, and I didn't think of Smalltalk
falling into that category.

Gerhard
 
M

mystilleef

Bruno said:
Please explain what's wrong ? The "bad naming" part is your own
statement, the "27000 lines" is your own statement too, and I don't see
what's wrong with the assumption that producing 27000 lines of Python
took some time.


How does this make my statement wrong ? Did I imply that you used this
attribute in each and any of the 27000 lines ?


Indeed, and whether you believe it or not, I do share your pain here. I
had to face similar problem, and without even find and grep (a dumb
'CASE Tool' that stored it's proprietary language code in a
non-documented binary format and was missing decent, reliable
search/replace utilities). And the project was actually more than
50KLOC, forms description excluded.


Agreed. I don't pretend to be better than anyone else at this.


Is actually this code already published ? If yes, you have the option of
making the 'tmp' accessor a computed attribute pointing to the better
named one, have the hidden getter/setter code emit a warning (including
useful informations about where the attributes is accessed - this is
opened to inspection), and officialy deprecate the badly named attribute
so you can definitively get rid of it in a near release.


Sorry, that's what I understood. If it's me being guilty of
overreaction, please accept my apologies.


Of course not. But since 27KLOC is far from trivial for a Python
application, one can think that you had time to notice something wrong
with the naming/use of this attribute.


Yes. You can of course encapsulate access to the attribute in a property
and have that property either emit a warning, raise an exception, log
the access etc, so if you missed some use of it, chances are you'll find
out pretty soon. Unit tests may help too - they can be great for
reporting broken code. You may also want to look if lint-like tools
(pylint etc) can help you there.


This I had understood. My point is that if you *see* a public attribute
name as really being an accessor with automagical default
getters/setters (FWIW, look at the __getattribute__ method and Python's
name lookup rules - you'll notice that it's really how it works), then
your "compare that to accessors" doesn't stand.


Why ?

In Python, a "method" is nothing else than a descriptor (attribute)
returning a callable. I agree that there's a small difference between a
callable and a non-callable object, in that you can apply the __call__
operator only to callables, but this doesn't make such a difference
here. In Python, methods are attributes. And naming is naming.


I may be misunderstanding your point about "Java having a clear
distinction between attributes and methods" and overinterpreting it as
implying "Python doesn't and it led me into a bad situation, hence Java
is Good(tm) and Python is Bad(tm)". If so, once again, I do apologize.


Not exactly a garden party.


Point addressed above.


The only dichotomy, from a technical POV, is that methods are callable -
but so are classes, and any object that has a '__call__' attribute that
is itself a callable. The 'dichotomy' here is the same as the dichotomy
between iterable and non iterable objects. Of between file-like and
non-file like objects. IOW, the difference amounts to supported
interface, not to the very nature of the concerned objects.


Yes, agreed. My fault. Let's take it back and not start fighting because
of a misunderstanding.

(snip)


I'd rather say that many people thinks that language-enforced access
restrictors and the "public methods/private data" scheme are unpythonic.
And that if you're not sure wether a given name should be API or
implementation then make it part of the API - but then choose a
meaningful name for it !-)



Ok. Python's name lookup all go thru the __getattribute__ accessor. So
obj.value
is a synonym for
obj.__getattribute__('value')

which, according to your definition, is behaviour. Hence, there's *no*
"data attribute" in Python.


The attribute itself may not perform any action, but looking up the name
is an action.


It becomes complex when the languages sees functions as first-order
objects supporting the Callable interfaces and let you define your own
callable objects, and sees attribute-access as call to an hidden (well,
not that much hidden) getter/setter mechanism that one as all lattitude
to customize in many ways. Seeing that way really takes you out of the
Java semantic/mindset (well, at least it did to me, and I guess for at
least a few others).


I was talking about the way you IMHO try to force-fit the common
"private data/public method" scheme (and it's associated dichotomy
between data and functions) into Python. Of course callable and
non-callable attributes usually have different usages, but we're really
far from the Java model where's the dichotomy between "data" and "code"
is irremediable.

Now wrt/ your naming problem, I think that you would not have done the
mistake if you had apply has much care to the naming of a non-callable
attribute as you assert (and I don't doubt you on this) you would have
for a callable one. You explain that you failed to do so because of you
viewing "data attributes" (ie non-callable attributes) as fundamentally
different from "methods" (ie callable attributes), so I try to share the
POV that Python is really different here, hoping this will help you
detect similar errors sooner.



Then if you felt it finally had to be part of the API *and* was badly
named, you should have renamed it immediatly.

But granted, it's easier to say so afterward than to make the right
choice in the moment.


By understanding "evolved" as a process (implying a timeline), and
"important part of the system" as widely used.

(snip fighting part)


That doesn't mean I get it right.

(snip, idem)

Okay, I feel I need to make myself clear. I certainly I'm not blaming
Python for my mistakes. And I don't think language X is better than
Python or vice-versa. Okay scrap the vice-versa. It was silly of me to
name the variable tmp regardless of whatever excuses I have. This also
doesn't mean in the future I wouldn't use shitty names for my
attributes. :) I most likely will. But at least now I know how to
minimize its impact of such carelessness. I mentioned them above but I
repeat it hear again.

1). Make all attributes of a class private/protected .
2). If a "non-callable" attribute is going to be used outside a class,
think about making it a property and name the property well, because
you never know...

Other than that we are just arguing semantics of language. For example,
your view of objects is by categorizing its attributes in callable and
non-callable. However, my categorization is state(data) and
behavior(methods). Neither ways of thinking about it is wrong. It just
reflects the way we design classes. When I'm coding I certainly don't
care about how Python accesses tmp. What I do care about is how to
change tmp without breaking code left, right and center and the
facilities Python provides for making my code robust. Most other OO
languages provide accessors in addition to keywords for that kind of
stuff. I knew it wasn't Pythonic, but I wanted to know the Pythonic way
to do it. So I asked. My query does not automatically suggest Python
sucks. Neither those it suggest that Java or other OO languages are
better. I asked because I sincerely wanted to know the Pythonic way
handling issues like that.
 
R

Roel Schroeven

mystilleef schreef:
Here are the lessons I've learned (the hard way).

1) Make all attributes of a class private or protected. You can make
them public later if need be. If anyone tells you making attributes
private/protected is not Pythonic ignore them. You'll thank me when
your code base grows.
2) If a data attribute might likely be an API, think about controlling
access to via "properties". That's the pythonic way to implement
accessors and mutators

I guess people who've done medium to large scale programming in Python
already know this.

Here's what I have learned from this discussion:

1. Give all symbols in your public interface a sensible name.
2. Don't use accessors and mutators for trivial attributes. If needed
you can always add them later via properties.

You seem to imply that your rule #1 would have prevented the problem
you're experiencing. After this whole discussion, frankly I still don't
understand how it would have done that.
 
B

Bruno Desthuilliers

mystilleef said:
I did already. If I had used Java, Eiffel, Smalltalk or C++, I would
have easily changed tmp to temporary_buffer

What I ask you is about renaming get_tmp/set_tmp to get_temporary_buffer
and set_temporary_buffer.
without having search and
replace or grep 27000 lines of code. The point of accessors in those
languages is encapsulation.

Nope, the point of accessors (in the meaning of get_XXX/set_XXX *public*
methods) is to remedy the lack of support for computed attributes syntax
(ie accessing something like a public data member when it really go thru
implementation methods).

Encapsulation is about depending on an interface, not an implementation.
Your problem is really with interface change, not implementation change.
Which means I can change any detail of
implementation, yes including names of attributes, without breaking
code.

I'm not asking about to rename an *implementation* attribute, but about
how to rename a pair of *API* badly named getter/setter.
 
B

Bruno Desthuilliers

mystilleef said:
Here are the lessons I've learned (the hard way).

1) Make all attributes of a class private or protected.

Unless they are obviously part of the implementation (ie: when you would
definitively had written getters/setters in Java), in which case make
them public (and name them with the same care you would have for Java
getters/setters). You can change the implementation later.
You can make
them public later if need be. If anyone tells you making attributes
private/protected is not Pythonic ignore them.

Don't. Just make sure you have the right *API*.
You'll thank me when
your code base grows.

Unless everybody ditch your code and roll their own because they are
bored with coding Java in Python.
2) If a data attribute might likely be an API, think about controlling
access to via "properties".

If you need to control anything - else just use a plain attribute.
That's the pythonic way to implement
accessors and mutators

I guess people who've done medium to large scale programming in Python
already know this.

Do 50+ KLOC count as "medium to large scale" here ?
 
A

Alex Martelli

Gerhard Fiedler said:
I'm not sure about which languages you are talking (pretty much all that
allow public methods also allow public attributes), [...]

Smalltalk is a very well known object-oriented language that behaves
otherwise, just as one example.

I didn't know that Smalltalk allows any methods at all... I wrote about
languages that allow public methods, and I didn't think of Smalltalk
falling into that category.

??? Smalltalk ONLY has methods -- no other "functions" or "procedures",
ONLY "methods". The one and only way you interact with an object is by
sending it a message to invoke a method -- that's what's known in most
other languages as "calling a method" (or "calling a function"). And
there is no such thing as a "nonpublic" method either, AFAIK (my
Smalltalk may be rusty, but there surely weren't in the Smalltalk I
learned years ago).


Alex
 
B

Bruno Desthuilliers

mystilleef wrote:
(snip)
Okay, I feel I need to make myself clear. I certainly I'm not blaming
Python for my mistakes. And I don't think language X is better than
Python or vice-versa. Okay scrap the vice-versa. It was silly of me to
name the variable tmp regardless of whatever excuses I have. This also
doesn't mean in the future I wouldn't use shitty names for my
attributes. :) I most likely will. But at least now I know how to
minimize its impact of such carelessness. I mentioned them above but I
repeat it hear again.

1). Make all attributes of a class private/protected .

Please re-read my answer to your previous mention of this and my other
remarks in this thread (and below in this thread) about the profound
differences between Python and Java wrt/ object model and attribute
access semantics.
2). If a "non-callable" attribute is going to be used outside a class,
think about making it a property

Unless you don't need to.
and name the property well,

Indeed !-)
Other than that we are just arguing semantics of language. For example,
your view of objects is by categorizing its attributes in callable and
non-callable.

And also API/implementation. These are to orthogonal problems - even in
Java FWIW !-)
However, my categorization is state(data) and
behavior(methods).

If properties are an equivalent of getters/setters, are properties state
or behaviour ? According to your views, they are behaviour, if I
understood you. In that case, what's the difference between 'directely'
(which in fact implies going thru __getattribute__) accessing a public
"data" attribute and accessing an implementation attribute by the mean
of a descriptor (property or custom) ?

* before:

# mylib.py
class Knight(object):
def __init__(self, name):
self.name = name

def sayHello(self):
print "hello, I'm %s" % self.name

# mymain.py
from mylib import Knight
k = Knight("Robin")
print k.name
k.sayHello()
k.name = "Lancelot"
k.sayHello()

* after
# mylib.py
class Knight(object):
def __init__(self, name):
self.name = name

@apply
def name():
def fget(self):
return self._xxx.capitalize()

def fset(self, name):
self._xxx = name

def sayHello(self):
print "hello, I'm %s" % self.name

# mymain.py
from mylib import Knight
k = Knight("Robin")
print k.name
k.sayHello()
k.name = "Lancelot"
k.sayHello()


Neither ways of thinking about it is wrong.

From the client code (including other methods of the same class) POV, is
Knight.name behaviour or state ? Please, don't see it as a flame or a
pissing context or whatever, and try to seriously answer this question.

Thinking in terms of state and behaviour is certainly not wrong in
itself, but what is state and what is behaviour ? If I pass a callable
as an argument to another callable, is the first one data or behaviour ?
If I return a callable from a another callable, is the first callable
data or behaviour ? In a Partial application object [1], is the stored
callable data or behaviour ? Is the class of an object data or
behaviour? Is it's metaclass data or behaviour ?

[1] http://www.python.org/dev/peps/pep-0309/

It just
reflects the way we design classes. When I'm coding I certainly don't
care about how Python accesses tmp.

Neither do I. But I have in mind how I can change the way it is accessed.
What I do care about is how to
change tmp without breaking code left, right and center and the
facilities Python provides for making my code robust.

cf Simon's answer and mines. But your original problem is not related to
tmp being callable or not, it's related to how you use to think about
them, hence name them.
Most other OO
languages provide accessors in addition to keywords for that kind of
stuff.

Most OO languages *don't* provide you accessors - they indeed *forces
you* to write them even for the most trivial cases. What Python (or any
other language supporting "computed attributes" syntax) gives you is a
way to avoid writing tedious, boilerplate code. Not more, not less.
I knew it wasn't Pythonic, but I wanted to know the Pythonic way
to do it. So I asked. My query does not automatically suggest Python
sucks. Neither those it suggest that Java or other OO languages are
better. I asked because I sincerely wanted to know the Pythonic way
handling issues like that.

Mark as implementation what is obviously implementation (be it callable
or not), mark as API what is obviously API (be it callable or not). In
both cases, there will be simple ways to either expose an implementation
name or make a non-callable attribute a property. And *always* try to be
careful about naming, at least (but not restricted to) wrt/ your API.
 
C

Carl Banks

mystilleef said:
Choosing bad identifiers is not a measure of intelligence. Given your
standing in the Python community, I expect more from you. If your only
contribution to this thread is snide and derisive remarks,

Which, excepting silence, is the best response a troll.


Carl Banks
 
S

Steve Holden

mystilleef said:
I'm glad I'm in tune with the "python community."


Right, but tmp isn't private.



Crap! Even in Python too most Public APIs are methods and functions.




Ha! I bet you haven't read too many Python codes.
One thing I can more or less guarantee is that if you'd invested the
time you've spent on this thread in actually performing whatever trivial
but tedious engineering work was required to achieve the renaming of
that attribute the job would have been completed long ago.

regards
Steve
 
R

riquito

mystilleef ha scritto:
Methods are used to perform actions. Data attributes usually aren't. We
are on different planes.

You aren't thinking pythonic. You should not make any difference
between accessing an attribute and accessing a function. They both just
need to receive the right data to process (and the module doc must tell
you wich is it).
There is of course many python code wich doesn't use properties, but
that's up to the developer: everybody write the way they like. You
could also find code with both getter/setter AND properties.
well used properties make your code cleaner, but if you don't find
yourself confortable with them, just don't use them, but never forget
them.

Riccardo
 
M

mystilleef

On State and Behavior:

To understand objects in terms of state and behavior you need to
absolve yourself from implementation details of languages and think at
an abstract level.

Take a button object, for example. It has state and behavior. Possible
states may include, is_active, is_focused, is_mapped, etc. Behavior is
what the button does when it responds to events, (e.g when you click on
it, or drag it, or position a pointer over it.) If you've done any form
of event based programming (GUI/Games/Simulation), this method of
thinking becomes natural.

As you can see, when I'm designing a button object, I don't care what
Python does with is_active. I don't care how it accesses. I don't care
what is_active means to Python. I don't care about Python's __get__
/__set__ special/latent functions or implementation details. is_active
to me and every other developer is a state of the button object. And at
that level that's all that matters. Python can tell me it's just ones
and zeros for all I care.

In very well designed systems, the state of an object should only be
changed by the object. For example, a third party randomly changing
is_active, (which Python lets you do freely and easily) from False to
True may crash your GUI. And I'm not making this up. Things like this
do really happen depending on the whackyness of your toolkit. So
sometimes, it is my duty to protect the state of an object. Especially
if its state cannot afford to be corrupted rendering the system
unstable. And situations like this are found a plenty in event based
programming. Which is programming objects based almost entirely on
state and behavior. As you can see this has nothing to do with Python
vs Java's vs X's implementation of accessors and how using them sucks,
or how they aren't Pythonic. Some domains just require this stuff.

One of the requirements for designing robust object systems is ensuring
the state of objects aren't easily contaminated. That "state" is the
objects data (read: stuff the object needs to do something "reliably").
And this is why many overzealous OO languages do "force" you to use
accessors. It's not because they hate you or aren't aware of the
convenience of having direct access to an object's attributes. It's
just because these languages convenience/robustness ratios are
different.

Thinking in terms of callable vs non-callable is not helpful for me,
because it isn't high level enough. Thinking in terms of state and
behavior is, because it works regardless of programming language or
implementations. This is the reason I like working with Python. I
wanted a language that didn't bore me with it semantics and allowed me
to focus on design. Let me reiterate, I'm not obsessing over language
semantics, I just need practical, not religious, solutions for my
problem domain.

Bruno said:
mystilleef wrote:
(snip)
Okay, I feel I need to make myself clear. I certainly I'm not blaming
Python for my mistakes. And I don't think language X is better than
Python or vice-versa. Okay scrap the vice-versa. It was silly of me to
name the variable tmp regardless of whatever excuses I have. This also
doesn't mean in the future I wouldn't use shitty names for my
attributes. :) I most likely will. But at least now I know how to
minimize its impact of such carelessness. I mentioned them above but I
repeat it hear again.

1). Make all attributes of a class private/protected .

Please re-read my answer to your previous mention of this and my other
remarks in this thread (and below in this thread) about the profound
differences between Python and Java wrt/ object model and attribute
access semantics.
2). If a "non-callable" attribute is going to be used outside a class,
think about making it a property

Unless you don't need to.
and name the property well,

Indeed !-)
Other than that we are just arguing semantics of language. For example,
your view of objects is by categorizing its attributes in callable and
non-callable.

And also API/implementation. These are to orthogonal problems - even in
Java FWIW !-)
However, my categorization is state(data) and
behavior(methods).

If properties are an equivalent of getters/setters, are properties state
or behaviour ? According to your views, they are behaviour, if I
understood you. In that case, what's the difference between 'directely'
(which in fact implies going thru __getattribute__) accessing a public
"data" attribute and accessing an implementation attribute by the mean
of a descriptor (property or custom) ?

* before:

# mylib.py
class Knight(object):
def __init__(self, name):
self.name = name

def sayHello(self):
print "hello, I'm %s" % self.name

# mymain.py
from mylib import Knight
k = Knight("Robin")
print k.name
k.sayHello()
k.name = "Lancelot"
k.sayHello()

* after
# mylib.py
class Knight(object):
def __init__(self, name):
self.name = name

@apply
def name():
def fget(self):
return self._xxx.capitalize()

def fset(self, name):
self._xxx = name

def sayHello(self):
print "hello, I'm %s" % self.name

# mymain.py
from mylib import Knight
k = Knight("Robin")
print k.name
k.sayHello()
k.name = "Lancelot"
k.sayHello()


Neither ways of thinking about it is wrong.

From the client code (including other methods of the same class) POV, is
Knight.name behaviour or state ? Please, don't see it as a flame or a
pissing context or whatever, and try to seriously answer this question.

Thinking in terms of state and behaviour is certainly not wrong in
itself, but what is state and what is behaviour ? If I pass a callable
as an argument to another callable, is the first one data or behaviour ?
If I return a callable from a another callable, is the first callable
data or behaviour ? In a Partial application object [1], is the stored
callable data or behaviour ? Is the class of an object data or
behaviour? Is it's metaclass data or behaviour ?

[1] http://www.python.org/dev/peps/pep-0309/

It just
reflects the way we design classes. When I'm coding I certainly don't
care about how Python accesses tmp.

Neither do I. But I have in mind how I can change the way it is accessed.
What I do care about is how to
change tmp without breaking code left, right and center and the
facilities Python provides for making my code robust.

cf Simon's answer and mines. But your original problem is not related to
tmp being callable or not, it's related to how you use to think about
them, hence name them.
Most other OO
languages provide accessors in addition to keywords for that kind of
stuff.

Most OO languages *don't* provide you accessors - they indeed *forces
you* to write them even for the most trivial cases. What Python (or any
other language supporting "computed attributes" syntax) gives you is a
way to avoid writing tedious, boilerplate code. Not more, not less.
I knew it wasn't Pythonic, but I wanted to know the Pythonic way
to do it. So I asked. My query does not automatically suggest Python
sucks. Neither those it suggest that Java or other OO languages are
better. I asked because I sincerely wanted to know the Pythonic way
handling issues like that.

Mark as implementation what is obviously implementation (be it callable
or not), mark as API what is obviously API (be it callable or not). In
both cases, there will be simple ways to either expose an implementation
name or make a non-callable attribute a property. And *always* try to be
careful about naming, at least (but not restricted to) wrt/ your API.
 
G

Gerhard Fiedler

In very well designed systems, the state of an object should only be
changed by the object.

IMO that's not quite true. Ultimately, the state always gets changed by
something else (user interaction, physical events); very few objects are
completely self-contained in their behavior.

In most systems (and you possibly have written some of them) are objects
whose state gets changed by other objects -- possibly through the
intermediation of setter methods that do nothing else but set the state.
There's no conceptual difference between directly setting the state or
calling a setter function that does nothing else but directly setting the
state -- except for one unnecessary level of indirection in the latter.
For example, a third party randomly changing is_active, (which Python
lets you do freely and easily) from False to True may crash your GUI.
And I'm not making this up. Things like this do really happen depending
on the whackyness of your toolkit.

That's quite true, but a setter that does nothing but change is_active
doesn't prevent this. If there is logic necessary to prevent state changes
in certain situations, this should be implemented. But whether you then
call this a part of the "behavior" (looking at the implementation as being
a setter method) or a part of the "state" (looking at the implementation as
being an added feature of the attribute) doesn't really make an objective
difference.
So sometimes, it is my duty to protect the state of an object.

Of course.
Which is programming objects based almost entirely on state and behavior.
As you can see this has nothing to do with Python vs Java's vs X's
implementation of accessors and how using them sucks, or how they aren't
Pythonic. Some domains just require this stuff.

Yes, but you seem to still be stuck in the paradigm that setting the state
is behavior if it comes from the outside -- probably because some languages
implement that way.

I'm not (yet) a Python programmer. To be honest, the one single feature
that attracted me to Python is the structuring by indenting... I never
understood why we have to indent (to be able to read) /and/ brace (to make
the compiler happy) in other languages -- I always thought that if
indenting helps me to understand the structure, the compiler should be able
to read exactly that :)

I come from a mostly C/C++/Java/PHP background, apparently similar to
yours. GUI, embedded, whatever. But I can understand that the difference
you are making is not based in a concept, it is based in an implementation.

It is an artificial difference to say that

o.is_active = true

is modifying state, whereas

o.set_active( true )

is dealing with behavior. Either way you are changing the state. Behavior,
that is, doing something, implies state change, one way or another,
sometimes relevant, sometimes not. There are (often embedded) systems, and
well-designed ones, that basically don't deal with what you call behavior
at all and handle everything through state change (of signals). Such
systems can be rock-solid and quite complex, and have a lot of "behavior".
And there are systems that do everything through what you call behavior
(e.g. getter/setter type implementations). Both types can work, both can
achieve the exactly same; this is just an implementation detail.
And this is why many overzealous OO languages do "force" you to use
accessors.

I'm not sure why you keep on harping on this. It seems to have been clearly
stated that in Python, every attribute /is/ an implied getter/setter pair
-- it's up to you to just let the language use the default (simple)
implementation, or override it with your own logic. It is also up to you to
call this added logic then a feature of "behavior" or of "state".
Thinking in terms of state and behavior is, because it works regardless
of programming language or implementations.

Agreed. But the distinction is not universal, objective, it is subjective.
Where you make it is your choice. As the examples above show, there are
well-working solid systems out there that work on both ends of your
spectrum: only behavior is exposed, or only state is exposed. Both can
achieve the same. It is a subjective choice to call this

o.is_active = true

state or behavior... the underlying setter implementation for is_active can
do everything you want your behavior to be -- or it can be the simple
straightforward default setter. It's just a subjective thing to say that
this is in the realm of behavior or of state. Didn't you say that state
changes can crash a system? That's behavior, as far as I am concerned :)
And there is little if any behavior that doesn't change the state --
especially setters are known to do that :)

Gerhard
 
M

mystilleef

Gerhard said:
IMO that's not quite true. Ultimately, the state always gets changed by
something else (user interaction, physical events); very few objects are
completely self-contained in their behavior.

Then in those cases the system becomes a victim of high coupling.
In most systems (and you possibly have written some of them) are objects
whose state gets changed by other objects -- possibly through the
intermediation of setter methods that do nothing else but set the state.
There's no conceptual difference between directly setting the state or
calling a setter function that does nothing else but directly setting the
state -- except for one unnecessary level of indirection in the latter.

It depends. If certain conditions need to be met before changing the
state of an object, then arbitrarily changing it can be dangerous. I
gave an example earlier.
That's quite true, but a setter that does nothing but change is_active
doesn't prevent this. If there is logic necessary to prevent state changes
in certain situations, this should be implemented. But whether you then
call this a part of the "behavior" (looking at the implementation as being
a setter method) or a part of the "state" (looking at the implementation as
being an added feature of the attribute) doesn't really make an objective
difference.

Of course using setters for the sake of just using them is pointless.
The reason to use them is if pre-conditions or post-conditions need to
be met. Or to control access to an objects states.
Of course.


Yes, but you seem to still be stuck in the paradigm that setting the state
is behavior if it comes from the outside -- probably because some languages
implement that way.

Not at all. Behaviors are just methods of an object. They are behaviors
whether they are called internally or externally.
I'm not (yet) a Python programmer. To be honest, the one single feature
that attracted me to Python is the structuring by indenting... I never
understood why we have to indent (to be able to read) /and/ brace (to make
the compiler happy) in other languages -- I always thought that if
indenting helps me to understand the structure, the compiler should be able
to read exactly that :)

I come from a mostly C/C++/Java/PHP background, apparently similar to
yours. GUI, embedded, whatever. But I can understand that the difference
you are making is not based in a concept, it is based in an implementation.

It is an artificial difference to say that

o.is_active = true

is modifying state, whereas

o.set_active( true )

is dealing with behavior. Either way you are changing the state. Behavior,
that is, doing something, implies state change, one way or another,
sometimes relevant, sometimes not. There are (often embedded) systems, and
well-designed ones, that basically don't deal with what you call behavior
at all and handle everything through state change (of signals). Such
systems can be rock-solid and quite complex, and have a lot of "behavior".
And there are systems that do everything through what you call behavior
(e.g. getter/setter type implementations). Both types can work, both can
achieve the exactly same; this is just an implementation detail.

If all set_active does is change is_active's state, then set_active is
pointless. See above for my point on the issue.
I'm not sure why you keep on harping on this. It seems to have been clearly
stated that in Python, every attribute /is/ an implied getter/setter pair
-- it's up to you to just let the language use the default (simple)
implementation, or override it with your own logic. It is also up to you to
call this added logic then a feature of "behavior" or of "state".


Agreed. But the distinction is not universal, objective, it is subjective.
Where you make it is your choice. As the examples above show, there are
well-working solid systems out there that work on both ends of your
spectrum: only behavior is exposed, or only state is exposed. Both can
achieve the same. It is a subjective choice to call this

o.is_active = true

state or behavior... the underlying setter implementation for is_active can
do everything you want your behavior to be -- or it can be the simple
straightforward default setter. It's just a subjective thing to say that
this is in the realm of behavior or of state. Didn't you say that state
changes can crash a system? That's behavior, as far as I am concerned :)
And there is little if any behavior that doesn't change the state --
especially setters are known to do that :)

Gerhard

State - behavior is not something I made up, so it isn't subjective. It
is a common term used in OO literature. In fact, the only reason I used
it is because I thought is was common knowledge. And behaviors are not
just necessarily getters/setters, they are methods of objects.
 

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

No members online now.

Forum statistics

Threads
473,767
Messages
2,569,572
Members
45,045
Latest member
DRCM

Latest Threads

Top