Lưu và tải các đối tượng và sử dụng dưa chua


114

Tôi đang cố gắng lưu và tải các đối tượng bằng picklemô-đun.
Đầu tiên tôi khai báo các đối tượng của mình:

>>> class Fruits:pass
...
>>> banana = Fruits()

>>> banana.color = 'yellow'
>>> banana.value = 30

Sau đó, tôi mở một tệp có tên là 'Fruits.obj' (trước đó tôi đã tạo một tệp .txt mới và tôi đã đổi tên thành 'Fruits.obj'):

>>> import pickle
>>> filehandler = open(b"Fruits.obj","wb")
>>> pickle.dump(banana,filehandler)

Sau khi thực hiện việc này, tôi đóng phiên của mình và bắt đầu một phiên mới và tôi đặt phiên tiếp theo (cố gắng truy cập vào đối tượng mà nó phải được lưu):

file = open("Fruits.obj",'r')
object_file = pickle.load(file)

Nhưng tôi có tin nhắn này:

Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "C:\Python31\lib\pickle.py", line 1365, in load
encoding=encoding, errors=errors).load()
ValueError: read() from the underlying stream did notreturn bytes

Tôi không biết phải làm gì vì tôi không hiểu thông điệp này. Có ai biết Làm cách nào để tải đối tượng 'chuối' của tôi không? Cảm ơn bạn!

CHỈNH SỬA: Như một số bạn đã yêu thích tôi đặt:

>>> import pickle
>>> file = open("Fruits.obj",'rb')

Không có vấn đề gì, nhưng điều tiếp theo tôi đặt là:

>>> object_file = pickle.load(file)

Và tôi có lỗi:

Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "C:\Python31\lib\pickle.py", line 1365, in load
encoding=encoding, errors=errors).load()
EOFError


Câu trả lời:


74

Đối với vấn đề thứ hai của bạn:

 Traceback (most recent call last):
 File "<stdin>", line 1, in <module>
 File "C:\Python31\lib\pickle.py", line
 1365, in load encoding=encoding,
 errors=errors).load() EOFError

Sau khi bạn đã đọc nội dung của tệp, con trỏ tệp sẽ ở cuối tệp - sẽ không có thêm dữ liệu nào để đọc. Bạn phải tua lại tệp để đọc lại từ đầu:

file.seek(0)

Tuy nhiên, những gì bạn thường muốn làm là sử dụng trình quản lý ngữ cảnh để mở tệp và đọc dữ liệu từ nó. Bằng cách này, tệp sẽ tự động được đóng sau khi khối kết thúc thực thi, điều này cũng giúp bạn tổ chức các hoạt động tệp của mình thành các phần có ý nghĩa.

Cuối cùng, cPickle là một triển khai nhanh hơn của mô-đun dưa chua trong C. Vì vậy:

In [1]: import cPickle

In [2]: d = {"a": 1, "b": 2}

In [4]: with open(r"someobject.pickle", "wb") as output_file:
   ...:     cPickle.dump(d, output_file)
   ...:

# pickle_file will be closed at this point, preventing your from accessing it any further

In [5]: with open(r"someobject.pickle", "rb") as input_file:
   ...:     e = cPickle.load(input_file)
   ...:

In [7]: print e
------> print(e)
{'a': 1, 'b': 2}

Loại cấu trúc dữ liệu này 'd = {"a": 1, "b": 2}' là gì?
Peterstone

1
@Peterstone: {"a": 1, "b": 2}tạo từ điển với các phím "a""b"trong đó. Đây được gọi là biểu thức hiển thị từ điển trong tài liệu trực tuyến. Đó chỉ là một trong những cách khác nhau mà một đối tượng kiểu dict, là một trong một số kiểu dữ liệu tích hợp sẵn tiêu chuẩn có sẵn trong Python, có thể được xây dựng.
martineau

2
Tại sao ký tự 'r' lại đặt tên tệp? Tôi không thấy điều đó trong tài liệu. Ngoài ra, nó gây khó khăn khi sử dụng một biến cho tên tệp.
SherylHohman

7
Nhìn vào câu trả lời này ngày hôm nay và nhận thấy nó chỉ áp dụng cho Python 2.x. Trong Python 3.x, người ta nên sử dụng trực tiếp picklenó sẽ cpickletự động nhập nếu nó có thể. docs.python.org/3.1/whatsnew/3.0.html#library-changes
Eskapp

41

Những điều sau đây phù hợp với tôi:

class Fruits: pass

banana = Fruits()

banana.color = 'yellow'
banana.value = 30

import pickle

filehandler = open("Fruits.obj","wb")
pickle.dump(banana,filehandler)
filehandler.close()

file = open("Fruits.obj",'rb')
object_file = pickle.load(file)
file.close()

print(object_file.color, object_file.value, sep=', ')
# yellow, 30

Điều này phù hợp với tôi, nhưng những gì tôi theo đuổi là đóng một phiên, mở một phiên mới và tải những gì tôi đã lưu trong một phiên đã qua. Tôi đóng phiên sau khi đặt dòng "filehandler.close ()" và tôi mở một dòng mới và đặt phần còn lại của mã của bạn, sau đó sau khi đặt "object_file = pickle.load (file)", tôi gặp lỗi này: Traceback ( cuộc gọi gần đây nhất cuối cùng): Tệp "<pyshell # 5>", dòng 1, trong <module> object_file = pickle.load (tệp) Tệp "C: \ Python31 \ lib \ pickle.py", dòng 1365, ở chế độ mã hóa tải = mã hóa, lỗi = lỗi) .load () AttributeError: 'module' đối tượng không có thuộc tính 'Trái cây'
Peterstone

3
@Peterstone: Trong phiên thứ hai, bạn sẽ cần có một định nghĩa class Fruitsđược xác định để pickle.load()có thể tái tạo đối tượng từ dữ liệu đã được lưu trong tệp nhị phân. Thực tiễn tốt nhất cho loại điều này là đặt class Fruitsđịnh nghĩa trong một tệp .py riêng biệt (biến nó thành một mô-đun tùy chỉnh) và sau importđó mô-đun đó hoặc các mục từ nó bất cứ khi nào cần (tức là cả hai phiên). Ví dụ: nếu bạn đặt nó trong một tệp có tên MyDataDefs.pythì bạn có thể viết from MyDataDefs import Fruits. Hãy cho tôi biết nếu điều này không rõ ràng và tôi sẽ cập nhật câu trả lời của mình cho phù hợp.
martineau

Trên thực tế, PEP 8 khuyến nghị sử dụng tất cả các ký tự viết thường cho tên mô-đun, vì vậy ví dụ ở cuối nhận xét cuối cùng của tôi nên nằm trong một tệp có tên my_data_defs.pybằng cách sử dụng from my_data_defs import Fruits.
martineau

24

Bạn cũng quên đọc nó dưới dạng nhị phân.

Trong phần viết của bạn, bạn có:

open(b"Fruits.obj","wb") # Note the wb part (Write Binary)

Trong phần đã đọc, bạn có:

file = open("Fruits.obj",'r') # Note the r part, there should be a b too

Vì vậy, hãy thay thế nó bằng:

file = open("Fruits.obj",'rb')

Và nó sẽ hoạt động :)


Đối với lỗi thứ hai của bạn, rất có thể nguyên nhân là do không đóng / đồng bộ hóa tệp đúng cách.

Hãy thử đoạn mã này để viết:

>>> import pickle
>>> filehandler = open(b"Fruits.obj","wb")
>>> pickle.dump(banana,filehandler)
>>> filehandler.close()

Và điều này (không thay đổi) để đọc:

>>> import pickle
>>> file = open("Fruits.obj",'rb')
>>> object_file = pickle.load(file)

Một phiên bản gọn gàng hơn sẽ sử dụng withcâu lệnh.

Để viết:

>>> import pickle
>>> with open('Fruits.obj', 'wb') as fp:
>>>     pickle.dump(banana, fp)

Để đọc:

>>> import pickle
>>> with open('Fruits.obj', 'rb') as fp:
>>>     banana = pickle.load(fp)

1
Tôi sử dụng phiên bản của bạn sử dụng câu lệnh with và tôi nhận được thông báo sau: Traceback (lần gọi gần đây nhất): Tệp "<pyshell # 20>", dòng 1, trong <module> print (banana.color) AttributeError: 'Fruits' đối tượng không có thuộc tính 'color'
Peterstone

17

Luôn mở ở chế độ nhị phân, trong trường hợp này

file = open("Fruits.obj",'rb')

6

Bạn đã không mở tệp ở chế độ nhị phân.

open("Fruits.obj",'rb')

Nên làm việc.

Đối với lỗi thứ hai của bạn, tệp rất có thể bị trống, có nghĩa là bạn đã vô tình làm trống nó hoặc sử dụng sai tên tệp hoặc một cái gì đó.

(Điều này giả sử bạn thực sự đã đóng phiên của mình. Nếu không, thì đó là do bạn đã không đóng tệp giữa quá trình ghi và đọc).

Tôi đã kiểm tra mã của bạn và nó hoạt động.


3

Có vẻ như bạn muốn lưu các phiên bản lớp của mình qua các phiên và sử dụng picklelà một cách hợp lý để thực hiện việc này. Tuy nhiên, có một gói được gọi là kleptotóm tắt việc lưu các đối tượng vào giao diện từ điển, vì vậy bạn có thể chọn chọn các đối tượng và lưu chúng vào một tệp (như được hiển thị bên dưới) hoặc chọn các đối tượng và lưu chúng vào cơ sở dữ liệu hoặc thay vì sử dụng dưa chua sử dụng json, hoặc nhiều tùy chọn khác. Điều thú vị kleptolà bằng cách trừu tượng hóa thành một giao diện chung, nó làm cho nó dễ dàng để bạn không cần phải nhớ các chi tiết cấp thấp về cách lưu thông qua chọn một tệp hoặc cách khác.

Lưu ý rằng Nó hoạt động cho các thuộc tính lớp được thêm động, điều mà pickle không thể làm được ...

dude@hilbert>$ python
Python 2.7.6 (default, Nov 12 2013, 13:26:39) 
[GCC 4.2.1 Compatible Apple Clang 4.1 ((tags/Apple/clang-421.11.66))] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> from klepto.archives import file_archive 
>>> db = file_archive('fruits.txt')
>>> class Fruits: pass
... 
>>> banana = Fruits()
>>> banana.color = 'yellow'
>>> banana.value = 30
>>> 
>>> db['banana'] = banana 
>>> db.dump()
>>> 

Sau đó, chúng tôi khởi động lại…

dude@hilbert>$ python
Python 2.7.6 (default, Nov 12 2013, 13:26:39) 
[GCC 4.2.1 Compatible Apple Clang 4.1 ((tags/Apple/clang-421.11.66))] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> from klepto.archives import file_archive
>>> db = file_archive('fruits.txt')
>>> db.load()
>>> 
>>> db['banana'].color
'yellow'
>>> 

Klepto hoạt động trên python2 và python3.

Nhận mã tại đây: https://github.com/uqfoundation


1

Bạn có thể sử dụng anycache để thực hiện công việc cho mình. Giả sử bạn có một hàm myfunctạo phiên bản:

from anycache import anycache

class Fruits:pass

@anycache(cachedir='/path/to/your/cache')    
def myfunc()
    banana = Fruits()
    banana.color = 'yellow'
    banana.value = 30
return banana

Anycache gọi myfunclần đầu tiên và chọn kết quả vào một tệp cachedirbằng cách sử dụng một mã định danh duy nhất (tùy thuộc vào tên hàm và các đối số) làm tên tệp. Trong bất kỳ lần chạy liên tiếp nào, đối tượng được ngâm sẽ được tải.

Nếu cachedirđược giữ nguyên giữa các lần chạy python, đối tượng ngâm được lấy từ lần chạy python trước đó.

Các đối số hàm cũng được tính đến. Triển khai đã cấu trúc lại hoạt động tương tự như vậy:

from anycache import anycache

class Fruits:pass

@anycache(cachedir='/path/to/your/cache')    
def myfunc(color, value)
    fruit = Fruits()
    fruit.color = color
    fruit.value = value
return fruit
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.