1

I have a class that has instances of other classes as its properties:

from ctypes import *

class C():
    A = propA()
    B = propB()

    def __init__(self):
        class c_struct(Structure):
            _fields_ = [('_A', c_int), ('_B', c_int)]

        self._c_struct = c_struct()

I need to somehow "connect" the propA and propB classes to the internal structure _c_struct created in C to eventually get the behavior like so:

c = C()

c.A.val = 12
c.B.val = 13

c._c_struct._A == c.A.val == 12

Where C.A is able to manipulate C._c_struct._A. I've attempted to do something like this:

from ctypes import *

class propParent():
    def set_ref(self, ext_ref):
        self._val = ext_ref

    @property
    def val(self):
        return self._val

    @val.setter
    def val(self, val):
        self._val = val

class propA(propParent):
    # Further definitions here
    pass

class propB(propParent):
    # Further definitions here
    pass

class C():
    A = propA()
    B = propB()

    def __init__(self):
        class c_struct(Structure):
            _fields_ = [('_A', c_int), ('_B', c_int)]

        self._c_struct = c_struct()

        self.A.set_ref(self._c_struct._A)
        self.B.set_ref(self._c_struct._B)

But it appears that self._c_struct._A returns a Python integer object and not the reference to the _A internal c_int object of the structure. How do I connect a property to a sub property of another property in the same class?

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
James Mertz
  • 8,459
  • 11
  • 60
  • 87
  • Would it be feasible to create a class with a single field? That would naturally pass the class by reference. – jhpratt Nov 07 '17 at 05:06
  • I'm not sure if I follow? My problem is coming from the fact that `self._c_struct._A` doesn't appear to be returning a reference when making the assignment. – James Mertz Nov 07 '17 at 05:15

1 Answers1

1

This'd seem like a fit for a descriptor, which the field c_struct._A is also:

In [3]: c_struct._A
Out[3]: <Field type=c_int, ofs=0, size=4>

In [4]: c_struct._A.__get__
Out[4]: <method-wrapper '__get__' of _ctypes.CField object at 0x7f967303cf48>

which is why it returns an int when accessed through a c_struct instance instead of the field itself. Start by defining one suitable for your use case:

class prop:

    def __init__(self, name):
        self.name = name

    def __repr__(self):
        return 'prop({!r})'.format(self.name)

    def __get__(self, instance, owner):
        if not instance:
            return self

        return getattr(instance._c_struct, '_{}'.format(self.name))

    def __set__(self, instance, value):
        setattr(instance._c_struct, '_{}'.format(self.name), value)

or just

def prop(name):
    name = '_{}'.format(name)
    return property(
        lambda self: getattr(self._c_struct, name),
        lambda self, value: setattr(self._c_struct, name, value))

Then define your original class:

class C:

    A = prop('A')
    B = prop('B')

    def __init__(self):
        class c_struct(Structure):
            _fields_ = [('_A', c_int), ('_B', c_int)]

        self._c_struct = c_struct()

Test:

In [36]: c = C()

In [37]: c.A
Out[37]: 0

In [38]: c._c_struct._A
Out[38]: 0

In [39]: c.A = 12

In [40]: c._c_struct._A
Out[40]: 12

For extra credit and if you're using a recent enough version of Python you can use object.__set_name__() to remove the need to duplicate the name, without having to resort to a meta class:

class prop:

    def __init__(self, name=None):
        self.name = name

    def __set_name__(self, owner, name):
        if self.name is None:
            self.name = name

    ...

and then define C as:

class C:

    A = prop()
    B = prop()

    ...
Ilja Everilä
  • 50,538
  • 7
  • 126
  • 127