Object Oriented Python 简明教程

Object Oriented Python - Object Serialization

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

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

Pickle

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

Pickle 是存储对象的运维最简单的方式。Python Pickle 模块是一种面向对象的方式,可以将对象直接存储在特殊的存储格式中。

What can it do?

  1. Pickle 可以非常容易地存储和复制字典以及列表。

  2. 存储对象属性并将其还原到相同状态。

What pickle can’t do?

  1. 它不保存对象的代码。只有它的属性值。

  2. 它不能存储文件句柄或连接套接字。

简言之,我们可以说,腌制是一种将数据变量存储到文件中并从中检索数据变量的方法,其中变量可以是列表、类等。

要腌制某些内容,您必须:

  1. import pickle

  2. 将变量写入文件,类似于

pickle.dump(mystring, outfile, protocol),

第三个参数协议是可选的。要解腌制某些内容,您必须:

导入pickle

将变量写入文件,类似于

myString = pickle.load(inputfile)

Methods

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

  1. dump() - dump()方法对此类对象序列号:open file(类似文件的对象)。

  2. dumps() - 序列号为字符型

  3. load() - 从类似open的对象解序列号。

  4. loads() - 从字符型解序列号。

基于以上程序,以下是“腌制”的一个示例。

pickling

Output

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类的实例,然后我们将其腌制,将我们的“Cat”实例转换为简单的字节数组。

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

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

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

Unpickling

将二进制数组转换为对象层次的过程称为解腌制。

解腌制过程是通过使用pickle模块的load()函数完成的,并从简单的字节数组中返回一个完整对象层次。

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

unpicking

Output

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 to Python

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

json to python

Output

json to python output

下面是示例 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(JavaScript 对象通知)是一种非常简单的格式,这也是它流行的原因之一。现在让我们通过下面的程序来了解 json 输出。

java script object notification

Output

java script object notification output

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

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

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

与 pickle 类似,我们可以使用 dumps 打印字符串,并使用 loads 加载。以下是示例:

string with dumps

YAML

YAML 可能是所有人编程语言中最接近于人类的 data 序列化标准。

Python yaml 模块称为 pyaml

YAML 是 JSON 的替代品:

  1. Human readable code - YAML 是最接近人类可读格式的数据,甚至它的主页内容也以 YAML 显示以便强调这一点。

  2. Compact code - 在 YAML 中,我们使用空格缩进表示结构,而不是括号。

  3. Syntax for relational data - 对于内部引用,我们使用锚 (&) 和别名 (*)。

  4. One of the area where it is used widely is for viewing/editing of data structures - 例如配置文件,在调试和文档标题期间转储。

Installing YAML

由于 yaml 不是内置模块,因此我们需手动安装它。在 Windows 计算机上安装 yaml 的最佳方法是通过 pip。在 Windows terminal 上运行以下命令来安装 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 模块,如果未找到错误,那么我们可以说,安装成功了。

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

script_yaml1.py
yaml

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

Output

yaml output

字典输出看起来很干净,即 key:value。

用空白分隔不同的对象。

列表用破折号 (-) 表示。

元组首先用 !!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 load function

由于输出看起来不是很好读,我在最后使用 json 对它进行了美化。比较我们获得的输出和我们拥有的实际 yaml 文件。

Output

yaml load function output

软件开发生命中最重要的一方面就是调试。在本节中,我们将了解通过内置调试器或第三方调试器进行 Python 调试的不同方式。

PDB – The Python Debugger

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

要设置断点,请插入以下行

pdb.set_trace()

Example

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)

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

通过这种方式,我们可以在函数/方法内部调试我们的程序。

Logging

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

Benefits of Logging

  1. Diagnostic logging − 它记录与应用程序操作相关的事件。

  2. Audit logging − 它记录用于业务分析的事件。

消息以“严重性”级别进行编写和记录。

  1. DEBUG (debug()) − 用于开发的诊断消息。

  2. INFO (info()) − 标准的“进度”消息。

  3. WARNING (warning()) − 检测到一个不严重的问题。

  4. ERROR (error()) − 遇到一个错误,可能很严重。

  5. CRITICAL (critical()) − 通常是一个致命错误(程序停止)。

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

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。然后,我们有三个不同的语句:debug 语句、info 语句和 warning 语句。

Output of logging1.py

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

由于 info 语句在 debug 语句之后,我们无法看到 debug 消息。要也在输出终端中获取 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

另外,默认行为表示,如果我们未设置任何日志记录级别,则为 warning。只需注释掉上述程序的第二行并运行该代码即可。

#logging.basicConfig(level = logging.DEBUG)

Output

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

Benchmarking

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

timeit

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

Output

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

Output

0.7713474590139164