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

Steven D'Aprano steve+comp.lang.python at pearwood.info
Tue Feb 3 12:40:22 EST 2015


Devin Jeanpierre wrote:

> On Mon, Feb 2, 2015 at 6:07 AM, Steven D'Aprano
> <steve+comp.lang.python at pearwood.info> wrote:
>> 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.
> 
> In this particular case, there is none. What if the body of the
> "method" was super().f() ?

What of it? The zero-argument form of super() is a compiler hack documented
as only working inside classes. Most methods don't even use super *at all*,
let alone the Python3-only zero argument form. Surely you cannot be serious
that the defining characteristic of what is or is not a method is whether
or not an explicit hack works?

But you know, I can duplicate the compiler hack and still use the
zero-argument form of super in a method defined on the outside of the
class.


py> class A(object):
...     pass
...
py> def magic():  # Don't do this at home!
...     __class__ = A
...     def f(self):
...             __class__
...             return super()  # Magic zero-argument form.
...     return f
...
py> A.f = magic()
py> a = A()
py> a.f()
<super: <class 'A'>, <A object>>


So to answer your question:

    What if the body of the "method" was super().f() ?


It would still work correctly. Because Python is just that awesome.


> Some methods can be defined outside of the body and still work exactly
> the same, but others won't.

"Some methods can be defined outside of the body".

AT LONG LAST THE LIGHT DAWNS! THE PENNY DROPS!

If some methods can be defined outside of the body of a class, then being
defined inside the body of a class does not define a method.

You say "some methods", but the reality is that *all methods* can do so.
Even if they use super. Even if they use the zero-argument form of super
(although that one is rather inconvenient).

The class statement is syntactic sugar for calling type(name, bases, ns):

class A(bases):
    x = 100
    f = lambda self: self.x + 1

is equivalent to:

A = type("A", bases, {'x': 100, 'f': lambda self: self.x + 1})

*ANY* class you create with the class statement can be created (less
conveniently) with type (or the appropriate metaclass).

Obviously the class statement form is convenient because it allows you to 
define the items of the namespace in place, which you can't always do using
type itself. With type, you may have to prepare the items first, insert
them into a dict, and pass it as the namespace argument. It also offers
convenient syntax for changing the metaclass. Most importantly, it is
simply more readable and nicer to be able to define the methods indented
inside the class statement. This is the natural way to define classes, and
given that the glossary need not be 100% complete and definitive, "function
defined inside a class body" is close enough to the truth.

But it is not *the whole truth*.


Not only can methods be defined outside of a class, but with a little
preparation functions defined inside of classes can remain functions.

py> class function(object):
...     def __init__(self, func):
...         self.func = func
...     def __get__(self, obj, cls=None):
...         return self.func
...
py> class B(object):
...     @function
...     def f(x, y):  # No self.
...             return x + y
...
py> B.f(23, 42)
65
py> b = B()
py> b.f(23, 42)
65
py> type(b.f)
<class 'function'>


(Alternatively, I could change the metaclass, although that's trickier.
Using a custom descriptor is just too easy.)


>> 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.
> 
> It isn't mystical. There are differences in semantics of defining
> methods inside or outside of a class that apply in certain situations
> (e.g. super(), metaclasses). You have cherrypicked an example that
> avoids them.

Out of the millions, billions of methods people write, how many do you think
use super? 1% 0.1%? 10%?

And you accuse *me* of cherrypicking.

Not that it matters. As I have shown, even the zero argument form of super
can be used.



-- 
Steven




More information about the Python-list mailing list