WANT: bad code in python (for refactoring example)

Jussi Piitulainen jussi.piitulainen at helsinki.fi
Fri Feb 17 04:07:18 EST 2017


Makoto Kuwata <kwa at kuwata-lab.com> writes:

> On Thu, Feb 16, 2017 at 6:53 AM, Erik <python at lucidity.plus.com> wrote:
>>
>> (Python code examples of what you think is "bad" vs "good" would be
>> useful).
>
> You are right.
>
> Bad code Example:
>
>     #
> https://codewords.recurse.com/issues/one/an-introduction-to-functional-programming
>
>     from random import random
>
>     def move_cars(car_positions):
>         return map(lambda x: x + 1 if random() > 0.3 else x,
>                    car_positions)
>
>     def output_car(car_position):
>         return '-' * car_position
>
>     def run_step_of_race(state):
>         return {'time': state['time'] - 1,
>                 'car_positions': move_cars(state['car_positions'])}
>
>     def draw(state):
>         print ''
>         print '\n'.join(map(output_car, state['car_positions']))
>
>     def race(state):
>         draw(state)
>         if state['time']:
>             race(run_step_of_race(state))
>
>     race({'time': 5,
>           'car_positions': [1, 1, 1]})

Here's a rewrite in functional style, which I consider somewhat better
than the original:

from random import random

def move(positions):
    return [ pos + bool(random() > 0.3) for pos in positions ]

def race(positions, steps):
    for step in range(steps):
        positions = move(positions)
        yield positions

def draw(positions):
    print(*('-' * pos for pos in positions), sep = '\n')    

if __name__ == '__main__':
    for step, positions in enumerate(race([1,1,1], 5)):
        step and print()
        draw(positions)

I did a number of things, but mainly I reconceptualized the race itself
explicitly as a sequence of positions. While a Python generator is an
extremely stateful object, with some care it works well in functional
style.

> Refactoring example:
>
>     from random import random
>
>     class Car(object):
>
>         def __init__(self):
>             self.position = 1
>
>         def move(self):
>             if random() > 0.3:
>                 self.position += 1
>             return self.position
>
>     class Race(object):
>
>         def __init__(self, n_cars=3):
>             self._cars = [ Car() for _ in range(n_cars) ]
>
>         def round(self):
>             for car in self._cars:
>                 car.move()
>
>         def report(self):
>             print("")
>             for car in self._cars:
>                 print('-' * car.position)
>
>         def run(self, n_rounds=5):
>             self.report()
>             for _ in range(n_rounds):
>                 self.round()
>                 self.report()
>
>     if __name__ == '__main__':
>         Race(3).run(5)

If you want to refactor bad code into better code, it would be more
honest to start with code that is already in your preferred style. That
would be a good lesson.

Now you've taken questionable code in a somewhat functional style and
refactored it into object-oriented style. But you don't call them that.
You call them bad code and good code. That's a bad lesson. It conflates
issues.

Take good functional code, refactor it into your preferred style. Also
do the reverse. That would be a good lesson, assuming your students are
ready for such discussion.



More information about the Python-list mailing list