Python tuần tự hóa coroutine

Tuần tự hóa là quá trình chuyển đổi một đối tượng thành một phương tiện có thể được lưu và truy xuất sau này. e. g. , lưu trạng thái của một đối tượng vào một tệp. Đối với bất kỳ dự án phức tạp nhẹ nào, tuần tự hóa là điều mà tất cả các nhà phát triển phải làm sớm hay muộn. Một trong những điều hay về ngôn ngữ Python là nó dễ sử dụng trong nhiều tác vụ lập trình phổ biến. Tệp IO, sơ đồ vẽ và truy cập nội dung web đều dễ dàng chỉ với một vài dòng mã. Tuần tự hóa cũng dễ dàng trong Python, trừ khi bạn đang cố tuần tự hóa lớp tùy chỉnh của riêng mình. Trong bài viết này, tôi muốn chia sẻ những gì tôi thấy là phương pháp hay nhất trong việc tuần tự hóa các đối tượng lớp của riêng bạn thành các đối tượng JSON. Toàn bộ code mẫu được chia sẻ tại đây

1. Định nghĩa lớp

Hãy bắt đầu với định nghĩa của một lớp mẫu

class Label:
def __init__(self, label, x, y, width, height):
self.label = label
self.x = x
self.y = y
self.width = width
self.height = height

Nếu bạn muốn tuần tự hóa (e. g. , để in ra đối tượng), bạn sẽ nhận được một thông báo bí truyền như thế này

label = Label("person", 10, 10, 4, 10)
print(label)
>> <__main__.Label object at 0x000002C3913EB2E0>

Thư viện JSON của Python cung cấp một phương thức tiện dụng gọi là json. dumps() để biến bất kỳ đối tượng Python nào thành JSON. Điều này là tốt vì vậy bạn gọi phương thức và truyền đối tượng. Nghe có vẻ đơn giản nên hãy làm điều đó

import json

print(json.dumps(label))
>>...
/usr/lib/python3.7/json/encoder.py in default(self, o)
177
178 """
--> 179 raise TypeError(f'Object of type {o.__class__.__name__} '
180 f'is not JSON serializable')
181

TypeError: Object of type ImageLabel is not JSON serializable

Chuyện gì đã xảy ra thế? . dumps() gọi bộ mã hóa cho một đối tượng tùy chỉnh và khi phát sinh lỗi cho đối tượng lớp vì không có bộ mã hóa

2. Yêu cầu

Chúng tôi có thể triển khai bộ mã hóa cho lớp này, nhưng hãy tìm một giải pháp tổng quát hơn đáp ứng các yêu cầu sau

  1. Xuất ra một JSON thích hợp. Kết quả tuần tự hóa là các đối tượng JSON thích hợp
  2. print() chỉ hoạt động. print() trên đối tượng không yêu cầu bất kỳ xử lý đặc biệt nào
  3. json. bãi () hoạt động. json. dumps() là cách mặc định để biến đối tượng Python thành đối tượng JSON và nó hoạt động như mong đợi
  4. Chung và có thể mở rộng. Nó rất dễ sửa đối với một lớp cụ thể nhưng chúng tôi muốn giải pháp của mình hoạt động với tất cả các loại lớp và mối quan hệ

Với những yêu cầu này, hãy đi sâu vào và khám phá các tùy chọn

3. Các giải pháp

Phương pháp 1. sử dụng json. bãi () và __dict__

Cách đơn giản nhất để tuần tự hóa là sử dụng phương thức __dict__ tích hợp để hiển thị nội dung của đối tượng

label = Label("Brake", 0, 867, 328, 36)
print(label.__dict__)
print(json.dumps(label.__dict__))
>{"label": "person", "x": 10, "y": 10, "width": 4, "height": 10}
>{"label": "person", "x": 10, "y": 10, "width": 4, "height": 10}

Cả print() và json. dumps() hiển thị nội dung của đối tượng ở định dạng JSON

Phương pháp 2. Triển khai __str__ và __repr__

Tuy nhiên, phương pháp trên bị hạn chế vì bạn cần gọi __dict__ trên đối tượng mỗi lần. Để in đơn giản, bạn cần triển khai phương thức __str__ hoặc __repr__ trong2 lớp

Cả __str__ và __repr__ đều trả về biểu diễn chuỗi của đối tượng. Sự khác biệt là khán giả. __str__ được cho là con người có thể đọc được và __repr__ cho máy (e. g. , được xử lý bởi các chương trình khác). Đối với mục đích của chúng tôi, chúng giống nhau nên __repr__ chỉ chuyển tiếp cuộc gọi tới __str__

label = Label("person", 10, 10, 4, 10)
print(label)
# print(json.dumps(label))

Nhưng json. dumps() vẫn không hoạt động vì nó vẫn cần bộ mã hóa

Phương pháp 3. Triển khai Bộ mã hóa JSON

Để hỗ trợ json. kết xuất trường hợp sử dụng, một cách là triển khai lớp bộ mã hóa tùy chỉnh bằng cách phân lớp từ JSONEncoder. Trong trường hợp của chúng tôi, vì chúng tôi muốn đối tượng ở định dạng từ điển JSON, chúng tôi chỉ trả lại từ điển

Bộ mã hóa JSON

Đây là đầu ra

# outputs of a LabelSimple class object
# which does not have print method
{"label": "person", "x": 10, "y": 10, "width": 4, "height": 10} <__main__.LabelSimple object at 0x7f5e3d8dc2d0>
{"label": "person", "x": 10, "y": 10, "width": 4, "height": 10}
# outputs of a Label class object
{"label": "person", "x": 10, "y": 10, "width": 4, "height": 10} {"label": "person", "x": 10, "y": 10, "width": 4, "height": 10} {"label": "person", "x": 10, "y": 10, "width": 4, "height": 10}

Bạn có thể thấy rằng MyEncoder hoạt động ngay cả đối với LabelSimple không có phương thức __str__ hoặc __repr__

Ưu điểm của phương pháp này là bạn không phải triển khai các phương thức bổ sung như __str__ hoặc __repr__ và hỗ trợ nhiều loại đối tượng lớp khác nhau. Điểm bất lợi là bạn phải triển khai một lớp tùy chỉnh

4. Xử lý các đối tượng phức tạp

Tuy nhiên, các phương pháp trên không hoạt động nếu bạn cần xử lý các trường hợp phức tạp hơn. Đây là một đầu ra JSON mẫu mà chúng tôi muốn xem

Các đối tượng của lớp Nhãn cần được chứa trong một lớp khác. Hãy định nghĩa một lớp chứa có tên là 'ImageLabelCollection' và triển khai phương thức __str__ sẽ trả về đối tượng JSON của đối tượng bộ sưu tập. Lưu ý rằng chúng tôi đã sử dụng MyEncoder sẽ trả về đối tượng từ điển của đối tượng Nhãn

Đầu ra trông khá tốt

# output from the __str__ method
{"version": 1, "type": "bounding-box-labels", "boundingBoxes": {"image1.jpg": [{"label": "person", "x": 10, "y": 10, "width": 4, "height": 10}, {"label": "car", "x": 20, "y": 20, "width": 5, "height": 11}]}}
# output from json.dumps() method
{"version": 1, "type": "bounding-box-labels", "bboxes": {"image1.jpg": [{"label": "person", "x": 10, "y": 10, "width": 4, "height": 10}, {"label": "car", "x": 20, "y": 20, "width": 5, "height": 11}]}}

Tuy nhiên, nếu bạn để ý kỹ, key của tập các đối tượng Label khác nhau ở đầu ra của 2 phương thức. Trong trường hợp của __str__, khóa là “boundingBoxes” trong khi json. kết xuất trả về “bboxes” thay vào đó. Lý do là vì json. dumps() không gọi phương thức __str__ mà thay vào đó chỉ trả về từ điển của chính đối tượng đó. Vì vậy, thay vì gọi __str__ mà lần lượt gọi phương thức __iter__ để lấy khóa chính xác, “boundingBoxes”, nó chỉ cần lấy tên biến lớp “bboxes” làm khóa cho đối tượng bộ sưu tập

Phương pháp 4. Triển khai phương thức to_json()

Để tuần tự hóa chính xác một đối tượng để nó hoạt động ngay cả đối với json. dumps(), bạn cần viết phương thức to_json() tùy chỉnh. Bản năng đầu tiên là thay thế phương thức mặc định trong JSONEncoder bằng to_json() và yêu cầu nó gọi __str__

JSONEncoder với phương thức to_json() tùy chỉnh

Sau khi sửa đổi các lớp, chúng tôi đã thử kiểm tra với các kết hợp khác nhau, nhưng không có kết quả nào đáp ứng yêu cầu của chúng tôi. Tất cả chúng đều có vấn đề về dấu ngoặc kép hoặc dấu ba vì các đối tượng Nhãn chứa đã được tạo thành các đối tượng chuỗi trong json của riêng chúng. dumps() nên việc gọi lại chúng sẽ thêm dấu ngoặc kép vào các đối tượng chuỗi. arghhh. Chúng tôi đã làm cho vấn đề tồi tệ hơn

# __str__ output
{"version": 1, "type": "bounding-box-labels", "boundingBoxes": {"image1.jpg": ["{\"label\": \"person\", \"x\": 10, \"y\": 10, \"width\": 4, \"height\": 10}", "{\"label\": \"car\", \"x\": 20, \"y\": 20, \"width\": 5, \"height\": 11}"]}}
# calling to_json() directly
{"version": 1, "type": "bounding-box-labels", "boundingBoxes": {"image1.jpg": ["{\"label\": \"person\", \"x\": 10, \"y\": 10, \"width\": 4, \"height\": 10}", "{\"label\": \"car\", \"x\": 20, \"y\": 20, \"width\": 5, \"height\": 11}"]}}
# json.dumps() with JSONEncoder
"{\"version\": 1, \"type\": \"bounding-box-labels\", \"boundingBoxes\": {\"image1.jpg\": [\"{\\\"label\\\": \\\"person\\\", \\\"x\\\": 10, \\\"y\\\": 10, \\\"width\\\": 4, \\\"height\\\": 10}\", \"{\\\"label\\\": \\\"car\\\", \\\"x\\\": 20, \\\"y\\\": 20, \\\"width\\\": 5, \\\"height\\\": 11}\"]}}"
# json.dumps() on to_json() output
"{\"version\": 1, \"type\": \"bounding-box-labels\", \"boundingBoxes\": {\"image1.jpg\": [\"{\\\"label\\\": \\\"person\\\", \\\"x\\\": 10, \\\"y\\\": 10, \\\"width\\\": 4, \\\"height\\\": 10}\", \"{\\\"label\\\": \\\"car\\\", \\\"x\\\": 20, \\\"y\\\": 20, \\\"width\\\": 5, \\\"height\\\": 11}\"]}}"

Phương pháp 5. Triển khai phương thức to_json() tùy chỉnh

Để giải quyết vấn đề về dấu ngoặc kép, bạn cần triển khai phương thức to_json() tùy chỉnh trong lớp vùng chứa. Để làm cho việc triển khai và gọi to_json dễ dàng hơn, chúng tôi hiện đang xác định một phương thức toàn cầu có tên là 'mặc định'

phương thức to_json được tùy chỉnh trong lớp vùng chứa

Công việc hơi tẻ nhạt vì bạn phải tự mình xử lý tuần tự hóa từng đối tượng lớp chứa, nhưng đáng để nỗ lực xử lý tuần tự hóa đơn giản và thống nhất cho tất cả các loại đối tượng một lần và mãi mãi. Đầu ra là chính xác những gì chúng tôi muốn

# __str__ method is called:
{"version": 1, "type": "bounding-box-labels", "boundingBoxes": {"image1.jpg": [{"label": "person", "x": 10, "y": 10, "width": 4, "height": 10}, {"label": "car", "x": 20, "y": 20, "width": 5, "height": 11}]}}
# json.dump() method calls to_json directly
{"version": 1, "type": "bounding-box-labels", "boundingBoxes": {"image1.jpg": [{"label": "person", "x": 10, "y": 10, "width": 4, "height": 10}, {"label": "car", "x": 20, "y": 20, "width": 5, "height": 11}]}}
Kết luận

Việc tuần tự hóa sẽ dễ dàng trong Python, nhưng tôi thường vấp phải các vấn đề kỳ lạ với việc tuần tự hóa JSON và đó là lý do tôi viết bài này. Bạn có thể quyết định xem bạn có muốn __str__ hành xử giống như json không. dumps() hoặc bạn muốn nó trả về các đối tượng chứa trong dấu ngoặc kép. Bây giờ bạn có toàn quyền kiểm soát và kiến ​​thức về cách thực hiện. Mã nguồn hoàn chỉnh được chia sẻ tại đây

Để đóng vòng lặp, nếu bạn muốn biết cách giải tuần tự hóa chuỗi được tuần tự hóa trở lại đối tượng lớp ban đầu của nó, hãy làm theo các bước tại đây

Python có coroutine không?

Trong Python, các coroutine tương tự như các trình tạo nhưng có một vài phương thức bổ sung và những thay đổi nhỏ trong cách chúng tôi sử dụng các câu lệnh năng suất . Trình tạo tạo dữ liệu để lặp lại trong khi coroutines cũng có thể tiêu thụ dữ liệu. bất kỳ giá trị nào chúng tôi gửi tới coroutine đều được ghi lại và trả về bởi biểu thức (yield).

Làm cách nào để tuần tự hóa mảng trong Python?

Sử dụng cls kwarg của json. bãi () và json. dumps() để gọi Bộ mã hóa JSON tùy chỉnh của chúng tôi, sẽ chuyển đổi mảng NumPy thành dữ liệu có định dạng JSON. Để tuần tự hóa mảng Numpy thành JSON, chúng ta cần chuyển đổi nó thành cấu trúc danh sách bằng cách sử dụng hàm tolist() .

Làm cách nào để tuần tự hóa một đối tượng trong Python?

Sử dụng Thư viện Pickle của Python . test_dict = {"Xin chào". "Thế giới. "} Luồng byte đại diện cho test_dict hiện được lưu trữ trong tệp “ test. dưa muối”. use pickle's dump() method. test_dict = {"Hello": "World!"} The byte stream representing test_dict is now stored in the file “ test. pickle ”!

Làm cách nào để tuần tự hóa đối tượng thành JSON trong Python?

Mô-đun json hiển thị hai phương thức để tuần tự hóa các đối tượng Python thành định dạng JSON. dump() sẽ ghi dữ liệu Python vào một đối tượng dạng tệp . Chúng tôi sử dụng điều này khi chúng tôi muốn tuần tự hóa dữ liệu Python của mình thành tệp JSON bên ngoài. dumps() sẽ ghi dữ liệu Python vào một chuỗi ở định dạng JSON.