# A program to display, update and save a dictionary as .csv

I'm now looking for feedback on v2.0 of this program instead.

I'd like some feedback about my code. What bad habits do I have? What advice could help me write more Pythonic?

I'm trying to write a console application and want a tool to display, update and save settings in a dictionary.

def file_as_string(file_name):
with open(file_name, 'r') as opened_file:
return string

def csv_as_dict(file_name):
file_name = file_name + '.csv'
import csv
dictionary = {}
dictionary[key] = value
return dictionary

def print_formatted_dict(dictionary, key_title , value_title):
print "{:^30} {:^15}".format('%s', '%s') % (key_title, value_title)
for setting, value in dictionary.items():
print "{:<30} {:<15}".format(setting, value)

def generic_prompt(prompt):
value = raw_input(prompt)
return value

def prompt_for_item(prompt, item):
prompt = prompt + ' ' + item + ': '
value = raw_input(prompt)
return value

def change_dict_value(dictionary, prompt, item):
value = prompt_for_item(prompt, item)
dictionary[item] = (value)
return dictionary

def csv_save_dict(dictionary, file_name):
file_name = file_name + '.csv'
import csv
csv_file = csv.writer(open(file_name, "w"))
for key, value in dictionary.items():
csv_file.writerow([key, value])

def change_dict(dictionary):
specify_question = 'Specify'
change_question = 'Change settings? '
setting_question = 'Setting to change: '
save_question = 'Save settings? '

change_item = generic_prompt(setting_question)
while change_item not in dictionary:
change_item = generic_prompt(setting_question)
dictionary = change_dict_value(dictionary, specify_question, change_item)
print_formatted_dict(dictionary, key_title, value_title)
return dictionary

print_formatted_dict(dictionary, key_title, value_title)
pass

dictionary = change_dict(dictionary)
return dictionary

def main():
default_dict = csv_as_dict(default_settings)
print_formatted_dict(default_dict, key_title, value_title)
return dictionary

#string variables
default_settings = 'default_settings'
filename_question = 'File name: '
key_title = 'Setting'
value_title = 'Value'

#list variables
yes_no = ['yes', 'no']

main()


The console with highlighted user input looks like this:

The program begins by reading a default_settings.csv file, mine looks like this:

-
A CSV up file and your output are both just text; please provide them as such, rather than as screenshots. – jonrsharpe Aug 12 '14 at 7:15

It's a pretty good program for a beginner.

A few points:

Put your import csv once at the top of the file, not in the functions that use csv. Importing is best thought of as an action at the start of your program, not as something that happens while performing an action.

You do this a lot:

while load_answer not in yes_no:


Anytime you find yourself repeating code, it'd be helpful to write a function instead. Something like:

   def ask_boolean_question(prompt):


Now your code can simply say:

  if ask_boolean_question(load_question):


And avoid repeating the loop everytime you ask a question.

def main():
manipulate_settings(default_settings)
filename = generic_prompt(filename_question)
manipulate_settings(filename)


Note that by reading this, you can see the program processes first the default settings, and then as many other settings files as you have inclination to use the program.

-

You have taken quite an odd approach to splitting this up into functions. For example, generic_prompt is totally pointless;

value = generic_prompt(prompt)


value = raw_input(prompt)


and just makes your code harder to read. And yet, as Winston points out, you have repeated the code for a yes/no input over and over again. prompt_for_item (value = raw_input('%s %s: ' % (prompt, item))) and change_dict_value (dictionary[item] = raw_input(...)) also seem suspect.

Both change_dict and change_dict_value mutate their argument and return it. This is unconventional; typically, functions that mutate arguments return None, implicitly or explicitly.

def csv_save_dict(dictionary, file_name):
file_name = file_name + '.csv'
...


It seems odd to expect the filename passed to a function to save a CSV to not include the extension. You should check this when the user inputs the filename, or at the very least guard this change with:

if not file_name.endswith('.csv'):


(although note that it's not actually necessary to call the file a .csv; the user may have specified a different extension)

Also, I would call this save_dict_as_csv, or just export_dict (now you don't have to rename if you switch from csv to e.g. pickle).

Some of your "constants" (usually UPPERCASE_WITH_UNDERSCORES in Python) seem oddly-placed. For example, key_title and value_title could be moved inside print_formatted_dict, removing the need for two arguments that only pass constants along. You could also make an input function get_filename, called for both importing and exporting, where filename_question could be moved to (this would also allow you to later add checking that the file in question does/doesn't exist, or switch to a file chooser). In both cases, I would just have a literal string, rather than separately define it, e.g.:

print "{:^30} {:^15}".format('%s', '%s') % ("Setting", "Value")


Your code has no explanation whatsoever. At the very least, you should add in some docstrings (see PEP-257) outlining what each function does.

-

It is good practice to follow code style guidelines from PEP8 and Google if you want to write more Pythonic code.

To be more specific:

    dictionary = change_dict_value(dictionary, specify_question, change_item)


Is to long, you can write it like this:

    dictionary = change_dict_value(dictionary, specify_question,
change_item)


Comments should have space after #, so:

# String variables


rather than:

#string variables


I'm finding writing docstrings for each and single function I write a very good practice. You can read more about docstrings formating in PEP257.

It is also common to write:

if __name__ == '__main__':
main()


rather than

main()


It helps to reuse once written code by creating new modules and packages, while keeping ability to run script directly. For the reference about packages and modules read Python documentation about them.

-