面向对象的Python - 对象序列化


在数据存储的上下文中,序列化是将数据结构或对象状态转换为可以存储(例如,在文件或内存缓冲区中)或稍后传输和重建的格式的过程。

在序列化过程中,对象被转换为可以存储的格式,以便以后能够对其进行反序列化并从序列化格式重新创建原始对象。

泡菜

Pickling 是将 Python 对象层次结构转换为字节流(通常不可读)并写入文件的过程,这也称为序列化。Unpickling 是相反的操作,即字节流被转换回工作的 Python 对象层次结构。

Pickle 是操作上最简单的存储对象的方法。Python Pickle模块是一种面向对象的方式,以特殊的存储格式直接存储对象。

它能做什么?

  • Pickle 可以非常轻松地存储和复制字典和列表。
  • 存储对象属性并将其恢复到相同的状态。

泡菜不能做什么?

  • 它不保存对象代码。只有它的属性值。
  • 它不能存储文件句柄或连接套接字。

简而言之,我们可以说,pickling 是一种在文件中存储和检索数据变量的方法,其中变量可以是列表、类等。

要腌制一些东西,你必须 -

  • 进口泡菜
  • 将变量写入文件,例如
pickle.dump(mystring, outfile, protocol),

其中第三个参数协议是可选的要取消某些内容,您必须 -

进口泡菜

将变量写入文件,例如

myString = pickle.load(inputfile)

方法

pickle 接口提供了四种不同的方法。

  • dump() - dump() 方法序列化到一个打开的文件(类似文件的对象)。

  • dumps() - 序列化为字符串

  • load() - 从类似开放的对象反序列化。

  • load() - 从字符串反序列化。

基于上述过程,下面是“酸洗”的示例。

酸洗

输出

My Cat pussy is White and has 4 legs
Would you like to see her pickled? Here she is!
b'\x80\x03c__main__\nCat\nq\x00)\x81q\x01}q\x02(X\x0e\x00\x00\x00number_of_legsq\x03K\x04X\x05\x00\x00\x00colorq\x04X\x05\x00\x00\x00Whiteq\x05ub.'

因此,在上面的示例中,我们创建了 Cat 类的实例,然后对其进行 pickle,将“Cat”实例转换为简单的字节数组。

通过这种方式,我们可以轻松地将字节数组存储在二进制文件或数据库字段中,并在稍后从我们的存储支持中将其恢复为原始形式。

另外,如果您想创建一个带有 pickled 对象的文件,您可以使用 dump() 方法(而不是 dumps*()* 方法)传递一个打开的二进制文件,pickling 结果将自动存储在文件中。

[….]
binary_file = open(my_pickled_Pussy.bin', mode='wb')
my_pickled_Pussy = pickle.dump(Pussy, binary_file)
binary_file.close()

脱酸

获取二进制数组并将其转换为对象层次结构的过程称为 unpickling。

unpickle 过程是通过使用 pickle 模块的 load() 函数完成的,并从简单的字节数组返回完整的对象层次结构。

让我们使用前面示例中的 load 函数。

拆开

输出

MeOw is black
Pussy is white

JSON

JSON(JavaScript Object Notation)已成为Python标准库的一部分,是一种轻量级的数据交换格式。人类很容易阅读和书写。它很容易解析和生成。

由于其简单性,JSON 是我们存储和交换数据的一种方式,这是通过 JSON 语法完成的,并在许多 Web 应用程序中使用。由于它是人类可读的格式,除了它在使用 API 时的有效性之外,这可能是在数据传输中使用它的原因之一。

JSON 格式数据的示例如下 -

{"EmployID": 40203, "Name": "Zack", "Age":54, "isEmployed": True}

Python 使 Json 文件的处理变得简单。用于此目的的模块是 JSON 模块。该模块应包含(内置)在您的 Python 安装中。

那么让我们看看如何将 Python 字典转换为 JSON 并将其写入文本文件。

JSON 到 Python

读取 JSON 意味着将 JSON 转换为 Python 值(对象)。json 库将 JSON 解析为 Python 中的字典或列表。为此,我们使用loads()函数(从字符串加载),如下所示 -

Json 到 Python

输出

Json 到 Python 输出

下面是一个示例 json 文件,

data1.json
{"menu": {
   "id": "file",
   "value": "File",
   "popup": {
      "menuitem": [
         {"value": "New", "onclick": "CreateNewDoc()"},
         {"value": "Open", "onclick": "OpenDoc()"},
         {"value": "Close", "onclick": "CloseDoc()"}
      ]
   }
}}

上面的内容(Data1.json)看起来像一个传统的字典。我们可以使用 pickle 来存储这个文件,但它的输出不是人类可读的形式。

JSON(Java 脚本对象通知)是一种非常简单的格式,这也是其流行的原因之一。现在让我们通过下面的程序查看 json 输出。

Java 脚本对象通知

输出

Java 脚本对象通知输出

上面我们打开 json 文件(data1.json)进行读取,获取文件处理程序并传递给 json.load 并返回对象。当我们尝试打印对象的输出时,它与 json 文件相同。虽然对象的类型是字典,但它是以Python对象的形式出现的。写入 json 很简单,就像我们看到这个 pickle 一样。上面我们加载了 json 文件,添加了另一个键值对并将其写回同一个 json 文件。现在,如果我们看到 data1.json,它看起来有所不同。即与我们之前看到的格式不同。

为了使我们的输出看起来相同(人类可读的格式),请将这对参数添加到程序的最后一行,

json.dump(conf, fh, indent = 4, separators = (‘,’, ‘: ‘))

与pickle类似,我们可以用dumps打印字符串,用loads打印字符串。下面是一个例子,

带转储的字符串

YAML

YAML 可能是所有编程语言中最人性化的数据序列化标准。

Python yaml模块称为pyaml

YAML 是 JSON 的替代方案 -

  • 人类可读的代码- YAML 是最人类可读的格式,甚至它的首页内容也以 YAML 显示来表明这一点。

  • 紧凑的代码- 在 YAML 中,我们使用空格缩进来表示结构而不是括号。

  • 关系数据的语法- 对于内部引用,我们使用锚点 (&) 和别名 (*)。

  • 它广泛使用的领域之一是查看/编辑数据结构- 例如配置文件、调试期间的转储和文档标题。

安装 YAML

由于yaml不是内置模块,因此我们需要手动安装。在 Windows 机器上安装 yaml 的最佳方法是通过 pip。在 Windows 终端上运行以下命令来安装 yaml,

pip install pyaml (Windows machine)
sudo pip install pyaml (*nix and Mac)

运行上述命令时,屏幕将根据当前最新版本显示如下内容。

Collecting pyaml
Using cached pyaml-17.12.1-py2.py3-none-any.whl
Collecting PyYAML (from pyaml)
Using cached PyYAML-3.12.tar.gz
Installing collected packages: PyYAML, pyaml
Running setup.py install for PyYAML ... done
Successfully installed PyYAML-3.12 pyaml-17.12.1

测试一下,进入Python shell,导入yaml模块,导入yaml,如果没有发现错误,就可以说安装成功了。

安装完pyaml后,我们看下面的代码,

script_yaml1.py
亚米尔

上面我们创建了三种不同的数据结构,字典、列表和元组。在每个结构上,我们执行 yaml.dump。重要的一点是输出如何在屏幕上显示。

输出

yaml 输出

字典输出看起来很干净。核心价值。

用于分隔不同对象的空白。

列表用破折号 (-) 表示

元组首先用 !!Python/tuple 表示,然后采用与列表相同的格式。

加载 yaml 文件

假设我有一个 yaml 文件,其中包含,

---
# An employee record
name: Raagvendra Joshi
job: Developer
skill: Oracle
employed: True
foods:
   - Apple
   - Orange
   - Strawberry
   - Mango
languages:
   Oracle: Elite
   power_builder: Elite
   Full Stack Developer: Lame
education:
   4 GCSEs
   3 A-Levels
   MCA in something called com

现在让我们编写一段代码,通过 yaml.load 函数加载这个 yaml 文件。下面是相同的代码。

Yaml 加载函数

由于输出看起来不太可读,所以我最后使用 json 来美化它。比较我们得到的输出和实际的 yaml 文件。

输出

yaml3

软件开发最重要的方面之一是调试。在本节中,我们将看到使用内置调试器或第三方调试器进行 Python 调试的不同方法。

PDB——Python 调试器

PDB模块支持设置断点。断点是程序的有意暂停,您可以在其中获取有关程序状态的更多信息。

要设置断点,请插入行

pdb.set_trace()

例子

pdb_example1.py
import pdb
x = 9
y = 7
pdb.set_trace()
total = x + y
pdb.set_trace()

我们在这个程序中插入了一些断点。程序将在每个断点处暂停(pdb.set_trace())。要查看变量内容,只需键入变量名称即可。

c:\Python\Python361>Python pdb_example1.py
> c:\Python\Python361\pdb_example1.py(8)<module>()
-> total = x + y
(Pdb) x
9
(Pdb) y
7
(Pdb) total
*** NameError: name 'total' is not defined
(Pdb)

按 c 或继续继续执行程序,直到下一个断点。

(Pdb) c
--Return--
> c:\Python\Python361\pdb_example1.py(8)<module>()->None
-> total = x + y
(Pdb) total
16

最终,您将需要调试更大的程序——使用子例程的程序。有时,您要查找的问题可能存在于子例程中。考虑以下程序。

import pdb
def squar(x, y):
   out_squared = x^2 + y^2
   return out_squared
if __name__ == "__main__":
   #pdb.set_trace()
   print (squar(4, 5))

现在运行上面的程序,

c:\Python\Python361>Python pdb_example2.py
> c:\Python\Python361\pdb_example2.py(10)<module>()
-> print (squar(4, 5))
(Pdb)

我们可以使用以获得帮助,但箭头指示即将执行的行。此时,按 s 到s可以进入该行。

(Pdb) s
--Call--
>c:\Python\Python361\pdb_example2.py(3)squar()
-> def squar(x, y):

这是对函数的调用。如果您想了解代码中的位置,请尝试 l -

(Pdb) l
1 import pdb
2
3 def squar(x, y):
4 -> out_squared = x^2 + y^2
5
6 return out_squared
7
8 if __name__ == "__main__":
9 pdb.set_trace()
10 print (squar(4, 5))
[EOF]
(Pdb)

您可以按 n 前进到下一行。此时,您位于 out_squared 方法内,并且可以访问函数内声明的变量,即 x 和 y。

(Pdb) x
4
(Pdb) y
5
(Pdb) x^2
6
(Pdb) y^2
7
(Pdb) x**2
16
(Pdb) y**2
25
(Pdb)

所以我们可以看到 ^ 运算符不是我们想要的,而是我们需要使用 ** 运算符来进行平方。

这样我们就可以在函数/方法中调试我们的程序。

记录

自 Python 2.3 版本以来,日志记录模块已成为 Python 标准库的一部分。由于它是一个内置模块,所有 Python 模块都可以参与日志记录,因此我们的应用程序日志可以包含您自己的消息以及来自第三方模块的消息。它提供了很多灵活性和功能。

日志记录的好处

  • 诊断日志记录- 它记录与应用程序操作相关的事件。

  • 审计日志记录- 它记录用于业务分析的事件。

消息以“严重性”&minu 级别写入和记录

  • DEBUG (debug()) - 用于开发的诊断消息。

  • INFO (info()) - 标准“进度”消息。

  • 警告 (warning()) - 检测到一个不严重的问题。

  • ERROR (error()) - 遇到错误,可能很严重。

  • CRITICAL (ritic()) - 通常是致命错误(程序停止)。

让我们看一下下面的简单程序,

import logging

logging.basicConfig(level=logging.INFO)

logging.debug('this message will be ignored') # This will not print
logging.info('This should be logged') # it'll print
logging.warning('And this, too') # It'll print

上面我们按照严重级别记录消息。首先我们导入模块,调用 basicConfig 并设置日志记录级别。我们上面设置的级别是INFO。然后我们有三个不同的语句:调试语句、信息语句和警告语句。

logging1.py 的输出

INFO:root:This should be logged
WARNING:root:And this, too

由于 info 语句位于 debug 语句下方,因此我们看不到调试消息。要在输出终端中也获取调试语句,我们需要更改的只是 basicConfig 级别。

logging.basicConfig(level = logging.DEBUG)

在输出中我们可以看到,

DEBUG:root:this message will be ignored
INFO:root:This should be logged
WARNING:root:And this, too

另外,默认Behave意味着如果我们不设置任何日志记录级别,则会发出警告。只需注释掉上面程序中的第二行并运行代码即可。

#logging.basicConfig(level = logging.DEBUG)

输出

WARNING:root:And this, too

Python内置的日志级别实际上是整数。

>>> import logging
>>>
>>> logging.DEBUG
10
>>> logging.CRITICAL
50
>>> logging.WARNING
30
>>> logging.INFO
20
>>> logging.ERROR
40
>>>

我们还可以将日志消息保存到文件中。

logging.basicConfig(level = logging.DEBUG, filename = 'logging.log')

现在,所有日志消息都将进入当前工作目录中的文件 (logging.log),而不是屏幕。这是一种更好的方法,因为它允许我们对收到的消息进行事后分析。

我们还可以使用日志消息设置日期戳。

logging.basicConfig(level=logging.DEBUG, format = '%(asctime)s %(levelname)s:%(message)s')

输出会得到类似的东西,

2018-03-08 19:30:00,066 DEBUG:this message will be ignored
2018-03-08 19:30:00,176 INFO:This should be logged
2018-03-08 19:30:00,201 WARNING:And this, too

标杆管理

基准测试或分析基本上是为了测试代码执行的速度以及瓶颈在哪里?这样做的主要原因是为了优化。

计时

Python 带有一个名为 timeit 的内置模块。您可以使用它来计时小代码片段。timeit 模块使用特定于平台的时间函数,以便您获得尽可能准确的计时。

因此,它允许我们比较每个代码的两次交付,然后优化脚本以获得更好的性能。

timeit 模块有一个命令行界面,但也可以导入。

调用脚本有两种方法。让我们首先使用该脚本,运行以下代码并查看输出。

import timeit
print ( 'by index: ', timeit.timeit(stmt = "mydict['c']", setup = "mydict = {'a':5, 'b':10, 'c':15}", number = 1000000))
print ( 'by get: ', timeit.timeit(stmt = 'mydict.get("c")', setup = 'mydict = {"a":5, "b":10, "c":15}', number = 1000000))

输出

by index: 0.1809192126703489
by get: 0.6088525265034692

上面我们使用了两种不同的方法,即通过下标和get来访问字典键值。我们执行语句 100 万次,因为它对于非常小的数据执行得太快。现在我们可以看到索引访问比 get 快得多。我们可以多次运行代码,执行时间会略有不同,以便更好地理解。

另一种方法是在命令行中运行上述测试。我们开始做吧,

c:\Python\Python361>Python -m timeit -n 1000000 -s "mydict = {'a': 5, 'b':10, 'c':15}" "mydict['c']"
1000000 loops, best of 3: 0.187 usec per loop

c:\Python\Python361>Python -m timeit -n 1000000 -s "mydict = {'a': 5, 'b':10, 'c':15}" "mydict.get('c')"
1000000 loops, best of 3: 0.659 usec per loop

以上输出可能会根据您的系统硬件以及系统中当前运行的所有应用程序而有所不同。

如果我们想调用一个函数,下面我们可以使用 timeit 模块。因为我们可以在函数内添加多个语句来测试。

import timeit

def testme(this_dict, key):
   return this_dict[key]

print (timeit.timeit("testme(mydict, key)", setup = "from __main__ import testme; mydict = {'a':9, 'b':18, 'c':27}; key = 'c'", number = 1000000))

输出

0.7713474590139164