PySide Bugzilla Closed for New Bugs

PySide is now a Qt Add-on and uses the Qt Project's JIRA Bug Tracker instead of this Bugzilla instance. This Bugzilla is left for reference purposes.

Bug 888 - Default constructor broken in case of multiple inheritance
: Default constructor broken in case of multiple inheritance
Status: CLOSED WONTFIX
Product: PySide
Classification: Unclassified
Component: PySide
: 1.0.3
: PC MS Windows XP/Vista/7
: P5 normal
Assigned To: renato filho
:
:
:
  Show dependency treegraph
 
Reported: 2011-06-15 22:05 EEST by Christian
Modified: 2011-08-23 00:37 EEST (History)
8 users (show)

See Also:


Attachments
Minimal example to reproduce non working default __init__ (1.88 KB, text/plain)
2011-06-15 22:05 EEST, Christian
Details
Minimal example to reproduce broken super mechanism (1.78 KB, text/plain)
2011-06-15 22:06 EEST, Christian
Details

Note You need to log in before you can comment on or make changes to this bug.
Description Christian 2011-06-15 22:05:37 EEST
Created attachment 347 [details]
Minimal example to reproduce non working default __init__

When deriving from multiple Qt classes and without providing a custom __init__
method will result in only partially initialized objects.

A related issue is when the parent constructors are invoked with the super
mechanism of Python.

For more details see attached examples.
Comment 1 Christian 2011-06-15 22:06:31 EEST
Created attachment 348 [details]
Minimal example to reproduce broken super mechanism
Comment 2 Hugo Parente Lima 2011-06-16 16:49:30 EEST
The same applies for any Python object, example:

class A (object):
    def __init__(self):
        print "A"

class B (object):
    def __init__(self):
        print "B"


class C (A, B):
    def __init__(self):
        super(C, self).__init__()
        print "C"

c = C()


This will print:

A
C

And B is left uninitialized, so it's seems to be a normal Python behavior, you
need to call both parent constructor manually.
Comment 3 Christian 2011-06-17 16:28:39 EEST
You example is misleading, when you want to use the super mechanism you have to
call super on each node in the diamond shaped inheritance tree:


class A (object):
    def __init__(self):
        super(A, self).__init__()
        print "A"

class B (object):
    def __init__(self):
        super(B, self).__init__()
        print "B"

class C (A, B):
    def __init__(self):
        super(C, self).__init__()
        print "C"

c = C()

This will print:

B
A
C


I suspect PySide is either missing some nodes or is using the older direct call
approach: Base.__init__(self, ...). Which leads to some serious problems in the
case of multiple inheritance. Look at the following:



class Base(object):
    def __init__(self):
        object.__init__(self)
        print 'Init Base'

class A (Base):
    def __init__(self):
        Base.__init__(self)
        print "A"

class B (Base):
    def __init__(self):
        Base.__init__(self)
        print "B"

class C (A, B):
    def __init__(self):
        A.__init__(self)
        B.__init__(self)
        print "C"

c = C()


This will print:

Init Base
A
Init Base
B
C

In contrast to that using super would print:

Init Base
B
A
C



class Base(object):
    def __init__(self):
        super(Base, self).__init__()
        print 'Init Base'

class A (Base):
    def __init__(self):
        super(A, self).__init__()
        print "A"

class B (Base):
    def __init__(self):
        super(B, self).__init__()
        print "B"

class C (A, B):
    def __init__(self):
        super(C, self).__init__()
        print "C"

c = C()
Comment 4 Hugo Parente Lima 2011-06-17 17:02:54 EEST
(In reply to comment #3)
> You example is misleading, when you want to use the super mechanism you have to
> call super on each node in the diamond shaped inheritance tree:
> 
> 
> class A (object):
>     def __init__(self):
>         super(A, self).__init__()
>         print "A"
> 
> class B (object):
>     def __init__(self):
>         super(B, self).__init__()
>         print "B"
> 
> class C (A, B):
>     def __init__(self):
>         super(C, self).__init__()
>         print "C"
> 
> c = C()
> 
> This will print:
> 
> B
> A
> C

What if A anb B has no default constructors? e.g. A constructor receives a
string object and B constructor another completely different object?

e.g.:

class Foo (object):
    def bar(self):
        return 1

class A (object):
    def __init__(self, number):
        super(A, self).__init__()
        self.n = number + 1
        print "A"

class B (object):
    def __init__(self, foo):
        super(B, self).__init__()
        self.foo = foo.bar()
        print "B"

How the C super call should be made?

> I suspect PySide is either missing some nodes or is using the older direct call
> approach: Base.__init__(self, ...). Which leads to some serious problems in the
> case of multiple inheritance. Look at the following:
> 
> 
> 
> class Base(object):
>     def __init__(self):
>         object.__init__(self)
>         print 'Init Base'
> 
> class A (Base):
>     def __init__(self):
>         Base.__init__(self)
>         print "A"
> 
> class B (Base):
>     def __init__(self):
>         Base.__init__(self)
>         print "B"
> 
> class C (A, B):
>     def __init__(self):
>         A.__init__(self)
>         B.__init__(self)
>         print "C"
> 
> c = C()
> 
> 
> This will print:
> 
> Init Base
> A
> Init Base
> B
> C
> 
> In contrast to that using super would print:
> 
> Init Base
> B
> A
> C
> 
> 
> 
> class Base(object):
>     def __init__(self):
>         super(Base, self).__init__()
>         print 'Init Base'
> 
> class A (Base):
>     def __init__(self):
>         super(A, self).__init__()
>         print "A"
> 
> class B (Base):
>     def __init__(self):
>         super(B, self).__init__()
>         print "B"
> 
> class C (A, B):
>     def __init__(self):
>         super(C, self).__init__()
>         print "C"
> 
> c = C()
Comment 5 Christian 2011-06-17 19:10:17 EEST
Good point, with arguments it starts to get tricky.

I would implement your example as following:


class Foo(object):
    def bar(self):
        return 1

class A(object):
    def __init__(self, number, **kwargs):
        super(A, self).__init__(**kwargs)
        self.n = number + 1
        print "A"

class B(object):
    def __init__(self, foo, **kwargs):
        super(B, self).__init__(**kwargs)
        self.foo = foo.bar()
        print "B"

class C(A, B):
    def __init__(self, number, foo, **kwargs):
        kwargs.update({'number': number, 'foo': foo})
        super(C, self).__init__(**kwargs)
        print "C"

c = C(2, Foo())
print c.n, c.foo

That will print:

B
A
C
3 1
Comment 6 Christian 2011-06-17 21:12:08 EEST
Instead of:


    kwargs.update({'number': number, 'foo': foo})
    super(C, self).__init__(**kwargs)

You could also do simply:

    super(C, self).__init__(number=number, foo=foo, **kwargs)
Comment 7 Hugo Parente Lima 2011-06-17 21:31:53 EEST
(In reply to comment #6)
> Instead of:
> 
> 
>     kwargs.update({'number': number, 'foo': foo})
>     super(C, self).__init__(**kwargs)
> 
> You could also do simply:
> 
>     super(C, self).__init__(number=number, foo=foo, **kwargs)

More problems rising up...

Not all class constructors on generated bindings supports keyword arguments,
and even so we can't guarantee that they all have different names, so how to
implement C inheriting from A and B bellow?

class A(object):
    def __init__(self, arg, **kwargs):
        super(A, self).__init__(**kwargs)
        self.n = arg + 1
        print "A"

class B(object):
    def __init__(self, arg, **kwargs):
        super(B, self).__init__(**kwargs)
        self.foo = arg.bar()
        print "B"
Comment 8 Christian 2011-06-19 05:32:46 EEST
I have to admit the whole topic opens many questions and I currently don't have
a good concept to implement __init__ with the super mechanism.

Although having clear advantages in diamond shaped inheritance trees the whole
super concept seems to be not very widespread in the Python community. And our
discussion shows that it is absolutely understandable why most ignore it and
here Python has failed to comply "There should be one-- and preferably only one
--obvious way to do it." in my opinion.

However thanks Hugo for the open discussion.
Comment 9 Hugo Parente Lima 2011-08-11 21:08:05 EEST
After this very useful discussion I'm marking this bug as wontfix as we can't
achieve a good solution to the problem due to Python deficiencies related to
the super mechanism and base classes with different constructor arguments
without names.

Regards
Comment 10 renato filho 2011-08-23 00:37:14 EEST
Release PySide 1.0.6