python - No se pueden cargar archivos usando pickle y múltiples módulos
multithreading login (3)
El problema es que está
encurtiendo objetos definidos en Configuración ejecutando realmente el módulo ''Configuración''
, luego está tratando de desempaquetar los objetos del módulo
GUI
.
Recuerde que pickle en realidad no almacena información sobre cómo se construye una clase / objeto, y necesita acceso a la clase cuando se desactiva. Ver wiki sobre el uso de Pickle para más detalles.
En los datos de pkl, verá que el objeto al que se hace referencia es
__main__.Manager
, ya que el módulo ''Configuración'' era
principal
cuando creó el archivo pickle (es decir, ejecutó el módulo ''Configuración'' como script principal para invocar la función
addUser
) .
Luego, intente desbloquear en ''Gui'', de modo que ese módulo tenga el nombre
__main__
, y esté importando Configuración dentro de ese módulo.
Entonces, por supuesto, la clase Manager será realmente
Settings.Manager
.
Pero el archivo pkl no lo sabe, y busca la clase Manager dentro de
__main__
, y lanza un AttributeError porque no existe (
Settings.Manager
sí, pero
__main__.Manager
no).
Aquí hay un conjunto mínimo de códigos para demostrar.
El módulo
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()
Ejecutas lo anterior para generar los datos de pickle.
El módulo
main_module.py
:
import pickle
import class_def
if __name__==''__main__'':
with open(''test_data.pkl'', ''rb'') as f:
users = pickle.load(f)
Ejecutas lo anterior para intentar abrir el archivo pickle, y esto arroja aproximadamente el mismo error que estabas viendo. (Ligeramente diferente, pero supongo que es porque estoy en Python 2.7)
La solución es:
- Hace que la clase esté disponible dentro del espacio de nombres del módulo de nivel superior (es decir, GUI o main_module) a través de una importación explícita, o
-
Usted crea el archivo pickle desde el mismo módulo de nivel superior en el que lo abrirá (es decir, llame a
Settings.addUser
desde la GUI oclass_def.main
desde main_module). Esto significa que el archivo pkl guardará los objetos comoSettings.Manager
oclass_def.Foo
, que luego se pueden encontrar en el espacio de nombresGUI
`main_module`.
Opción 1 ejemplo:
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)
Opción 2 ejemplo:
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)
Estoy tratando de crear un sistema de usuario, que usa una configuración y un módulo Gui, y cuando el módulo GUI solicita que el archivo se cargue usando pickle, sigo recibiendo un error de atributo. esto es del módulo de configuración:
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)
y este es el módulo 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_())
cada usuario es una clase y se coloca en una lista y luego la lista se guarda usando pickle cuando cargo solo el archivo de configuración y verifico el inicio de sesión, todo funciona bien, pero cuando abro el módulo GUI e intento verificar que no déjame, el error que estoy recibiendo:
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)>
Lea primero la respuesta mencionada por
zehnpaard
para conocer el motivo del error de atributo.
Además de la solución que ya proporcionó, en
python3
puede usar la clase
pickle.Unpickler
y anular el método
find_class
como se menciona a continuación:
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()
Si tiene una clase definida fuera del módulo, cuyo objeto está en los datos de pickle, debe importar la clase
from outside_module import DefinedClass1, DefinedClass2, DefinedClass3
with open(''pickle_file.pkl'', ''rb'') as f:
pickle_data = pickle.load(f)