Python 无法使用 pickle 和多个模块加载文件
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/27732354/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Unable to load files using pickle and multiple modules
提问by Inthuson
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:
我正在尝试创建一个用户系统,它使用设置和 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)
and this is the GUI module:
这是 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_())
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:
每个用户都是一个类,并被放入一个列表中,然后当我只加载设置文件并验证登录时使用 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)>
采纳答案by zehnpaard
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.
问题是您通过实际运行“设置”模块来酸洗设置中定义的对象,然后您尝试从GUI
模块中取消对象。
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 Picklefor more details.
请记住,pickle 实际上并不存储有关如何构造类/对象的信息,并且在 unpickling 时需要访问该类。有关使用 Pickle的更多详细信息,请参阅wiki。
In the pkl data, you see that the object being referenced is __main__.Manager
, as the 'Settings' module was mainwhen you created the pickle file (i.e. you ran the 'Settings' module as the main script to invoke the addUser
function).
在 pkl 数据中,您看到被引用的对象是__main__.Manager
,因为在创建 pickle 文件时“设置”模块是主要的(即,您将“设置”模块作为调用该addUser
函数的主脚本运行)。
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).
然后,您尝试在 'Gui' 中解压 - 以便该模块具有 name __main__
,并且您正在该模块中导入 Setting 。所以当然 Manager 类实际上是Settings.Manager
. 但是 pkl 文件不知道这一点,并在 中查找 Manager 类__main__
,并抛出一个 AttributeError 因为它不存在(Settings.Manager
确实存在,但__main__.Manager
不存在)。
Here's a minimal code set to demonstrate.
这是用于演示的最小代码集。
The class_def.py
module:
该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()
You run the above to generate the pickle data.
The main_module.py
module:
您运行上面的代码来生成pickle 数据。该main_module.py
模块:
import pickle
import class_def
if __name__=='__main__':
with open('test_data.pkl', 'rb') as f:
users = pickle.load(f)
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)
您运行上面的命令以尝试打开 pickle 文件,这会引发与您看到的大致相同的错误。(略有不同,但我猜那是因为我使用的是 Python 2.7)
The solution is either:
解决方案是:
- You make the class available within the namespace of the top-level module (i.e. GUI or main_module) through an explicit import, or
- 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, orclass_def.main
from main_module). This means that the pkl file will save the objects asSettings.Manager
orclass_def.Foo
, which can then be found in theGUI
`main_module` namespace.
- 您可以通过显式导入在顶级模块(即 GUI 或 main_module)的命名空间中使用该类,或者
- 您从与您将在其中打开它的模块相同的顶级模块创建pickle 文件(即
Settings.addUser
从GUI调用或class_def.main
从main_module 调用)。这意味着 pkl 文件会将对象保存为Settings.Manager
orclass_def.Foo
,然后可以在GUI
`main_module` 命名空间中找到它。
Option 1 example:
选项 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)
Option 2 example:
选项 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)
回答by Pankaj Saini
Please first read the answer mentioned by zehnpaardto know the reason for the attribute error. Other than the solution he already provided, in python3
you can use the pickle.Unpickler
class and override the find_class
method as mentioned below:
请先阅读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()
回答by Vinoj John Hosan
If you have a class defined outside the module, whose object is in pickle data, you have to import the class
如果在模块外定义了一个类,其对象在pickle数据中,则必须导入该类
from outside_module import DefinedClass1, DefinedClass2, DefinedClass3
with open('pickle_file.pkl', 'rb') as f:
pickle_data = pickle.load(f)
回答by Tristan Brown
If you're still getting this error even after importing the appropriate classes in the loading module (zehnpaard's solution #1), then the find_class
function of pickle.Unpickler
can be overwritten and explicitly directed to look in the current module's namespace.
如果你还在甚至加载模块中导入适当类(后收到此错误zehnpaard的解决方案#1),那么find_class
功能pickle.Unpickler
可以被覆盖,并且明确地指向看在当前模块的名称空间。
import pickle
from settings import Manager
class CustomUnpickler(pickle.Unpickler):
def find_class(self, module, name):
try:
return super().find_class(__name__, name)
except AttributeError:
return super().find_class(module, name)
pickle_data = CustomUnpickler(open('file_path.pkl', 'rb')).load()
## No exception trying to get 'Manager'
Note: This method loses the relative-import path information stored in module
. So, be careful of namespace collisions in your pickled classes.
注意:此方法会丢失存储在module
. 因此,请注意pickle 类中的命名空间冲突。