Derek Martin a écrit :
On Sun, Jul 27, 2008 at 09:39:26PM +0200, Bruno Desthuilliers wrote:
As for the latter part of #3, self (or some other variable) is
required in the parameter list of object methods,
It's actually the parameter list of the *function* that is used as the implementation of a method. Not quite the same thing.

The idea that Python behaves this way is new to me.  For example, the
tutorials make no mention of it:

  http://docs.python.org/tut/node11.html#SECTION0011300000000000000000

The Python reference manual has very little to say about classes,
indeed.  If it's discussed there, it's buried somewhere I could not
easily find it.

Yeps, most of the doc didn't really follow Python's evolutions alas. But it's still documented - I've learned all this from the doc.

You'll find more details starting here:

http://www.python.org/doc/newstyle/

and a couple more stuff in the language specs part of the doc:

http://docs.python.org/ref/descriptors.html
http://docs.python.org/ref/descriptor-invocation.html


consistency mandates that the target object of the method is part of
the parameter list of the *function*, since that's how you make
objects availables to a function.

Fair enough, but I submit that this distinction is abstruse,

The distinction between class interface (the method call) and class implementation (the function called by the method) ?

and
poorly documented,

This is certainly true. Patches to the doc are welcome.

and also generally not something the average
application developer should want to or have to care about...

I don't know what's an "average application developper", but as an application developper myself, I feel I have to care about the implementation of my programs, just like I feel I have to care about knowing enough about the languages I use to use them properly.

it's of
interest primarily to computer scientists and language enthusiasts.
The language should prefer to hide such details from the people using
it.

There I beg to disagree. Transparently exposing most of it's object model is a design choice, and is for a great part responsible for Python expressive power and flexibility. And all this is really part of the language - I mean, it's a public API, not an implementation detail. FWIW, I'm certainly not what you'd call a "computer scientist" (I left school at 16 and have absolutely no formal education in CS).

Anyway: "the language" (IOW: the language's designer) made a different choice, and I'm very grateful he did.

however when the method is *called*, it is omitted.
Certainly not.

Seems not so certain to me...  We disagree, even after your careful
explanation.

You're of course (and hopefully) entitled the right to disagree !-)

 See below.

You need to lookup the corresponding attribute *on a given object*
to get the method. Whether you write

  some_object.some_method()

or

  some_function(some_object)

you still need to explicitely mention some_object.

But these two constructs are conceptually DIFFERENT,

Why so ?

whether or not
their implementation is the same or similar.  The first says that
some_method is defined within the name space of some_object.

The first says that you're sending the message "some_method" to some_object. Or, to express it in Python terminology, that you're looking up the name "some_method" on some_object, and try to call the object returned by the attribute lookup mechanism, whatever that object is (function, method, class or any other callable).

Now saying that it implies that "some_method is defined within the name space of some_object" depends on the definitions of 'defined', 'within' and 'namespace' (more on this below).

 The
second says that some_object is a parameter of some_function...

Yes. It also say that some_function knows enough about some_object to accept it as a parameter (or at least that the developper that passed some_object to some_function thought / expected it would be the case).

You know, the dotted notation ("obj.attrib") is, well, just a notation. It's by no mean necessary to OO. You could have a perfectly valid object system where the base notation is "some_message(some_object)" instead of being "some_object.some_message" - and FWIW, in Common Lisp - which BTW have one of the richer object systems around -, the syntax for method call is the same as the syntax for function call, IOW "(func_or_method_name object arg1 arg2 argN)".


Namespace != parameter!!!!!!!!!

Functions parameters are part of the namespace of the function body.

Please don't get me wrong : I'm not saying your point is moot, just suggesting another possible way to look at the whole thing.

To many people previously familiar with OO programming in other
languages (not just Java or C++), but not intimately familiar with
Python's implementation details,

It's actually not an implementation detail - it's part of the language spec.

the first also implies that
some_method is inherently part of some_object,

There again, I disagree. To me, it implies that some_object understands the 'some_method' message. Which is not the same thing. Ok, here's a possible implementation:

# foo.py

def foo(obj):
    return obj.__class__.__name__


# bar.py
from foo import foo

class Meta(type):
    def __new__(meta, name, bases, attribs):
        cls = type.__new__(meta, name, bases, attribs)
        old_getattr = getattr(cls, '__getattr__', None)

        def _getattr(self, attrname):
            if attrname == 'some_method':
                return lambda self=self: foo(self)
            elif callable(old_getattr):
                return old_getattr(self, attrname)
            else:
                raise AttributeError("blah blah")

        cls.__getattr__ = _getattr
        return cls

# baaz.py
import bar

class Quux(object):
    __metaclass__ = bar.Meta


class Baaz(object):
    def __init__(self):
        self._nix = Quux()
    def __getattr__(self, name):
        return getattr(self._nix, name)

# main.py
import baaz
some_object = baaz.Baaz()



Is 'some_method' "inherently part of" some_object here ? There isn't even an object named 'some_method' anywhere in the above code...

(and no, don't tell me, I know: it's a very convoluted way to do a simple thing - but that's not that far from things you could find in real-life library code for not-so-simple things).

in which case
explicitly providing a parameter to pass in the object naturally seems
kind of crazy.  The method can and should have implicit knowledge of
what object it has been made a part.

The method does. Not the function.

Here's a possible (and incomplete) Python implementation of the method type:

class Method(object):
    def __init__(self, func, instance, cls):
        self.im_func = func
        self.im_self = instance
        self.im_class = cls
    def __call__(self, *args, **kw):
        if self.im_self:
            args = (self.im_self, ) + args
            return self.im_func(*args, **kw)
        elif isinstance(args[0], self.im_class):
            return self.im_func(*args, **kw)
        else:
            raise TypeError("blah blah")

Part of the point of using
objects is that they do have special knowledge of themselves...

s/do/seem to/


they
(generally) manipulate data that's part of the object.  Conceptually,
the idea that an object's methods can be defined outside of the scope
of the object,

s/object/class/

and need to be told what object they are part
> of/operating on is somewhat nonsensical...

That's still how other OOPLs work, you know. But they hide the whole damn thing out and close the box, while Python exposes it all. And I can tell you from experience that it's a sound idea - this gives you full control about your object's behaviour.

wrt/ functions being defined outside classes then used as part of the implementation of a class, I fail to see where is the problem - but I surely see how it can help avoiding quite a lot of boilerplate when wisely used.

Thus when an object method is called, it must be called with one fewer
arguments than those which are defined.   This can be confusing,
especially to new programmers.
This is confusing as long as you insist on saying that what you "def"ined is a method - which is not the case.

I can see now the distinction, but please pardon my prior ignorance,
since the documentation says it IS the case, as I pointed out earlier.

Yeps. Part of the problem is that OO terminology doesn't have any clear, unambiguous definition - so terms like 'method' can be used with somewhar different meanings. Most of Python's doc use the term 'method' for functions defined within class statements - and FWIW, that's usually what I do to.

Furthermore, as you described, defining the function within the scope
of a class binds a name to the function and then makes it a method of
the class.   Once that happens, *the function has become a method*.

The function doesn't "become" a method - it's __get__ method returns a method object, that itself wraps the object and the function (cf above snippet). What's get stored in the class __dict__ is really the function:

>>> class Foo(object):
...     def bar(self):
...         print "bar(%s)" % self
...
>>> Foo.__dict__['bar']
<function bar at 0xb7ccaf7c>
>>>

Whether you bind the name within or outside of the class statement doesn't change anything.

To be perfectly honest, the idea that an object method can be defined
outside the scope of an object

I assume you meant "outside the class statement's body" ?

(i.e. where the code has no reason to
have any knowledge of the object)

Just any code "using" an object need to have at least some knowledge of this object, you know. Or do you mean that one should not pass message to any other object than self ? This seems like a pretty severe restriction to me - in fact, I fail to see how one could write any code that way !-)


seems kind of gross to me...   another
Python wart.

Nope. A *great* strength.

One which could occasionally be useful I suppose,

More than "occasionaly". Lots of frameworks use that (usually in conjonction with metaclasses) to inject attributes (usually functions) into your objects. Have a look at how most Python ORM work.

but a
wart nonetheless.

Your opinion. But then you wont probably like Python. May I suggest Ruby instead - it has a much more canonical object model ?-)

Err, no, wait - while dynamically adding attributes / methods to objects / classes is possible but not that common in Python (outside frameworks and ORMs at least), it's close to a national sport in Ruby. Nope, you won't like Ruby neither...

 This seems inherently not object-oriented at all,

Because things happens outside a class statement ? Remember, it's *object* oriented, not class oriented. Classes are not part of the base definitions of OO, and just aren't necessary to OO (have a look at Self, Io, or Javascript).

As far as I'm concerned, "object oriented" is defined by

1/ an object has an identity, a state and a behaviour
2/ objects communicate by sending messages to each others

And that's all for the OO theory - everything else is (more or less) language-specific. As you can see, there's no mention of "class" here, and not even of "method". All you have is identity, state, behaviour and messages - IOW, high level concepts that can be (are are indeed) implemented in many different ways.

for reasons I've already stated.  It also strikes me as a feature
designed to encourage bad programming practices.

For which definition of "bad" ?

Your views on what OO is are IMHO very restricted - I'd say, restricted to what the C++/Java/UML guys defined as "being OO".

Anyway: you'd be surprised by the self (no pun) discipline of most Python programmers. Python let you do all kind of crazy things, but almost no one seems to get over the board.

FWIW, if you find the idea of a "method" defined outside the class statement shocking, what about rebinding the class of an object at runtime ? You may not know it, but the class of an object is just another attribute, and nothing prevents you from rebinding it to any other object whenever you want !-)

Even discounting that, if Python had a keyword which referenced the
object of which a given peice of code was a part, e.g. self, then a
function written to be an object method could use this keyword *even
if it is defined outside of the scope of a class*.  The self keyword,
once the function was bound to an object, would automatically refer to
the correct object.   If the function were called outside of the
context of an object, then referencing self would result in an
exception.

This could probably be implemented, but it would introduce additional complexity. As I already said somewhere in this thread, as far as I'm concerned, as long as it doesn't break any existing code and doesn't impose restrictions on what is actually possible, I wouldn't care that much - but I think it would be mostly a waste of time (IMHO etc).

You'll probably argue that this takes away your ability to define a
function and subsequently use it both as a stand-alone function and
also as a method.

I could. FWIW, I've almost never had a need for such a construction yet, and I don't remember having seen such a thing anywhere.

But anyway, to avoid breaking code, the modification would still have to take into account functions using an explicit self (or cls) in the function's signature. I'm afraid this would end up making a mess of something that was originally simple.

 I'm OK with that -- while it might occasionally
be useful, I think if you feel the need to do this, it probably means
your program design is wrong/bad.  More than likely what you really
needed was to define a class that had the function as a method, and
another class (or several) that inherits from the first.

Not designing things the CanonicalUMLJavaMainstreamWay(tm) doesn't mean the design is wrong. Also, there are some problems that just can't be solved that way - or are overly (and uselessly) tedious to solve that way.

Talking about design, you may not have noticed yet, but quite a lot of the OO design patterns are mostly workaround the lack of flexibility in Java and C++ (hint: how would you implement the decorator pattern in Python ?). And while we're at it, the GoF (IMHO one of the best books on OO design) lousily insists on composition/delegation being often a way better design than inheritance (which FWIW is what Python does with method calls being delegated to functions).


The point is that you don't get access to the object "within itself". You get access to an object *within a function*.

Thus methods are not really methods at all,

Please show me where you get access to the object "within itself" in any other OO language. Methods (for the usual acceptation of the term) are *not* "within" the instances. And they access instances thru a reference to it, reference that get injected into the code one way or another. Most languages make this implicit, Python makes it explicit. So what ?

 which would seem to
suggest that Python's OO model is inherently broken (albeit by design,
and perhaps occasionally to good effect).

Here again, you are being overly dogmatic IMHO. Being explicit, or just being different from mainstream, is not the same as being "broken".

The fact that a function is defined within a class statement doesn't imply any "magic",

It does indeed -- it does more than imply.  It states outright that
the function is defined within the namespace of that object,

s/object/class/

and as
such that it is inherently part of that object.

s/object/class/

 So why should it need
to be explicitly told about the object of which it is already a part?

Because it's the simplest thing to do ?-)

More seriously, methods are usually (in Python - always in most OOPLs) part of a class, not of it's instances - IOW, the same code is shared by all instances of a same class. And the language implementation needs to make the instance accessible to the method code one way or another.

From this POV, Python doesn't behave differently - except that it choosed to expose the fact and make it part of the API.

It further does indeed imply, to hordes of programmers experienced
with OO programming in other languages, that as a member, property,
attribute, or what ever you care to call it, of the object, it should
have special knowledge about the object of which it is a part.

class Foo(object):
    some_dict = dict()

    def __init__(self, some_int, some_list, some_string):
        self.int = some_int
        self.list = some_list
        self.string = some_string

foo = Foo(42, range(3), "yadda")

Where do you see that 42, range(3) and "yadda" have any knowledge of foo?

Translate this to any other OOPLs and tell me if the answer is different.


IOW : there's one arguably good reason to drop the target object from functions used as methods implementation, which is to make Python looks more like Java

No, that's not the reason.  I don't especially like Java, nor do I use
it.

Sorry, I usually use 'Java' as a generic branding for the whole static struct-based class-oriented mindset - UML / C++ / Java / C# etc - by opposition to dynamic OOPLs.

Anyway, in this particular case, it was not even what I meant, so please accept my apologies and s/Java/canonical/ in the above remark.

The reason is to make the object model behave more intuitively.

I understand that having to specify the target object can seem disturbing, at least at first. Now once you know why, I personnaly find it more "intuitive" to not have different constructs for functions, methods, and functions-to-be-used-as-methods. I write functions, period. IOW:

, and there's at least two good reason to keep it the way it is,
which are simplicity (no special case) and consistency (no special
case).

Clearly a lot of people find that it is less simple TO USE.

I would say it requires a bit more typing. Does that make it less simple to use ? I'm not sure. Am I biased here ? After many years of Python programming, I don't even notice typing 'self' in the argument list no more, so very probably: yes.

 The point
of computers is to make hard things easier... if there is a task that
is annoying, or tedious, or repetitive, it should be done by code, not
humans.

Which BTW is why I really enjoy having the possibility to modify a class at runtime - believe me, it can save quite a lot of boilerplate...

 This is something that Python should do automatically for its
users.

This point is debatable, indeed. Still, the only serious reason I see here is to make Python look more like most mainstream OOPLs, and while it may be a good idea - I'm not making any judgement on this - I can happily live with the current state of things as far as I'm concerned. Anyway, the decision doesn't belong to me.
--
http://mail.python.org/mailman/listinfo/python-list

Reply via email to