[Python-3000] Generic function PEP won't make it in time

Talin talin at acm.org
Tue Apr 24 08:17:44 CEST 2007


Guido van Rossum wrote:
> On 4/23/07, Phillip J. Eby <pje at telecommunity.com> wrote:
>> At 04:49 PM 4/23/2007 -0700, Guido van Rossum wrote:
>>>> I suppose there is some value in requiring people to override @abstract
>>>> methods to make a subclass instantiable, versus merely using the lack of an
>>>> @abstract class decorator to indicate that a subclass is concrete.  But I
>>>> wonder if being able to have just one @abstract decorator (that always
>>>> means "you can't call this by default") mightn't be worth giving up that
>>>> tiny bit of extra type checking?
>>> I prefer to follow the lead of C++ here -- an abstract class is
>>> abstract by virtue of having at least one abstract method.
>> It's been way too long since I did any C++, but isn't it the case that an
>> abstract (aka "pure virtual"?) method in C++ is one that *can't be
>> invoked*?  [pause to Google it...]  Well I'll be darned.  I didn't know you
>> could actually provide an implementation for a pure virtual
>> function.  Hm.  Learn something new every day...  except while I was
>> writing C++ evidently.  :)

I'm not sure what Phillip is talking about here - C++ does not allow you 
to instantiate a class that has non-overridden pure virtual methods, and 
no pure virtual method can have a body. You cannot provide an 
implementation for a pure virtual function.

Now, there are occasions in C++ where it is possible for buggy code to 
"call" a pure virtual function - but the result is always an exception.

Recall that in C++, when an instance is destructed, as each base class's 
destructor is called the vtable pointer is modified to point to the 
vtable for that base class, not the vtable for the actual class of the 
instance. (I've always had problems with this rule, but there it is.)

If you attempt to call a subclass method after that subclass's 
destructor has been called (which is, of course, an error), you may end 
up invoking the little stub function which a pure virtual vtable entry 
points to. This is about as "legal" as dereferencing memory after it has 
been freed.

> Actually I just meant to follow its lead in defining that a class is
> abstract as soon as one or more of its methods are abstract. In Java
> you need to repeat this by making the class abstract; that seems
> unpythonic.
> 
>> But are there any other languages besides C++ that have this idiom?  I'm
>> not familiar with any that do (at least, not that I know of!), and the fact
>> that C++ allows it seems a bit non-obvious to me.  Java and C# pretty much
>> do @abstract the way I proposed it, for example:
>>
>> Java: http://java.sun.com/docs/books/tutorial/java/IandI/abstract.html
>> C#: http://www.codeproject.com/useritems/Abstract_CLS_MTHD.asp
>>
>> That is, they both separate class-abstraction from method-abstraction, even
>> though the latter implies the former.  Thus, you can have an abstract class
>> 'Iterator' even if all its methods are non-abstract.  (However, if any of
>> its methods are abstract, the class itself is required to be abstract.)
> 
> I don't see the point of having an abstract class without abstract methods.
> 
> The main reasons I came up (without knowing C++ allows this too!) with
> the idea of giving abstract methods a valid implementation wer (a)
> there's got to be *something* in the body; (b) to provide an end point
> for cooperative-MI-style code.
> 
>> As someone with more recent background in Java than C++, I find the idea of
>> abstract methods having an executable implementation to be quite confusing,
>> and suspect that other people with that Java or C# background will do the
>> same thing.  That is, skim the explanation and miss the significant
>> difference between the C++ way and what they're used to.
> 
> Well, too bad. After a day of coding in Java I start typing curly
> braces and semicolons too. But that doesn't mean Python should adopt
> these.

I think I am in (weak) agreement with Phillip here. In every language 
that I am familiar with, the adjective "abstract" when applied to a 
method implies two things: (1) that the method does not have a body, and 
(2) the method must be implemented by a subclass if that subclass is 
able to be instantiated. In fact, (2) is merely a consequence of (1), 
when combined with the additional constraint that no instantiated class 
can have methods with no body.

However, I think what is going on is that you're using the word 
"abstract" in a slightly difference sense. Normally, an abstract method 
provides merely an interface, in other words it defines the method name, 
argument types, and return type of a method - and nothing more.

What you're doing, I think, is saying that an abstract method provides 
all those things, and in addition it provides a kind of sketch or 
outline of what the functionality ought to be. I don't have a problem 
with that part so much.

But in addition there is a sense that you also want these methods to be 
useful helper functions for base classes to call, but you don't want 
them to be called directly. In languages such as Java and C++, this is 
what "protected" is for - methods that aren't available to the outside 
world, but which can be called by subclasses.

So it seems to me -- at least from the standpoint of someone steeped in 
the mindset of Java / C++ / C# et al -- that you are conflating 
"abstract" and "protected".

In what is considered "good style" for such languages, protected helper 
methods ought to have different names than the methods which invoke 
them. So for example, you might have the following:

    /// Abstract base class for hashable object
    class Hashable {
    public:
       /// Declare abstract hash method
       virtual int GetHash() const = 0;

    protected:
       /// Protected helper method
       int DefaultHashFunc() { return 0; }
    };

    // A concrete class derived from hashable
    class MyConcreteClass : public Hashable {
    public:
       /// Declare abstract hash method
       virtual int GetHash() const { return DefaultHashFunc(); }
    };

Granted, in this particular case the decision to provide a helper 
function is rather odd. Why go through the trouble of defining 
DefaultHash() in the base class that simply returns 0, when any 
non-trivial subclass will provide a real implementation of GetHash() 
instead of merely forwarding the call to DefaultHash()?

But the solution isn't to give Hashable::GetHash() a body. From a C++ 
programmer's view, there's only two choices: Either Hashable::GetHash() 
is abstract, or it's not. If it's abstract, then every subclass is 
required to re-implement it, and giving it a body has no purpose 
whatsoever; If it's not, then subclasses are allowed to default to its 
implementation.

>>> That the abstract methods are still somewhat useful implementations is
>>> mostly to provide a valid (if not necessarily useful) end point for
>>> super-calling in cooperative MI schemes.
>> Right; I guess my point is that if those "somewhat" useful implementations
>> are useful, they're useful, and there's no need to treat the corresponding
>> class as "abstract" (in the "non-instantiable" sense) in that case.  For
>> example, I could pass an Iterable() to something that expected an iterable.
> 
> Perhaps. Though I think it would defeat the purpose of being required
> to provide an implementation. If forced to choose, I'd rather remove
> the meaningful bodies (and replace them with "raise
> NotImplementedError") than remove the @abstractmethod decorators. But
> I prefer the current proposal.

-- Talin


More information about the Python-3000 mailing list