Невозможно загрузить файлы с помощью 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, когда я загружаю только файл настроек и проверяю логин, все работает нормально, но когда я открываю модуль графического интерфейса пользователя и пытаюсь убедиться, что он не работает позвольте мне, ошибку я получаю:

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

5 ответов

Решение

Проблема в том, что вы выбираете объекты, определенные в настройках, фактически запустив модуль "Настройки", а затем пытаетесь отобрать объекты из GUI модуль.

Помните, что pickle на самом деле не хранит информацию о том, как создается класс / объект, и ему необходим доступ к классу при расщеплении. Смотрите вики по использованию Pickle для более подробной информации.

В данных pkl вы видите, что ссылка на объект __main__.Manager, так как модуль "Настройки" был основным при создании файла pickle (т.е. вы запустили модуль "Настройки" в качестве основного сценария для вызова addUser функция).

Затем вы пытаетесь открепить в "Gui" - так что модуль имеет имя __main__и вы импортируете Setting в этот модуль. Так что, конечно, класс менеджера будет на самом деле Settings.Manager, Но файл pkl этого не знает и ищет класс Manager внутри __main__и выдает AttributeError, потому что он не существует (Settings.Manager делает, но __main__.Manager не делает).

Вот минимальный набор кода для демонстрации.

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)

Решение либо:

  1. Вы делаете класс доступным в пространстве имен модуля верхнего уровня (т. Е. GUI или main_module) через явный импорт, или
  2. Вы создаете файл pickle из того же модуля верхнего уровня, что и тот, в котором вы его открываете (т.е. вызываете Settings.addUser из графического интерфейса или class_def.main из основного_модуля). Это означает, что файл 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'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)

Пожалуйста, сначала прочитайте ответ, упомянутый 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()

Если вы все еще получаете эту ошибку даже после импорта соответствующих классов в модуль загрузки ( решение 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'

Примечание. Этот метод теряет информацию об относительном пути импорта, хранящуюся в module. Итак, будьте осторожны с конфликтами пространств имен в ваших маринованных классах.

Если у вас есть класс, определенный вне модуля, чей объект находится в данных pickle, вы должны импортировать класс

from outside_module import DefinedClass1, DefinedClass2, DefinedClass3 

with open('pickle_file.pkl', 'rb') as f:
    pickle_data = pickle.load(f)

если вы используете модель дампа/загрузки укропа, будет работать

      import dill
from sklearn.preprocessing import FunctionTransformer

sp_clf = FunctionTransformer(lambda X:X.astype('float').fillna(0).applymap(abs))

with open('temp.joblib','wb') as io:
    dill.dump(sp_clf,io)

with open('temp.joblib','rb') as io:
    dd=dill.load(io)
Другие вопросы по тегам