2

I'm kind of new (1 day) to Python so maybe my question is stupid. I've already looked here but I can't find my answer.

I need to modify the content of an array at a random offset with a random size. I have a Python API to interface a DDL for an USB device which I can't modify. There is a function just like this one :

def read_device(in_array):
    # The DLL accesses the USB device, reads it into in_array of type array('B')
    # in_array can be an int (the function will create an array with the int 
    # size and return it), or can be an array or, can be a tuple (array, int)

In MY code, I create an array of, let's say, 64 bytes and I want to read 16 bytes starting from the 32rd byte. In C, I'd give &my_64_array[31] to the read_device function. In python, if a give :

read_device(my_64_array[31:31+16])

it seems that in_array is a reference to a copy of the given subset, therefore my_64_array is not modified.

What can I do ? Do I have to split my_64_array and recombine it after ??

Community
  • 1
  • 1
Michel L
  • 456
  • 7
  • 19

3 Answers3

1

Seeing as how you are not able to update and/or change the API code. The best method is to pass the function a small temporary array that you then assign to your existing 64 byte array after the function call.

So that would be something like the following, not knowing the exact specifics of your API call.

the_64_array[31:31+16] = read_device(16)
Wessie
  • 3,460
  • 2
  • 13
  • 17
0

When reading a list subset you're calling __getitem__ with a slice(x, y) argument of that list. In your case these statements are equal:

my_64_array[31:31+16]
my_64_array.__getitem__(slice(31, 31+16))

This means that the __getitem__ function can be overridden in a subclass to obtain different behaviour.

You can also set the same subset using a[1:3] = [1,2,3] in which case it'd call a.__setitem__(slice(1, 3), [1,2,3])

So I'd suggest either of these:

  • pass the list (my_64_array) and a slice object to read_device instead of passing the result of __getitem__, after which you could read the necessary data and set the corresponding offsets. No subclassing. This is probably the best solution in terms of readability and ease of development.
  • subclassing list, overriding __getitem__ and __setitem__ to return instances of that subclass with a parent reference, and then change all modifying or reading methods of a list to reference a parent list instead. This might be a little tricky if you're new to python, but basically, you'd exploit that python list properties are largely defined by the methods inside a list instance. This is probably better in terms of performance as you can create references.
  • If read_device returns the resulting list, and that list is of equal size, you can do this: a[x:y] = read_device(a[x:y])
sapht
  • 2,789
  • 18
  • 16
  • 1
    `__getslice__` is [deprecated since 2.0](http://docs.python.org/reference/datamodel.html?highlight=__getslice__#object.__getslice__) in favour of `__getitem__` with a `slice()` argument. – glglgl Sep 20 '12 at 09:31
  • @sapht : I **cannot** modify read_device, this is an API function. Thank you for the `__getitem__` and `__setitem__` explanation ! – Michel L Sep 20 '12 at 09:50
  • @MichelL But you probably can wrap it? – glglgl Sep 20 '12 at 12:39
  • oh yeah I would if it were up to me only ! – Michel L Sep 20 '12 at 13:15
0

It's precisely as you say, if you input a slice into a function it creates a reference copy of the slice.

Two possible methods to add it later (assuming read_device returns the relevant slice):

my_64_array = my_64_array[:32] + read_device(my_64_array[31:31+16]) + my_64_array[31+16:]
# equivalently, but around 33% faster for even small arrays (length 10), 3 times faster for (length 50)...
my_64_array[31:31+16] = read_device(my_64_array[31:31+16])

So I think you should be using the latter.

.

If it was a modifiable function (but it's not in this case!) you could be to change your functions arguments (one is the entire array):

def read_device(the_64_array, start=31, end=47):
   # some code
   the_64_array[start:end] = ... #modifies `in_array` in place

and call read_device(my_64_array) or read(my_64_array, 31, 31+16).

Andy Hayden
  • 359,921
  • 101
  • 625
  • 535
  • Thank you for your answer. As I said to @sapht, I cannot modify the API function. I've thought about slicing and recombining after but I'm not sure about the memory efficency. Example :a = [0, 1, 2, 3, 4] – Michel L Sep 20 '12 at 11:16
  • @MichelL Whoops! I understand... thinking hat is on. Does it *return* the array? – Andy Hayden Sep 20 '12 at 11:24
  • Yes, the prototype is **(int return, u08[] data_in, u16 num_read) = read_device(Device handle, u08[] data_in)** – Michel L Sep 20 '12 at 11:29
  • @MichelL I *think* you want to use: `my_64_array[31:31+16] = read_device(my_64_array[31:31+16])` as this is significantly faster. – Andy Hayden Sep 20 '12 at 11:34
  • WTF!? How come didn't I think of it ! I did somehting like : `temp = read_device(my_64_array[31:31+16])` `if len(temp) != 16 :` ` # return with some error to avoid shrinking/expanding my_64_array` `my_64_array[31:31+16] = temp` – Michel L Sep 20 '12 at 13:10