Base Class Constructors And Assignment Operators Python

Now let's expose a C++ class to Python.

Consider a C++ class/struct that we want to expose to Python:

structWorld{voidset(std::stringmsg){this->msg=msg;}std::stringgreet(){returnmsg;}std::stringmsg;};

We can expose this to Python by writing a corresponding Boost.Python C++ Wrapper:

#include<boost/python.hpp>usingnamespaceboost::python;BOOST_PYTHON_MODULE(hello){class_<World>("World").def("greet",&World::greet).def("set",&World::set);}

Here, we wrote a C++ class wrapper that exposes the member functions and . Now, after building our module as a shared library, we may use our class in Python. Here's a sample Python session:

>>>importhello>>>planet=hello.World()>>>planet.set('howdy')>>>planet.greet()'howdy'

Our previous example didn't have any explicit constructors. Since is declared as a plain struct, it has an implicit default constructor. Boost.Python exposes the default constructor by default, which is why we were able to write

>>>planet=hello.World()

We may wish to wrap a class with a non-default constructor. Let us build on our previous example:

structWorld{World(std::stringmsg):msg(msg){}voidset(std::stringmsg){this->msg=msg;}std::stringgreet(){returnmsg;}std::stringmsg;};

This time has no default constructor; our previous wrapping code would fail to compile when the library tried to expose it. We have to tell about the constructor we want to expose instead.

#include<boost/python.hpp>usingnamespaceboost::python;BOOST_PYTHON_MODULE(hello){class_<World>("World",init<std::string>()).def("greet",&World::greet).def("set",&World::set);}

exposes the constructor taking in a (in Python, constructors are spelled "").

We can expose additional constructors by passing more s to the member function. Say for example we have another World constructor taking in two doubles:

class_<World>("World",init<std::string>()).def(init<double,double>()).def("greet",&World::greet).def("set",&World::set);

On the other hand, if we do not wish to expose any constructors at all, we may use instead:

class_<Abstract>("Abstract",no_init)

This actually adds an method which always raises a Python RuntimeError exception.

Data members may also be exposed to Python so that they can be accessed as attributes of the corresponding Python class. Each data member that we wish to be exposed may be regarded as read-only or read-write. Consider this class :

structVar{Var(std::stringname):name(name),value(){}std::stringconstname;floatvalue;};

Our C++ class and its data members can be exposed to Python:

class_<Var>("Var",init<std::string>()).def_readonly("name",&Var::name).def_readwrite("value",&Var::value);

Then, in Python, assuming we have placed our Var class inside the namespace hello as we did before:

>>>x=hello.Var('pi')>>>x.value=3.14>>>printx.name,'is around',x.valuepiisaround3.14

Note that is exposed as read-only while is exposed as read-write.

>>>x.name='e'Traceback(mostrecentcalllast):File"<stdin>",line1,in?AttributeError:can'tsetattribute

In C++, classes with public data members are usually frowned upon. Well designed classes that take advantage of encapsulation hide the class' data members. The only way to access the class' data is through access (getter/setter) functions. Access functions expose class properties. Here's an example:

structNum{Num();floatget()const;voidset(floatvalue);...};

However, in Python attribute access is fine; it doesn't neccessarily break encapsulation to let users handle attributes directly, because the attributes can just be a different syntax for a method call. Wrapping our class using Boost.Python:

class_<Num>("Num").add_property("rovalue",&Num::get).add_property("value",&Num::get,&Num::set);

And at last, in Python:

>>>x=Num()>>>x.value=3.14>>>x.value,x.rovalue(3.14,3.14)>>>x.rovalue=2.17

Take note that the class property is exposed as read-only since the setter member function is not passed in:

.add_property("rovalue",&Num::get)

In the previous examples, we dealt with classes that are not polymorphic. This is not often the case. Much of the time, we will be wrapping polymorphic classes and class hierarchies related by inheritance. We will often have to write Boost.Python wrappers for classes that are derived from abstract base classes.

Consider this trivial inheritance structure:

structBase{virtual~Base();};structDerived:Base{};

And a set of C++ functions operating on and object instances:

voidb(Base*);voidd(Derived*);Base*factory(){returnnewDerived;}

We've seen how we can wrap the base class :

class_<Base>("Base");

Now we can inform Boost.Python of the inheritance relationship between and its base class . Thus:

class_<Derived,bases<Base>>("Derived");

Doing so, we get some things for free:

  1. Derived automatically inherits all of Base's Python methods (wrapped C++ member functions)
  2. If Base is polymorphic, objects which have been passed to Python via a pointer or reference to can be passed where a pointer or reference to is expected.

Now, we will expose the C++ free functions and and :

def("b",b);def("d",d);def("factory",factory);

Note that free function is being used to generate new instances of class . In such cases, we use to instruct Python to adopt the pointer to and hold the instance in a new Python object until the the Python object is destroyed. We will see more of Boost.Python call policies later.

def("factory",factory,return_value_policy<manage_new_object>());

In this section, we will learn how to make functions behave polymorphically through virtual functions. Continuing our example, let us add a virtual function to our class:

structBase{virtual~Base(){}virtualintf()=0;};

One of the goals of Boost.Python is to be minimally intrusive on an existing C++ design. In principle, it should be possible to expose the interface for a 3rd party library without changing it. It is not ideal to add anything to our class . Yet, when you have a virtual function that's going to be overridden in Python and called polymorphically from C++, we'll need to add some scaffoldings to make things work properly. What we'll do is write a class wrapper that derives from that will unintrusively hook into the virtual functions so that a Python override may be called:

structBaseWrap:Base,wrapper<Base>{intf(){returnthis->get_override("f")();}};

Notice too that in addition to inheriting from , we also multiply- inherited (See Wrapper). The template makes the job of wrapping classes that are meant to overridden in Python, easier.

BaseWrap's overridden virtual member function in effect calls the corresponding method of the Python object through .

Finally, exposing :

class_<BaseWrap,boost::noncopyable>("Base").def("f",pure_virtual(&Base::f));

signals Boost.Python that the function is a pure virtual function.

Note

member function and methods

Python, like many object oriented languages uses the term methods. Methods correspond roughly to C++'s member functions

We've seen in the previous section how classes with pure virtual functions are wrapped using Boost.Python's class wrapper facilities. If we wish to wrap non-pure-virtual functions instead, the mechanism is a bit different.

Recall that in the previous section, we wrapped a class with a pure virtual function that we then implemented in C++, or Python classes derived from it. Our base class:

structBase{virtualintf()=0;};

had a pure virtual function . If, however, its member function was not declared as pure virtual:

structBase{virtual~Base(){}virtualintf(){return0;}};

We wrap it this way:

structBaseWrap:Base,wrapper<Base>{intf(){if(overridef=this->get_override("f"))returnf();returnBase::f();}intdefault_f(){returnthis->Base::f();}};

Notice how we implemented . Now, we have to check if there is an override for . If none, then we call .

Finally, exposing:

class_<BaseWrap,boost::noncopyable>("Base").def("f",&Base::f,&BaseWrap::default_f);

Take note that we expose both and . Boost.Python needs to keep track of 1) the dispatch function and 2) the forwarding function to its default implementation . There's a special function for this purpose.

In Python, the results would be as expected:

>>>base=Base()>>>classDerived(Base):...deff(self):...return42...>>>derived=Derived()

Calling :

>>>base.f()0

Calling :

>>>derived.f()42

Python Operators

C is well known for the abundance of operators. C++ extends this to the extremes by allowing operator overloading. Boost.Python takes advantage of this and makes it easy to wrap C++ operator-powered classes.

Consider a file position class and a set of operators that take on FilePos instances:

classFilePos{};FilePosoperator+(FilePos,int);FilePosoperator+(int,FilePos);intoperator-(FilePos,FilePos);FilePosoperator-(FilePos,int);FilePos&operator+=(FilePos&,int);FilePos&operator-=(FilePos&,int);booloperator<(FilePos,FilePos);

The class and the various operators can be mapped to Python rather easily and intuitively:

class_<FilePos>("FilePos").def(self+int()).def(int()+self).def(self-self).def(self-int()).def(self+=int()).def(self-=other<int>()).def(self<self);

The code snippet above is very clear and needs almost no explanation at all. It is virtually the same as the operators' signatures. Just take note that refers to FilePos object. Also, not every class that you might need to interact with in an operator expression is (cheaply) default-constructible. You can use in place of an actual instance when writing "self expressions".

Special Methods

Python has a few more Special Methods. Boost.Python supports all of the standard special method names supported by real Python class instances. A similar set of intuitive interfaces can also be used to wrap C++ functions that correspond to these Python special functions. Example:

classRational{public:operatordouble()const;};Rationalpow(Rational,Rational);Rationalabs(Rational);ostream&operator<<(ostream&,Rational);class_<Rational>("Rational").def(float_(self)).def(pow(self,other<Rational>)).def(abs(self)).def(str(self));

Need we say more?

Note

What is the business of ? Well, the method requires the to do its work (i.e. is used by the method defined by .


Creating Python class

Example 1 Start from very simple example

class student:
   def display(self, name ):
      print "your name is ", name
st1 = student()
st2 = student()
st1.display("mohit")
st2.display("Bhaskar")

Out-put

Python class example

in above example a class named student has been created. A function named display is defined. display function contain two argument self and name. Two objects st1 and st2 have been created
Syntax of object creation
st1 = student()
You call the class using class name and call the function with arguments
st1.display(argument)
Let us discuss our next example

class student:
   number =40
   def dis(self, name ):
      print name
      print number
st1 = student()
st1.dis("game")

Out-put

Python class example 2

The above example shows that number =40, while executing, it is giving error, number is a class variable whose value would be shared among all instances of a this class. This can be accessed as student.number from inside the class or outside the class. Method inside the class can access the class variables by using self.

Consider another example with pictorial diagram.

Showing local and class variable

Click to view code
Lots of sketching is there, Now you can understand the meaning of self to access class variable from method dis(). I used self because the interpreter binds the parameter self to that object (means class, object is the instance of class). The code binds the parameter self in the method dis() to the student class object referenced by the variable st1. The code for dis() can then use self to access that particular object's number. Otherwise, methods behave just like functions.

Below diagram is showing the graphical representation.

self bind the method with the object


Let us discuss a comparison between, accessing class variable using self and accessing class variable using class name.

Python class comparsion self and class access

Click to view code
Click to view code
You can see a clear difference from above figure, self.number is just accessing the variable. But student.number can change the class variable because class variable number and class.number inside the method are same.

Python __init__ Method

In Object oriented programming, you have heard about constructor. The __init__ must begin and end with two consecutive underscores. Here __init__ works as class's constructor, because it is run automatically when a user instantiates the class. Let us discuss with example

__init__() initalize one by one

Click to view code
In above example, empcount value is increasing. The object emp1 called the method inf() after initilization of __init__().


Python Class Inheritance

Inheritance h allows you to base a new class on an existing one. By doing so, the new child class automatically gets all of the methods and attributes of the existing parent class. In this way you can say the child class inherits the attributes of its parent class. Syntax class DerivedClassName(BaseClassName): <statement-1> . . . <statement-N> Let us discuss a example

Python Class Inheritance

Click to view code
The above example shows simple example of inheritance. The child class inherit the properties of parent class, that's why object of child class can access the method of parent class.

Python Overriding Methods

You can also override your parent class methods. By doing so, you can add special or different functionality in your subclass.
Let us discuss next example.

Python method overriding

Click to view code
In this example object c of child class call the method sum() but method sum() of parent class is overridden by child class

Python Operator Overloading

Python operators work for built-in classes. But same operator behaves differently with different types. For example add + use to add two integer and float. When it comes to string + operator concatenate two strings. This feature in Python, that allows same operator to have different meaning according to the context is called operator overloading.
For example consider of adding the (x,y) coordinates.

Python Operator overloading

Click to view code
You see add + is not adding coordinates, giving error.
The operator module exports a set of functions implemented in C corresponding to the intrinsic operators of Python. For example, operator.add(x, y) or operator.__add__(a, b) is equivalent to the expression x+y. The function names are those used for special class methods; variants without leading and trailing __ are also provided for convenience.
Ok, now back to operator overloading. Now we are going to do + operator overloading.

comparison or equal to operator

The above program basically executed in 4 steps.
Step 1 and step 2 : Object p1 and p2 initialize the constructor __init__ with arguments.
Step 3 : When you do print p1+ p2
in __add__(self,other) method, we have added coordinates x1 with coordinates x2 and coordinates y1 with coordinates y2.
Step 4 : Printing the coordinates.

Operator Overloading Special Functions in Python
OperatorExpressionInternally
Additionp1 + p2p1.__add__(p2)
Subtractionp1 - p2p1.__sub__(p2)
Multiplicationp1 * p2p1.__mul__(p2)
Powerp1 ** p2p1.__pow__(p2)
Divisionp1 / p2p1.__truediv__(p2)
Floor Divisionp1 // p2p1.__floordiv__(p2)
Remainder (modulo)p1 % p2p1.__mod__(p2)
Bitwise Left Shiftp1 << p2p1.__lshift__(p2)
Bitwise Right Shiftp1 >> p2p1.__rshift__(p2)
Bitwise ANDp1 & p2p1.__and__(p2)
Bitwise ORp1 | p2p1.__or__(p2)
Bitwise XORp1 ^ p2p1.__xor__(p2)
Bitwise NOT~p1p1.__invert__()


Overloading Comparison Operators in Python
We can overload comparison operators as well. Suppose, we wanted to implement the less than or equal to symbol .

Python Operator overloading

Click to view code
The < = symbol in our Vector class. Let us compare the magnitude of these points from the origin and return the result for this purpose.

Comparison Operator Overloading in Python.
OperatorExpressionInternally
Less thanp1 < p2p1.__lt__(p2)
Less than or equal top1 <= p2p1.__le__(p2)

Equal to

p1 == p2p1.__eq__(p2)
Not equal top1 != p2p1.__ne__(p2)
Greater thanp1 > p2p1.__gt__(p2)
Greater than or equal top1 >= p2p1.__ge__(p2)

Python Private variable

In Python there are private attribute. Private variable begin with an underscore. Private attributes will not be directly visible to outsiders.
Let us discuss the example.

Accessing private variable

Click to view code
Out side the class if you are accessing the private attibute, it will give error if you want to access the private attributes of class do as shown in next example

Python Accessing private variable

Click to view code
Use syntax object._class-name__private-attribute

Python Private methods

Python doesn't have real private methods, so one underline in the beginning of a method or attribute means you shouldn't access this method, because it's not part of the API. It's very common when using properties:
Let us discuss the example.

accessing private methods

Click to view code
To access private method you have to do same as we have done for access the private variable. a.__info() gives the error while accessing the private method.





Comments

Leave a Reply

Your email address will not be published. Required fields are marked *