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映射拆分操作符用法实例
May 19 Python
python解决网站的反爬虫策略总结
Oct 26 Python
Python中表示字符串的三种方法
Sep 06 Python
Python和Java进行DES加密和解密的实例
Jan 09 Python
Python实现购物评论文本情感分析操作【基于中文文本挖掘库snownlp】
Aug 07 Python
Python从单元素字典中获取key和value的实例
Dec 31 Python
python命令行工具Click快速掌握
Jul 04 Python
python计算无向图节点度的实例代码
Nov 22 Python
基于python实现简单网页服务器代码实例
Sep 14 Python
python 多线程共享全局变量的优劣
Sep 24 Python
python 提高开发效率的5个小技巧
Oct 19 Python
python函数超时自动退出的实操方法
Dec 28 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&amp;&amp;mysql)五
2006/10/09 PHP
php 设计模式之 单例模式
2008/12/19 PHP
php计算给定时间之前的函数用法实例
2015/04/03 PHP
php使用post数组的键值创建同名变量并赋值的方法
2015/04/03 PHP
php session的锁和并发
2016/01/22 PHP
PHP实现获取毫秒时间戳的方法【使用microtime()函数】
2019/03/01 PHP
php实现将数组或对象写入到文件的方法小结【三种方法】
2020/04/22 PHP
如何判断鼠标是否在DIV的区域内
2013/11/13 Javascript
通过onmouseover选项卡实现img图片的变化
2014/02/12 Javascript
js遍历子节点子元素附属性及方法
2014/08/19 Javascript
jQuery实现tab选项卡效果的方法
2015/07/08 Javascript
jQuery制作圣诞主题页面 更像是爱情影集
2016/08/10 Javascript
Bootstrap 网站实例之单页营销网站
2016/10/20 Javascript
浅谈JavaScript中promise的使用
2017/01/11 Javascript
Node.js+jade+mongodb+mongoose实现爬虫分离入库与生成静态文件的方法
2017/09/20 Javascript
原生javascript实现文件异步上传的实例讲解
2017/10/26 Javascript
使用vue自定义指令开发表单验证插件validate.js
2019/05/23 Javascript
Vue.js数字输入框组件使用方法详解
2019/10/19 Javascript
WEB前端性能优化的7大手段详解
2020/02/04 Javascript
使用Python进行新浪微博的mid和url互相转换实例(10进制和62进制互算)
2014/04/25 Python
NumPy 数组使用大全
2019/04/25 Python
Python基本语法之运算符功能与用法详解
2019/10/22 Python
python bluetooth蓝牙信息获取蓝牙设备类型的方法
2019/11/29 Python
python两个_多个字典合并相加的实例代码
2019/12/26 Python
Pytorch的mean和std调查实例
2020/01/02 Python
python如何编写win程序
2020/06/08 Python
编写python代码实现简单抽奖器
2020/10/20 Python
西班牙高科技产品购物网站:MejorDeseo
2019/09/08 全球购物
linux面试题参考答案(8)
2015/08/11 面试题
孝老爱亲模范事迹
2014/01/24 职场文书
父母寄语大全
2014/04/12 职场文书
村当支部个人对照检查材料思想汇报
2014/10/06 职场文书
2014感恩节演讲稿大全
2014/10/11 职场文书
思想政治表现评语
2015/01/04 职场文书
springboot+zookeeper实现分布式锁
2022/03/21 Java/Android
Nginx虚拟主机的配置步骤过程全解
2022/03/31 Servers