OOP techniques in Python

Discussion in 'Python' started by Panos Laganakos, Apr 27, 2006.

  1. 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.
     
    Panos Laganakos, Apr 27, 2006
    #1
    1. Advertisements

  2. Panos Laganakos

    Duncan Booth Guest

    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.
     
    Duncan Booth, Apr 27, 2006
    #2
    1. Advertisements

  3. 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
     
    Steven Bethard, Apr 27, 2006
    #3
  4. 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
     
    Philippe Martin, Apr 27, 2006
    #4
  5. [Please don't top-post]

    Philippe Martin top-posted:
    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
     
    Steven Bethard, Apr 27, 2006
    #5
  6. Yes - but some of these techniques are somewhat python-specific.
    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 !-)
    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.
    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.
     
    bruno at modulix, Apr 27, 2006
    #6
  7. 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
     
    Philippe Martin, Apr 27, 2006
    #7
  8. 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).
     
    Edward Elliott, Apr 27, 2006
    #8
  9. --

    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'
     
    Philippe Martin, Apr 27, 2006
    #9
  10. Panos Laganakos

    Duncan Booth Guest

    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.
     
    Duncan Booth, Apr 27, 2006
    #10
  11. 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.)
     
    Edward Elliott, Apr 27, 2006
    #11

  12. I'm not sure I understand what you mean ... I did get a strange new message
    from my email client and disabled the signature.
     
    Philippe Martin, Apr 27, 2006
    #12
  13. :) 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
     
    Philippe Martin, Apr 27, 2006
    #13
  14. Look again at the post I replied to (great-grandparent of this one). It's
    not your sig quote that was the problem.
     
    Edward Elliott, Apr 27, 2006
    #14
  15. 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
     
    Panos Laganakos, Apr 28, 2006
    #15
  16. 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.
    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...
    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.
    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.
     
    Dennis Lee Bieber, Apr 28, 2006
    #16
  17. 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__']
     
    Dennis Lee Bieber, Apr 28, 2006
    #17
  18. 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
     
    Steven Bethard, Apr 28, 2006
    #18
  19. Thanks,

    Did not know that.

    Philippe


     
    Philippe Martin, May 1, 2006
    #19
    1. Advertisements

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 (here). After that, you can post your question and our members will help you out.