无法使用 pickle 和多个模块加载文件 [英] Unable to load files using pickle and multiple modules

查看:22
本文介绍了无法使用 pickle 和多个模块加载文件的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试创建一个使用设置和 Gui 模块的用户系统,当 GUI 模块请求使用 pickle 加载文件时,我不断收到属性错误.这是来自设置模块:

I'm trying to create a user system, which uses a setting and Gui module, and when the GUI module requests for the file to load up using pickle, I keep getting an attribute error. this is from the settings module:

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模块:

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 模块并尝试验证它时不让我,我得到的错误:

each user is a class and are put into a list and then the list is saved using pickle when I load up just the settings file and verify the login everything works fine but when I open up the GUI module and try to verify it doesn't let me, the error I'm getting:

Traceback (most recent call last):
  File "C:Users`ProgramLoginGUI.py", line 53, in <module>
    Settings.loadUsers(users)
  File "C:UsersProgramSettings.py", line 51, in loadUsers
    temp = pickle.load(file)
AttributeError: Can't get attribute 'Manager' on <module '__main__' (built-in)>

推荐答案

问题是您通过实际运行设置"模块来酸洗设置中定义的对象,然后您正在尝试从 GUI 模块中解压对象.

The issue is that you're pickling objects defined in Settings by actually running the 'Settings' module, then you're trying to unpickle the objects from the GUI module.

请记住,pickle 实际上并不存储有关如何构造类/对象的信息,并且在 unpickling 时需要访问该类.有关详细信息,请参阅维基使用 Pickle.

Remember that pickle doesn't actually store information about how a class/object is constructed, and needs access to the class when unpickling. See wiki on using Pickle for more details.

在 pkl 数据中,您看到被引用的对象是 __main__.Manager,因为在创建 pickle 文件时,设置"模块是 ma​​in(即您运行设置"模块作为调用 addUser 函数的主脚本).

In the pkl data, you see that the object being referenced is __main__.Manager, as the 'Settings' module was main when you created the pickle file (i.e. you ran the 'Settings' module as the main script to invoke the addUser function).

然后,您尝试在 'Gui' 中解压 - 因此该模块的名称为 __main__,并且您正在该模块中导入 Setting.所以当然 Manager 类实际上是 Settings.Manager.但是 pkl 文件不知道这一点,并在 __main__ 中查找 Manager 类,并抛出一个 AttributeError 因为它不存在(Settings.Manager 确实存在,但是__main__.Manager 没有).

Then, you try unpickling in 'Gui' - so that module has the name __main__, and you're importing Setting within that module. So of course the Manager class will actually be Settings.Manager. But the pkl file doesn't know this, and looks for the Manager class within __main__, and throws an AttributeError because it doesn't exist (Settings.Manager does, but __main__.Manager doesn't).

这是用于演示的最小代码集.

Here's a minimal code set to demonstrate.

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()

您运行上面的代码来生成pickle 数据.main_module.py 模块:

You run the above to generate the pickle data. The main_module.py module:

import pickle

import class_def

if __name__=='__main__':
    with open('test_data.pkl', 'rb') as f:
        users = pickle.load(f)

您运行上面的命令以尝试打开pickle文件,这会抛出与您看到的大致相同的错误.(略有不同,但我猜那是因为我使用的是 Python 2.7)

You run the above to attempt to open the pickle file, and this throws roughly the same error that you were seeing. (Slightly different, but I'm guessing that's because I'm on Python 2.7)

解决方案是:

  1. 您可以通过显式导入使类在顶级模块(即 GUI 或 main_module)的命名空间中可用,或
  2. 您从与打开它的模块相同的顶级模块创建 pickle 文件(即从 GUI 调用 Settings.addUserclass_def.main> 来自 main_module).这意味着 pkl 文件会将对象保存为 Settings.Managerclass_def.Foo,然后可以在 GUI`main_module 中找到它们` 命名空间.
  1. You make the class available within the namespace of the top-level module (i.e. GUI or main_module) through an explicit import, or
  2. You create the pickle file from the same top-level module as the one that you will open it in (i.e. call Settings.addUser from GUI, or class_def.main from main_module). This means that the pkl file will save the objects as Settings.Manager or class_def.Foo, which can then be found in the GUI`main_module` namespace.

选项 1 示例:

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 示例:

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)

这篇关于无法使用 pickle 和多个模块加载文件的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆