5
\$\begingroup\$

I have the following code:

dic = {}
bin_rep = [1 if digit == '1' else 0 for digit in bin(n)[2:]]
if len(bin_rep) < size_bin:
    bin_rep += [0] * (size_bin - len(bin_rep))
# turn this into dictionnary - might all be done better?
for i in range(0, size_bin):
    dic[self.p.get_agents()[i]] = bin_rep[i]
return dic

Essentially, I'm taking an number n, turning it into a list (its binary representation, potentially padded with 0 up to size_bin), say:

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

From that padded list, I create a dictionary, where key==agent name and value==1/0, say:

{'a1':0, 'a2':1 ... 'an':0}

(The array as a whole corresponds to a team, the 0/1 represents the inclusion of an agent into the team. So each team corresponds to a binary array which can be represented as an int.)

The code works, using list and dictionary comprehension. However, would someone have a cleaner/smarter way to perform this? It's good if it's optimal, though not crucial, and would like to keep it readable.

I absolutely need to have the ultimate result into a dictionary because other parts of the code use that representation.

EDIT: using python 3.6

\$\endgroup\$

3 Answers 3

4
\$\begingroup\$

Suggestions

  • enumerate is built-in function useful here
  • dict comprehensions can be used to create dictionaries from arbitrary key and value expressions
  • imho '{:b}'.format(n) is easier to read than bin(n)[2:]
  • in additional you may use OrderedDict if you often iterate over agents 1, 2, 3, 4, ...

Final code (n = 10, size_bin = 6)

{'a{}'.format(k): int(v) for k, v in enumerate('{:b}'.format(10).ljust(6, '0'), start=1)}

and with full utilize format syntax

{'a{}'.format(k): int(v) for k, v in enumerate('{:<0{}b}'.format(10, 6), start=1)}
\$\endgroup\$
3
  • 3
    \$\begingroup\$ Rather than using ljust you can add it to the format. '{:0{}b}'.format(10, 6). You can also use this with f-strings, a, b = 10, 6 f'{a:0{b}b}' \$\endgroup\$
    – Peilonrayz
    Commented Nov 25, 2017 at 21:39
  • \$\begingroup\$ Yes, '{:0<{}b}'.format(10, 6) is shorter and better if someone familiar with format syntax \$\endgroup\$
    – vaeta
    Commented Nov 25, 2017 at 22:05
  • \$\begingroup\$ Woah that is a lot shorter and I think it reads well (after looking up the format function). I'll leave it a bit more time in case others wants to submit answer, otherwise accept it. \$\endgroup\$
    – Francky_V
    Commented Nov 26, 2017 at 16:01
3
\$\begingroup\$

As far as I can tell, you're wanting a test to determine if certain bits are set in a number, and you want to use named constants for the individual bits.

This is fine, except that I think you'll come back to not wanting to use named constants later. So you'll want some kind of programmatic access.

For that, I strongly recommend you check out the documentation of the various enum classes - I think either Flag (recommended) or IntFlag will do what you need.

If you have a particular set of name:value mappings in mind, go ahead and spell them out. You don't say what version of Python you are using, and it matters in this case. For 3.6 you might try something like:

import enum

class Agent(enum.Flag):
    HILTON   = 0b01000000
    SMART    = 0b00100000
    BOND     = 0b00010000
    LEITER   = 0b00001000
    POWERS   = 0b00000100
    SHAGWELL = 0b00000010

def active_agents(n):
    active = [agent.name for agent in Agent if n & agent]
    return active

For < 3.6, there's no Flag enum, so you'll have to use IntEnum something like:

def active_agents(n):
    active = [agent.name 
        for agent in Agent.__members__.values()   # <-- awkward!
        if n & agent]
    return active
\$\endgroup\$
3
  • \$\begingroup\$ That's an interesting approach, but I do not no ahead of time the number of agents (varies from problem to problem), and another part of the code specifically uses dictionnary for the actual solving. Since it uses a library I'm not familiar with (PuLP) I don't have time to get into reformating the Pulp-related code so it doesn't need a dict... hence the use of the dictionnary. +1 still \$\endgroup\$
    – Francky_V
    Commented Nov 27, 2017 at 15:17
  • \$\begingroup\$ Is there an upper limit on number of agents? Like 64 bits or 32 bits? You could still use 'a1' ... 'a32' for that. \$\endgroup\$
    – aghast
    Commented Nov 27, 2017 at 17:11
  • \$\begingroup\$ Well I could put an arbitrary large limit I suppose. But then the algo may or not still find a solution as-is, plus it would entail making a potentially large number of unecessary calculation for most problems (since even if those agents aren't really there, the algo would still have to use them in its calculation). Unless I adapt to algo to that I guess, but there's no need to go that far for a representation. \$\endgroup\$
    – Francky_V
    Commented Nov 28, 2017 at 4:05
0
\$\begingroup\$

This isn't really much shorter, but to me, converting from int to str and back to int in order to extract bits seems... well, weird. So, here:

def bitmask_to_dict(mask, mlength=16, prefix='a'):
    return {
        '%s%d' % (prefix, i): (mask >> (mlength - i)) & 1
        for i in range(mlength)
    }
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.