OOP techniques in Python

P

Panos Laganakos

I've been thinking if there's a point in applying some specific OOP
techniques in Python as we do in other languages.

i.e. we usually define private properties and provide public functions
to access them, in the form of:
get { ... } set { ... }

Should we do the same in Python:

self.__privateAttr = 'some val'

def getPrivateAttr(self):
return self.__privateAttr

Or there's no point in doing so?

Some other techniques come to mind, but I think that Python tends to
allow the programmer to access stuff he wants even though he shouldn't
or in the form of a dict or list, rather than a method to do so.
 
D

Duncan Booth

Panos said:
i.e. we usually define private properties and provide public functions
to access them, in the form of:
get { ... } set { ... }

Should we do the same in Python:

self.__privateAttr = 'some val'

def getPrivateAttr(self):
return self.__privateAttr

Or there's no point in doing so?
If you want to do more than just setting an attribute or returning it then
define a property, but if you just want to save the value there is no point
using a property. In some other languages you cannot change an attribute
into a property without changing the interface to the class, so in those
languages you need to make everything a property just in case you ever need
it to be a property in the future.

In Python, when you need it to become a property you can change it without
breaking anything, so there is no need to obfuscate it prematurely.
 
S

Steven Bethard

Panos said:
we usually define private properties and provide public functions
to access them, in the form of:
get { ... } set { ... }

Should we do the same in Python:

self.__privateAttr = 'some val'

def getPrivateAttr(self):
return self.__privateAttr

Or there's no point in doing so?

There is no point in doing so. You should use plain attributes whenever
possible (that is, whenever you're really just doing a get or a set,
with no computation). The only reason to use getters and setters is so
that you can change the implementation later if you need to. But python
allows you to do this with properties:
.... def __init__(self, x):
.... self.x = x
........ def _get_x(self):
.... return self._x * 2
.... def _set_x(self, value):
.... self._x = value / 2
.... x = property(_get_x, _set_x)
.... def __init__(self, x):
.... self.x = x
....42

Which should not be interpreted as saying you should start writing a
bunch of properties now. ;) Instead, only introduce a property when you
find that something must remain an attribute (presumably for backwards
compatibility reasons) but for whatever reason it now needs some
additional computation.

STeVe
 
P

Philippe Martin

Why is that ? to me it makes sense when I see self.__m_var that I'm dealing
with a member variable taht derived classes will not see/access.

Philippe
 
S

Steven Bethard

[Please don't top-post]

Steven said:
>> we usually define private properties and provide public functions
>> to access them, in the form of:
>> get { ... } set { ... }
>>
>> Should we do the same in Python:
>>
>> self.__privateAttr = 'some val'
>>
>> def getPrivateAttr(self):
>> return self.__privateAttr
>>
>> Or there's no point in doing so?
>
> There is no point in doing so. You should use plain attributes
> whenever possible (that is, whenever you're really just doing a get or
> a set, with no computation). The only reason to use getters and
> setters is so that you can change the implementation later if you need
> to. But python allows you to do this with properties: [snip]
> Which should not be interpreted as saying you should start writing a
> bunch of properties now. ;) Instead, only introduce a property when
> you find that something must remain an attribute (presumably for
> backwards compatibility reasons) but for whatever reason it now needs
> some additional computation.

Philippe Martin top-posted:
Why is that ? to me it makes sense when I see self.__m_var that I'm dealing
with a member variable taht derived classes will not see/access.

If the OP uses a private attribute in combination with a getter and a
setter, he's basically still exposing the attribute as public, but
making it harder to get and set. There's no reason to do that. If
you're going to expose a public API to part of the class that just
assigns and returns a value (with no computation), you might as well
make that API an attribute instead of a pair of methods.

STeVe
 
B

bruno at modulix

Panos said:
I've been thinking if there's a point in applying some specific OOP
techniques in Python as we do in other languages.

Yes - but some of these techniques are somewhat python-specific.
i.e. we usually define private properties and provide public functions
to access them, in the form of:
get { ... } set { ... }

Should we do the same in Python:
No.


Or there's no point in doing so?

Absolutely. Python has descriptors and properties, which allow you to
switch from a direct attribute access to a computed attributes without
breaking the API. In fact, think of public attributes as automatic
getter/setters pairs, that you can customize when needed !-)
Some other techniques come to mind, but I think that Python tends to
allow the programmer to access stuff he wants even though he shouldn't

s/he shouldn't/he knows he's messing with implementation details and
assume the consequences.

FWIW, accessing 'private' attributes in Java or C++ is not a big deal.
or in the form of a dict or list, rather than a method to do so.

Python is *object* oriented - not class-oriented - and doesn't force you
to put everything into a class definition. Now everything in Python is
an object (including functions and classes and modules...). This is a
much less restricted (and IMHO much much more powerful) approach than
what you may learn from Java, C++, C# etc.
 
P

Philippe Martin

Steven said:
[Please don't top-post]

Steven said:
Panos said:
we usually define private properties and provide public functions
to access them, in the form of:
get { ... } set { ... }

Should we do the same in Python:

self.__privateAttr = 'some val'

def getPrivateAttr(self):
return self.__privateAttr

Or there's no point in doing so?

There is no point in doing so. You should use plain attributes
whenever possible (that is, whenever you're really just doing a get or
a set, with no computation). The only reason to use getters and
setters is so that you can change the implementation later if you need
to. But python allows you to do this with properties: [snip]
Which should not be interpreted as saying you should start writing a
bunch of properties now. ;) Instead, only introduce a property when
you find that something must remain an attribute (presumably for
backwards compatibility reasons) but for whatever reason it now needs
some additional computation.

Philippe Martin top-posted:
Why is that ? to me it makes sense when I see self.__m_var that I'm
dealing with a member variable taht derived classes will not see/access.

If the OP uses a private attribute in combination with a getter and a
setter, he's basically still exposing the attribute as public, but
making it harder to get and set. There's no reason to do that. If
you're going to expose a public API to part of the class that just
assigns and returns a value (with no computation), you might as well
make that API an attribute instead of a pair of methods.

STeVe

[Please don't top-post]

OK I won't, is that a general rule? (I've been top posting for quite some
time now and it is the first time I see that warning)

Philippe
 
E

Edward Elliott

Panos said:
i.e. we usually define private properties and provide public functions
to access them, in the form of:
get { ... } set { ... }

Should we do the same in Python:
Or there's no point in doing so?

Some other techniques come to mind, but I think that Python tends to
allow the programmer to access stuff he wants even though he shouldn't
or in the form of a dict or list, rather than a method to do so.

There's no point. Private access can only be advisory anyway -- there are
ways around it in every language. Convention serves just as well, people
know that touching _foo is done at their own risk. It's not creating extra
hoops to jump through the few times you really do need to touch a private
var.

Others already mentioned how to transparently change attributes to
properties.

If you're worried about enforcing restrictions in your code base, get a lint
checker to flag any expression of the form name._foo where name isn't
'self'. Yes you can still access _foo other ways, but you'll never get
perfect enforcement anyway. Maybe the extra step will make you think
twice, if that's what you want (and it seems to be).
 
P

Philippe Martin

Edward said:
There's no point. Private access can only be advisory anyway -- there are
ways around it in every language. Convention serves just as well, people
know that touching _foo is done at their own risk. It's not creating
extra hoops to jump through the few times you really do need to touch a
private var.

Others already mentioned how to transparently change attributes to
properties.

If you're worried about enforcing restrictions in your code base, get a
lint checker to flag any expression of the form name._foo where name isn't
'self'. Yes you can still access _foo other ways, but you'll never get
perfect enforcement anyway. Maybe the extra step will make you think
twice, if that's what you want (and it seems to be).

--

What then is the point of the double underscore (if any) ?:


In [2]:class test:
.2.: _one_underscore=1
.2.: __two_underscores=2
.2.:

In [3]:t = test()

In [4]:t._one_underscore=2

In [5]:t.__two_underscores=3

In [6]:t.__two_underscores
Out[6]:3

In [7]:dir (test)
Out[7]:['__doc__', '__module__', '_one_underscore',
'_test__two_underscores']

In [8]:test._one_underscore
Out[8]:1

In [9]:test.__two_underscores
---------------------------------------------------------------------------
exceptions.AttributeError Traceback (most recent
call last)

/home/philippe/<ipython console>

AttributeError: class test has no attribute '__two_underscores'
 
D

Duncan Booth

Philippe said:
Steven said:
[Please don't top-post]

OK I won't, is that a general rule? (I've been top posting for quite some
time now and it is the first time I see that warning)

Yes. Other suggestions you might get are not to bottom post, and certainly
not (as you did elsewhere) to post your response as a signature (i.e.
following a line containing only two dashes and a space).

The accepted way to follow up is to trim the post to which you are
responding so as to maintain sufficient context to make the post
intelligible on its own, but not to quote the entire post: most newsreader
programs will help you in this by either warning or indeed refusing to post
responses which have too many quoted lines compared with new content.

It is perfectly reasonable if you are making several points to intersperse
your comments with other quoted sections.

Some newsgroups use a different convention and expect top-posting without
trimming of the quoted posts, but these very much are in the minority.
 
E

Edward Elliott

Philippe Martin wrote:
''

On the other hand, foo.__doc__ and foo.__name__ work fine.

(I was going to quote your post but my reader interprets everything after
the two dashes as your sig and ignores it. And I won't bother to fix it.)
 
P

Philippe Martin

Edward said:
Philippe Martin wrote:
''

On the other hand, foo.__doc__ and foo.__name__ work fine.

(I was going to quote your post but my reader interprets everything after
the two dashes as your sig and ignores it. And I won't bother to fix it.)


I'm not sure I understand what you mean ... I did get a strange new message
from my email client and disabled the signature.
 
P

Philippe Martin

Duncan said:
Philippe said:
Steven said:
[Please don't top-post]

OK I won't, is that a general rule? (I've been top posting for quite some
time now and it is the first time I see that warning)

Yes. Other suggestions you might get are not to bottom post, and certainly
not (as you did elsewhere) to post your response as a signature (i.e.
following a line containing only two dashes and a space).

The accepted way to follow up is to trim the post to which you are
responding so as to maintain sufficient context to make the post
intelligible on its own, but not to quote the entire post: most newsreader
programs will help you in this by either warning or indeed refusing to
post responses which have too many quoted lines compared with new content.

It is perfectly reasonable if you are making several points to intersperse
your comments with other quoted sections.

Some newsgroups use a different convention and expect top-posting without
trimming of the quoted posts, but these very much are in the minority.

:) I did try the signature for the first time today, and stopped as I get
weird errors from my client and certainly do not wish to get flame just
because I like a cute message.

My assumption always was:
1) keep all data in the post so new viewers can understand get the full
content, including the initial post

2) write on top because when you open a file, you want the chronological
info on top instead of at the very bottom ... since because of 1) this
could be miles away.

But if the rules are otherwise then I'll just follow them.


Regards,

Philippe
 
E

Edward Elliott

Philippe said:
I'm not sure I understand what you mean ... I did get a strange new
message from my email client and disabled the signature.

Look again at the post I replied to (great-grandparent of this one). It's
not your sig quote that was the problem.
 
P

Panos Laganakos

Thanks for all the useful answers :)

Alot of stuff to take into consideration/chew on. I come up with
similar drawbacks now and then, 'cause some OOP techniques can be made
in Python relatively simpler or plainly different (still simpler
though). Though I am hesitant on how to act on certain occasations, as
I was with this one, luckily I can get to post here and hear all these
cool comments/opinions. :D
 
D

Dennis Lee Bieber

:) I did try the signature for the first time today, and stopped as I get
weird errors from my client and certainly do not wish to get flame just
because I like a cute message.
Probably because you wrote all your response between the "-- <nl>"
and the real signature text. The convention is that the "-- <nl>"
defines the start of the signature, and most clients won't quote
signatures.
My assumption always was:
1) keep all data in the post so new viewers can understand get the full
content, including the initial post
If they need more than just context (especially since this is a
Usenet newsgroup that is linked to a mailing list), they can always use
the references headers to retrieve the previous message...
2) write on top because when you open a file, you want the chronological
info on top instead of at the very bottom ... since because of 1) this
could be miles away.
This procedure may work for one-to-one email (it mirrors snail mail
in which one is /attaching/ a courtesy copy of the mail to which one is
replying) -- since 1<>1 implies the recipient knows what they wrote, so
the copy is just a refresher for details.

It doesn't work well for group messages, because anyone coming in
new has to read the quoted material from bottom up.
But if the rules are otherwise then I'll just follow them.

Unless it has been superceded (I did find a draft that claims it
would supercede this, but a quick glance makes me think the author was
trying to make M$ Outlook response style standard -- IE, just the
opposite of what this RFC defines) this is the common recommendations:
http://www.dtcc.edu/cs/rfc1855.html

3.1 User Guidelines
3.1.1 General Guidelines for mailing lists and NetNews

* Read both mailing lists and newsgroups for one to two months
before you post anything. This helps you to get an understanding of the
culture of the group.

<snip>

* If you are sending a reply to a message or a posting be sure you
summarize the original at the top of the message, or include just enough
text of the original to give a context. This will make sure readers
understand when they start to read your response. Since NetNews,
especially, is proliferated by distributing the postings from one host
to another, it is possible to see a response to a message before seeing
the original. Giving context helps everyone. But do not include the
entire original!
* Again, be sure to have a signature which you attach to your
message. This will guarantee that any peculiarities of mailers or
newsreaders which strip header information will not delete the only
reference in the message of how people may reach you.
 
D

Dennis Lee Bieber

What then is the point of the double underscore (if any) ?:

To prevent masking/shadowing of inherited attributes...
.... def __init__(self):
.... self.__WhoMe = "From A"
.... print "A : ", dir(self)
.... super(A, self).__init__()
.... .... def __init__(self):
.... self.__WhoMe = 42
.... print "B : ", dir(self)
.... super(B, self).__init__()
.... .... def __init__(self):
.... self.__WhoMe = "I'm confuzzled"
.... print "Confusion: ", dir(self)
.... super(Confusion, self).__init__()
.... Confusion: ['_Confusion__WhoMe', '__class__', '__delattr__',
'__dict__', '__doc__', '__getattribute__', '__hash__', '__init__',
'__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__',
'__setattr__', '__str__', '__weakref__']
A : ['_A__WhoMe', '_Confusion__WhoMe', '__class__', '__delattr__',
'__dict__', '__doc__', '__getattribute__', '__hash__', '__init__',
'__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__',
'__setattr__', '__str__', '__weakref__']
B : ['_A__WhoMe', '_B__WhoMe', '_Confusion__WhoMe', '__class__',
'__delattr__', '__dict__', '__doc__', '__getattribute__', '__hash__',
'__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__',
'__repr__', '__setattr__', '__str__', '__weakref__']
Note that A, B, and Confusion each have "__WhoMe". Also notice how
each __init__ invokes the parent module __init__; each one adds its
__WhoMe to the object without masking those defined in others.

Without the __, you'd have only ONE attribute after all of that; as
shown next...
.... def __init__(self):
.... self._WhoMe = "From A"
.... print "A : ", dir(self)
.... super(A, self).__init__()
.... .... def __init__(self):
.... self._WhoMe = 42
.... print "B : ", dir(self)
.... super(B, self).__init__()
.... .... def __init__(self):
.... self._WhoMe = "I'm confuzzled"
.... print "Confusion: ", dir(self)
.... super(Confusion, self).__init__()
.... Confusion: ['_WhoMe', '__class__', '__delattr__', '__dict__',
'__doc__', '__getattribute__', '__hash__', '__init__', '__module__',
'__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__',
'__str__', '__weakref__']
A : ['_WhoMe', '__class__', '__delattr__', '__dict__', '__doc__',
'__getattribute__', '__hash__', '__init__', '__module__', '__new__',
'__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__str__',
'__weakref__']
B : ['_WhoMe', '__class__', '__delattr__', '__dict__', '__doc__',
'__getattribute__', '__hash__', '__init__', '__module__', '__new__',
'__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__str__',
'__weakref__']
 
S

Steven Bethard

Dennis said:
To prevent masking/shadowing of inherited attributes...

Note that it can fail to do this if you accidentally (or purposefully)
name a class the same as a parent or ancestor class:
.... def __init__(self):
.... self.__str = 'module 1'
.... def get_module_1_confusion(self):
.... return self.__str
........ def __init__(self):
.... self.__str = 'module 2'
....'module 2'

So you still need to at least check that the name of your subclass is
different from the name of all its ancestors. This doesn't come up that
often, but I know a few people have been bitten by it.

STeVe
 
P

Philippe Martin

Thanks,

Did not know that.

Philippe


What then is the point of the double underscore (if any) ?:

To prevent masking/shadowing of inherited attributes...
... def __init__(self):
... self.__WhoMe = "From A"
... print "A : ", dir(self)
... super(A, self).__init__()
...... def __init__(self):
... self.__WhoMe = 42
... print "B : ", dir(self)
... super(B, self).__init__()
...... def __init__(self):
... self.__WhoMe = "I'm confuzzled"
... print "Confusion: ", dir(self)
... super(Confusion, self).__init__()
...Confusion: ['_Confusion__WhoMe', '__class__', '__delattr__',
'__dict__', '__doc__', '__getattribute__', '__hash__', '__init__',
'__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__',
'__setattr__', '__str__', '__weakref__']
A : ['_A__WhoMe', '_Confusion__WhoMe', '__class__', '__delattr__',
'__dict__', '__doc__', '__getattribute__', '__hash__', '__init__',
'__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__',
'__setattr__', '__str__', '__weakref__']
B : ['_A__WhoMe', '_B__WhoMe', '_Confusion__WhoMe', '__class__',
'__delattr__', '__dict__', '__doc__', '__getattribute__', '__hash__',
'__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__',
'__repr__', '__setattr__', '__str__', '__weakref__']
Note that A, B, and Confusion each have "__WhoMe". Also notice how
each __init__ invokes the parent module __init__; each one adds its
__WhoMe to the object without masking those defined in others.

Without the __, you'd have only ONE attribute after all of that; as
shown next...
... def __init__(self):
... self._WhoMe = "From A"
... print "A : ", dir(self)
... super(A, self).__init__()
...... def __init__(self):
... self._WhoMe = 42
... print "B : ", dir(self)
... super(B, self).__init__()
...... def __init__(self):
... self._WhoMe = "I'm confuzzled"
... print "Confusion: ", dir(self)
... super(Confusion, self).__init__()
...Confusion: ['_WhoMe', '__class__', '__delattr__', '__dict__',
'__doc__', '__getattribute__', '__hash__', '__init__', '__module__',
'__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__',
'__str__', '__weakref__']
A : ['_WhoMe', '__class__', '__delattr__', '__dict__', '__doc__',
'__getattribute__', '__hash__', '__init__', '__module__', '__new__',
'__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__str__',
'__weakref__']
B : ['_WhoMe', '__class__', '__delattr__', '__dict__', '__doc__',
'__getattribute__', '__hash__', '__init__', '__module__', '__new__',
'__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__str__',
'__weakref__']
--
============================================================== <
(e-mail address removed) | Wulfraed Dennis Lee Bieber KD6MOG <
(e-mail address removed) | Bestiaria Support Staff <
============================================================== <
Home Page: <http://www.dm.net/~wulfraed/> <
Overflow Page: <http://wlfraed.home.netcom.com/> <
 

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,744
Messages
2,569,482
Members
44,901
Latest member
Noble71S45

Latest Threads

Top