Re: Creating lambdas inside generator expression
On 29/06/2022 23:17, Chris Angelico wrote: On Thu, 30 Jun 2022 at 02:49, Johannes Bauer wrote: But now consider what happens when we create the lambdas inside a list comprehension (in my original I used a generator expresison, but the result is the same). Can you guess what happens when we create conds like this? conds = [ lambda msg: msg.hascode(z) for z in ("foo", "bar") ] I certainly could not. Here's what it outputs: Check for bar False Check for bar False I.e., the iteration variable "z" somehow gets bound inside the lambda not by its value, but by its reference. All checks therefore refence only the last variable. Yep, that is the nature of closures. (Side point: This isn't actually a generator expression, it's a list comprehension; current versions of Python treat them broadly the same way, but there was previously a difference in the way scoping worked.) What you're seeing is a consequence of the way that closures work, and it is a very good thing most of the time :) The usual way to "snapshot" a variable is what you showed in your followup: a default argument value. def f(..., z=z): ... z has been snapshot (As others have pointed out, this isn't unique to lambdas; any function will behave that way.) Antoon offered another variant, but written as a pair of lambda functions, it's a little hard to see what's going on. Here's the same technique written as a factory function: def does_it_have(z): return lambda msg: msg.hascode(z) conds = [does_it_have(z) for z in ("foo", "bar")] Written like this, it's clear that the variable z in the comprehension is completely different from the one inside does_it_have(), and they could have different names if you wanted to. This is a fairly clean way to snapshot too, and has the advantage that it doesn't pretend that the function takes an extra parameter. While I'd go with Chris' suggestion there are two other options: functools.partial() and operator.methodcaller(). Example: >>> class Msg: def __init__(self, msg): self.msg = msg def hascode(self, code): return code in self.msg >>> conds = [partial(lambda z, msg: msg.hascode(z), z) for z in ("foo", "bar")] >>> [cond(Msg("barbaz")) for cond in conds] [False, True] >>> conds = [methodcaller("hascode", z) for z in ("foo", "bar")] >>> [cond(Msg("barbaz")) for cond in conds] [False, True] -- https://mail.python.org/mailman/listinfo/python-list
Byte arrays and DLLs
I have an application in which I wanted a fixed-length "array of bytes" (that's intended as an informal term) where I could read and write individual bytes and slices, and also pass the array to a DLL (one I wrote in C) which expects an "unsigned char *" parameter. I am using ctypes to talk to the DLL but am open to alternatives. Speed is important. My OS is Windows 10. I started off using a bytearray object (bytes does not support item assignment), but I couldn't find any way of passing it to the DLL directly. Instead I had to convert it to a different type before passing it, e.g. bytes(MyArray) or (ctypes.c_char * LEN).from_buffer(MyArray)) # LEN is the length of MyArray, knownin advance but this was slow, I think because the array data is being copied to a separate object. Eventually after consulting Googol I came up with using a memoryview: MyArray = memoryview(bytearray( )) # can read and write to this and passing it to the DLL as MyArray.tobytes() and was gratified to see a modest speed improvement. (I don't know for sure if it is still copying the array data, though I would guess not.) Is this a sensible approach, or am I still missing something? AKAIK it is not possible to give ctypes a bytearray object and persuade it to give you a pointer to the actual array data, suitable for passing to a DLL. Is this (a) false (b) for historical reasons (c) for some other good reason? TIA Rob Cliffe -- https://mail.python.org/mailman/listinfo/python-list
Re: Byte arrays and DLLs
On 6/30/22, Rob Cliffe via Python-list wrote: > > AKAIK it is not possible to give ctypes a bytearray object and persuade > it to give you a pointer to the actual array data, suitable for passing > to a DLL. You're overlooking the from_buffer() method. For example: >>> ba = bytearray(10) >>> ca = (ctypes.c_char * len(ba)).from_buffer(ba) >>> ca.value = b'spam&eggs' >>> ba bytearray(b'spam&eggs\x00') Note that the bytearray can't be resized while a view of the data is exported. For example: >>> ba.append(97) Traceback (most recent call last): File "", line 1, in BufferError: Existing exports of data: object cannot be re-sized >>> del ba[-1] Traceback (most recent call last): File "", line 1, in BufferError: Existing exports of data: object cannot be re-sized -- https://mail.python.org/mailman/listinfo/python-list