Unable to load files using pickle and multiple modules
我正在尝试创建一个使用设置和Gui模块的用户系统,当GUI模块请求使用pickle加载文件时,我不断收到属性错误。 这是来自设置模块:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 | import pickle import hashlib class User(object): def __init__(self, fname, lname, dob, gender): self.firstname = fname self.lastname = lname self._dob = dob self.gender = gender self.type = 'General' self._username = '' self._hashkey = '' def Report(self): print("Full Name: {0} {1} Date of Birth: {2} Gender: {3} Access Level: {4}".format(self.firstname,self.lastname, self._dob, self.gender, self.type)) print(self._username) def Genusername(self): self._username = str(str(self._dob)[:2] + self.firstname[:2] + self.lastname[:2]) saveUsers(users) def Genhashkey(self, password): encoded = password.encode('utf-8','strict') return hashlib.sha256(encoded).hexdigest() def Verifypassword(self, password): if self._hashkey == self.Genhashkey(password): return True else: return False class SAdmin(User): def __init__(self, fname, lname, dob, gender): super().__init__(fname, lname, dob, gender) self.type = 'Stock Admin' class Manager(User): def __init__(self, fname, lname, dob, gender): super().__init__(fname, lname, dob, gender) self.type = 'Manager' def saveUsers(users): with open('user_data.pkl', 'wb') as file: pickle.dump(users, file, -1) # PICKLE HIGHEST LEVEL PROTOCOL def loadUsers(users): try: with open('user_data.pkl', 'rb') as file: temp = pickle.load(file) for item in temp: users.append(item) except IOError: saveUsers([]) def userReport(users): for user in users: print(user.firstname, user.lastname) def addUser(users): fname = input('What is your First Name? > ') lname = input('What is your Last Name? > ') dob = int(input('Please enter your date of birth in the following format, example 12211996 > ')) gender = input("What is your gender? 'M' or 'F' >") level = input("Enter the access level given to this user 'G', 'A', 'M' >") password = input("Enter a password: >") if level == 'G': usertype = User if level == 'A': usertype = SAdmin if level == 'M': usertype = Manager users.append(usertype(fname, lname, dob, gender)) user = users[len(users)-1] user.Genusername() user._hashkey = user.Genhashkey(password) saveUsers(users) def deleteUser(users): userReport(users) delete = input('Please type in the First Name of the user do you wish to delete: > ') for user in users: if user.firstname == delete: users.remove(user) saveUsers(users) def changePass(users): userReport(users) change = input('Please type in the First Name of the user you wish to change the password for : > ') for user in users: if user.firstname == change: oldpass = input('Please type in your old password: > ') newpass = input('Please type in your new password: > ') if user.Verifypassword(oldpass): user._hashkey = user.Genhashkey(newpass) saveUsers(users) else: print('Your old password does not match!') def verifyUser(username, password): for user in users: if user._username == username and user.Verifypassword(password): return True else: return False if __name__ == '__main__': users = [] loadUsers(users) |
这是GUI模块:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 | from PyQt4 import QtGui, QtCore import Settings class loginWindow(QtGui.QDialog): def __init__(self): super().__init__() self.initUI() def initUI(self): self.lbl1 = QtGui.QLabel('Username') self.lbl2 = QtGui.QLabel('Password') self.username = QtGui.QLineEdit() self.password = QtGui.QLineEdit() self.okButton = QtGui.QPushButton("OK") self.okButton.clicked.connect(self.tryLogin) self.cancelButton = QtGui.QPushButton("Cancel") grid = QtGui.QGridLayout() grid.setSpacing(10) grid.addWidget(self.lbl1, 1, 0) grid.addWidget(self.username, 1, 1) grid.addWidget(self.lbl2, 2, 0) grid.addWidget(self.password, 2, 1) grid.addWidget(self.okButton, 3, 1) grid.addWidget(self.cancelButton, 3, 0) self.setLayout(grid) self.setGeometry(300, 300, 2950, 150) self.setWindowTitle('Login') self.show() def tryLogin(self): print(self.username.text(), self.password.text()) if Settings.verifyUser(self.username.text(),self.password.text()): print('it Woks') else: QtGui.QMessageBox.warning( self, 'Error', 'Incorrect Username or Password') class Window(QtGui.QMainWindow): def __init__(self): super().__init__() if __name__ == '__main__': app = QtGui.QApplication(sys.argv) users = [] Settings.loadUsers(users) if loginWindow().exec_() == QtGui.QDialog.Accepted: window = Window() window.show() sys.exit(app.exec_()) |
每个用户都是一个类,并被放入一个列表,然后当我加载设置文件时,使用pickle保存列表,并验证登录一切正常,但是当我打开GUI模块并尝试验证它不 让我,我得到的错误:
1 2 3 4 5 6 | Traceback (most recent call last): File"C:\Users`Program\LoginGUI.py", line 53, in <module> Settings.loadUsers(users) File"C:\Users\Program\Settings.py", line 51, in loadUsers temp = pickle.load(file) AttributeError: Can't get attribute 'Manager' on <module '__main__' (built-in)> |
问题是你通过实际运行'Settings'模块来腌制在Settings中定义的对象,然后你试图从
请记住,pickle实际上并不存储有关如何构造类/对象的信息,并且在unpickling时需要访问该类。有关详细信息,请参阅使用Pickle的wiki。
在pkl数据中,您看到被引用的对象是
然后,您尝试在'Gui'中进行unpickling - 以便模块具有名称
这是一个最小的代码集来演示。
1 2 3 4 5 6 7 8 9 10 11 12 13 | import pickle class Foo(object): def __init__(self, name): self.name = name def main(): foo = Foo('a') with open('test_data.pkl', 'wb') as f: pickle.dump([foo], f, -1) if __name__=='__main__': main() |
您运行上面的操作来生成pickle数据。
1 2 3 4 5 6 7 | import pickle import class_def if __name__=='__main__': with open('test_data.pkl', 'rb') as f: users = pickle.load(f) |
您运行以上操作以尝试打开pickle文件,这会抛出与您看到的大致相同的错误。 (略有不同,但我猜这是因为我在Python 2.7上)
解决方案是:
选项1示例:
1 2 3 4 5 6 7 8 | import pickle import class_def from class_def import Foo # Import Foo into main_module's namespace explicitly if __name__=='__main__': with open('test_data.pkl', 'rb') as f: users = pickle.load(f) |
选项2示例:
1 2 3 4 5 6 7 8 | import pickle import class_def if __name__=='__main__': class_def.main() # Objects are being pickled with main_module as the top-level with open('test_data.pkl', 'rb') as f: users = pickle.load(f) |
请先阅读zehnpaard提到的答案,了解属性错误的原因。除了他已提供的解决方案之外,在
1 2 3 4 5 6 7 8 9 10 11 | import pickle class CustomUnpickler(pickle.Unpickler): def find_class(self, module, name): if name == 'Manager': from settings import Manager return Manager return super().find_class(module, name) pickle_data = CustomUnpickler(open('file_path.pkl', 'rb')).load() |
如果你有一个在模块外定义的类,其对象是pickle数据,
你必须导入这个类
1 2 3 4 | from outside_module import DefinedClass1, DefinedClass2, DefinedClass3 with open('pickle_file.pkl', 'rb') as f: pickle_data = pickle.load(f) |