码农家园

关闭
导航

关于python:无法使用pickle和多个模块加载文件


loginmultithreadingpicklepyqt4python

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)>
相关讨论

  • 我不知道这是否与您的问题有关,但如果您使用with打开文件,则不需要close。当with块结束时,上下文管理器将自动关闭它。此外,您的verifyUser方法无法正常工作。它只查看用户列表中的第一个用户。
  • 感谢您的反馈!,是的,我只尝试过一个用户,我会重做,你知道我为什么会收到这个错误吗?
  • 您是否可以发布user_data.pkl文件的内容,假设它在此阶段是测试数据?
  • €?Ñ ]"?__main__"?Manager""")}"’"}"(?lastname"?Hammer"?gender"?M"?_dob"JüxË?_hashkey"?@99b3bcf690e653a177c602dd9999093b9eb29e50a3af9a059af3fcbfab476a16"? _username"?30JaHa"?type"h? firstname"?Jack"uba.这是一个pickle文件,所以它讨厌它看起来像什么,它的对象包含firstname,lastname,DOB,username,hashkey(password)和acceslevel
  • 对,还有三个问题 - 你的用户对象是从名为Manager的类创建的吗?这是在设置模块中定义的吗?你是如何实际创建user_data.pkl的?
  • 有一个名为user的主类,然后是子类,这个是来自manager子类的,是的,这些类都是在settings模块中创建的:def saveUsers(users): with open('user_data.pkl', 'wb') as file: pickle.dump(users, file, -1),它们由这个函数加载: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([])
  • 好的,谢谢,我认为相关代码不是您的登录窗口的详细信息,而是各个用户的类定义。您的设置模块有多大?它是否小到足以让您编辑问题并添加整个模块而不仅仅是三个函数和名称__ =='__ main'子句?
  • 好吧你在那里添加了完整的模块,它不是那么大的tbh


问题是你通过实际运行'Settings'模块来腌制在Settings中定义的对象,然后你试图从GUI模块中取消对象的取消。

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

在pkl数据中,您看到被引用的对象是__main__.Manager,因为当您创建pickle文件时,'Settings'模块是主要的(即您将'Settings'模块作为主脚本运行以调用addUser功能)。

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

这是一个最小的代码集来演示。

class_def.py模块:

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数据。
main_module.py模块:

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

解决方案是:

  • 您可以通过显式导入使该类在顶级模块的命名空间(即GUI或main_module)中可用,或者
  • 您可以从与其打开的模块相同的顶级模块创建pickle文件(即从GUI调用Settings.addUser,或从main_module调用class_def.main)。这意味着pkl文件将对象保存为Settings.Manager或class_def.Foo,然后可以在GUI`main_module`命名空间中找到它。
  • 选项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)
    相关讨论

    • 谢谢!!这真的很有帮助,程序现在正在运行,你是正确的导入用户类,这似乎比选项2更有效!
    • 很高兴听你这样说!我同意,在这个阶段,在GUI模块中显式导入每个类要简单得多。另一方面,一旦你完成代码,我想你将使用GUI界面添加/删除用户 - 所以你的对象将以GUI作为主要模块进行pickle,类将是Settings.User, Settings.Manager等
    • 是的,因为这只是登录gui我现在认为它的效率,顺便说一句,如果我有另一个问题?我应该再问一次吗?
    • 是的,如果这是一个单独的问题,那么创建另一个问题比继续添加到这个问题更好。
    • 如果你知道怎么做会有帮助吗?同一个项目的一部分! stackoverflow.com/questions/27745094/…
    • 我读到了这个答案,我想。"当然!!!"。拿点你的好人。


    请先阅读zehnpaard提到的答案,了解属性错误的原因。除了他已提供的解决方案之外,在python3中,您可以使用pickle.Unpickler类并覆盖find_class方法,如下所述:

    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()
    相关讨论

    • 真棒!我发现这个解决方案比zehnpaard接受的答案更强大,因为它允许在代码中而不是在生成的pickle中更改类(或函数)的位置。
    • 对于任何想知道,这个解决方案确实扩展到多个导入,只需添加额外的if语句。
    • @ thedeg123是的,你也可以这样做


    如果你有一个在模块外定义的类,其对象是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)


    • 关于python:不同模块中的Pickling和Unpickling

    Copyright ©  码农家园 联系:[email protected]