GG_Python
GG_Python

Reputation: 3541

Property setter for a single list element

All, I have a class with a several list objects, defined as follows:

class Device:

  def __init__(self):
    self._channels = [None]*6
    self._outputs = [None]*4

  @property
  def channels(self):
    return self._channels

  @channels.setter
  def channels(self,value):
    print("inside:",self.channels, value)
    self._channels = value

The strange thing here is that calling device.channels[1] = 'try' works, but doesn't seem to 'go through' the @setter.channels function. The output from the following reveals the oddity:

device = Device()
print("before:",device.channels)
device.channels[1] = "try"
print("after:",frmdeviced.channels)
device.channels = "try2"
print("check:",frm4d.channels)

and the output is:


 before: [None, None, None, None, None, None] 
after: [None, 'try', None, None, None, None] # accessing single element is achieved
# , but not through @channels.setter!
inside: [None, 'try', None, None, None, None] try # only here we're
check: try2 # at least the setter works..

Since I require logic to run when a single element of channels is set, this behavior is problematic. I'm wondering what's the underlying python mechanism that causes this behavior, and how is it over-ridden? Is there a more pythonic way to achieve the goal of setting/getting a specific list element?

Upvotes: 3

Views: 1240

Answers (2)

RootTwo
RootTwo

Reputation: 4418

To instrument the lists, create an instrumented list class. Sample Jupyter session:

In [23]: class MonitoredList(list):
             def __setitem__(self, index, value):
                 # run special logic here
                 print("setting index {} to {}".format(index, value))
                 super(MonitoredList, self).__setitem__(index, value)

In [24]: zz = MonitoredList([None]*6)

In [25]: zz
Out[25]: [None, None, None, None, None, None]

In [26]: zz[3] = 42
         setting index 3 to 42

In [27]: zz
Out[27]: [None, None, None, 42, None, None]

Upvotes: 3

AlokThakur
AlokThakur

Reputation: 3741

device.channels[1] = "try" is going to access "@property" getter method first, which return a list and then indexing operation would be performed on list not on device. Below example demonstrate it-

>>> class Device:

  def __init__(self):
    self._channels = [None]*6
    self._outputs = [None]*4

  @property
  def channels(self):
    print("inside @property")
    return self._channels

  @channels.setter
  def channels(self,value):
    print("inside:",self.channels, value)
    self._channels = value


>>> device = Device()
>>> device.channels[1] = "try"
inside @property

Upvotes: 5

Related Questions