1

I have an enum that represents the directions you're allowed to move for a given cell in a maze:

class Direction(Flag):
    NORTH = 1
    EAST = 2
    SOUTH = 4
    WEST = 8
    NE = NORTH | EAST
    NW = NORTH | WEST
    ...etc
    NESW = NORTH | EAST | SOUTH | WEST

This makes it easy to check if you can go west, you can just check cell.directions & Direction.WEST. But what if I want to iterate over the possible directions? Something like for d in cell.directions: ..., but you can't do this. If I didn't alias every possible combination of directions (which makes things easier in my code), then I could do:

for d in Direction:
    if cell.directions & d:
        ....

But this won't work for me because it would iterate over all the combined directions too, rather than just the four basic cardinal directions. Is there a good solution here?

codebreaker
  • 763
  • 1
  • 7
  • 24
  • 4
    Why are `NE`, `NW`, etc. included in your enum in the first place? Not sure that makes sense. – ChrisGPT was on strike Dec 06 '22 at 18:21
  • What do you mean by "possible directions"? Does that include NESW? – Sören Dec 06 '22 at 18:42
  • @Chris Why not? Is that sort of a deprecated thing to do? It make it easier when setting up the maze to have aliases like that so I don't have to keep doing `Direction.NORTH | Direction.EAST` (part of the maze is hard-coded). I suppose I could just make constants outside of the enum though... – codebreaker Dec 06 '22 at 19:05
  • 1
    @codebreaker, "northeast" is a direction, but when you say `NE` I don't think you mean that. You mean "north is available, and east is available". It's not a single direction. A set of available directions is fundamentally different from a single direction. – ChrisGPT was on strike Dec 06 '22 at 19:08
  • @Chris that's fair. Maybe the name of my class/constants is misleading, but I still don't see why it's bad practice to alias these groups. – codebreaker Dec 06 '22 at 19:21
  • @codebreaker, just not how I'd approach it. Ethan's answer about Python 3.11 is very interesting, I didn't realize Python would be that clever. – ChrisGPT was on strike Dec 06 '22 at 19:21

2 Answers2

3

In Python 3.11 this works as you would expect: iteration only provides the canonical (i.e. powers of two) flags, all others are considered aliases.

Even in prior versions, though, this works:

from enum import Flag

class Direction(Flag):
    NORTH = 1
    EAST = 2
    SOUTH = 4
    WEST = 8

NORTH, EAST, SOUTH, WEST = Direction

directions = NORTH | EAST
d = NORTH
if d in directions:
    print('going North!')

Disclosure: I am the author of the Python stdlib Enum, the enum34 backport, and the Advanced Enumeration (aenum) library.

Ethan Furman
  • 63,992
  • 20
  • 159
  • 237
  • Wow, that's great. I guess this is what I get for putting off updating to 3.11. This does not work for me in 3.10 though! – codebreaker Dec 06 '22 at 19:18
  • @codebreaker: I fixed the typo in my code, and made sure the example will run. Are you still having difficulties? Of so, what's the error? – Ethan Furman Dec 06 '22 at 19:54
  • Sorry, the code example looks like it would work in 3.10. I thought you were saying iteration on a Flag would work in earlier versions. – codebreaker Dec 06 '22 at 20:59
  • @codebreaker: Iteration does work on Flags, and has since they were introduced in 3.6. Prior to 3.11 you would also get the aliases when iterating. – Ethan Furman Dec 06 '22 at 21:11
  • Right, I did just notice that I wasn't getting the aliases anymore if I did `list(Direction)`. But what I meant by "iteration on a Flag" was `list(Direction.NW)` now works and gives me `[Direction.NORTH, Direction.WEST]`. – codebreaker Dec 06 '22 at 21:23
1

You could just create a list basic_directions = [NORTH, EAST, SOUTH, WEST] and use the same for loop that you wrote before:

for d in basic_directions:
    if cell.directions & d:
        ....
Quintium
  • 358
  • 2
  • 11