Inheriting from object

Bengt Richter bokr at oz.net
Sun Jul 3 14:53:28 EDT 2005


On Sat, 02 Jul 2005 12:26:49 -0700, Scott David Daniels <Scott.Daniels at Acm.Org> wrote:

>Bengt Richter wrote:
>> On Thu, 30 Jun 2005 08:54:31 -0700, Scott David Daniels <Scott.Daniels at Acm.Org> wrote:
>>>Or, perhaps:
>>>    class foo(object):
>>>        def __init__(self, *args, **kwargs):
>>>            super(foo, self).__init__(self, *args, **kwargs)
>>>            ...
>>>
>> 
>> Doesn't super(foo, self).__init__ return a bound method, so you don't
>> need to pass self again? I.e.,
>>               super(foo, self).__init__(*args, **kwargs)
>
>Yes, of course (a silly cut-o / paste-o).
>
>> BTW, there's something about referring to type(self) by its not
>> always dependably bound (though usually global) name that bothers me.
>> 
>> I wonder if the above common use of super could be implemented as a property of object,
>> so you'd normally inherit it and be able to write
>>     self.super.__init__(*args, **kwargs)  # (maybe spell it self.__super__.__init__(...) I suppose)
>> 
>> I.e., self.__super__ would effectively return the equivalent of
>>     super(type(self), self)
>
>The problem with this is:
>
>     class A(object):
>         def __init__(self, *args, **kwargs):
>             print 'Set A(*%r, **%r)' % (args, kwargs)
>     class B(A):
>         def __init__(self, *args, **kwargs):
>             print 'Set B(*%r, **%r)' % (args, kwargs)
>             super(B, self).__init__(*args, **kwargs)
>     class C(B):
>         def __init__(self, *args, **kwargs):
>             print 'Set C(*%r, **%r)' % (args, kwargs)
>             super(C, self).__init__(*args, **kwargs)
>
>     class D(A):
>         def __init__(self, *args, **kwargs):
>             print 'Set D(*%r, **%r)' % (args, kwargs)
>             super(type(self), self).__init__(*args, **kwargs)
>     class E(D):
>         def __init__(self, *args, **kwargs):
>             print 'Set E(*%r, **%r)' % (args, kwargs)
>             super(type(self), self).__init__(*args, **kwargs)
>
>
>You'll see the problem when you attempt to create an instance of E.
>All of the others work just fine.
>
Ok, I had a brain-o ;-) If we had class decorators analogous to function decorators,
we could write
    @deco(args)
    class X: ...

instead of
    class X: ...
    X = deco(args)(X)

below, but anyway (untested beond what you see), using your example, have a look:

----< super_cls_deco.py >-------------------------------------------------------
# super_cls_deco.py -- bokr 2005-07-03
from ut.presets import presets # function local presets decorator
def preset_super_ubm(target_method_name, super_method_name, alias=None):
    """
    class decorator to preset an unbound super-method as a local
    alias name in a target method of the decorated class.
    """
    if alias is None: alias = 'SUPER'+super_method_name # for local name in target method
    def super_deco(cls):
        if not getattr(cls, target_method_name):
            raise ValueError, 'class %s does not have a %s method' %(
                cls.__name__, target_method_name)
        for base in cls.mro()[1:]:
            if hasattr(base, super_method_name):
                ubm = getattr(base, super_method_name)
                setattr(cls, target_method_name, presets(**{alias:ubm})(
                    cls.__dict__[target_method_name]))
                return cls
        raise ValueError, '%s not found as super-method' % super_method_name
    return super_deco

def test():
    class A(object):
        def __init__(self, *args, **kwargs):
            print 'Set A(*%r, **%r)' % (args, kwargs)
            SUPER__init__(self, *(('from A:',)+args), **kwargs)
    A = preset_super_ubm('__init__', '__init__')(A)
    class B(A):
        def __init__(self, *args, **kwargs):
            print 'Set B(*%r, **%r)' % (args, kwargs)
            SUPER__init__(self, *(('from B:',)+args), **kwargs)
            #super(B, self).__init__(*args, **kwargs)
    B = preset_super_ubm('__init__', '__init__')(B)

    class C(B):
        def __init__(self, *args, **kwargs):
            print 'Set C(*%r, **%r)' % (args, kwargs)
            SUPER__init__(self, *(('from C:',)+args), **kwargs)
            #super(C, self).__init__(*args, **kwargs)
    C = preset_super_ubm('__init__', '__init__')(C)

    class D(A):
        def __init__(self, *args, **kwargs):
            print 'Set D(*%r, **%r)' % (args, kwargs)
            SUPER__init__(self, *(('from D:',)+args), **kwargs)
            #super(type(self), self).__init__(*args, **kwargs)
    D = preset_super_ubm('__init__', '__init__')(D)

    class E(D):
        def __init__(self, *args, **kwargs):
            print 'Set E(*%r, **%r)' % (args, kwargs)
            SUPER__init__(self, *(('from E:',)+args), **kwargs)
            #super(type(self), self).__init__(*args, **kwargs)
    E = preset_super_ubm('__init__', '__init__')(E)

    print '... from creating instance %s\n' % A('some', 'args', a='keyword')
    print '... from creating instance %s\n' % B('some', 'args', a='keyword')
    print '... from creating instance %s\n' % C('some', 'args', a='keyword')
    print '... from creating instance %s\n' % D('some', 'args', a='keyword')
    print '... from creating instance %s\n' % E('some', 'args', a='keyword')

if __name__ == '__main__':
    test()
--------------------------------------------------------------------------------

which results in:

[11:45] C:\pywk\clp>py24 super_cls_deco.py
Set A(*('some', 'args'), **{'a': 'keyword'})
... from creating instance <__main__.A object at 0x02F031EC>

Set B(*('some', 'args'), **{'a': 'keyword'})
Set A(*('from B:', 'some', 'args'), **{'a': 'keyword'})
... from creating instance <__main__.B object at 0x02F031EC>

Set C(*('some', 'args'), **{'a': 'keyword'})
Set B(*('from C:', 'some', 'args'), **{'a': 'keyword'})
Set A(*('from B:', 'from C:', 'some', 'args'), **{'a': 'keyword'})
... from creating instance <__main__.C object at 0x02F031EC>

Set D(*('some', 'args'), **{'a': 'keyword'})
Set A(*('from D:', 'some', 'args'), **{'a': 'keyword'})
... from creating instance <__main__.D object at 0x02F031EC>

Set E(*('some', 'args'), **{'a': 'keyword'})
Set D(*('from E:', 'some', 'args'), **{'a': 'keyword'})
Set A(*('from D:', 'from E:', 'some', 'args'), **{'a': 'keyword'})
... from creating instance <__main__.E object at 0x02F031EC>

(presets is my function byte-code-hacking decorator that presets local names
in a function at decoration-time without using the default-argument hack)
E.g.,

 >>> from ut.presets import presets
 >>> def foo(): print msg
 ...
 >>> foo()
 Traceback (most recent call last):
   File "<stdin>", line 1, in ?
   File "<stdin>", line 1, in foo
 NameError: global name 'msg' is not defined
 >>> import dis
 >>> dis.dis(foo)
   1           0 LOAD_GLOBAL              0 (msg)
               3 PRINT_ITEM
               4 PRINT_NEWLINE
               5 LOAD_CONST               0 (None)
               8 RETURN_VALUE
 >>> foo = presets(msg='this is a preset')(foo) # manual decoration call
 >>> foo()
 this is a preset
 >>> dis.dis(foo)
   1           0 LOAD_CONST               1 ('this is a preset')
               3 STORE_FAST               0 (msg)

   3           6 LOAD_FAST                0 (msg)
               9 PRINT_ITEM
              10 PRINT_NEWLINE
              11 LOAD_CONST               0 (None)
              14 RETURN_VALUE

A class decorator would avoid that nasty global name reference that bothers me ;-)

Regards,
Bengt Richter



More information about the Python-list mailing list