Strange infinite recursion in metaclass's __new__

Discussion in 'Python' started by Jp Calderone, Nov 10, 2003.

  1. Jp Calderone

    Jp Calderone Guest

    Due to some bizarre constraints placed on me, I've written the following
    metaclass:

    import types

    def remove(t, o):
    return tuple([e for e in t if t not in o])

    class BizarreMetaclass(type):
    def __new__(klass, name, bases, attrs):
    if name == 'BaseClass':
    return type.__new__(klass, name, bases, attrs)
    return types.ClassType(name, remove(bases, [object]), attrs)

    class BaseClass(object):
    __metaclass__ = BizarreMetaclass

    class Foo(BaseClass):
    pass

    The solution is extremely hacky (but I've already tried and discarded
    about a dozen more straightforward attempted solutions).

    It is also broken, but it is broken in a way that I do not understand.

    Can anyone explain the unbounded recursion that occurs?

    Jp
     
    Jp Calderone, Nov 10, 2003
    #1
    1. Advertising

  2. Jp Calderone

    anton muhin Guest

    Jp Calderone wrote:
    > Due to some bizarre constraints placed on me, I've written the following
    > metaclass:
    >
    > import types
    >
    > def remove(t, o):
    > return tuple([e for e in t if t not in o])
    >
    > class BizarreMetaclass(type):
    > def __new__(klass, name, bases, attrs):
    > if name == 'BaseClass':
    > return type.__new__(klass, name, bases, attrs)
    > return types.ClassType(name, remove(bases, [object]), attrs)
    >
    > class BaseClass(object):
    > __metaclass__ = BizarreMetaclass
    >
    > class Foo(BaseClass):
    > pass
    >
    > The solution is extremely hacky (but I've already tried and discarded
    > about a dozen more straightforward attempted solutions).
    >
    > It is also broken, but it is broken in a way that I do not understand.
    >
    > Can anyone explain the unbounded recursion that occurs?
    >
    > Jp
    >


    What's strange?

    object isn't immediate parent of Foo. Therefore, you actually don't
    alter bases list :)

    anton.
     
    anton muhin, Nov 10, 2003
    #2
    1. Advertising

  3. Jp Calderone wrote:

    > Due to some bizarre constraints placed on me, I've written the following
    > metaclass:
    >
    > import types
    >
    > def remove(t, o):
    > return tuple([e for e in t if t not in o])
    >
    > class BizarreMetaclass(type):
    > def __new__(klass, name, bases, attrs):
    > if name == 'BaseClass':
    > return type.__new__(klass, name, bases, attrs)
    > return types.ClassType(name, remove(bases, [object]), attrs)
    >
    > class BaseClass(object):
    > __metaclass__ = BizarreMetaclass
    >
    > class Foo(BaseClass):
    > pass
    >
    > The solution is extremely hacky (but I've already tried and discarded
    > about a dozen more straightforward attempted solutions).
    >
    > It is also broken, but it is broken in a way that I do not understand.
    >
    > Can anyone explain the unbounded recursion that occurs?


    type.ClassType bends over backwards to accomodate classes that do in
    fact have a metaclass of 'type'; this is basically to let you code:

    class WeirdMix(classicone, object): ...

    to obtain a _new-style_ class otherwise equivalent to classicone (save
    for whatever additions and overrides you may choose to do in the body
    of WeirdMix).

    Since you don't remove Baseclass from among the bases before calling
    types.ClassType, then it identifies that one base class has a metaclass
    of BizarreMetaclass and defers to it -- whence the recursion. I did try
    to cover this in my Europython tutorial on metaclasses, btw -- it IS an
    important point, and an often-overlooked one.

    Since it does not appear to me that you NEED 'BaseClass' to remain
    among the bases of Foo, why don't you remove it, just as you remove
    object...?

    There's also a bug in your remove function, and the name of the first
    arg to __new__ in a metaclass should be mcl (just like it should be
    cls for __new__ in a normal class and self for other methods) -- I
    proposed that at Europython and Guido liked it;-) -- so, overall:

    import types

    def remove(t, o):
    return tuple([e for e in t if e not in o])

    class BizarreMetaclass(type):
    omit = [object]
    def __new__(mcl, name, bases, attrs):
    if name == 'BaseClass':
    result = type.__new__(mcl, name, bases, attrs)
    mcl.omit.append(result)
    return result
    return types.ClassType(name, remove(bases, mcl.omit), attrs)

    class BaseClass(object):
    __metaclass__ = BizarreMetaclass

    class Foo(BaseClass):
    pass

    f = Foo()
    print f, type(f), f.__class__
    print isinstance(f, BaseClass)
    print issubclass(Foo, BaseClass)

    this print, as required and expected:


    <__main__.Foo instance at 0x402decac> <type 'instance'> __main__.Foo
    False
    False


    Alex
     
    Alex Martelli, Nov 10, 2003
    #3
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Steven Bethard
    Replies:
    2
    Views:
    480
    Steven Bethard
    Feb 16, 2005
  2. ironfroggy
    Replies:
    16
    Views:
    447
    Michele Simionato
    Jun 3, 2005
  3. Matthew Wilson
    Replies:
    1
    Views:
    642
  4. Steven D'Aprano

    Recursion error in metaclass

    Steven D'Aprano, Jun 11, 2011, in forum: Python
    Replies:
    3
    Views:
    340
    Terry Reedy
    Jun 11, 2011
  5. Steven D'Aprano

    Metaclass of a metaclass

    Steven D'Aprano, Jun 5, 2012, in forum: Python
    Replies:
    1
    Views:
    322
Loading...

Share This Page