-4

I am trying to access a global variable in a function however it seems to believe it to be NoneType. Any help? The error seems to believed that the dictionary 'students' is NoneType and it is not reading it as a global variable even though I tell it to. Is this an error with my code or is it an issue with this version of python, I cannot replicate the error on any other program (using versions from 2.7-3.3.3!)

#Imports
from math import floor
#Global Declarations (for later)
#Functions
def loadData():
    """Loads the students.txt file if it exists."""
    studentsFile = open("students.txt", "r")
    data = studentsFile.read()
    students = exec(data)
    studentsFile.close()
    return students
def fileExists():
    """Checks if the students.txt file exists."""
    try:
        studentsFile = open("students.txt", "r")
        studentsFile.close()
        students = loadData()
    except FileNotFoundError:
        studentsFile = open("students.txt", "w")
        studentsFile.close()
        students = {}
    print(students)
    return students
def saveToFile():
    global students
    studentsFile = open("students.txt", "w")
    studentsFile.write(str(students))
    studentsFile.close()
def gradeCalc(score):
    possibleGrades = {"A*" : [x for x in range(90, 101)], "A" : [x for x in range(80, 90)], "B" : [x for x in range(70, 80)], "C" : [x for x in range(60, 70)], "D" : [x for x in range(50, 60)], "E" : [x for x in range(40, 50)], "F" : [x for x in range(30, 40)], "G" : [x for x in range(20, 30)], "U" : [x for x in range(0, 20)]}
    for key in possibleGrades:
            if int(floor(score)) in possibleGrades[key]:
                grade = key
def studentExists(studentName):
    global students
    print(students)
    print(studentName)
    try:
        testVar = students[studentName]
        return True
    except KeyError:
        return False
def doneTest(studentName, testName):
    try:
        testVar = students[studentName][testName]
        return True
    except KeyError:
        return False
def integerInput(msg):
    while True:
        try:
            integer = int(input(msg))
            return integer
        except ValueError:
            print("Not a whole number.")
def scoreInput():
    while True:
        studentScore = integerInput("Enter the students score: ")
        if studentScore <= 100 and studentScore >= 0:
            break
        else:
            print("Not between (and including) 0 and 100.")
    return studentScore
def getData():
    global students
    quantity = integerInput("How many students do you wish to add? ")
    test = str(input("Please enter a test name: ").lower())
    for i in range(0, quantity):
        name = str(input("Student name: ").title())
        score = scoreInput()
        if studentExists(name) == False:
            students[name] = {}
        students[name][test] = score
        students[name][test+"Grade"]=gradeCalc(score)
def displayData():
    global students
    studentsToDisplay = []
    listOfGrades = []
    print("You must choose a mode. How many students got each grade or the grades for an individual student.")
    mode = input("Please select 'grade' or 'student'").lower()
    if mode == 'grade':
        test = str(input("For which test do you want to access the grades for? ").lower())
        for key in students:
            if doneTest(key, test)==True:
                studentsToDisplay.append(key)
        for item in studentsToDisplay:
            listOfGrades.append(students[item][test+"Grade"])
        print("Grades:\nA*:", ListOfGrades.count("A*"), "\nA:", ListOfGrades.count("A"), "\nB:", ListOfGrades.count("B"), "\nC:", ListOfGrades.count("C"), "\nD:", ListOfGrades.count("D"), "\nE:", ListOfGrades.count("E"), "\nF:", ListOfGrades.count("F"), "\nG:", ListOfGrades.count("G"), "\nU:", ListOfGrades.count("U"))
    elif mode == "student":
        studentName = str(input("Student name: ").title())
        test = str(input("Test name: ").lower())
        if doneTest(studentName, test) == True:
            print(studentName, "got", students[studentName][test], "and this is grade:", students[studentName][test+"Grade"])
def main():
    global students
    students = fileExists()
    while True:
        choice = str(input("Do you want to add students/tests or do you want to review previous data? (accepts 'add' and 'review') ").lower())
        if choice == "add":
            getData()
            saveToFile()
        elif choice == "review":
            displayData()
        else:
            exitChoice = str(input("Do you want to exit? ").lower())
            if exitChoice == "yes" or exitChoice == "y":
                break
main()

I get this error:

Traceback (most recent call last):
  File "V:\GCSE 2013\sj.sully\Programme Library\Assessed Tasks and CAT Practice\CAT Practice Scenario 1 Code.py", line 108, in <module>
    main()
  File "V:\GCSE 2013\sj.sully\Programme Library\Assessed Tasks and CAT Practice\CAT Practice Scenario 1 Code.py", line 100, in main
    getData()
  File "V:\GCSE 2013\sj.sully\Programme Library\Assessed Tasks and CAT Practice\CAT Practice Scenario 1 Code.py", line 71, in getData
    if studentExists(name) == False:
  File "V:\GCSE 2013\sj.sully\Programme Library\Assessed Tasks and CAT Practice\CAT Practice Scenario 1 Code.py", line 39, in studentExists
    testVar = students[studentName]
TypeError: 'NoneType' object is not subscriptable

I am using python 3.3.0.

Bach
  • 6,145
  • 7
  • 36
  • 61
  • 1
    Isn't it *your* job to debug *your* code, especially if it's for GCSE? In two words, try actually declaring **global** `students` variable. Note that whatever you define inside function is local to that function. – J0HN May 08 '14 at 13:23
  • Or, better than `global`, make it an explicit argument and/or return value – jonrsharpe May 08 '14 at 13:32

1 Answers1

1

well, if students is None, it's because you did not have students defined before you were using it.

But using globals for such a problem is insane. Actually, using globals is evil and should not be done.

Python is an OOP language, and thus you should use the right tools for the right task. For your specific use case, you can use a class, such as:

class StudentsData:
    def __init__(self):
        self.students = []

    def loadData(self):
        pass

    def fileExists(self):
        pass

    def save(self):
        pass

    def scoreInput(self):
        pass

    def display(self):
        pass

but there are functions not meant to be used in a class, such as:

def gradeCalc(score):
    pass

def main():
    pass

I'm not giving you the implementations, because you need to try and fail to understand how to make a correct OOP design. I'm pretty sure you're also doing computer programming classes, so read your courses on the topic as well! ;-)

Edit: To read more on the topic, there's a list of resources in the following SO answer:

Edit #2: The only exception I can think of the "global is evil" rule, is for declaring constants (i.e. variable that you won't modify), so you can have them all defined at a single place of your program. I've read that people tend to use state/env objects as globals, which can be considered as singletons, and can easily be considered bad programing. You need to think twice before doing such a thing, as often using a class maybe a better solution…

Also, if you read python's sources, you'll find examples of the use of globals. But it would'nt be the only awful stuff implemented carelessly in python…

Community
  • 1
  • 1
zmo
  • 24,463
  • 4
  • 54
  • 90
  • +1 for good explanation:) – sundar nataraj May 08 '14 at 13:34
  • Using globals isn't evil _per se_. Global variables are a tool. An easily abused tool, but it is still a tool. Better to educate on _why_ globals should be avoided rather than just make a proclamation. Perhaps you could update your answer to include a link to an essay on why global variables should be avoided. – Bryan Oakley May 08 '14 at 15:02