2

I saw the page:

I'm familiar with this SO question for creating enums in Python. However, I can't find an example anywhere of an enum that has functions.

I'm mainly a Java programmer. I wrote this code in Java:

public enum Role {
    SOLDIER(4),
    DEMOMAN(2),
    SCOUT(4),
    MEDIC(2);

    private final int maxPlayers;

    private Role(int maxPlayers) {
        this.maxPlayers = maxPlayers;
    }

    public int getMaxPlayers() {  return maxPlayers; }
}

I tried to do the same in Python:

class Klass:
    SCOUT = 1
    SOLDIER = 2
    DEMOMAN = 3
    MEDIC = 4

    @staticmethod
    def maxPlayers(klass):
        return {
            Klass.SCOUT : 4,
            Klass.SOLDIER : 4,
            Klass.DEMOMAN : 2,
            Klass.MEDIC : 2,
        }[klass]

For some reason. I feel like I'm doing it wrong. What is the best practice for associating functions to enums in Python?

I don't actually care if the suggested answer doesn't use an enum; I'm just trying to understand the best practice for implementing the above Java code in Python.

I'm willing to use this enum in the following:

class Players(dict):
    def countKlass(self, klass):
        count = len(filter(lambda x: x == klass, self.values()))
        return count
Community
  • 1
  • 1
Weiner Nir
  • 1,435
  • 1
  • 15
  • 19
  • Your Java code is not a pure enum; it's a subclass of enum. If you just change Klass such that it subclasses from Enum (a PEP 435 enum as mentioned in your link) it should work exactly as you expect. They are integers, so doing a lookup into a dictionary seems straightforward enough. – Corley Brigman Oct 23 '13 at 13:53
  • This may help: http://stackoverflow.com/questions/36932/how-can-i-represent-an-enum-in-python – Hoopdady Oct 23 '13 at 13:54
  • My python code works. But i guess that this is not the best practice. – Weiner Nir Oct 23 '13 at 13:57
  • Would it be a problem not to use enums and just make a dictionary. You'd have to pass `maxPlayers` a string of `'SOLDIER'` for example. I don't know enough about your code to know if that is an easy adjustment, but most people don't use enums in python. – Hoopdady Oct 23 '13 at 13:58
  • I less care about easy or not. Im just trying to understand the best practice to do so in python in general. – Weiner Nir Oct 23 '13 at 14:00
  • As noted in the comments for the other question, generally, the answer about enums in python is "you don't" – Ian McLaird Oct 23 '13 at 14:02
  • I could also create a base class `Klass` and then inherit it to `Soldier` `Scout` `Medic` `Demoman`, Then do something like `virtual function` to overwrite the method maxPlayers. Is it a good practice? – Weiner Nir Oct 23 '13 at 14:04

3 Answers3

3

Let's remember what enums in Java really are -- static final instances of a class. All they really are is just named constants.

Generally, python favors a looser (or if you prefer, more flexible) coding style than Java. Part of that is that there's no such thing as a constant, and part of it is trusting users of your code not to do crazy things. In that vein, you can get a similar effect to your java code by doing this:

class Role(object):
    class RoleType(object):
        def __init__(self, maxPlayers):
            self.maxPlayers = maxPlayers

    SOLDIER = RoleType(4)
    DEMOMAN = RoleType(2)
    SCOUT = RoleType(4)
    MEDIC = RoleType(2)

Then you can access them like this

s = Role.SOLDIER
print s.maxPlayers  # Prints 4

What this doesn't do is prevent users of your library from creating new roles. Doing so is slightly awkward, though, which should be a hint to the user that "they're doing it wrong".

newRole = Role.RoleType(22)  # as an example

If you go this route, you just more or less have to live with that. If you favor using convention though, this generally won't be a problem. They can always just access the values you've defined.

Ian McLaird
  • 5,507
  • 2
  • 22
  • 31
2

If you are looking for best practices, typically you'd just make a dictionary.

klass = {'SCOUT':4,
         'SOLDIER':4,
         'DEMOMAN':2,
         'MEDIC':2,}

print klass['MEDIC']
2

If you want this to be a class method you could say:

class Klass:
    def maxPlayers(self, klass):
        return {'SCOUT':4,
                'SOLDIER':4,
                'DEMOMAN':2,
                'MEDIC':2,}[klass]

This is how you'd use it:

print Klass().maxPlayers('MEDIC')
2
Hoopdady
  • 2,296
  • 3
  • 25
  • 40
  • I like this answer. But it still make me do something like `Klass().maxPlayers('MEDIC')` I think it is not a good practice. because i have to write `'MEDIC'` – Weiner Nir Oct 23 '13 at 14:10
  • Are you against that? What would you like to it to do? – Hoopdady Oct 23 '13 at 14:11
  • I think the issue here is that this makes it less clear that `maxPlayers` is really a de facto property of `MEDIC`, which is the way the OP wants to think of it. – Ian McLaird Oct 23 '13 at 14:24
1

Using the AutoEnum recipe, your code could look like this:

class Role(AutoEnum):

    SCOUT = 4
    SOLDIER = 4
    DEMOMAN = 2
    MEDIC = 2

    def __init__(self, max_players):
        self.max_players = max_players

And now you have the benefits of the new Python Enum (backported if you don't have 3.4):

--> Role.SCOUT
<Role.SCOUT: 3>
--> Role.SCOUT.max_players
4
Community
  • 1
  • 1
Ethan Furman
  • 63,992
  • 20
  • 159
  • 237