There are several problems with both this code and this approach. There are many file formats that are useful for this kind of thing; one very popular one that has built-in Python support is JSON.
import json
from pprint import pprint
old_players = [
{
'name': 'Bob',
'result': 'success?',
'difficulty': 'hard',
'score': 55,
},
{
'name': 'Tatsuki',
'result': 'embarrassment',
'difficulty': 'easy',
'score': -2,
},
]
with open('player-file.json', 'w') as outfile:
outfile.write(json.dumps(old_players))
with open('player-file.json', 'r') as infile:
new_players = json.loads(infile.read())
pprint(new_players)
# [{'difficulty': 'hard', 'name': 'Bob', 'result': 'success?', 'score': 55},
# {'difficulty': 'easy', 'name': 'Tatsuki', 'result': 'embarrassment', 'score': -2}]
namedtuple
isn't something I see used often. Using it with JSON can be a bit wonky, and while there are workarounds, it might be a better idea to either use a Player
class with a simple custom serializer, subclass a class generated by namedtuple
that defines a method to return either JSON or a JSON-formattable dict (pretty convoluted), or write a separate function that explicitly translates your namedtuple
objects into JSON.
Regarding reading your existing format:
from pprint import pprint
from collections import namedtuple
Player = namedtuple("Player", ["name", "result", "difficulty", "score"])
def chunks(l, n):
"""Yield successive n-sized chunks from l."""
for i in range(0, len(l), n):
yield l[i:i + n]
with open('/tmp/players.old', 'r') as infile:
lines = [l.strip() for l in infile.readlines()]
for player_values in chunks(lines, 4):
pprint(Player(*player_values))
# Player(name='George', result='lost', difficulty='H', score='18')
# Player(name='Holly', result='lost', difficulty='H', score='28')
# Player(name='Marcus', result='won', difficulty='H', score='30')
The chunks
function comes from this answer. It depends on you knowing the number of values you're unpacking per player, which can't change for this format.
When lines
is read here, a list comprehension is used to strip the newline from the end of each value.
Finally, the Player
tuple is instantiated with player_values
, a list generated by chunks
and expanded using *
. This means, instead of passing the list player_values
to the function Player.__init__(...)
, the individual values will be sent as *args
. So effectively, instead of Player([name, result, difficulty, score])
, the method call becomes Player(name, result, difficulty, score)
.
While this technically retrieves the values, note that the score
that's assigned here is a string, not a numeric value. If you want that to be cast to an int
, for example, you'd need to write out the full instantiation:
# ...
for player_values in chunks(lines, 4):
pprint(Player(
player_values[0],
player_values[1],
player_values[2],
int(player_values[3]),
))
# Player(name='George', result='lost', difficulty='H', score=18)
# Player(name='Holly', result='lost', difficulty='H', score=28)
# Player(name='Marcus', result='won', difficulty='H', score=30)