Calmcode - objects: properties

Object properties in Python

1 2 3 4 5 6 7 8 9 10

Let's add another method to calculate the expected value of the dice.

import random 

class Dice:
    def __init__(self, probs):
        self.probs = probs
    
    def roll(self, n=1):
        sides = list(self.probs.keys())
        probabilities = list(self.probs.values())
        return random.choices(sides, weights=probabilities, k=n)
    
    def expected_value(self):
        return sum(i * p for i, p in self.probs.items())

Given this new method you can now calculate the expected value of the dice.

dice = Dice({1: 1/6, 2: 1/6, 3: 1/6, 4: 1/6, 5: 1/6, 6: 1/6})
dice.expected_value()

Decorator

We can add some syntactic sugar to our code such that the .expected_value() method behaves like a property .expected_value (note the lack of backets). You can use the @property decorator to do this, which is globally available in Python.

import random 

class Dice:
    def __init__(self, probs):
        self.probs = probs
    
    def roll(self, n=1):
        sides = list(self.probs.keys())
        probabilities = list(self.probs.values())
        return random.choices(sides, weights=probabilities, k=n)
    
    @property
    def expected_value(self):
        return sum(i * p for i, p in self.probs.items())

You can get the same value as before, but without having to call the method.

dice = Dice({1: 1/6, 2: 1/6, 3: 1/6, 4: 1/6, 5: 1/6, 6: 1/6})
dice.expected_value

From __init__

Alternatively, given that it is really just a one-liner, we can add also the .expected_value property in the __init__ method. Here's how you might do that:

import random 

class Dice:
    def __init__(self, probs):
        self.probs = probs
        self.expected_value = sum(i * p for i, p in self.probs.items())
    
    def roll(self, n=1):
        sides = list(self.probs.keys())
        probabilities = list(self.probs.values())
        return random.choices(sides, weights=probabilities, k=n)

As you can see, there is more than one way to do things. Given the fact that the .expected_value property can really be calculated via a one-liner it can be a good idea to add it in the __init__ method. But there are consequences. In this particular case we'd have a hard to adding a docstring, which could be much easier it we had a method.