farza
farza

Reputation: 263

What's the best way to handle optional values in Python?

I have a data structure below which I use to keep track of certain data from a video game.

values = {
    'kills': None,
    'assists': None,
    'deaths': None,
    'minutes': None,
    'seconds': None,
}

Depending on what data I have available I fill up the values as I go. But, all the attributes are optional and could be None. Ex. kills could be 12 or None. It all depends on what data I had available in that moment. I could make this a bit better by having an object to store the data, but I'll still have the issue of these optional values.

What's the best way to deal with optionals like this in Python?

Upvotes: 1

Views: 10932

Answers (2)

Raymond Hettinger
Raymond Hettinger

Reputation: 226694

Your instincts were right. There's no need to store all possible options. Just store the ones that are applicable.

Solution 1: Use get() instead of storing Nones

Usually, we don't store all the possible None values in a dictionary. Just fill in the the non-None values and use the dict.get() method for fetch values:

>>> options = {'weapon': 'dagger'}
>>> print(options.get('weapon'))
dagger
>>> print(options.get('food'))
None

Solution 2: Counter defaults to zero for numeric data

If the values are all numeric, consider using collections.Counter() which lets optional entries default to zero:

>>> from collections import Counter
>>> options = Counter(kills=4, deaths=2)
>>> options['kills']
4
>>> options['deaths'] += 1
>>> options['deaths']
3
>>> options['assists']
0

Solution 3: getattr() for optional data in instances or classes

The getattr() function lets you specific default values for attribute lookup when using instances or classes. The instance approach is especially helpful for tracking data for each player:

>>> anka = Player()
>>> vlad = Player()
>>> anka.hits = 5
>>> vlad.assists = 2
>>> print(getattr(anka, 'hits', None))
5
>>> print(getattr(vlad, 'hits', None))
None

Solution 4: For known defaults, use a ChainMap

The ChainMap class lets you link pairs of dictionaries together to treat them as a single entity.

>>> from collections import ChainMap
>>> defaults = ChainMap(dict(kills=0, assists=0, location='start', status='alive'))
>>> anka = defaults.new_child()
>>> vlad = defaults.new_child()
>>> anka['hits'] = 5
>>> vlad['assists'] = 2
>>> anka['hits']
5
>>> vlad['location']
'start'

This last solution lets you organize all possible defaults in one place, while letting instances store only the data that is needed. It also provids simple dictionary access so there is no need for get() or getattr() for every lookup.

Hope one of these solutions is a good fit for your problem :-)

Upvotes: 4

Tdaw
Tdaw

Reputation: 181

If you simply never want to initialize the keys until you have to, you can use defaultdict from the collections package to insert new keys on the fly without the need of initially setting the key value pair.

from collections import defaultdict

values = defaultdict(int)

values['seconds'] += 1
values['assists'] += 1

Upvotes: 0

Related Questions