deriving in python from a C++ base class
hi there, I have trouble extending a demo from the tutorial (taken from http://www.boost.org/libs/python/doc/tutorial/doc/exposing_classes.html). Instead of just instantiating objects of type 'World' in python, I subclass 'World' first, making calls to base class methods from the constructor: class MyWorld(World): def __init__(self): self.set('hi there') print self.greet() that ought to be possible (or so I thought), but instantiating a 'MyWorld' object (still inside python) yields: Traceback (most recent call last): File "test.py", line 18, in ? world = MyWorld() File "test.py", line 10, in __init__ self.set('hi there') TypeError: bad argument type for built-in operation Reading on in the tutorial I find some information about polymorphism on http://www.boost.org/libs/python/doc/tutorial/doc/class_virtual_functions.ht..., but that page explicitely states that the use of auxiliary wrapper classes is only needed in case I want to access the virtual (python overridden) methods from within C++, which is not (yet) my case, i.e. as far as I'm aware of in my little test there isn't any form of polymorphism involved. What am I missing ? Thanks, Stefan
--- Stefan Seefeld <seefeld@sympatico.ca> wrote:
class MyWorld(World): def __init__(self): self.set('hi there') print self.greet()
I think you have to call World.__init__() before you can use the instance. Here is more information: http://www.boost.org/libs/python/doc/PyConDC_2003/bpl.html#inheritance Ralf __________________________________ Do you Yahoo!? SBC Yahoo! DSL - Now only $29.95 per month! http://sbc.yahoo.com
Ralf W. Grosse-Kunstleve wrote:
--- Stefan Seefeld <seefeld@sympatico.ca> wrote:
class MyWorld(World): def __init__(self): self.set('hi there') print self.greet()
I think you have to call World.__init__() before you can use the instance. Here is more information:
http://www.boost.org/libs/python/doc/PyConDC_2003/bpl.html#inheritance
thanks, that works ! I still have a question, though: The reason I missed that was that in my real code I initialized the class_<> with a no_init argument, as that was declared to be the way to make the (C++) class non-instantiable. However, even though I only want subclasses to be instantiated, I of course do need to initialize the base class (i.e. run its constructor). How should I do that, then ? Thanks again, Stefan
Stefan Seefeld <seefeld@sympatico.ca> writes:
Ralf W. Grosse-Kunstleve wrote:
--- Stefan Seefeld <seefeld@sympatico.ca> wrote:
class MyWorld(World): def __init__(self): self.set('hi there') print self.greet() I think you have to call World.__init__() before you can use the instance. Here is more information: http://www.boost.org/libs/python/doc/PyConDC_2003/bpl.html#inheritance
thanks, that works !
I still have a question, though:
The reason I missed that was that in my real code I initialized the class_<> with a no_init argument, as that was declared to be the way to make the (C++) class non-instantiable.
Passing no_init to the class_<...> doesn't affect the C++ class' instantiability; it only affects the instantiability of the corresponding Python class.
However, even though I only want subclasses to be instantiated, I of course do need to initialize the base class (i.e. run its constructor).
How should I do that, then ?
I'm afraid there's no good way to impose that restriction yet. As a matter of fact, no_init should be more sophisticated and raise an exception only when the Python class of the "self" argument is an exact match for the wrapped class (not a subclass). Maybe you'd like to contribute the code for that? Note, however, that it still wouldn't be a very useful check, since there's nothing to prevent: >>> class Foo(WrappedClass): ... pass ... >>> x = Foo() Foo is essentially identical to WrappedClass, so what have you enforced by preventing: >>> x = WrappedClass ?? -- Dave Abrahams Boost Consulting www.boost-consulting.com
Ralf W. Grosse-Kunstleve wrote:
--- Stefan Seefeld <seefeld@sympatico.ca> wrote:
class MyWorld(World): def __init__(self): self.set('hi there') print self.greet()
I think you have to call World.__init__() before you can use the instance.
playing more with the code, I'm observing the following: * instantiating 'MyWorld' in python will not call the World's constructor * instantiating a 'MyWorld' object from within C++ will call the constructor * adding a call to 'World.__init__(self)' into the definition of MyWorld.__init__ will call World's constructor twice, or, more generally, n + 1 times, if n is the number of times I call World.__init__. That seems to me quite error-prone and I'm wondering why it is done the way it is. More than error-prone, I'm wondering how I could make it such that I can instantiate 'MyWorld' from python *and* C++, and still get the base constructor called once per instance in both cases. Thanks, Stefan PS: by the way, I found the article you cite very useful, and I think that it illustrates things that are not or less clearly documented in the tutorial / reference. May be some of the text could be copied over...
Stefan Seefeld <seefeld@sympatico.ca> writes:
Ralf W. Grosse-Kunstleve wrote:
--- Stefan Seefeld <seefeld@sympatico.ca> wrote:
class MyWorld(World): def __init__(self): self.set('hi there') print self.greet() I think you have to call World.__init__() before you can use the instance.
playing more with the code, I'm observing the following:
* instantiating 'MyWorld' in python will not call the World's constructor
Right. Not calling the base __init__ is always an option in Python.
* instantiating a 'MyWorld' object from within C++ will call the constructor
Huh? MyWorld is a Python object. How do you instantiate MyWorld from within C++? The only way I know how to do that is: void instantiate_myworld(myworld_class) { object myworld_object = myworld_class(); ... } And that will do exactly the same thing as: >>> MyWorld() in Python. No World::World() gets called.
* adding a call to 'World.__init__(self)' into the definition of MyWorld.__init__ will call World's constructor twice, or, more generally, n + 1 times, if n is the number of times I call World.__init__.
How? Test case please?
That seems to me quite error-prone and I'm wondering why it is done the way it is.
What you're describing doesn't match any understanding I have of the system, so I can't say yet.
More than error-prone, I'm wondering how I could make it such that I can instantiate 'MyWorld' from python *and* C++, and still get the base constructor called once per instance in both cases.
Need clarification.
Thanks, Stefan
PS: by the way, I found the article you cite very useful, and I think that it illustrates things that are not or less clearly documented in the tutorial / reference. May be some of the text could be copied over...
Specific suggestions will certainly be considered. -- Dave Abrahams Boost Consulting www.boost-consulting.com
Hi David, David Abrahams wrote:
PS: by the way, I found the article you cite very useful, and I think that it illustrates things that are not or less clearly documented in the tutorial / reference. May be some of the text could be copied over...
Specific suggestions will certainly be considered.
something that I'd like to understand is the relationship between the C++ classes / objects and the corresponding python classes / objects. I noted that I can call the parent's __init__ function multiple times, which invokes the C++ base class constructor. It seems that way I can instantiate more than one base class objects. Is that a specific boost.python problem/feature or is that part of the python object model ? (It's hard to debug python, i.e. I can't log the 'this pointer' in python, so I only know from boost.python that there are multiple C++ base instances).... Stefan
Stefan Seefeld <seefeld@sympatico.ca> writes:
Hi David,
David Abrahams wrote:
PS: by the way, I found the article you cite very useful, and I think that it illustrates things that are not or less clearly documented in the tutorial / reference. May be some of the text could be copied over... Specific suggestions will certainly be considered.
something that I'd like to understand is the relationship between the C++ classes / objects and the corresponding python classes / objects.
Is that covered in the paper? If so, *specifically* which sections of text would you like to see "copied over?"
I noted that I can call the parent's __init__ function multiple times, which invokes the C++ base class constructor. It seems that way I can instantiate more than one base class objects. Is that a specific boost.python problem/feature or is that part of the python object model ? (It's hard to debug python, i.e. I can't log the 'this pointer' in python, so I only know from boost.python that there are multiple C++ base instances)....
The fact that you can call __init__ as many times as you want is part of the Python model. The fact that, for a wrapped class, it creates a C++ object each time is part of the way Boost.Python works. It might be possible to issue an exception when that happens but it hardly seems worthwhile to me. -- Dave Abrahams Boost Consulting www.boost-consulting.com
David Abrahams wrote:
something that I'd like to understand is the relationship between the C++ classes / objects and the corresponding python classes / objects.
Is that covered in the paper? If so, *specifically* which sections of text would you like to see "copied over?"
Well, partly. The section entitled 'Inheritance' discusses the initialization of the base class through a call to it's __init__ method, and you specifically talk about the lookup of the base class: "Because C++ object construction is a one-step operation, C++ instance data cannot be constructed until the arguments are available in the __init__ function..." This and the following paragraphs make some very important points. As a C++ programmer I'm used to a specific object model / type system, so I believe it would be very useful to discuss how that maps to python, and how boost.python does the dispatching. You may consider this a detail especially for first-time users, but it really helps to understand these things to be efficient in the use of boost.python.
I noted that I can call the parent's __init__ function multiple times, which invokes the C++ base class constructor. It seems that way I can instantiate more than one base class objects. Is that a specific boost.python problem/feature or is that part of the python object model ? (It's hard to debug python, i.e. I can't log the 'this pointer' in python, so I only know from boost.python that there are multiple C++ base instances)....
The fact that you can call __init__ as many times as you want is part of the Python model. The fact that, for a wrapped class, it creates a C++ object each time is part of the way Boost.Python works. It might be possible to issue an exception when that happens but it hardly seems worthwhile to me.
Ok. But I do believe that it should be explained. Forgetting to call __init__ on the base class may be a common error, especially for C++ programmers who are used to things like default constructors. Thanks, Stefan
Stefan Seefeld <seefeld@sympatico.ca> writes:
David Abrahams wrote:
something that I'd like to understand is the relationship between the C++ classes / objects and the corresponding python classes / objects. Is that covered in the paper? If so, *specifically* which sections of text would you like to see "copied over?"
Well, partly. The section entitled 'Inheritance' discusses the initialization of the base class through a call to it's __init__ method, and you specifically talk about the lookup of the base class:
"Because C++ object construction is a one-step operation, C++ instance data cannot be constructed until the arguments are available in the __init__ function..."
This and the following paragraphs make some very important points.
Could you be specific about the ending point of the text you'd like to see incorporated as well as the beginning point?
As a C++ programmer I'm used to a specific object model / type system, so I believe it would be very useful to discuss how that maps to python, and how boost.python does the dispatching.
That's not really in the paper, is it?
You may consider this a detail especially for first-time users, but it really helps to understand these things to be efficient in the use of boost.python.
Sure, I agree.
I noted that I can call the parent's __init__ function multiple times, which invokes the C++ base class constructor. It seems that way I can instantiate more than one base class objects. Is that a specific boost.python problem/feature or is that part of the python object model ? (It's hard to debug python, i.e. I can't log the 'this pointer' in python, so I only know from boost.python that there are multiple C++ base instances)....
The fact that you can call __init__ as many times as you want is part of the Python model. The fact that, for a wrapped class, it creates a C++ object each time is part of the way Boost.Python works. It might be possible to issue an exception when that happens but it hardly seems worthwhile to me.
Ok. But I do believe that it should be explained. Forgetting to call __init__ on the base class may be a common error, especially for C++ programmers who are used to things like default constructors.
That's a good idea. Joel, what do you think about mentioning in the tutorial that wrapped C++ objects are created by calling their __init__ functions? -- Dave Abrahams Boost Consulting www.boost-consulting.com
David Abrahams wrote:
"Because C++ object construction is a one-step operation, C++ instance data cannot be constructed until the arguments are available in the __init__ function..."
This and the following paragraphs make some very important points.
Could you be specific about the ending point of the text you'd like to see incorporated as well as the beginning point?
As a C++ programmer I'm used to a specific object model / type system, so I believe it would be very useful to discuss how that maps to python, and how boost.python does the dispatching.
That's not really in the paper, is it?
no. When I ran into the problem with the non-initialized base class, it was Ralf who pointed me to this paper (see the beginning of this thread). Reading the cited paragraph, I started to understand the problem. That made me curious so I tried calling __init__ multiple times, so I discovered that multiple base objects were bound to my derived class. I'v been working with python for quite some time, but in this particular context with a C++ base class this cought my attention, as this is obviously something were the C++ and the python way of life differ dramatically. That's why I'd like to see some discussion of these issues.
Ok. But I do believe that it should be explained. Forgetting to call __init__ on the base class may be a common error, especially for C++ programmers who are used to things like default constructors.
That's a good idea. Joel, what do you think about mentioning in the tutorial that wrapped C++ objects are created by calling their __init__ functions?
May be if the tutorial stays as it is, but at appropriate places you point to some special chapters with a 'for details see...' icon, covering particular topics such as this in depth. Thanks a lot ! Stefan
Stefan Seefeld <seefeld@sympatico.ca> wrote:
Ok. But I do believe that it should be explained. Forgetting to call __init__ on the base class may be a common error, especially for C++ programmers who are used to things like default constructors.
That's a good idea. Joel, what do you think about mentioning in the tutorial that wrapped C++ objects are created by calling their __init__ functions?
May be if the tutorial stays as it is, but at appropriate places you point to some special chapters with a 'for details see...' icon, covering particular topics such as this in depth.
I think it's better that this issue be added in the tutorial. It's too easy to miss. A short paragraph will go a long way. -- Joel de Guzman joel at boost-consulting.com http://www.boost-consulting.com http://spirit.sf.net
participants (4)
-
David Abrahams -
Joel de Guzman -
Ralf W. Grosse-Kunstleve -
Stefan Seefeld