I'm primarily a C++ developer, but I fairly frequently end up writing Python scripts. I'm currently writing a dice simulator for a game, and I'm not certain of the best way, in Python, the solve my problem.
There are three player skills, and each player is strong at one, medium at one, and weak at one. I've written some classes that calculate the faces of the die from each of the player skils.
The skills live in an enum, rather than writing strings all over the place. However, there's also a chance that the skill will be "doubled", and become stronger.
Given that I am returning a list of Skill enum element, what is the best way to indicate a skill has been doubled?
Things I've considered so far:
Extending the Skill Enum to include doubled skills, but that makes the mapping of player skills to faces harder
Creating a DoubleSkill Enum, but then any changes to the Skill Enum would need to be replicated in DoubleSkill
My preferred option, which is creating a wrapper DoubleSkill class that can be constructed from a Skill. However, then it isn't of type enum, which makes my strongly-typed C++ instincts nervous
import random
from abc import ABC, abstractmethod
from enum import Enum
from collections import namedtuple
class Skill(Enum):
Might = 1,
Wisdom = 2,
Cunning = 3
class Die(ABC):
#abstractmethod
def _faces(self):
'''Returns the faces of this die'''
pass
def roll(self):
'''Returns a random face'''
return random.choice(self._faces())
PlayerSkills = namedtuple("PlayerSkills", ("strong", "medium", "weak"))
class PlayerDie(Die):
#abstractmethod
def _skills(self):
'''Returns the characer's skills'''
pass
def _faces(self):
'''Work out the faces of the die based off the skills'''
skills = self._skills()
#I want this to return a representation of the skill, not a string.
#But then I'd be mixing Enums and not-Enums
return [
self._double(skills.strong),
self._double(skills.medium),
skills.strong.name,
skills.strong.name,
skills.medium.name,
skills.weak.name
]
def _double(self, skill):
return f"Double {skill.name} (block)"
class CookDie(PlayerDie):
def _skills(self):
return PlayerSkills(Skill.Might, Skill.Cunning, Skill.Wisdom)
print(CookDie().roll())
One possible way is to make your enumration a Flag instead:
from enum import Flag, auto
class Skill(Flag):
Might = auto()
Wisdom = auto()
Cunning = auto()
Doubled = auto()
and in use:
>>> for i in (1, 2, 4, 9, 10, 12):
... i, Skill(i)
(1, <Skill.Might: 1>)
(2, <Skill.Wisdom: 2>)
(4, <Skill.Cunning: 4>)
(9, <Skill.Doubled|Might: 9>)
(10, <Skill.Doubled|Wisdom: 10>)
(12, <Skill.Doubled|Cunning: 12>)
If you would like a prettier str() (or one at all for a combined member), you can add your own __str__:
class Skill(Flag):
Might = auto()
Wisdom = auto()
Cunning = auto()
Doubled = auto()
#
def __str__(self):
cls = self.__class__
cls_name = cls.__name__
doubled = (self & cls.Doubled or "") and " x2"
base = (self & ~cls.Doubled) or self
name = base.name
if name is None:
name = '|'.join(s.name for s in Skill if s & base)
return "%s.%s%s" % (cls_name, name, doubled)
and in use:
for i in (1, 2, 4, 9, 10, 12):
i, str(Skill(i))
(1, 'Skill.Might')
(2, 'Skill.Wisdom')
(4, 'Skill.Cunning')
(9, 'Skill.Might x2')
(10, 'Skill.Wisdom x2')
(12, 'Skill.Cunning x2')
Related
How can I get all instances of a enum class that fulfill a criterion on a given, but unknown field?
I have the following three classes deriving from Enum, two simple ones (VariableType and VariableCategory) and one, in which the instances actually store content in fields (Variable).
class VariableCategory(Enum):
SHORT = auto()
MEDIUM = auto()
LONG = auto()
class VariableType(Enum):
ONE = auto()
TWO = auto()
class Variable(Enum):
A = ('Variable A',
VariableCategory.SHORT,
VariableType.ONE,
1)
B = ('Variable B',
VariableCategory.MEDIUM,
VariableType.TWO,
2)
C = ('Variable V',
VariableCategory.SHORT,
VariableType.TWO,
3)
def __init__(self, name: str, variable_category: VariableCategory, variable_type: VariableType,
number:int) -> None:
self.name = name
self.variable_category = variable_category
self.variable_type = variable_type
self.number = number
Now, I would like to create a class function, that can take an arbitrary parameter of types VariableType or VariableCategory and return all corresponding instances of Variable:
#classmethod
def by_x(cls, x: Union[VariableType, VariableCategory]):
# something like return [cls[member] for member in cls.__members__ if x in cls[member]]
For example, given a VariableType the according instances of Variable, as in:
>>> Variable.by_x(VariableType.ONE)
[Variable.A]
>>> Variable.by_x(VariableType.TWO)
[Variable.B, Variable.C]
>>> Variable.by_x(VariableCategory.SHORT)
[Variable.A, Variable.C]
>>> Variable.by_x(VariableCategory.MEDIUM)
[Variable.B]
>>> Variable.by_x(VariableCategory.LONG)
[]
I also know, that I can use more specific functions for one of the fields of the Variable enum:
#classmethod
def by_variable_type(cls, variable_type: VariableType) -> List['Variable']:
return [cls[member] for member in cls.__members__ if cls[member].variable_type == variable_type]
However, I do not known, how to make just one, generic function instead of many specific ones (that is, without checking the type of the parameter and calling the specific solution accordingly).
#classmethod
def by_x(cls, criterion):
return [
m
for m in cls
if m.variable_category == criterion
or m.variable_type == criterion
]
As an aside, you cannot assign to self.name -- it raises an AttributeError.
This solution does what you want.
The get_by_values class method can receive a list, or list of lists with any combination of values you want satisfied. It guarantees to only return members that fully satisfied one of the combinations you passed in. It returns a set of Enum members, so guaranteeing there aren't any repetitions. If you used a list in the return, you would get ordered results corresponding to every argument you passed in.
Variable members already have Variable.name you declare them with. In case you want something more expressive I'd go with __str__ or __init__, perhaps combining with VariableCategory.name and VariableType.name.
from enum import Enum, auto
class VariableCategory(Enum):
SHORT = auto()
MEDIUM = auto()
LONG = auto()
class VariableType(Enum):
ONE = auto()
TWO = auto()
class Variable(Enum):
A = VariableCategory.SHORT, VariableType.ONE
B = VariableCategory.MEDIUM, VariableType.TWO
C = VariableCategory.SHORT, VariableType.TWO
def __init__(self, variable_category: VariableCategory, variable_type: VariableType):
self.variable_category = variable_category
self.variable_type = variable_type
def in_value(self, argument):
if set(argument).issubset(set(self.value)):
return self
#classmethod
def get_by_values(cls, *args):
return {member for member in cls
if member in {member.in_value(arg) for arg in args}}
first = [VariableCategory.SHORT, VariableType.ONE]
second = [VariableCategory.SHORT]
print(*Variable.get_by_values(first, second))
# Variable.A Variable.C
print(*Variable.get_by_values([VariableCategory.MEDIUM]))
# Variable.B
print(*Variable.get_by_values([VariableType.TWO]))
# Variable.B Variable.C
EDIT: Replaced for's with set comprehensions. The expanded for's section is below:
def in_value(self, argument):
for element in argument:
if element not in self.value:
return False
return True
#classmethod
def get_by_values(cls, *args):
result = list()
for one_member in Variable:
for argument in args:
if one_member.in_value(argument):
if one_member not in result:
result.append(one_member)
return result
I am adapting some code provided by a company (for controlling an instrument). I am not a python expert myself so I would like to understand if there is any particular advantage of doing something one way or the other.
Their code is basically
def enum(**enums):
return type('Enum', (), enums)
Inst = enum(
CONTINUE = 0,
STOP = 1,
LOOP = 2,
END_LOOP = 3,
JSR = 4,
RTS = 5,
BRANCH = 6,
LONG_DELAY = 7,
WAIT = 8,
RTI = 9
)
and they use it to access the values of the above "entries" like:
Inst.RTS
% 5
From my understanding, Inst is a type object.
For the purposes of storing strings and their associated values, I could also define a dict:
Inst = {'CONTINUE':0, 'STOP':1, 'LOOP':2, 'END_LOOP':3, 'JSR':4, 'RTS':5}
and then basically getting the same result:
Inst['RTS']
% 5
Question
What is the use / what are the advantages of a type object over a normal dict? Or is the above example just a bad example?
With:
def enum(**enums):
return type('Enum', (), enums)
You are actually creating a class with the value of the argument enums as the __dict__ attribute, which is a dict itself, so it is not technically any "better" than a dict, but its usage is syntactically simpler than a dict, since:
Inst.RTS
is arguably cleaner to read and easier to type than
Inst['RTS']
Using the three-argument form of type is similar to defining a class:
class type(name, bases, dict)
With three arguments, return a new type object. This is essentially a
dynamic form of the class statement.
Your Inst expression is a needlessly convoluted but practically equivalent way to explicitly define this class:
class Enum:
CONTINUE = 0
STOP = 1
LOOP = 2
END_LOOP = 3
JSR = 4
RTS = 5
BRANCH = 6
LONG_DELAY = 7
WAIT = 8
RTI = 9
Inst = Enum
The advantage of a class is that attributes are well-defined by the class. For example, you can use it to type-hint, type-check or reference the exact enum set. In contrast, a dict only has some keys and some values -- the exact members are coincidental.
Note that Python has the enum module since Python 3.4. It allows to define proper enums that replicate the features known from enums in other languages:
from enum import Enum, auto
class Inst(int, Enum):
CONTINUE = auto()
STOP = auto()
LOOP = auto()
END_LOOP = auto()
JSR = auto()
RTS = auto()
BRANCH = auto()
LONG_DELAY = auto()
WAIT = auto()
RTI = auto()
I am new to python and I would like to pass an enum as an argument to a constructor, within a function.
EDIT: I am working on a program with a class that has to organize different types of data, but most of these data types can be treated the same way. This data won't be all be added at the same time or in a foreseeable order. I would therefore like to keep the same functions, and just change the way the constructor stores the data. Let's consider this simpler example:
Say I have an enum
from enum import Enum, auto
class HouseThing(Enum):
people = auto()
pets = auto()
furniture = auto()
And I have a class House that can contain some or all of those things
class House():
def __init__(self, address, people = None, pets = None,
furniture = None):
self.address = address,
if self.people is not None:
self.people = people
etc....
And now I want to have a function that makes new furbished houses, but I want to use a function that could be used for any house:
house_things = HouseThing.furniture
def make_house_with_some_house_things(neighborhood, house_things):
neighborhood.append(House(house_things.name = house_things.name))
Is there a way to do this without first testing what kind of HouseThing house_things is first? house_things.name passes a string, but I would like it to be able to use it as a keyword.
I'm not sure exactly what you are trying to achieve here, but for the sake of solving the puzzle:
First, change House to determine what it has been passed:
class House():
def __init__(self, address, *house_things):
self.address = address
for ht in house_things:
if ht is HouseThings.people:
self.people = ht
elif ht is HouseThings.pets:
self.pets = ht
elif ht is HouseThings.furniture:
self.furniture = ht
else:
raise ValueError('unknown house thing: %r' % (ht, ))
Then, change make_house_with_some_house_things to just pass the house things it was given:
def make_house_with_some_house_things(neighborhood, house_things):
neighborhood.append(House(house_things))
I am making a basic python RPG for my daughter and looking for the best way to store character stats that can be added a and subtracted from. Right now, I am using a dictionary so that she can view a list of her stats; however, I can't see a way to automatically add or subtract from objects in the list.
For example, I have
CatAbilities = {'speed': 5, 'claw': 3}
etc. And I want to have speed go down by 2 when, for example, her cat runs to avoid a dog. Is there a better way to do this while keeping a structure that let's her view her stats easily?
Why not use classes?
class Animal:
def __init__(self):
self.Abilities = {}
def PrintStats(self):
print self.Abilities
class Cat(Animal):
def __init__(self):
self.Abilities = {'speed': 5, 'claw': 3}
def ChasedByDog(self):
self.Abilities['speed'] -= 2
def main():
Kitty = Cat()
Kitty.PrintStats()
Kitty.ChasedByDog()
Kitty.PrintStats()
if(__name__ == '__main__'):
main()
Django-EnumFields lets you combine Enum fields in Django (the clue was in the title).
Can you combine nest these?
Here's an example that plays off the docs:
from django.db import models
from django_enumfield import enum
class BeerStyle(enum.Enum):
LAGER = 0
STOUT = 1
WEISSBIER = 2
class SoftDrinkStyle(enum.Enum):
COKE = 3
LEMONADE = 4
class Drink(models.Model):
style = enum.EnumField(????, default=BeerStyle.LAGER)
I don't know what would go in place of ????, or if there is a better way to get this nested/combination to play out with Django. I'm mainly asking as I want Enum behaviour, with the ability to probe different types, e.g. in a save method, check for User age if the Drink is or type Beer.
Is this possible? Having played with this for a bit I don't see how.
Having looked over how Python Enums work, this looks like the best behaviour to mock up 'subclasses'
from django.db import models
from django_enumfield import enum
class DrinkStyle(enum.Enum):
LAGER = (0, 'Beer')
STOUT = (1, 'Beer')
WEISSBIER = (2, 'Beer')
COKE = (3, 'SoftDrink')
LEMONADE = (4, 'SoftDrink')
def __init__(self, id, drink_type):
self.id = id
self.type = drink_type
#property
def type(self):
return self.drink_type
class Drink(models.Model):
style = enum.EnumField(DrinkStyle, default=DrinkStyle.LAGER)
Then use DrinkStyle.COKE.type to return the type.