class methods vs. functions

Discussion in 'Python' started by beliavsky@aol.com, Jul 14, 2004.

  1. Guest

    What are the pros and cons of defining a method of a class versus defining
    a function that takes an instance of the class as an argument? In the example
    below, is it better to be able to write
    z.modulus() or modulus(z)? Is there a difference between Python and C++ in
    this regard?

    from math import sqrt
    class xy:
    def __init__(self,x,y):
    self.x = x
    self.y = y
    def modulus(self):
    return sqrt(self.x**2 + self.y**2)

    def modulus(z):
    return sqrt(z.x**2 + z.y**2)

    z = xy(3,4)
    print z.modulus()
    print modulus(z)



    ----== Posted via Newsfeed.Com - Unlimited-Uncensored-Secure Usenet News==----
    http://www.newsfeed.com The #1 Newsgroup Service in the World! >100,000 Newsgroups
    ---= 19 East/West-Coast Specialized Servers - Total Privacy via Encryption =---
     
    , Jul 14, 2004
    #1
    1. Advertising

  2. On 14 Jul 2004, wrote:

    > What are the pros and cons of defining a method of a class versus defining
    > a function that takes an instance of the class as an argument? In the example
    > below, is it better to be able to write
    > z.modulus() or modulus(z)? Is there a difference between Python and C++ in
    > this regard?


    The answer to this, as for most questions in computer science, is "it
    depends". :) The answer (which is partially my opinion) has to do with
    interfaces, and what you want to accomplish:

    - Case 1 -

    All of your objects conform to the 'xy' interface. This interface
    requeries that an object has both .x and .y member variables. You can then
    define a 'modulus' function that is defined to operate on any object
    implementing the 'xy' interface:

    > def modulus(z):
    > return sqrt(z.x**2 + z.y**2)


    Upside:

    No matter what the object is, or from where it comes, so long as it
    implements the 'xy' interface, you can now take the modulus of it.

    Downside:

    You can't take the modulus of anything that doesn't implement 'xy'.

    - Case 2 -

    Some of your objects implement the 'xy' interface, others don't (e.g. they
    don't make sense with it). Either way, you want to be able to find their
    modulus. You can do this by making all your objects conform to the
    'modulus' interface. This interface requires that an object have a
    ..modulus() member function:

    > from math import sqrt
    > class xy:
    > def __init__(self,x,y):
    > self.x = x
    > self.y = y
    > def modulus(self):
    > return sqrt(self.x**2 + self.y**2)


    Upside:

    You can take the modulus of any object you create, regardless of whether
    it can implement the 'xy' interface or not.

    Downside:

    If you aren't the designer of the object, you can't take the modulus of
    it, even if it does implement the 'xy' interface.

    So, the choice is up to you. You can always provide both, though:

    def mymodulus(z):
    try:
    return z.modulus()
    except AttributeError:
    return modulus(z)

    In case you can't tell, I'm as undecided on the issue as you ;)
     
    Christopher T King, Jul 14, 2004
    #2
    1. Advertising

  3. On Wed, Jul 14, 2004 at 03:38:40PM -0400, Christopher T King wrote:
    > On 14 Jul 2004, wrote:
    >
    > > What are the pros and cons of defining a method of a class versus defining
    > > a function that takes an instance of the class as an argument?


    >From a technical point of view Christopher's explanation seems to be OK,

    but my limited studies of object oriented programming taught me that
    from outside an object you should not use its data-attributes directly.
    Rather you should send the object messages, ie use methods of this object.
    So z.modules() seems to be the better idea, and modules(z) the worse one.

    I suppose that this means that you should treat each name foo of a
    data-attribute as if it was called __foo.
    egbert
    --
    Egbert Bouwman - Keizersgracht 197 II - 1016 DS Amsterdam - 020 6257991
    ========================================================================
     
    Egbert Bouwman, Jul 15, 2004
    #3
  4. "" <beliavsky@127.0.0.1:7501> writes:

    > What are the pros and cons of defining a method of a class versus defining
    > a function that takes an instance of the class as an argument? In the example
    > below, is it better to be able to write
    > z.modulus() or modulus(z)?


    You're essentially comparing the message-passing paradigm to the
    generic function paradigm.

    One advantage of message-passing, is that it separates the messages
    into namespaces, so you don't have to be as careful about chosing your
    message names.

    One disadvantage of message-passing, is that it separates the messages
    into namespaces, so you are less careful about chosing your message
    names, and end up confusing your clients. :)

    One advantage of generic functions is that multiple dispatch is much
    more natural (dare I say, easier?).

    One disadvantage of generic functions is that, if you are not careful,
    your code ends up distributed all over the shop.
     
    Jacek Generowicz, Jul 15, 2004
    #4
  5. Egbert Bouwman <> writes:

    > but my limited studies of object oriented programming taught me that
    > from outside an object you should not use its data-attributes directly.
    > Rather you should send the object messages,


    Message-passing, while being the most common style of OO, is not the
    only style of OO.

    You should use the object's interface. This may or may not be
    implemented via messages.

    > ie use methods of this object. So z.modules() seems to be the
    > better idea, and modules(z) the worse one.


    How do you know (without looking at the implementation of
    "modules") that "modules(z)" is not "using a method" ?

    This is a rhetorical question, which probably needs a bit of
    comment. By "method" I don't necessarily mean "Python class or
    instance method". You might like to think about "add(a,b)".

    > I suppose that this means that you should treat each name foo of a
    > data-attribute as if it was called __foo.


    Why treat it as private, if it isn't marked as private? If it isn't
    marked as private, then it's part of the interface. If it's part of
    the interface, then it is intended for use by clients. If it's
    intended for use by clients, then use it.


    > my limited studies of object oriented programming taught me


    Be careful not to let Java or C++ (or anyone strongly influenced by
    them) to shape your understanding of object oriented programming. You
    would be doing yourself a disservice.
     
    Jacek Generowicz, Jul 15, 2004
    #5
  6. On Thu, Jul 15, 2004 at 02:10:06PM +0200, Jacek Generowicz wrote:

    > Why treat it as private, if it isn't marked as private? If it isn't
    > marked as private, then it's part of the interface. If it's part of
    > the interface, then it is intended for use by clients. If it's
    > intended for use by clients, then use it.


    My impression is that you can hardly call this OOO:
    Object Oriented Orthodoxy. For instance, from the GOF I learned:
    - the object's internal state can not be accessed directly
    - operations are the only way to change an object's internal state
    - requests (or messages) are the only way to get an object
    to execute an operation
    - a signature consists of an operation's name, parameters and return value
    - the interface to an object is the set of all signatures of its operations

    It is not easy to find definitions of 'interface' in python books,
    but I cannot find descriptions that contradict the GOF.

    And now you suggest that everything that is accessible is part
    of the interface. I think I prefer the orthodoxy.

    I suppose that is what you mean when you say:
    > Be careful not to let Java or C++ (or anyone strongly influenced by
    > them) to shape your understanding of object oriented programming. You
    > would be doing yourself a disservice.

    but I need more explanations, arguments, references.

    You seem to encourage the following practice:
    >>> class C: pass
    >>> c = C()
    >>> c.x = 9

    because the internal state of c is not private,
    so it is part of the interface, so it is intended for use,
    so I should use it.

    I finally got rid of my goto's, and now you do this to me.
    egbert

    --
    Egbert Bouwman - Keizersgracht 197 II - 1016 DS Amsterdam - 020 6257991
    ========================================================================
     
    Egbert Bouwman, Jul 15, 2004
    #6
  7. Egbert Bouwman <> wrote in message news:<>...
    > From a technical point of view Christopher's explanation seems to be OK,
    > but my limited studies of object oriented programming taught me that
    > from outside an object you should not use its data-attributes directly.
    > Rather you should send the object messages, ie use methods of this object.
    > So z.modules() seems to be the better idea, and modules(z) the worse one.


    This argument usually stems from the idea that only calls to methods
    are "sent messages". This is true in a language like C++ or Java
    where a class like:

    class XY {
    double x, y;
    double modulus();
    }

    allocates space for two doubles (x and y) and then puts the modulus
    method in its method lookup table. The concern is that, if the owner
    of class XY determines that there is a better way of representing the
    data portions of the class, e.g.:

    class XY {
    double radians, length;
    double modulus();
    }

    then anyone who used the XY class's data-attributes directly will then
    be in trouble (since they have been removed). This is why in a
    language like C++ or Java you'll usually see these fields declared as
    private, and thus only the methods are considered to be part of the
    class's interface.

    In Python, we can avoid some of this issue because of the getattr and
    setattr type functionality. Imagine I have the class:

    class xy:
    def __init__(self,x,y):
    self.x = x
    self.y = y
    def modulus(self):
    return sqrt(self.x**2 + self.y**2)

    And then imagine I've decided I would rather represent the class with
    the radians, length formulation:

    class xy:
    def __init__(self, radians, length):
    self.radians = radians
    self.length = length
    def modulus(self):
    return ... # calculation in radians

    Now I have to make Christopher's decision. If I believe that users of
    my class never access the x and y variables, only the modulus method,
    (i.e. if I envision this class with Christopher's "Case 2" interface)
    then I'm done. On the other hand, if I believe that x and y are part
    of the interface I've presented to users of the class, (i.e.
    Christopher's "Case 1" interface), I need to continue to provide
    access to these "attributes". In Python, we can continue to make such
    attributes available, even if we don't actually have an 'x' or 'y'
    field in the class anymore, using the getattr/setattr functionality:

    def __getattr__(self, name):
    if name == 'x':
    return ... # calculation of x from radians and length
    if name == 'y':
    return ... # calculation of y from radians and length
    def __setattr__(self, name):
    ... # set radians or length based on x or y

    So if you decide that your data attributes need to change, you haven't
    destroyed compatibility with users of your class, even if your data
    attributes were publicly available. Continuing to support the older
    attributes isn't necessarily trivial, but at least it's /possible/
    (unlike C++ or Java).

    Steve
     
    Steven Bethard, Jul 15, 2004
    #7
  8. On Thu, 15 Jul 2004, Egbert Bouwman wrote:

    > My impression is that you can hardly call this OOO:
    > Object Oriented Orthodoxy. For instance, from the GOF I learned:
    > - the object's internal state can not be accessed directly
    > - operations are the only way to change an object's internal state
    > - requests (or messages) are the only way to get an object
    > to execute an operation
    > - a signature consists of an operation's name, parameters and return value
    > - the interface to an object is the set of all signatures of its operations


    Replace .x with .getx() and .y with .gety() if it suits you, but often in
    the case where an object is mostly used as a container (similar to a C
    struct), direct access to its attributes is considered the norm:

    a = 3+5j
    a.real # --> 3.0
    a.imag # --> 5.0

    If you so choose, you can make .x and .y read-only with a little
    __setattr__ magic (there's probably an easier way to do this in new-style
    classes), as they are in the complex type:

    a.real = 4 # --> AttributeError

    Sure, it may not fit with "proper OO methodology", but it's a good way of
    making visible the fact that "this object is a container, I'm retrieving
    one of the variables it contains". If the interface is well-defined, it
    doesn't matter how you access it, so long as you follow the interface's
    design.
     
    Christopher T King, Jul 15, 2004
    #8
  9. On Thu, Jul 15, 2004 at 09:40:53AM -0700, Steven Bethard wrote:
    >
    > So if you decide that your data attributes need to change, you haven't
    > destroyed compatibility with users of your class, even if your data
    > attributes were publicly available. Continuing to support the older
    > attributes isn't necessarily trivial, but at least it's /possible/
    > (unlike C++ or Java).


    Yes, I see that in real life access to the internal state of an
    object is sometimes necessary. And Python allows it.

    But what should i think of systems in which the users actually
    have to use that licence ?

    egbert
    --
    Egbert Bouwman - Keizersgracht 197 II - 1016 DS Amsterdam - 020 6257991
    ========================================================================
     
    Egbert Bouwman, Jul 15, 2004
    #9
  10. On Thu, Jul 15, 2004 at 02:10:23PM -0400, Christopher T King wrote:
    > but often in
    > the case where an object is mostly used as a container (similar to a C
    > struct), direct access to its attributes is considered the norm:
    >
    >
    > Sure, it may not fit with "proper OO methodology", but it's a good way of
    > making visible the fact that "this object is a container, I'm retrieving
    > one of the variables it contains". If the interface is well-defined, it
    > doesn't matter how you access it, so long as you follow the interface's
    > design.
    >

    I am beginning to see the light. Thank you all.
    egbert
    --
    Egbert Bouwman - Keizersgracht 197 II - 1016 DS Amsterdam - 020 6257991
    ========================================================================
     
    Egbert Bouwman, Jul 15, 2004
    #10
  11. On 15 Jul 2004, at 18:00, Egbert Bouwman wrote:

    > On Thu, Jul 15, 2004 at 02:10:06PM +0200, Jacek Generowicz wrote:
    >
    >> Why treat it as private, if it isn't marked as private? If it isn't
    >> marked as private, then it's part of the interface. If it's part of
    >> the interface, then it is intended for use by clients. If it's
    >> intended for use by clients, then use it.

    >
    > My impression is that you can hardly call this OOO:
    > Object Oriented Orthodoxy. For instance, from the GOF I learned:


    Aaah, the dear GOF book. You do realize that the GOF book is, to a
    lange extent, a book about the shortcomings of C++, don't you?

    For example, think about how you would implement the Decorator and the
    State patterns in Python.

    I would advise against basing your understanding of what object
    oriented programming is about, on the GOF book.

    > - the object's internal state can not be accessed directly


    This is blatantly false, as your code snippet below clearly
    demonstrates. (You probably meant "should not" rather that "can not".
    The "should not" is a dogma. Understand its motivation, and decide for
    yourself to what extent you want to buy it.)

    > - operations are the only way to change an object's internal state


    Ditto. (... "are the only way" -> "should be the only way" ...)

    > - requests (or messages) are the only way to get an object
    > to execute an operation


    Read up about properties in Python.

    Strictly speaking, it's not an object which executes an operation;
    rather methods carry out operations on objects. And it's certainly not
    true that messages are the only way to get a method to carry out an
    operation on objects ... put differently: generic functions exist.

    > It is not easy to find definitions of 'interface' in python books,


    How about "Anything that does not start with an underscore" ?

    (One can argue about the details of this proto-definition, but in terms
    of value per word, it's pretty good, I'd say.)

    > And now you suggest that everything that is accessible is part
    > of the interface.


    No. I suggest that everything that is part of the interface is part of
    the interface. Put in other terms, everything that is not marked as
    private, is part of the interface (or in some languages, everything
    that is marked as public, is part of the interface --- though "private"
    and "public" might not be the words which are used to describe a
    similar concept in those languages). Some languages (notably C++ and
    Java) consider that marking things as private is not good enough (I
    guess that the assumption is that people who program in those languages
    are too stupid to understand the importance of interfaces and to
    respect them), and such languages attempt to make the private parts
    inaccessible.

    Encapsulation and access restriction are orthogonal concepts, which
    have been conflated in Java and C++. That doesn't make them any less
    orthogonal, as concepts ... so don't make the (all too common mistake)
    of believing that they are actually one and the same.

    > I think I prefer the orthodoxy.


    Into bondage, are you ? :)

    > I suppose that is what you mean when you say:
    >> Be careful not to let Java or C++ (or anyone strongly influenced by
    >> them) to shape your understanding of object oriented programming. You
    >> would be doing yourself a disservice.

    > but I need more explanations, arguments, references.


    Well, in terms of explanations and arguments, I was steering you
    towards a discussion of the generic-function OO paradigm ... but you
    seem not to have followed that up.

    In terms of references ... try looking into some languages which
    support OO, which predate Java and C++ (or which are otherwise not
    influenced by them, or, at least, look very different from them.) You
    could try Smalltalk, or Common Lisp, for example.

    > You seem to encourage the following practice:
    >>>> class C: pass
    >>>> c = C()
    >>>> c.x = 9


    Absolutely.

    > the internal state of c is not private,


    This is not generally true, but it is true in this case.

    For example, I would discourage the following:

    class C:
    def __init__(self):
    self._x = 3

    c = C()
    c._x = 9

    Note that I would not forbid it. In fact, I would encourage it, in some
    situations. For example

    a) You are debugging your application, or investigating its behaviour

    b) The author of C hadn't anticipated some perfectly valid use you wish
    to make of C, and didn't provide you with an adequate interface
     
    Jacek Generowicz, Jul 15, 2004
    #11
  12. Greg Ewing Guest

    Egbert Bouwman wrote:
    > My impression is that you can hardly call this OOO:
    > Object Oriented Orthodoxy. For instance, from the GOF I learned:
    > - the object's internal state can not be accessed directly
    > - operations are the only way to change an object's internal state


    What you're missing is that, in Python, attribute access
    *is* an operation, on the same footing as calling a method.
    The object can be programmed to respond to it in any
    desired way. So, exposing an attribute as part of the
    public interface in no way limits the current or future
    implementation.

    This is in contrast to languages such as C++ and Java,
    where attribute access cannot be overridden, so you
    are forced to use accessor methods for everything in
    order to keep interface and implementation separate.
    The rules as you have learned them were designed for
    those languages.

    --
    Greg Ewing, Computer Science Dept,
    University of Canterbury,
    Christchurch, New Zealand
    http://www.cosc.canterbury.ac.nz/~greg
     
    Greg Ewing, Jul 16, 2004
    #12
  13. Egbert Bouwman <> writes:

    > On Thu, Jul 15, 2004 at 09:40:53AM -0700, Steven Bethard wrote:
    > >
    > > So if you decide that your data attributes need to change, you haven't
    > > destroyed compatibility with users of your class, even if your data
    > > attributes were publicly available. Continuing to support the older
    > > attributes isn't necessarily trivial, but at least it's /possible/
    > > (unlike C++ or Java).

    >
    > Yes, I see that in real life access to the internal state of an
    > object is sometimes necessary.


    Let's just think about this statement. In real life, access to the
    internal state of an object is _always_ necessary. What would be the
    point of the internal state, if there were no means of accessing it ?

    (Yes, I'm being pedantic, but we are talking about programming
    computers, and computers are pedantic in the extreme ... so you'd
    better be used to expressing yourself accurately :)

    > And Python allows it.


    So does C++:

    class A {
    public:
    int i;
    };

    A a;
    a.i;

    OK, this goes against C++ dogma, so you might prefer this:

    class A {
    private:
    int i;
    public:
    int getx() { return this->i; }
    void seti(int i) { this->i = i; }
    };

    A a;
    a.i;

    You're still accessing the internal state. And it doesn't have to be
    that trivial:

    class complex {
    private:
    double r;
    double i;
    public:
    double get_modulus() {
    return sqrt(i*i + r*r);
    }
    ...
    };


    C c;
    c.get_modulus();

    You're still accessing internal state. The whole point of internal
    state is that it be accessed somehow; if you couldn't access it, it
    would be a waste of memory. Now, you might wish to debate _how_ it
    should or should not be acessed ...

    > But what should i think of systems in which the users actually
    > have to use that licence ?


    Maybe you want to refine your question.

    As I said elsewhere ... you should probably think about the point of
    interfaces (others have addressed this point upthread); you should
    understand the concept of data abstraction. If you understand that,
    you should be able to judge for yourself what is a good idea and what
    is not. Dogmas are for people who can't think for themseleves.

    I guess I could summarize my message thus:

    Don't just repeat words; understand the concepts behind the words.
     
    Jacek Generowicz, Jul 16, 2004
    #13
  14. A B Carter Guest

    "" <beliavsky@127.0.0.1:7501> wrote
    > from math import sqrt
    > class xy:
    > def __init__(self,x,y):
    > self.x = x
    > self.y = y
    > def modulus(self):
    > return sqrt(self.x**2 + self.y**2)
    >
    > def modulus(z):
    > return sqrt(z.x**2 + z.y**2)
    >
    > z = xy(3,4)
    > print z.modulus()
    > print modulus(z)
    >


    In the example given the use of a function is IMHO a straightforward
    mistake. The rivers will not run with blood if you do this, but it is
    nonetheless a clear violation of basic OO design.

    By using a function you're implying that it will work, if not with all
    objects then with some wide range of semi-generic or base objects. But
    most objects will not have x and y attributes. In other cases an
    object may have such attributes but store string values or possess
    attributes that could be sensibly used as arguments to a modulus
    function but have the wrong names. A fundamental insight of the OO
    approach is the benefit of associating a data structure with a set of
    methods that work with that data structure. By incorporating modulus
    as a method in the xy class you avoid the problems mentioned above.

    It makes sense to have functions that work with all objects, such as
    the id() function or they work with basic Python types such as numeric
    values. A modulus function would be fine if it took two numeric values
    and returned the square root of the sum of their squares, but it would
    then be just the math.hypot function.

    Python does place a premium on rapid prototyping and in consequence
    possesses an OO implementation that doesn't get in the way of coding.
    The joy of Python is that you can usually concentrate on the problem
    and not be distracted by the need to satisfy additional requirements
    of OO purity. However Python is an OO language; all of the standard
    concepts still apply and all of the usual guidelines should still be
    followed. You still write to the interface, you still encapsulate your
    data and you still associate methods with data structures. This isn't
    a matter of OO orthodoxy but of practical design that led to the
    development of OO.

    A B Carter
     
    A B Carter, Jul 16, 2004
    #14
  15. Guest

    Egbert Bouwman <> wrote in message news:<>...

    > My impression is that you can hardly call this OOO:
    > Object Oriented Orthodoxy. For instance, from the GOF I learned:


    Excuse me, but what is "GOF"?

    I thought of Good Old Fortran, but them I am an odd duck :).
     
    , Jul 16, 2004
    #15
  16. Nick Efford Guest

    On Fri, 16 Jul 2004 04:52:50 -0700, beliavsk wrote:

    > Excuse me, but what is "GOF"?
    >


    GOF = Gang of Four = Gamma, Helm, Johnson, Vlissides
    authors of the seminal work on design patterns


    N.
     
    Nick Efford, Jul 16, 2004
    #16
    1. Advertising

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

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. E11
    Replies:
    1
    Views:
    4,842
    Thomas Weidenfeller
    Oct 12, 2005
  2. Xiangliang Meng
    Replies:
    1
    Views:
    1,629
    Victor Bazarov
    Jun 21, 2004
  3. John M. Gabriele
    Replies:
    18
    Views:
    1,174
    Steven Bethard
    Feb 18, 2005
  4. Oltmans
    Replies:
    6
    Views:
    360
    Terry Reedy
    Mar 11, 2009
  5. Kenneth McDonald
    Replies:
    5
    Views:
    343
    Kenneth McDonald
    Sep 26, 2008
Loading...

Share This Page