Referring Enum members to each other - python

I defined the following Enum in Python:
class Unit(Enum):
GRAM = ("g")
KILOGRAM = ("kg", GRAM, 1000.0)
def __init__(self, symbol, base_unit = None, multiplier = 1.0):
self.symbol = symbol
self.multiplier = multiplier
self.base_unit = self if base_unit is None else base_unit
I would expect that
print(Unit.GRAM.base_unit)
print(Unit.KILOGRAM.base_unit)
will return
Unit.GRAM
Unit.GRAM
However, what I get is quite confusing
Unit.GRAM
g
Why is it so?

The way Python defines a class involves creating a new scope, processing a bunch of statements (variable assignments, function definitions, etc.), and then actually creating a class object based on the local variables which exist after all those statements have run. Nothing gets converted into Enum instances until that last step.
You could understand it somewhat like this:
def make_class_Unit():
GRAM = ("g")
KILOGRAM = ("kg", GRAM, 1000.0)
def __init__(self, symbol, base_unit = None, multiplier = 1.0):
self.symbol = symbol
self.multiplier = multiplier
self.base_unit = self if base_unit is None else base_unit
return make_class(name='Unit', base=Enum, contents=locals())
Unit = make_class_Unit()
Looking at it this way, hopefully you can tell that at the time when KILOGRAM is defined, GRAM is really just a string. It doesn't become a Unit instance until the last stage, where I call the (imaginary) make_class() function.1
1Even though the make_class function I used above doesn't actually exist under that name, it's not too different from what Python really does, which is calling the constructor of type or a metaclass (which in this case is the metaclass for Enums).

DavidZ explained the problem well.
The last bit that you need to solve this problem is this: when the __init__ of each member is being run, the Enum has been created -- so you can call it:
self.base_unit = self if base_unit is None else self.__class__(base_unit)

Related

Python Class Objects/Attributes

I am learning python with an online course and have been doing fine, I have stumbled across and issue. Normally when having an issue i just google to find the answer or look up guides but the problem here is I'm not even sure what I'm looking for!
I currently have the below code. I have a task to make each workout intensity have a specific value, such as Low = 3, Medium = 6 and High = 12. Then I can find the calories burned via duration * the values dependant on the intensity and duration passed into the class.
What I don't know is how do I assign a value to a class method? I tried lists and Dictionarys and both are throwing errors. I tried writing and If statement to try if self.intensity(getattr) = "Low": Then x = 3, etc.
I really am not sure where to even start to find an answer hence asking you guys and girls.
The code currently is (I am aware pieces are missing also I'm currently only focusing on assigning values to the Intensity
class ExerciseSession:
def __init__(self, exercise, intensity, duration):
self.exercise = exercise
self.intensity = intensity
self.duration = duration
def get_exercise(self):
return self.exercise
def get_intensity(self):
return self.intensity
def get_duration(self):
return self.duration + " minutes"
def set_exercise(self, excersice):
self.set_exercise = exercise
def set_intensity(self, intensity):
self.set_intensity = intensity
def set_duration(self, duration):
self.set_duration = duration
new_exercise = ExerciseSession("Running", "Low", 60)
print(new_exercise.get_exercise())
print(new_exercise.get_exercise())
print(new_exercise.get_intensity())
print(new_exercise.get_duration())
new_exercise.set_exercise("Swimming")
new_exercise.set_intensity("High")
new_exercise.set_duration(30)
print(new_exercise.get_exercise())
print(new_exercise.get_intensity())
print(new_exercise.get_duration())
print(new_exercise.get_intensity())
print(new_exercise.get_duration())
new_exercise.set_exercise("Swimming")
new_exercise.set_intensity("High")
new_exercise.set_duration(30)
print(new_exercise.get_exercise())
print(new_exercise.get_intensity())
print(new_exercise.get_duration())
Am i just doing lists/dictionaries wrong within a class or am I missing something incredibly easy here. I understand classes and methods but it seems some things work slightly different when inside a class etc.
Firstly - are your setters implemented as they are in your question? If so, you appear to be trying to override your methods with a value. There is also a small typo in set_exercise(). Compare the below with your question:
def set_exercise(self, exercise):
self.exercise = exercise
def set_intensity(self, intensity):
self.intensity = intensity
def set_duration(self, duration):
self.duration = duration
You can then write your test statements outside of the class to figure out what value to use when calculating the number of calories consumed based on the intensity.
Alternatively, Python provides a neat way of encapsulating data without resorting to getter/setter methods through the #property decorator. The property can then be set and retrieved in a simple fashion. This is particularly useful in more complex situations where an attribute is derived from other attributes, meaning you will always access the most up-to-date attributes. This is covered in more detail here: "public" or "private" attribute in Python ? What is the best way?
class ExerciseSession:
def __init___(self, exercise, intensity, duration):
self._exercise = exercise
self._intensity = intensity
self._duration = duration
#property
def exercise(self):
return self._exercise
#property
def intensity(self):
if self._intensity == "Low":
out = 3
elif self._intensity == "Medium":
out = 6
elif self._intensity == "High":
out = 12
else:
out = None
return out
#property
def duration(self):
return self._duration
Note: the "_" before each instance attribute is used to indicate the internal attribute is conventionally private.
These properties can then be accessed as follows (note we do not have to call any methods, e.g. new_exercise.exercise()):
new_exercise = ExerciseSession("Running", "Low", 60)
print(new_exercise.exercise)
print(new_exercise.intensity)
print("{} minutes.".format(new_exercise.duration))
If you need to be able to update the type of exercise/duration/intensity on the object, rather than just creating a new one, you can add setter methods using the #.setter decorator, e.g.:
#exercise.setter
def exercise(self, value):
self._exercise = value
and these properties can be updated as:
new_exercise.exercise = "Swimming"
doing the same :-)
def calories_burned(self):
if self.intensity == "Low":
return 4 * self.duration
elif self.intensity == "Medium":
return 8 * self.duration
else:
return 12 * self.duration

When to NOT use the self convention in Python?

I've just recently wrapped my head around the self convention in Python and have begun making more complex code. However, an experienced programmer and friend of mine told me that to use self for every variable in a class method is wasteful.
I understand that self will cause the variable to become attributed to that class. So would it be true that, unless the need arises, it is good practice to avoid using self?
Below is some code that fetches League of Legends information from an API and stores each variable in self.var_name to illustrate how I'm (perhaps unnecessarily) using self.
async def getChampInfo(self, *args):
""" Return play, ban, and win rate for a champ """
self.uri = "http://api.champion.gg/v2/champions/{}?api_key={}"
self.champ = " ".join(args)
self.champID = lu.getChampID(self.champ)
self.res = requests.get(self.uri.format(
self.champID, League.champion_gg_api_key)).json()
self.role = self.res[0]["role"]
self.role_rate = self.res[0]["percentRolePlayed"]
self.play_rate = self.res[0]["playRate"]
self.win_rate = self.res[0]["winRate"]
self.ban_rate = self.res[0]["banRate"]
There are cases where using self is not needed.
Off the top of my head:
when the variable is only used in 1 function, or is created inside a function/method and only used in that function/method
when the variable doesn't need to be shared between methods
when the variable doesn't need to be exposed to other classes/scopes/contexts
Another partial answer is that when creating metaclass/factories/composition something like this might make more sense to move away from the convention of using self like:
class Factory(object):
def __init__(cls, *args, **kwargs):
thing = cls(args, kwargs)
I might be missing some stuff here, but those are what i can think of at the moment.
related:
https://stackoverflow.com/a/7722353/2026508
What is the purpose of self?
self will cause a variable to become attributed to an instance of the class, not the class itself. I don't know if you meant that or not, but it's certainly worth thinking about.
Variables in the class-wide scope can be divided into two categories: class and instance variables. Class variables are defined at the beginning of the class definition, outside of any method. If a variable is constant for all instances, or it is only used in class/static methods, it should be a class variable. Often, such variables are true constants, though there are numerous cases where they aren't. Instance variables are generally defined in __init__, but there are numerous cases where they should be defined elsewhere. That being said, if you don't have a good reason not to, define instance variables in __init__, as this keeps your code (and class) organized. It is perfectly acceptable to give them placeholder values (such as None), if you know the variable is essential to the state of the instance but its value is not determined until a certain method is called.
Here's a good example:
class BaseGame:
"""Base class for all game classes."""
_ORIGINAL_BOARD = {(0,0): 1, (2,0): 1, (4,0): 1, (6,0): 1, (8,0): 1,
(1,2): 1, (3,2): 1, (5,2): 1, (7,2): 1, (2,4): 1,
(4,4): 1, (6,4): 1, (3,6): 1, (5,6): 1, (4,8): 0}
_POSSIBLE_MOVES = {(0,0): ((4,0),(2,4)),
(2,0): ((4,0),(2,4)),
(4,0): ((-4,0),(4,0),(2,4),(-2,4)),
(6,0): ((-4,0),(-2,4)),
(8,0): ((-4,0),(-2,4)),
(1,2): ((4,0),(2,4)),
(3,2): ((4,0),(2,4)),
(5,2): ((-4,0),(-2,4)),
(7,2): ((-4,0),(-2,4)),
(2,4): ((4,0),(2,4),(-2,-4),(2,-4)),
(4,4): ((-2,-4,),(2,-4)),
(6,4): ((-4,0),(-2,4),(-2,-4),(2,-4)),
(3,6): ((-2,-4),(2,-4)),
(5,6): ((-2,-4),(2,-4)),
(4,8): ((-2,-4),(2,-4))}
started = False
def __call__(self):
"""Call self as function."""
self.started = True
self.board = __class__._ORIGINAL_BOARD.copy()
self.peg_count = 14
self.moves = []
#staticmethod
def _endpoint(peg, move):
"""Finds the endpoint of a move vector."""
endpoint = tuple(map(add, peg, move))
return endpoint
#staticmethod
def _midpoint(peg, move):
"""Finds the midpoint of a move vector."""
move = tuple(i//2 for i in move)
midpoint = tuple(map(add, peg, move))
return midpoint
def _is_legal(self, peg, move):
"""Determines if a move is legal or not."""
endpoint = self._endpoint(peg, move)
midpoint = self._midpoint(peg, move)
try:
if not self.board[midpoint] or self.board[endpoint]:
return False
else:
return True
except KeyError:
return False
def find_legal_moves(self):
"""Finds all moves that are currently legal.
Returns a dictionary whose keys are the locations of holes with
pegs in them and whose values are movement vectors that the pegs
can legally move along.
"""
pegs = [peg for peg in self.board if self.board[peg]]
legal_moves = {}
for peg in pegs:
peg_moves = []
for move in __class__._POSSIBLE_MOVES[peg]:
if self._is_legal(peg, move):
peg_moves.append(move)
if len(peg_moves):
legal_moves[peg] = peg_moves
return legal_moves
def move(self, peg, move):
"""Makes a move."""
self.board[peg] = 0
self.board[self._midpoint(peg, move)] = 0
self.board[self._endpoint(peg, move)] = 1
self.peg_count -= 1
self.moves.append((peg, move))
def undo(self):
"""Undoes a move."""
peg, move = self.moves.pop()
self.board[peg] = 1
self.board[self._midpoint(peg, move)] = 1
self.board[self._endpoint(peg, move)] = 0
self.peg_count += 1
def restart(self):
"""Restarts the game."""
self.board = __class__._ORIGINAL_BOARD.copy()
self.peg_count = 14
self.moves.clear()
_ORIGINAL_BOARD and _POSSIBLE_MOVES are true constants. While started is not a constant, as its value depends on whether the __call__ method was invoked or not, its default value, False, IS constant for all instances, so I declared it as a class variable. Notice that in __call__ (don't worry about why I used __call__ instead of __init__), I redefined it as an instance variable, as __call__ starts the game, and therefore when it is invoked, the instance's state has changed from the class default, "not started", to "started".
Also notice that the other methods besides __call__ regularly change the value of the instance variables, but that they are not initially defined in said methods, as there is no compelling reason for them to be.

Where did I mess up in this program for tracking faction alliances?

I have a program that models kingdoms and other groups (called 'factions' in my code).
class Faction:
def __init__(self, name, allies=[]):
self.name = name
self.allies = allies
def is_ally_of(self, other_faction):
if self in other_faction.allies:
return True
else:
return False
def become_ally(self, other_faction, both_ally=True):
""" If both_ally is false, this does *not* also
add self to other_faction's ally list """
if self.is_ally_of(other_faction):
print("They're already allies!")
else:
self.allies.append(other_faction)
if both_ally == True:
other_faction.become_ally(self, False)
RezlaGovt = Faction("Kingdom of Rezla")
AzosGovt = Faction("Azos Ascendancy")
I want to be able to call a factions become_ally() method to add factions to the ally lists, like this:
RezlaGovt.become_ally(AzosGovt) # Now AzosGovt should be in RezlaGovt.allies,
# and RezlaGovt in AzosGovt.allies
What actually happens is this:
RezlaGovt.become_ally(AzosGovt)
# prints "They're already allies!"
# now AzosGovt is in the allies list of both AzosGovt and RezlaGovt,
# but RezlaGovt isn't in any allies list at all.
Whenever I try to call become_ally(), the code should check to make sure they aren't already allies. This is the part that isn't working. Every time I call become_ally(), it prints "They're already allies!", regardless of if they actually are.
I also tried to use if self in other_faction.allies:, but that had the same problem.
I strongly suspect that the problem is with my use of self, but I don't know what terms to Google for more information.
You can't use mutable arguments as the default argument to a function.
def __init__(self, name, allies=[]):
When the default is used, it's the same list each time, so they have the same allies; mutating one changes the other because they're actually the same thing.
Change to:
def __init__(self, name, allies=None):
if allies is None:
allies = []
Alternatively, copy the allies argument unconditionally (so you're not worried about a reference to it surviving outside the class and getting mutated under the class):
def __init__(self, name, allies=[]):
self.allies = list(allies) # Which also guarantees a tuple argument becomes list
# and non-iterable args are rejected
Change this function.
def is_ally_of(self, other_faction):
if other_faction in self.allies:
return True
else:
return False
Check your own data not that of the passed in object.
Also
def __init__(self, name, allies=[]):
Is a bug waiting to happen. Your allies list will be a static list shared between all instances. Instead use
def __init__(self, name, allies=None):
self.name = name
self.allies = allies or []

Allow overriding of object method during __init__ by accepting replacement function as argument

Say I have a class (Rectangle) which implements a method (describe) as follows:
class Rectangle(object):
def __init__(self, height, width):
self.height = height
self.width = width
def describe(self):
return 'Rectangle with height {:0.2f} and width {:0.2f}'.format(float(self.height),
float(self.width))
This works as expected:
r = Rectangle(5, 3)
r.describe()
>>> 'Rectangle with height 5.00 and width 3.00'
I would like to be able to specify, at instantiation, an alternative function which would take the place of describe. I believe the below works:
import functools as ft
class RectangleEnhanced(object):
def __init__(self, height, width, description_function=None):
self.height = height
self.width = width
if description_function is None:
self.describe = self.default_describe
else:
self.describe = ft.partial(description_function, self)
def default_describe(self):
return 'Rectangle with height {:0.2f} and width {:0.2f}'.format(float(self.height),
float(self.width))
So that:
s = RectangleEnhanced(5, 3)
s.describe()
>>> 'Rectangle with height 5.00 and width 3.00'
continues to work as before, but, in addition:
def area_description(enh_rect):
return 'Rectangle with area {:0.2f}'.format(float(enh_rect.height * enh_rect.width))
t = RectangleEnhanced(5, 3, area_description)
t.describe()
>>> 'Rectangle with area 15.00'
Is this a reasonable approach to this problem? I can't imagine that I'm the first person to want to do this, and so I'm nervous that the approach below is suboptimal/unpythonic/bad/etc. Is there a "right" way to handle this?
Edit
Here's an example closer to my use case:
class FilterableCollection(object):
def __init__(self, items, owner, purpose, filterfunc=None):
self.items = set(items)
self.owner = owner
self.purpose = purpose
if filterfunc is None:
self.filterfunc = lambda x: True
else:
self.filterfunc = ft.partial(filterfunc, self)
def filtered(self):
return filter(self.filterfunc, self.items)
items = ['fun_ball', 'boring_ball', 'fun_bear', 'boring_bear']
owner = 'Bill'
purpose = 'fun'
f = FilterableCollection(items, owner, purpose)
print f.filtered()
def is_applicable(self, item):
return self.purpose in item
g = FilterableCollection(items, owner, purpose, is_applicable)
print g.filtered()
which returns:
['fun_bear', 'fun_ball', 'boring_ball', 'boring_bear']
['fun_bear', 'fun_ball']
as expected. So the idea is that when you create a specific instance of a FilterableCollection you can create a custom filter (which may depend on other attributes of that particular FitlerableCollection) which is then available to be called whenever. So there might be 10 FilterableCollections floating around, and they can each be filtered with their respective filters by calling the .filtered method.
I'm very open to the idea of doing this with inheritance or any other technique. But how would that apply in this case?
The essence is that for each instance of the class, I want to be able
to perform a certain kind of filtering on some data attached to the
instance. Depending on the context, the kind of filtering required
could vary widely.
What you describe here is known as the strategy pattern, and your example implementation is almost as pythonic as possible - Python functions being objects, quite a few design patterns requiring a full blown "functor" class in most mainstream languages are implemented with a plain function in Python.
The only "improvement" I can see would be to get rid of the partial - Python functions do know how to become instance methods:
if description_function is None:
self.describe = self.default_describe
else:
self.describe = description_function.__get__(self)
You can read this for more about this behaviour : https://wiki.python.org/moin/FromFunctionToMethod
Not that this would change anything from a purely functional (no pun intended) but it will certainly make you look as a PythonGuru(tm)
Your implementation makes sense. It is common for a class to take an optional param in its __init__ and to use a senisble default value if it isn't being passed. In your case that parameter is a function.
So this is a good implementation given you really really want to do that.
However, like many other commentator point out, this doesn't sound like a very good idea. The difference between taking a simple value and taking a function to override a method like this, is that the function param affects the behavior of the instance. Instances of the same type shouldn't really have different behavior.
Therefor using different types (e.g. using inheritance), is the most straightforward approach.
Since you asked, here is how you can use inheritance here:
class FilterableCollection(object):
def __init__(self, items, owner, purpose):
self.items = set(items)
self.owner = owner
self.purpose = purpose
def filtered(self):
return self.items
class ApplicabilityFilterableCollection(FilterableCollection):
def filtered(self):
return [ item for item in self.items if self.purpose in item ]
f = FilterableCollection(items, owner, purpose)
print f.filtered()
g = ApplicabilityFilterableCollection(items, owner, purpose)
print g.filtered()

Access class variables with same function

I want to create a function within a class that can access two different members with the same function. For example in the code below, I want both of the lines below to use the 'apply' function on different variables in the class
print(state.apply(rate))
print(state.apply(wage))
I had thought if I put in a dummy variable in the function definition (called exposure), it would replace it with the variables passed to the function (rate and wage in the example below). What is the correct way of doing this in python 3?
class State():
def __init__(self):
self.rate = 0
self.wage = 0
def apply(self, exposure):
self.exposure = self.exposure - 1
return self.exposure
state = State()
rate = State.rate
wage = State.wage
print(state.apply(rate))
print(state.apply(wage))
EDIT: I had made a typo where I had State instead of state in each print statement. I have now corrected this
This would be the only way:
class State:
def __init__ (self):
self.rate = 0
self.wage = 0
def apply (self, exposure):
setattr(self, exposure, getattr(self, exposure) - 1)
return getattr(self, exposure)
>>> state = State()
>>> print(state.apply('rate'))
-1
>>> print(state.apply('wage'))
-1
>>> print(state.apply('wage'))
-2
Note that those are instance variables, so you cannot access them using the type, State, but only using the object, state.
However, I would say, that whatever you are trying, you’re doing it wrong. If you describe your actual problem, we may be able to suggest a way better solution for it instead.

Categories

Resources