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