dunder-docs (was Python is DOOMED! Again!)

Steven D'Aprano steve+comp.lang.python at pearwood.info
Mon Feb 2 09:07:24 EST 2015


Devin Jeanpierre wrote:

> On Mon, Feb 2, 2015 at 4:06 AM, Steven D'Aprano
> <steve+comp.lang.python at pearwood.info> wrote:

>> instance.f is a method by the glossary definition. Its type is identical
>> to types.MethodType, which is what I used to create a method by hand.
> 
> You are assuming that they are both methods, just because they are
> instances of a type called "MethodType". This is like assuming that a
> Tree() object is made out of wood.

No. It is "assuming" that a Tree() object is a Tree() object.

Run this code:

# === cut ===

class K(object):
    def f(self): pass

def f(self): pass

instance = K()
things = [instance.f, f.__get__(instance, K)]
from random import shuffle
shuffle(things)
print(things)

# === cut ===


You allege that one of these things is a method, and the other is not. I
challenge you to find any behavioural or functional difference between the
two. (Object IDs don't count.)

If you can find any meaningful difference between the two, I will accept
that methods have to be created as functions inside a class body.

Otherwise you are reduced to claiming that there is some sort of mystical,
undetectable "essence" or "spirit" that makes one of those two objects a
real method and the other one a fake method, even though they have the same
type, the same behaviour, and there is no test that can tell you which is
which.


> The documentation is free to define things in terms other than types
> and be correct. 

If you wanted to argue that "method" is a generic term, and we have instance
methods, class methods, static methods, and any other sort of method we
care to create using the descriptor protocol, then I would agree you have a
point.

But since we're just talking about instance methods, Python doesn't care how
they came into existence. You can use def to create a function inside a
class body, inject a function into the class, call the descriptor __get__
method, or use the types.MethodType type object, it is all the same. You
can use a def statement, or a lambda, or types.FunctionType if you are
really keen. It makes no difference.

Do I expect the glossary to go into such pedantic detail? No, of course not.
But I do expect anyone with a few years of Python programming experience to
be able to understand that what makes a method be a method is its type and
behaviour, not where it came from.


> There are many properties of functions-on-classes that 
> callable instance attributes that are instances of MethodType do not
> have, as we've already noticed. isinstance can say one thing, and the
> documentation another, and both can be right, because they are saying
> different things.
> 
> 
> For an example we can all agree on, this is not an instance of
> collections.Iterable, but the docs claim it is iterable:
> https://docs.python.org/2/glossary.html#term-iterable
> 
>     class MyIterable(object):
>         def __getitem__(self, i): return i

"Iterable" is a generic term, not a type. Despite the existence of the
collections.Iterable ABC, "iterable" refers to any type which can be
iterated over, using either of two different protocols.

As I said above, if you wanted to argue that "method" was a general term for
any callable attached to an instance or class, then you might have a point.
But you're doing something much weirder: you are arguing that given two
objects which are *identical* save for their object ID, one might be called
a method, and the other not, due solely to where it was created. Not even
where it was retrieved from, but where it was created.

If you believe that "method or not" depends on where the function was
defined, then this will really freak you out:


py> class Q:
...     def f(self): pass  # f defined inside the class
...
py> def f(self): pass  # f defined outside the class
...
py> f, Q.f = Q.f, f  # Swap the "inside" f and the "outside" f.
py> instance = Q()
py> instance.f  # Uses "outside" f, so not a real method!
<bound method Q.f of <__main__.Q object at 0xb7b8fcec>>
py> MethodType(f, instance)  # Uses "inside" f, so is a real method!
<bound method Q.f of <__main__.Q object at 0xb7b8fcec>>



-- 
Steven




More information about the Python-list mailing list