[C++-sig] how to override __setattr__ of an extension class

Holger Joukl Holger.Joukl at LBBW.de
Tue Feb 28 17:52:46 CET 2012


Hi,

> The docs regarding the mutable copying problem are very misleading.
> Following Python [1] and Boost [2] scripts do not produce the same
results:
>
>
> [1]
> b = a = []
> c = list(a)
> a.append("s1")
> print a.count("s1"), b.count("s1"), c.count("s1") #prints 1 1 0
>
> [2]
> list a, b, c;
> b = a;
> c = list(a);
> a.append("s1");
> list d = extract<list>(a);
> printf("%i %i %i %i\n", a.count("s1"), b.count("s1"), c.count("s1"),
> d.count("s1")); //prints 1 1 1 1
>
> In [2] expected was 1 1 0 1 according to Pythonic behaviour (it states
> in docs that such behaviour is mimicked). This is in conflict with
> example provided in the tutorial.

Ok, so it seems behaviour is different when dealing with
boost::python::object vs
PyObject* as constructor arguments.

Using extract<> to modify mutable objects is only necessary when dealing
with PyObject*:

// boost_dict.cpp
/* build cmdline:

/apps/local/gcc/4.6.1/bin/g++ -g -Wall -R /apps/local/gcc/4.6.1/lib/
-R /var/tmp/boost/gcc/boost_1_47_0/stage/gcc-4.6.1/py2.7/boost/1.47.0/lib/
-L /var/tmp/boost/gcc/boost_1_47_0/stage/gcc-4.6.1/py2.7/boost/1.47.0/lib/
-I /var/tmp/boost/gcc/boost_1_47_0/
-I /apps/local/gcc/4.6.1/include/python2.7/  -L /apps/local/gcc/4.6.1/lib
boost_dict.cpp -lboost_python -lrt -lpython2.7
*/

#include <boost/python.hpp>
#include <iostream>

namespace bp = boost::python;


int main(void) {
    // otherwise we core dump
    Py_Initialize();

    bp::object value(1);

    std::cout << "arg is bp::dict:" << std::endl;
    {
        bp::dict d1, d2, d3;
        d2 = d1;
        d3 = bp::dict(d1);
        d1["k1"] = value;
        bp::dict d4 = bp::extract<bp::dict>(d1);

        std::cout << d1.has_key("k1")
                  << " " << d2.has_key("k1")
                  << " " << d3.has_key("k1")
                  << " " << d4.has_key("k1") << std::endl;
    }

    std::cout << std::endl;

    std::cout << "arg is bp::handle<(PyObj*)" << std::endl;
    {
        bp::dict d1, d2, d3;

        // must use a handle to feed PyObj* to bp::objects
        bp::handle<> dict_pyobj_handle(d1.ptr());

        // Note: no operator= for handle<>
        d2 = bp::dict(dict_pyobj_handle);
        d3 = bp::dict(dict_pyobj_handle);
        d1["k1"] = value;
        bp::dict d4 = bp::extract<bp::dict>(d1.ptr());
        std::cout << d1.has_key("k1")
                  << " " << d2.has_key("k1")
                  << " " << d3.has_key("k1")
                  << " " << d4.has_key("k1") << std::endl;
    }


    Py_Finalize();
    return 0;
}


$ ./a.out
arg is bp::dict:
1 1 1 1

arg is bp::handle<(PyObj*)
1 0 0 1

So I think the tutorial example (if that's what you meant?)
"
C++:


dict d(x.attr("__dict__"));  // copies x.__dict__
d['whatever'] = 3;           // modifies the copy
"


is pretty subtle, as - if I understand correctly - this


- retrieves an attribute proxy from x


- that returns the bp::object-wrapped __dict__ attribute (which copies the
original PyObject* __dict__)


- constructs d from this new bp::object (which increases the refcount but
doesn't make another copy)


and the assignment operates on this new bp::object.

Regards
Holger

Landesbank Baden-Wuerttemberg
Anstalt des oeffentlichen Rechts
Hauptsitze: Stuttgart, Karlsruhe, Mannheim, Mainz
HRA 12704
Amtsgericht Stuttgart



More information about the Cplusplus-sig mailing list