Convert boolean list to list of base10 integers Context:

I'm coding up a simple genetic optimization algorithm from first principles, and have decided to use boolean/binary strings as genes so that I can flip bits on or off as a form of mutation. In order to evaluate the fitness of the solutions that it generates I need to be able to decode the binary strings produced into the integer values that I'm optimizing for. The genetic optimization technique that I'm using returns it's individuals as a list, so I am extracting the integers as follows:

Problem:

Take the boolean/binary list...

l = [1, 0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 1, 1, 0, 1, 1]


...split it into 8-bit sublists...

[[1, 0, 1, 0, 0, 1, 1, 1], [0, 0, 1, 0, 1, 0, 1, 0], [0, 0, 1, 1, 1, 0, 1, 1]]


...concatenate each sublist into a string...

[10100111, 101010, 111011]


...convert those strings from binary to decimal...

[167, 42, 59]


...then scale those integers between 50 and 150...

[115, 66, 73]


My solution:

[int(int(str(bin), 2)/2.55)+50 for bin in [int(''.join(\
    map(str,num))) for num in [l[i:i +8] for i in range(0, len(l), 8)]]]


This feels like a very ugly way to go tackle the problem - can anyone come up with a nicer way to go about it?
The individual steps that you describe in the Problem section are each simple and easy to follow. Yet you write your code in basically a single line. To understand it I had to tear it apart like you did in your "explanation for humans":

scaled = [
    int(int(str(bin), 2)/2.55)+50
        for bin in [
            int(''.join(map(str, num)))
                for num in [
                    l[i : i+8] for i in
                        range(0, len(l), 8)]]]


I have also reformatted the code to better see the underlying structure. Together with your explanation, this works for understanding it.

But since you chose to not include the explanation within your code, I would have no chance to understand it by reading it alone.

Therefore, I find it easier to understand when you name the intermediate steps:

chunks = [l[i : i+8] for i in range(0, len(l), 8)]
binaries = [int(''.join(map(str, chunk))) for chunk in chunks]
numbers = [int(str(binary), 2) for binary in binaries]
scaled = [round(50 + (150 - 50) * number / (255 - 0)) for number in numbers]


There is a pythonic shortcut for step 1, and steps 2 and 3 can be combined into one:

chunks = zip(*[iter(l)]*8)
numbers = [sum(bit &lt;&lt; shift for bit, shift in zip(chunk, reversed(range(8))) for chunk in chunks]
scaled = [round(50 + (150 - 50) * number / (255 - 0)) for number in numbers]


The aim of PEP8 is to ensure readable code. Having too many different ideas in a single line of code does not count as readable to me. Therefore I prefer the form with the intermediate steps.

Sure, the broken down version of the code is longer, but the reader of your code can take a deep breath after each step and inspect the intermediate result, just as you did in your explanation. Therefore the code should look this way.

By the way, I replaced the very last int with round since I think it is more appropriate. Decide for yourself. I also replaced the magic number 2.55 with the numbers from your explanation. This makes the numbers less magic.