This is part of the Semicolon&Sons Code Diary - consisting of lessons learned on the job. You're in the dumb-mistakes-and-gotchas category.
Last Updated: 2024-11-21
The lesson in this piece is intended for programmers who work in more than one programming language.
I was writing software to limit the dynamic range (i.e. note volumes) in midi files. The
following loop that was supposed to take different actions depending on
whether or not an "observation" (of a midi event) had already been made (observation_already_made
)
def calibrate_existing_dynamic_range(midi_data):
min_observed = None
max_observed = None
for message in track:
if message.type == 'note_on':
observation_already_made = max_observed and min_observed
if observation_already_made:
max_observed = max(message.velocity, max_observed)
min_observed = min(message.velocity, min_observed)
else:
max_observed = message.velocity
min_observed = message.velocity
The code was not acting as expected. When I looked at the data, most of the
values for min_observed
were 0
. In the Ruby language, my first programming language and
therefore my template for others, 0
is truthy. So if the max_observed
was
even 0
then observation_already_made = max_observed and min_observed
would
evaluate to 0
, which would be truthy.
However: 0
isn't truthy in Python! It is falsey. Therefore the
observation_already_made
was never set to something truthy.
What I should have done instead was check that there was something other than null/none:
observation_already_made = max_observed is not None and min_observed is not None
In general, truthiness rules differ across languages and it's easy to get tripped up when you switch languages often (as I suspect many programmers end up doing). Therefore it's wisest to explicitly test against null etc. rather than rely on implicit truthiness rules.