Python  序列化反序列化和异常处理的问题小结


Posted in Python onDecember 24, 2022

1.迭代器

迭代是访问集合元素的一种方式。迭代器是一个可以记住遍历的位置的对象。迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束。迭代器只能往前不会后退。

1.1 可迭代对象

我们已经知道可以对list、tuple、str等类型的数据使用for...in...的循环语法从其中依次拿到数据进行使用,我们把这样的过程称为遍历,也叫迭代

但是,是否所有的数据类型都可以放到for...in...的语句中,然后让for...in...每次从中取出一条数据供我们使用,即供我们迭代吗?

>>> for i in 100:
 ...     print(i)
 ...
 Traceback (most recent call last):
   File "<stdin>", line 1, in <module>
 TypeError: 'int' object is not iterable
 >>>
# int整型不是iterable,即int整型不是可以迭代的

我们把可以通过for...in...这类语句迭代读取一条数据供我们使用的对象称之为可迭代对象(Iterable)。

1.2 如何判断一个对象是否可以迭代

可以使用 isinstance() 判断一个对象是否是 Iterable 对象:

In [50]: from collections import Iterable
 
 In [51]: isinstance([], Iterable)
 Out[51]: True
 
 In [52]: isinstance({}, Iterable)
 Out[52]: True
 
 In [53]: isinstance('abc', Iterable)
 Out[53]: True
 
 In [54]: isinstance(mylist, Iterable)
 Out[54]: False
 
 In [55]: isinstance(100, Iterable)
 Out[55]: False

1.3 可迭代对象的本质

我们分析对可迭代对象进行迭代使用的过程,发现每迭代一次(即在for...in...中每循环一次)都会返回对象中的下一条数据,一直向后读取数据直到迭代了所有数据后结束。那么,在这个过程中就应该有一个“人”去记录每次访问到了第几条数据,以便每次迭代都可以返回下一条数据。我们把这个能帮助我们进行数据迭代的“人”称为迭代器(Iterator)

可迭代对象的本质就是可以向我们提供一个这样的中间“人”即迭代器帮助我们对其进行迭代遍历使用。

可迭代对象通过__iter__方法向我们提供一个迭代器,我们在迭代一个可迭代对象的时候,实际上就是先获取该对象提供的一个迭代器,然后通过这个迭代器来依次获取对象中的每一个数据.

那么也就是说,一个具备了__iter__方法的对象,就是一个可迭代对象。

from collections.abc import Iterable
 class Demo(object):
     def __init__(self, n):
         self.n = n
         self.current = 0
     def __iter__(self):
         pass
 
 demo = Demo(10)
 print(isinstance(demo, Iterable))  # True
 
 for d in demo:   # 重写了 __iter__ 方法以后,demo就是一个一个可迭代对象了,可以放在for...in的后面
     print(d)
 
 # 此时再使用for...in循环遍历,会提示 TypeError: iter() returned non-iterator of type 'NoneType'
 # 这是因为,一个可迭代对象如果想要被for...in循环,它必须要有一个迭代器

1.4 迭代器Iterator

通过上面的分析,我们已经知道,迭代器是用来帮助我们记录每次迭代访问到的位置,当我们对迭代器使用next()函数的时候,迭代器会向我们返回它所记录位置的下一个位置的数据。实际上,在使用next()函数的时候,调用的就是迭代器对象的__next__方法(Python3中是对象的__next__方法,Python2中是对象的next()方法)。所以,我们要想构造一个迭代器,就要实现它的*next*方法。但这还不够,python要求迭代器本身也是可迭代的,所以我们还要为迭代器实现__iter__方法,而__iter__方法要返回一个迭代器,迭代器自身正是一个迭代器,所以迭代器的__iter__方法返回自身即可。

一个实现了*iter*方法和*next*方法的对象,就是迭代器。

class MyIterator(object):
     def __init__(self, n):
         self.n = n
         self.current = 0
 
     # 自定义迭代器需要重写__iter__和__next__方法
     def __iter__(self):
         return self
 
     def __next__(self):
         if self.current < self.n:
             value = self.current
             self.current += 1
             return value
         else:
             raise StopIteration
 
 my_it = MyIterator(10)
 
 for i in my_it:    # 迭代器重写了__iter__方法,它本身也是一个可迭代对象
     print(i)

1.5 如何判断一个对象是否迭代器

调用一个对象的__iter__方法,或者调用iter()内置函数,可以获取到一个可迭代对象的迭代器。

names = ['hello', 'good', 'yes']
 print(names.__iter__())  # 调用对象的__iter__()方法
 print(iter(names))  # 调用iter()内置函数

可以使用 isinstance() 判断一个对象是否是 Iterator 对象:

from collections.abc import Iterator
 names = ['hello', 'good', 'yes']
 print(isinstance(iter(names), Iterator))

1.6 for...in...循环的本质

for item in Iterable 循环的本质就是先通过iter()函数获取可迭代对象Iterable的迭代器,然后对获取到的迭代器不断调用next()方法来获取下一个值并将其赋值给item,当遇到StopIteration的异常后循环结束。

1.7 迭代器的应用场景

我们发现迭代器最核心的功能就是可以通过next()函数的调用来返回下一个数据值。如果每次返回的数据值不是在一个已有的数据集合中读取的,而是通过程序按照一定的规律计算生成的,那么也就意味着可以不用再依赖一个已有的数据集合,也就是说不用再将所有要迭代的数据都一次性缓存下来供后续依次读取,这样可以节省大量的存储(内存)空间。

举个例子,比如,数学中有个著名的斐波拉契数列(Fibonacci),数列中第一个数为0,第二个数为1,其后的每一个数都可由前两个数相加得到:

0, 1, 1, 2, 3, 5, 8, 13, 21, 34, ...

现在我们想要通过for...in...循环来遍历迭代斐波那契数列中的前n个数。那么这个斐波那契数列我们就可以用迭代器来实现,每次迭代都通过数学计算来生成下一个数。

class FibIterator(object):
     """斐波那契数列迭代器"""
     def __init__(self, n):
         """
        :param n: int, 指明生成数列的前n个数
        """
         self.n = n
         # current用来保存当前生成到数列中的第几个数了
         self.current = 0
         # num1用来保存前前一个数,初始值为数列中的第一个数0
         self.num1 = 0
         # num2用来保存前一个数,初始值为数列中的第二个数1
         self.num2 = 1
 
     def __next__(self):
         """被next()函数调用来获取下一个数"""
         if self.current < self.n:
             num = self.num1
             self.num1, self.num2 = self.num2, self.num1+self.num2
             self.current += 1
             return num
         else:
             raise StopIteration
 
     def __iter__(self):
         """迭代器的__iter__返回自身即可"""
         return self
 
 
 if __name__ == '__main__':
     fib = FibIterator(10)
     for num in fib:
         print(num, end=" ")

2.生成器

利用迭代器,我们可以在每次迭代获取数据(通过next()方法)时按照特定的规律进行生成。但是我们在实现一个迭代器时,关于当前迭代到的状态需要我们自己记录,进而才能根据当前状态生成下一个数据。为了达到记录当前状态,并配合next()函数进行迭代使用,我们可以采用更简便的语法,即生成器(generator)。生成器是一类特殊的迭代器

2.1 创建生成器方法1

要创建一个生成器,有很多种方法。第一种方法很简单,只要把一个列表生成式的 [ ] 改成 ( )

In [15]: L = [ x*2 for x in range(5)]
 
 In [16]: L
 Out[16]: [0, 2, 4, 6, 8]
 
 In [17]: G = ( x*2 for x in range(5))
 
 In [18]: G
 Out[18]: <generator object <genexpr> at 0x7f626c132db0>
 
 In [19]:

创建 L 和 G 的区别仅在于最外层的 [ ] 和 ( ) , L 是一个列表,而 G 是一个生成器。我们可以直接打印出列表L的每一个元素,而对于生成器G,我们可以按照迭代器的使用方法来使用,即可以通过next()函数、for循环、list()等方法使用。

In [19]: next(G)
Out[19]: 0
 
In [20]: next(G)
Out[20]: 2
 
In [21]: next(G)
Out[21]: 4
 
In [22]: next(G)
Out[22]: 6
 
In [23]: next(G)
Out[23]: 8
 
In [24]: next(G)
---------------------------------------------------------------------------
StopIteration                             Traceback (most recent call last)
<ipython-input-24-380e167d6934> in <module>()
----> 1 next(G)
 
StopIteration:
 
In [25]:
In [26]: G = ( x*2 for x in range(5))
 
In [27]: for x in G:
   ....:     print(x)
   ....:     
0
2
4
6
8
 
In [28]:

2.2 创建生成器方法2

generator非常强大。如果推算的算法比较复杂,用类似列表生成式的 for 循环无法实现的时候,还可以用函数来实现。

我们仍然用上一节提到的斐波那契数列来举例,回想我们在上一节用迭代器的实现方式:

class FibIterator(object):
    """斐波那契数列迭代器"""
    def __init__(self, n):
        """
        :param n: int, 指明生成数列的前n个数
        """
        self.n = n
        # current用来保存当前生成到数列中的第几个数了
        self.current = 0
        # num1用来保存前前一个数,初始值为数列中的第一个数0
        self.num1 = 0
        # num2用来保存前一个数,初始值为数列中的第二个数1
        self.num2 = 1
 
    def __next__(self):
        """被next()函数调用来获取下一个数"""
        if self.current < self.n:
            num = self.num1
            self.num1, self.num2 = self.num2, self.num1+self.num2
            self.current += 1
            return num
        else:
            raise StopIteration
 
    def __iter__(self):
        """迭代器的__iter__返回自身即可"""
        return self

注意,在用迭代器实现的方式中,我们要借助几个变量(n、current、num1、num2)来保存迭代的状态。现在我们用生成器来实现一下。

In [30]: def fib(n):
   ....:     current = 0
   ....:     num1, num2 = 0, 1
   ....:     while current < n:
   ....:         yield num1
   ....:         num1, num2 = num2, num1+num2
   ....:         current += 1
   ....:     return 'done'
   ....:
 
In [31]: F = fib(5)
 
In [32]: next(F)
Out[32]: 1
 
In [33]: next(F)
Out[33]: 1
 
In [34]: next(F)
Out[34]: 2
 
In [35]: next(F)
Out[35]: 3
 
In [36]: next(F)
Out[36]: 5
 
In [37]: next(F)
---------------------------------------------------------------------------
StopIteration                             Traceback (most recent call last)
<ipython-input-37-8c2b02b4361a> in <module>()
----> 1 next(F)
 
StopIteration: done

在使用生成器实现的方式中,我们将原本在迭代器__next__方法中实现的基本逻辑放到一个函数中来实现,但是将每次迭代返回数值的return换成了yield,此时新定义的函数便不再是函数,而是一个生成器了。简单来说:只要在def中有yield关键字的 就称为 生成器

此时按照调用函数的方式( 案例中为F = fib(5) )使用生成器就不再是执行函数体了,而是会返回一个生成器对象( 案例中为F ),然后就可以按照使用迭代器的方式来使用生成器了。

In [38]: for n in fib(5):
   ....:     print(n)
   ....:     
1
1
2
3
5
 
In [39]:

但是用for循环调用generator时,发现拿不到generator的return语句的返回值。如果想要拿到返回值,必须捕获StopIteration错误,返回值包含在StopIteration的value中:

In [39]: g = fib(5)
 
In [40]: while True:
   ....:     try:
   ....:         x = next(g)
   ....:         print("value:%d"%x)      
   ....:     except StopIteration as e:
   ....:         print("生成器返回值:%s"%e.value)
   ....:         break
   ....:     
value:1
value:1
value:2
value:3
value:5
生成器返回值:done
 
In [41]:

总结:

  • 使用了yield关键字的函数不再是函数,而是生成器。(使用了yield的函数就是生成器)
  • yield关键字有两点作用:
  • 保存当前运行状态(断点),然后暂停执行,即将生成器(函数)挂起
  • 将yield关键字后面表达式的值作为返回值返回,此时可以理解为起到了return的作用
  • 可以使用next()函数让生成器从断点处继续执行,即唤醒生成器(函数)
  • Python3中的生成器可以使用return返回最终运行的返回值,而Python2中的生成器不允许使用return返回一个返回值(即可以使用return从生成器中退出,但return后不能有任何表达式)。

2.3 使用send唤醒

我们除了可以使用next()函数来唤醒生成器继续执行外,还可以使用send()函数来唤醒执行。使用send()函数的一个好处是可以在唤醒的同时向断点处传入一个附加数据。

例子:执行到yield时,gen函数作用暂时保存,返回i的值; temp接收下次c.send("python"),send发送过来的值,c.next()等价c.send(None)

In [10]: def gen():
   ....:     i = 0
   ....:     while i<5:
   ....:         temp = yield i
   ....:         print(temp)
   ....:         i+=1
   ....:

使用send

In [43]: f = gen()
 
In [44]: next(f)
Out[44]: 0
 
In [45]: f.send('haha')
haha
Out[45]: 1
 
In [46]: next(f)
None
Out[46]: 2
 
In [47]: f.send('haha')
haha
Out[47]: 3
 
In [48]:

使用next函数

In [18]: f = gen()
 
In [19]: f.__next__()
Out[19]: 0
 
In [20]: f.__next__()
None
Out[20]: 1
 
In [21]: f.__next__()
None
Out[21]: 2
 
In [22]: f.__next__()
None
Out[22]: 3
 
In [23]: f.__next__()
None
Out[23]: 4
 
In [24]: f.__next__()
None
---------------------------------------------------------------------------
StopIteration                             Traceback (most recent call last)
<ipython-input-24-39ec527346a9> in <module>()
----> 1 f.__next__()
 
StopIteration:

使用__next__()方法(不常使用)

In [18]: f = gen()
 
In [19]: f.__next__()
Out[19]: 0
 
In [20]: f.__next__()
None
Out[20]: 1
 
In [21]: f.__next__()
None
Out[21]: 2
 
In [22]: f.__next__()
None
Out[22]: 3
 
In [23]: f.__next__()
None
Out[23]: 4
 
In [24]: f.__next__()
None
---------------------------------------------------------------------------
StopIteration                             Traceback (most recent call last)
<ipython-input-24-39ec527346a9> in <module>()
----> 1 f.__next__()
 
StopIteration:

3.property属性的使用

property属性是一种用起来像是实例属性一样的特殊属性,可以对应于某个方法。

class Foo:
    def func(self):
        pass
 
    # 定义property属性
    @property
    def prop(self):
        pass
 
# ############### 调用 ###############
foo_obj = Foo()
foo_obj.func()  # 调用实例方法
foo_obj.prop  # 调用property属性

property属性的定义和调用要注意一下几点:

  • 定义时,在实例方法的基础上添加 @property 装饰器;并且仅有一个self参数
  • 调用时,无需括号
方法:foo_obj.func()
  property属性:foo_obj.prop

简单的实例

对于京东商城中显示电脑主机的列表页面,每次请求不可能把数据库中的所有内容都显示到页面上,而是通过分页的功能局部显示,所以在向数据库中请求数据时就要显示的指定获取从第m条到第n条的所有数据 这个分页的功能包括:

  • 根据用户请求的当前页和总数据条数计算出 m 和 n
  • 根据m 和 n 去数据库中请求数据
# ############### 定义 ###############
class Pager:
    def __init__(self, current_page):
        # 用户当前请求的页码(第一页、第二页...)
        self.current_page = current_page
        # 每页默认显示10条数据
        self.per_items = 10 
 
    @property
    def start(self):
        val = (self.current_page - 1) * self.per_items
        return val
 
    @property
    def end(self):
        val = self.current_page * self.per_items
        return val
 
# ############### 调用 ###############
p = Pager(1)
p.start  # 就是起始值,即:m
p.end  # 就是结束值,即:n

从上述可见

Python的property属性的功能是:property属性内部进行一系列的逻辑计算,最终将计算结果返回。

3.1 property属性的两种方式

  • 装饰器 即:在方法上应用装饰器
  • 类属性 即:在类中定义值为property对象的类属性

3.1.1 装饰器

  • 在类的实例方法上应用@property装饰器

Python中的类有经典类和新式类,新式类的属性比经典类的属性丰富。( 如果类继object,那么该类是新式类 )

经典类的实现:

class Goods:
    @property
    def price(self):
        return "laowang"
 
obj = Goods()
result = obj.price  # 自动执行 @property 修饰的 price 方法,并获取方法的返回值
print(result)

新式类的实现:

class Goods:
    """
    只有在python3中才有@xxx.setter  @xxx.deleter
    """
    def __init__(self):
        # 原价
        self.original_price = 100
        # 折扣
        self.discount = 0.8
 
    @property
    def price(self):
        new_price = self.original_price * self.discount
        return new_price
 
    @price.setter
    def price(self, value):
        self.original_price = value
 
    @price.deleter
    def price(self):
        del self.original_price
obj = Goods()
obj.price          # 获取商品价格
obj.price = 200    # 修改商品原价
del obj.price      # 删除商品原价

总结:

  • 经典类中的属性只有一种访问方式,其对应被 @property 修饰的方法
  • 新式类中的属性有三种访问方式,并分别对应了三个被@property、@方法名.setter、@方法名.deleter修饰的方法

3.1.2 类属性方式

当使用类属性的方式创建property属性时,经典类和新式类无区别。

class Foo:
    def get_bar(self):
        return 'laowang'
    BAR = property(get_bar)
 
obj = Foo()
reuslt = obj.BAR  # 自动调用get_bar方法,并获取方法的返回值
print(reuslt)

property方法中有个四个参数

  • 第一个参数是方法名,调用 对象.属性 时自动触发执行方法
  • 第二个参数是方法名,调用 对象.属性 = XXX 时自动触发执行方法
  • 第三个参数是方法名,调用 del 对象.属性 时自动触发执行方法
  • 第四个参数是字符串,调用 对象.属性.doc ,此参数是该属性的描述信息
class Foo(object):
    def get_bar(self):
        print("getter...")
        return 'laowang'
 
    def set_bar(self, value): 
        """必须两个参数"""
        print("setter...")
        return 'set value' + value
 
    def del_bar(self):
        print("deleter...")
        return 'laowang'
 
    BAR = property(get_bar, set_bar, del_bar, "description...")
 
obj = Foo()
 
obj.BAR  # 自动调用第一个参数中定义的方法:get_bar
obj.BAR = "alex"  # 自动调用第二个参数中定义的方法:set_bar方法,并将“alex”当作参数传入
desc = Foo.BAR.__doc__  # 自动获取第四个参数中设置的值:description...
print(desc)
del obj.BAR  # 自动调用第三个参数中定义的方法:del_bar方法

总结:

  • 定义property属性共有两种方式,分别是【装饰器】和【类属性】,而【装饰器】方式针对经典类和新式类又有所不同。
  • 通过使用property属性,能够简化调用者在获取数据的流程。

到此这篇关于Python  序列化反序列化和异常处理的文章就介绍到这了,更多相关Python  序列化反序列化内容请搜索三水点靠木以前的文章或继续浏览下面的相关文章希望大家以后多多支持三水点靠木!

Python 相关文章推荐
Python使用gensim计算文档相似性
Apr 10 Python
Python实现的人工神经网络算法示例【基于反向传播算法】
Nov 11 Python
Python多继承原理与用法示例
Aug 23 Python
Python XML转Json之XML2Dict的使用方法
Jan 15 Python
python和mysql交互操作实例详解【基于pymysql库】
Jun 04 Python
Python 使用PyQt5 完成选择文件或目录的对话框方法
Jun 27 Python
在PyTorch中Tensor的查找和筛选例子
Aug 18 Python
python读取当前目录下的CSV文件数据
Mar 11 Python
python Django 反向访问器的外键冲突解决
May 20 Python
python使用opencv resize图像不进行插值的操作
Jul 05 Python
利用python 下载bilibili视频
Nov 13 Python
python中time、datetime模块的使用
Dec 14 Python
Python find()、rfind()方法及作用
Dec 24 #Python
Python使用pandas导入xlsx格式的excel文件内容操作代码
Dec 24 #Python
Python 第三方库 openpyxl 的安装过程
Dec 24 #Python
python index() 与 rindex() 方法的使用示例详解
Dec 24 #Python
python+pyhyper实现识别图片中的车牌号思路详解
Dec 24 #Python
python pandas 解析(读取、写入)CSV 文件的操作方法
Dec 24 #Python
Python TypeError: ‘float‘ object is not subscriptable错误解决
Dec 24 #Python
You might like
PHP编码规范-php coding standard
2007/03/16 PHP
php disk_free_space 返回目录可用空间
2010/05/10 PHP
利用phpExcel实现Excel数据的导入导出(全步骤详细解析)
2013/11/26 PHP
php不使用copy()函数复制文件的方法
2015/03/13 PHP
用Javscript实现表单复选框的全选功能
2007/05/25 Javascript
javascript 点击整页变灰的效果(可做退出效果)。
2008/01/09 Javascript
基于jQuery的日期选择控件
2009/10/27 Javascript
javascript中使用replaceAll()函数实现字符替换的方法
2010/12/25 Javascript
用jquery存取照片的具体实现方法
2013/06/30 Javascript
javascript设置金额样式转换保留两位小数示例代码
2013/12/04 Javascript
JQuery中使用ajax传输超大数据的解决方法
2014/07/14 Javascript
JS模式之单例模式基本用法
2015/06/30 Javascript
JavaScript脚本库编写的方法
2015/12/09 Javascript
JavaScript焦点事件、鼠标事件和滚轮事件使用详解
2016/01/15 Javascript
JS简单判断函数是否存在的方法
2017/02/13 Javascript
Vue.js学习记录之在元素与template中使用v-if指令实例
2017/06/27 Javascript
原生js实现密码输入框值的显示隐藏
2017/07/17 Javascript
JS排序算法之冒泡排序,选择排序与插入排序实例分析
2017/12/13 Javascript
vue+vuex+axios+echarts画一个动态更新的中国地图的方法
2017/12/19 Javascript
Vue cli构建及项目打包以及出现的问题解决
2018/08/27 Javascript
python调用xlsxwriter创建xlsx的方法
2018/05/03 Python
python导入模块交叉引用的方法
2019/01/19 Python
django实现将后台model对象转换成json对象并传递给前端jquery
2020/03/16 Python
Django自关联实现多级联动查询实例
2020/05/19 Python
Python魔术方法专题
2020/06/19 Python
CSS3教程(2):网页边框半径和网页圆角
2009/04/02 HTML / CSS
HTML5的一个显示电池状态的API简介
2015/06/18 HTML / CSS
美国踏板车和轻便摩托车销售网站:Mega Motor Madness
2020/02/26 全球购物
总经理岗位职责范本
2014/02/02 职场文书
人事主管岗位职责说明书
2014/07/30 职场文书
如何写早恋检讨书
2014/09/10 职场文书
2015年事业单位办公室文员工作总结
2015/04/24 职场文书
家长通知书家长意见
2015/06/03 职场文书
孔繁森观后感
2015/06/10 职场文书
2015中学学校工作总结
2015/07/20 职场文书
js前端面试常见浏览器缓存强缓存及协商缓存实例
2022/06/21 Javascript