Why new Python 2.5 feature "class C()" return old-style class ?

Bengt Richter bokr at oz.net
Sun Apr 23 18:12:01 EDT 2006


On Tue, 11 Apr 2006 16:30:10 +0200, bruno at modulix <onurb at xiludom.gro> wrote:

>looping wrote:
>> bruno at modulix wrote:
>> 
>>>looping wrote:
>>>
>>>>Peter Hansen wrote:
>>>>
>>>>
>>>>>Georg Brandl wrote:
>>>>>
>>>>>
>>>>>>class C():
>>>>>>
>>>>>>is meant to be synonymous with
is meant by whom, and according to what rationale?

>>>>>>
>>>>>>class C:
>>>>>>
>>>>>>and therefore cannot create a new-style class.
>>>>>
>>>>>I think "looping" understands that, but is basically asking why anyone
>>>>>is bothering with a change that involves a part of the language that is
>>>>>effectively deprecated.  In other words, class(): never used to be
>>>>>valid, so why make it valid now?
Because it it consistent with passing an empty bases-tuple to type,
which is where a non-empty bases-tuple goes. (See more on metaclass logic below).

>>>>>
>>>>>-Peter
>>>>
>>>>
>>>>Exact.
>>>>But I think that if we make "class C():" a synonym of "class
>>>>C(object):", it will save lot of keystrokes ;-)
>>>
+1

>>>Since the class statement without superclass actually creates an
>>>old-style class,  I'd expect the "class MyClass():" variant to behave
>>>the same. Sacrifying readability and expliciteness just to save half a
>>>dozen keystrokes is not pythonic IMHO.
>>>
>> 
>> 
>> I don't think readability suffer 
>
>It does. The statement "class X():" imply there's no superclass, so it
>definitiveley should behave the same as "class X:".
>
I'm not sure what you mean by "no superclass," comparing 

 >>> class X: pass
 ...
 >>> x = X()
 >>> type(x).mro()
 [<type 'instance'>, <type 'object'>]

with

 >>> class Y(object): pass
 ...
 >>> y = Y()
 >>> type(y).mro()
 [<class '__main__.Y'>, <type 'object'>]


The way I look at it, in new-style-capable Python,

    class X: pass

is really effectively sugar for

    class X: __metaclass__ = types.ClassType

and
    class X(bases): pass

is sugar for

    class X(bases): __metaclass__ = type

so
    class X(): pass

ought to be sugar for

    class X(): __metaclass__ = type

I.e., "old-style classes" really inherit methods from a metaclass that overrides
the methods of object, so that new-style descriptor logic can be tweaked to provide the
old behaviors for classes not having type as metaclass.

>> and expliciteness could sometimes be
>> sacrified to simplify the life of developer: ex "abcd"[0:3]  ->
>> "abcd"[:3].
>
>Here there's no ambiguity.
>
>> And for newbies, the somewhat magic behavior of the "object" superclass
>> is not so clear even that it is very explicit.
>
>There's no magic involved here. And I really doubt that having
>inconsistant behaviour for "class X():" wrt/ "class X:" will help here.
>
Again, IMO any bases-tuple including empty should be sugar for __metaclass__ = type, i.e.,

    class X(): pass

should be consistent with the empty parens in

    X = type("X",(),{}),

not with the backwards-compatibility cleverness (really) of effectively
switching default metaclass to

    X = types.ClassType("X",(),{})

using an empty and valid base class tuple as a flag for the switcheroo. Note that the code
erroneously creates an empty tuple for class X:pass, as if it were class X():pass.

(IMO the proper way to indicate the you don't have a tuple is to use None or some other sentinel,
not abuse a perfectly legal tuple value).

 >>> dis.dis(compile('class X:pass','','exec'))
   1           0 LOAD_CONST               0 ('X')   <<--+-- ought to be LOAD_CONST 0 (None)
               3 BUILD_TUPLE              0         <<--'
               6 LOAD_CONST               1 (<code object X at 02EE7EA0, file "", line 1>)
               9 MAKE_FUNCTION            0
              12 CALL_FUNCTION            0
              15 BUILD_CLASS
              16 STORE_NAME               0 (X)
              19 LOAD_CONST               2 (None)
              22 RETURN_VALUE

vs code for class x(something):pass

 >>> dis.dis(compile('class X(object):pass','','exec'))
   1           0 LOAD_CONST               0 ('X')
               3 LOAD_NAME                0 (object)
               6 BUILD_TUPLE              1
               9 LOAD_CONST               1 (<code object X at 02EFB9A0, file "", line 1>)
              12 MAKE_FUNCTION            0
              15 CALL_FUNCTION            0
              18 BUILD_CLASS
              19 STORE_NAME               1 (X)
              22 LOAD_CONST               2 (None)
              25 RETURN_VALUE

IMO generating an empty tuple for class X():pass is a natural variation of the immediately above.
What is un-natural is using an empty tuple as a logical flag for old-style classes (implementing
the latter by selecting types.ClassType as the metaclass instead of type)
for what would othewise work perfectly normally with the default metaclass of type.

Code generation would need to provide something other than an empty tuple
on the stack for class X:pass (IWT None would work?) as the logic flag for old-style classes,
and build_class in ceval.c would have to be changed to recognize
the new flag value (None?) for calling types.ClassType,
and pass tuples (including empty) through to type.

>> When I write script I don't use new-style class 
>
>You should.
>
>> cause is bother me to
>> type "(object)" when I don't need their features.
so for now put

    __metaclass__ = type

once at the top of your module source, and all your

    class X:
        ...

will be interpreted as

    class X:
        __metaclass__ = type
        ...

instead of implicitly as

    class X:
        __metaclass__ = types.ClassType
        ...

>
>Please repeat this 101 times each morning:
>"thou shall not use old-style classes for they are deprecated".
>
>(snip)
>
>> So this new syntax is a good way to boost their uses without bother
>> with compatibility of existing code IMHO.
>
>It's mostly a good way to add inconsistency and confusion to a situation
>that's already confusing enough for newbies.
I don't agree with your idea of inconsistency.

IMO it would be better to explain that a legal basetuple value (empty tuple) is currently
being abused as a logical flag to call types.ClassType(clsname, basestuple, clsdict)
instead of type(clsname, basestuple, clsdict), and explain that it will be corrected, so that
class X():pass will now call the latter, consistent with class X(bases):pass.

Bottom line: IMO class C():pass should create a new-style class,
and the parens serve well as a reminder of which kind it is, whether empty or not,
until py3k.

I.e., make it easy for newbies: parens means new-style, no parens means old-style, until py3k.

Pontificating pushes my counter-pontificating button; that's the only explanation
I have for doing this. I was going to stop wasting time, but find myself
unable as yet fully to abandon scanning clp and python-dev ;-/

Regards,
Bengt Richter



More information about the Python-list mailing list