Невозможно загрузить файлы с помощью pickle и нескольких модулей
Я пытаюсь создать пользовательскую систему, которая использует настройки и модуль Gui, и когда модуль GUI запрашивает файл для загрузки с использованием pickle, я продолжаю получать ошибку атрибута. это из модуля настроек:
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}\nDate of Birth: {2}\nGender: {3}\nAccess 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?\n > ')
lname = input('What is your Last Name?\n > ')
dob = int(input('Please enter your date of birth in the following format, example 12211996\n> '))
gender = input("What is your gender? 'M' or 'F'\n >")
level = input("Enter the access level given to this user 'G', 'A', 'M'\n > ")
password = input("Enter a password:\n > ")
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:\n > ')
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 :\n > ')
for user in users:
if user.firstname == change:
oldpass = input('Please type in your old password:\n > ')
newpass = input('Please type in your new password:\n > ')
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:
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 и пытаюсь убедиться, что он не работает позвольте мне, ошибку я получаю:
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)>
Ответы
Ответ 1
Проблема заключается в том, что вы травильные объекты, определенные в настройках, фактически запустив модуль "Настройки" , затем вы пытаетесь раскрыть объекты из модуля GUI
.
Помните, что pickle фактически не хранит информацию о том, как построен класс/объект, и нуждается в доступе к классу при распаковке. Подробнее см. wiki по использованию Pickle.
В данных pkl вы видите, что ссылающийся объект __main__.Manager
, поскольку модуль "Настройки" был main, когда вы создали файл pickle (т.е. вы запустили модуль "Настройки" как основной script для вызова функции addUser
).
Затем вы пытаетесь раскрыть в "Gui" - так что модуль имеет имя __main__
, и вы импортируете параметр в этом модуле. Поэтому, конечно, класс Manager будет Settings.Manager
. Но файл pkl этого не знает и ищет класс Менеджера в __main__
и выдает AttributeError, потому что он не существует (Settings.Manager
does, но __main__.Manager
does not).
Здесь минимальный код, который нужно продемонстрировать.
Модуль class_def.py
:
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()
Вы запускаете приведенное выше, чтобы генерировать данные рассола.
Модуль main_module.py
:
import pickle
import class_def
if __name__=='__main__':
with open('test_data.pkl', 'rb') as f:
users = pickle.load(f)
Вы запускаете вышеуказанное, чтобы попытаться открыть файл pickle, и это вызывает примерно ту же ошибку, что и вы. (Немного отличается, но я предполагаю, что, поскольку я на Python 2.7)
Решение: либо
- Вы делаете класс доступным в пространстве имен модуля верхнего уровня (т.е. GUI или main_module) через явный импорт или
- Вы создаете файл pickle из того же самого верхнего уровня, что и тот, который вы его откроете (т.е. вызовите
Settings.addUser
из графического интерфейса пользователя или class_def.main
из main_module). Это означает, что файл pkl сохранит объекты как Settings.Manager
или class_def.Foo
, которые затем можно найти в пространстве имен GUI
`main_module`.
Пример варианта 1:
import pickle
import class_def
from class_def import Foo # Import Foo into main_module namespace explicitly
if __name__=='__main__':
with open('test_data.pkl', 'rb') as f:
users = pickle.load(f)
Вариант 2:
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)
Ответ 2
Пожалуйста, сначала прочитайте ответ, упомянутый zehnpaard, чтобы узнать причину ошибки атрибута. Помимо решения, которое он уже предоставил, в python3
вы можете использовать класс pickle.Unpickler
и переопределить метод find_class
как упомянуто ниже:
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()
Ответ 3
Если у вас есть класс, определенный вне модуля, чей объект находится в данных pickle, вы должны импортировать класс
from outside_module import DefinedClass1, DefinedClass2, DefinedClass3
with open('pickle_file.pkl', 'rb') as f:
pickle_data = pickle.load(f)