Flask分页iter_pages之None分析

昨天看一了下问答区的问题,很多人都咨询了flask在分页的时候总是会遍历出None,这是怎么回事呢?先来一段小程序:数据库信息:代码程序:if name == “main”: user=User.query.paginate(1,2) for i in user.iter_pages(): print(i,end=" “) 输出信息:1 2 3 4 5 None 8 9 今天我自己看了一下源码,来分析一下def iter_pages(self, left_edge=2, left_current=2, right_current=5, right_edge=2): last = 0 for num in xrange(1, self.pages + 1): if num <= left_edge or \ (num > self.page - left_current - 1 and \ num < self.page + right_current) or \ num > self.pages - right_edge: if last + 1 != num: yield None yield num last = num可以很明了的看到当last+1 != num的时候就会返回None,那么接下来的问题就是如果可以使last+1 != num,那就是当num <= left_edge or (num > self.page - left_current - 1 and num < self.page + right_current) or num > self.pages - right_edge不成立的时候,下次循环就会使last+1 != num。那么,我们如何结果很多人想要完整遍历的目的呢?那就是在默认参数总改变值,使上述的三个条件中任意一个永远成立,如使num <= left_edge永远成立for i in user.iter_pages(left_edge=user.pages): print(i,end=” “) 输出结果:1 2 3 4 5 7 8 9这样就完美解决了。当然以上促使以上三个任意条件永远成立都可以进行完整遍历。 ...

January 3, 2019 · 1 min · jiezi

Python进阶:设计模式之迭代器模式

在软件开发领域中,人们经常会用到这一个概念——“设计模式”(design pattern),它是一种针对软件设计的共性问题而提出的解决方案。在一本圣经级的书籍《设计模式:可复用面向对象软件的基础》(1991年,Design Patterns - Elements of Reusable Object-Oriented Software)中,它提出了23种设计模式。迭代器模式就是其中的一种,在各种编程语言中都得到了广泛的应用。本文将谈谈 Python 中的迭代器模式,主要内容:什么是迭代器模式、Python 如何实现迭代器模式、itertools 模块创建迭代器的方法、其它运用迭代器的场景等等,期待与你共同学习进步。1、什么是迭代器模式?维基百科有如下定义:迭代器是一种最简单也最常见的设计模式。它可以让用户透过特定的接口巡访容器中的每一个元素而不用了解底层的实现。——维基百科简单地说,迭代器模式就是一种通用性的可以遍历容器类型(如序列类型、集合类型等)的实现方式。使用迭代器模式,可以不关心遍历的对象具体是什么(如字符串、列表、字典等等),也不需要关心遍历的实现算法是什么,它关心的是从容器中遍历/取出元素的结果。按遍历方式划分,迭代器可分为内部迭代器与外部迭代器,它们的区别在于执行迭代动作与维持迭代状态的不同。通常而言,迭代器是一次性的,当迭代过一轮后,再次迭代将获取不到元素。2、Python的迭代器模式由于迭代器模式的使用太常见了,所以大多数编程语言都给常见的容器类型实现了它,例如 Java 中的 Collection,List、Set、Map等。在 Java 中使用迭代器遍历 List 可以这么写:List<String> list = new ArrayList<>();Iterator<String> iterator = list.iterator();while(iterator.hasNext()){ System.out.println(iterator.next());}ArrayList 类通过自身的 iterator() 方法获得一个迭代器 iterator,然后由该迭代器实例来落实遍历过程。Python 当然也应用了迭代器模式,但它的实现思路跟上例却不太一样。首先,Python 认为遍历容器类型并不一定要用到迭代器,因此设计了可迭代对象。list = [1,2,3,4]for i in list: print(i,end=" “) # 1 2 3 4for i in list: print(i,end=” “) # 1 2 3 4上例中的 list 是可迭代对象(Iterable),但并不是迭代器(虽然在底层实现时用了迭代器的部分思想)。Python 抓住了迭代器模式的本质,即是“迭代”,赋予了它极高的地位。如此设计的好处显而易见:(1)写法简便,用意直白;(2)可重复迭代,避免一次性迭代器的缺陷;(3)不需要创建迭代器,减少开销。可迭代对象可看作是广义的迭代器,同时,Python 也设计了普通意义的狭义的迭代器。list = [1,2,3,4]it = iter(list)for i in it: print(i,end=” “) # 1 2 3 4for i in it: print(i,end=” “) # 无输出上例中的 iter() 方法会将可迭代对象变成一个迭代器。从输出结果可以看出,该迭代器的迭代过程是一次性的。由此看来,Python 其实是将“迭代器模式”一拆为二来实现:一是可迭代思想,广泛播种于容器类型的对象中,使它们都可迭代;一是迭代器,一种特殊的可迭代对象,承担普通意义上的迭代器所特有的迭代任务。 同时,它还提供了将可迭代对象转化为迭代器的简易方法,如此安排,真是将迭代器模式的效力发挥到了极致。(关于可迭代对象与迭代器的更多区别、以及它们的实现原理,请参见《Python进阶:迭代器与迭代器切片》)3、创建迭代器创建迭代器有如下方式:(1)iter() 方法,将可迭代对象转化成迭代器;(2)iter() 与 next() 魔术方法,定义类实现这两个魔术方法;(3)itertools 模块,使用内置模块生成迭代器;(4)其它创建方法,如 zip() 、map() 、enumerate() 等等。四类方法各有适用场所,本节重点介绍 itertools 模块。它可以创建三类迭代器:无限迭代器、有限迭代器与组合迭代器。3.1 无限迭代器count(start=0, step=1) :创建一个从 start (默认值为 0) 开始,以 step (默认值为 1) 为步长的的无限整数迭代器。cycle(iterable) :对可迭代对象的元素反复执行循环。repeat(object [,times]) :反复生成 object 至无限,或者到给定的 times 次。import itertoolsco = itertools.count()cy = itertools.cycle(‘ABC’)re = itertools.repeat(‘A’, 30)# 注意:请分别执行;以下写法未加终止判断,只能按 Ctrl+C 退出for n in co: print(n,end=” “) # 0 1 2 3 4……for n in cy: print(n,end=” “) # A B C A B C A B……for n in re: print(n,end=” “) # A A A A A A A A….(30个)3.2 有限迭代器以上方法,比较常用的有:chain() 将多个可迭代对象(可以是不同类型)连接成一个大迭代器;compress() 方法根据真假过滤器筛选元素;groupby() 把迭代器中相邻的重复元素挑出来放在一起;islice() 方法返回迭代器切片(用法参见《Python进阶:迭代器与迭代器切片》);tee() 方法根据可迭代对象创建 n 个(默认2个)迭代器副本。for c in itertools.chain(‘ABC’, [1,2,3]): print(c,end=” “)# 输出结果:A B C 1 2 3for c in itertools.compress(‘ABCDEF’, [1, 1, 0, 1, 0, 1]): print(c,end=” “)# 输出结果:A B D Ffor key, group in itertools.groupby(‘aaabbbaaccd’): print(key, ‘:’, list(group))# 输出结果:a : [‘a’, ‘a’, ‘a’]b : [‘b’, ‘b’, ‘b’]a : [‘a’, ‘a’]c : [‘c’, ‘c’]d : [’d’]itertools.tee(‘abc’, 3)# 输出结果:(<itertools._tee at 0x1fc72c08108>, <itertools._tee at 0x1fc73f91d08>, <itertools._tee at 0x1fc73efc248>)3.3 组合迭代器product() :求解多个可迭代对象的笛卡尔积。permutations() :求解可迭代对象的元素的全排列。combinations():求解可迭代对象的元素的组合。for i in itertools.product(‘ABC’, [1,2]): print(i, end=” “)# 输出结果:(‘A’, 1) (‘A’, 2) (‘B’, 1) (‘B’, 2) (‘C’, 1) (‘C’, 2)for i in itertools.permutations(‘ABC’, 2): print(i, end=” “)# 输出结果:(‘A’, ‘B’) (‘A’, ‘C’) (‘B’, ‘A’) (‘B’, ‘C’) (‘C’, ‘A’) (‘C’, ‘B’)for i in itertools.combinations(‘ABC’, 2): print(i, end=” “)# 输出结果:(‘A’, ‘B’) (‘A’, ‘C’) (‘B’, ‘C’)for i in itertools.combinations(‘ABCD’, 3): print(i, end=” “)# 输出结果:(‘A’, ‘B’, ‘C’) (‘A’, ‘B’, ‘D’) (‘A’, ‘C’, ‘D’) (‘B’, ‘C’, ‘D’)4、强大的内置迭代器方法迭代器模式的使用场景实在太普遍了,而 Python 也为迭代器的顺利使用而提供了很多便利的条件,本节将介绍相关的几个内置方法。这些方法非常常用而且强大,是 Python 进阶的必会内容。4.1 zip() 方法zip() 方法可以同时迭代多个序列,并各取一个元素,生成一个可返回元组的迭代器。此迭代器的长度以较短序列的长度保持一致,若想生成较长序列的长度,需要使用 itertools 模块的 zip_longest() 方法。import itertoolsa = [1, 2, 3]b = [‘w’, ‘x’, ‘y’, ‘z’]for i in zip(a,b): print(i,end=” “) # (1, ‘w’) (2, ‘x’) (3, ‘y’)# 空缺值以 None 填补for i in itertools.zip_longest(a,b): print(i,end=” “) # (1, ‘w’) (2, ‘x’) (3, ‘y’) (None, ‘z’)4.2 enumerate() 方法enumerate() 方法接收一个序列类型参数,生成一个可返回元组的迭代器,元组内容是下标及其对应的元素值。它还可接收一个可选参数,指定下标的起始值,默认是0 。注意:众所周知,Python 中序列的索引值从 0 开始,但是,enumerate() 可以达到改变起始索引数值的效果。seasons = [‘Spring’, ‘Summer’, ‘Fall’, ‘Winter’]for i in enumerate(seasons): print(i,end=” “) #输出结果:(0, ‘Spring’) (1, ‘Summer’) (2, ‘Fall’) (3, ‘Winter’)for i in enumerate(seasons, start=7): print(i,end=” “) #输出结果:(7, ‘Spring’) (8, ‘Summer’) (9, ‘Fall’) (10, ‘Winter’)4.3 map() 方法map() 方法的参数是一个函数及一个或多个可迭代对象,它会将可迭代对象的元素映射到该函数中,然后迭代地运行该函数,返回结果也是一个迭代器。当存在多个可迭代对象参数时,迭代长度等于较短对象的长度。def square(x): return x ** 2l = map(square, [1, 2, 3, 4, 5])print(list(l))# 输出结果:[1, 4, 9, 16, 25]m = map(lambda x, y: x + y, [1, 3, 5, 7, 9], [2, 4, 6, 8, 10, 2])print(list(m))# 输出结果:[3, 7, 11, 15, 19]4.4 filter() 方法filter() 方法的参数是一个判断函数及一个可迭代对象,遍历可迭代对象执行判断函数,过滤下判断为True 的元素,与它相对,若想保留判断为 False 的元素,可使用 itertoole 模块的 filterfalse() 方法。import itertoolsfi = filter(lambda x: x%2, range(10))ff = itertools.filterfalse(lambda x: x%2, range(10))for i in fi: print(i,end=” “)# 输出结果:1 3 5 7 9for i in ff: print(i,end=” “)# 输出结果:0 2 4 6 85. 小结迭代器模式几乎是 23 种设计模式中最常用的设计模式,本文主要介绍了 Python 是如何运用迭代器模式,并介绍了 itertools 模块生成迭代器的 18 种方法,以及 5 种生成迭代器的内置方法。 相关链接:itertools模块文档:http://t.cn/R6cGtfwPython进阶:迭代器与迭代器切片Python进阶:全面解读高级特性之切片!—————–本文原创并首发于微信公众号【Python猫】,后台回复“爱学习”,免费获得20+本精选电子书。 ...

January 2, 2019 · 3 min · jiezi

Flask_sqlalchemy之数据分页

在做项目中很多时候我们都需要分页,可能很多人都知道使用paginate进行分页,可是然后就不知道了,今天像大家介绍一下个paginate分页。paginate(self, page=None, per_page=None, error_out=True, max_per_page=None) page:需要查询第几页的数据,默认值:1 源代码""" if page is None: try: page = int(request.args.get(‘page’, 1)) except (TypeError, ValueError): if error_out: abort(404) page = 1""" per_page:每页数据量,默认值:20 源代码""" if per_page is None: try: per_page = int(request.args.get(‘per_page’, 20)) except (TypeError, ValueError): if error_out: abort(404) per_page = 20""" error_out:当参数为True时,会有404响应,在page<1或per_page<0都会响应404 源代码""" if page < 1: if error_out: abort(404) else: page = 1 if per_page < 0: if error_out: abort(404) else: per_page = 20""" max_per_page:每页最大数据量,如果指定,则默认取其与per_page的最小值 源代码""" if max_per_page is not None: per_page = min(per_page, max_per_page)“““其实今天我想写的并不是这几个参数,而是它的返回值,Pagination对象,既然他返回了一个Pagination对象,那么这个东西是什么,有什么用呢?Pagination(query, page, per_page, total, items):一个帮助分页的类has_next:判断是否有下一页has_prev:判断是否有上一页next_num:返回下一页页码prev_num:返回上一页页码page :当前页码pages:总页数per_page:每一页的数据量prev():返回Pagination上一页的对象next():返回Pagination下一页的对象items:返回当前页面项目,可遍历iter_pages(left_edge = 2,left_current = 2,right_current = 5,right_edge = 2):迭代分页中的页码好了,今天的内容我介绍完了,大家尝试一下吧。 ...

January 2, 2019 · 1 min · jiezi

python之验证码生成

今天向大家总结一下python在做项目时用到的验证码生成工具:gvcode与captchagvcode全称:graphic-verification-code安装:pip install gvcode使用:import gvcodes, v = gvcode.generate() #序列解包s.show() #显示生成的验证码图片print(v) #打印验证码字符串效果:captcha安装:pip install captcha使用:from captcha.image import ImageCaptchafrom random import randintlist = [‘0’, ‘1’, ‘2’, ‘3’, ‘4’, ‘5’, ‘6’, ‘7’, ‘8’, ‘9’, ‘a’, ‘b’, ‘c’, ’d’, ’e’, ‘f’, ‘g’, ‘h’, ‘i’, ‘j’, ‘k’, ’l’, ’m’, ’n’, ‘o’, ‘p’, ‘q’, ‘r’, ’s’, ’t’, ‘u’, ‘v’, ‘w’, ‘x’, ‘y’, ‘z’, ‘’, ‘B’, ‘C’, ‘D’, ‘E’, ‘F’, ‘G’, ‘H’, ‘I’, ‘J’, ‘K’, ‘L’, ‘M’, ‘N’, ‘O’, ‘P’, ‘Q’, ‘R’, ‘S’, ‘T’, ‘U’, ‘V’, ‘W’, ‘X’, ‘Y’, ‘Z’]chars = ‘‘for i in range(4): chars += list[randint(0, 62)]image = ImageCaptcha().generate_image(chars)image.show()效果: ...

January 1, 2019 · 1 min · jiezi

Python进阶:迭代器与迭代器切片

Python进阶:迭代器与迭代器切片在前两篇关于 Python 切片的文章中,我们学习了切片的基础用法、高级用法、使用误区,以及自定义对象如何实现切片用法(相关链接见文末)。本文是切片系列的第三篇,主要内容是迭代器切片。迭代器是 Python 中独特的一种高级特性,而切片也是一种高级特性,两者相结合,会产生什么样的结果呢?1、迭代与迭代器首先,有几个基本概念要澄清:迭代、可迭代对象、迭代器。迭代 是一种遍历容器类型对象(例如字符串、列表、字典等等)的方式,例如,我们说迭代一个字符串“abc”,指的就是从左往右依次地、逐个地取出它的全部字符的过程。(PS:汉语中迭代一词有循环反复、层层递进的意思,但 Python 中此词要理解成单向水平线性 的,如果你不熟悉它,我建议直接将其理解为遍历。)那么,怎么写出迭代操作的指令呢?最通用的书写语法就是 for 循环。# for循环实现迭代过程for char in “abc”: print(char, end=" “)# 输出结果:a b cfor 循环可以实现迭代的过程,但是,并非所有对象都可以用于 for 循环,例如,上例中若将字符串“abc”换成任意整型数字,则会报错: ‘int’ object is not iterable .这句报错中的单词“iterable”指的是“可迭代的”,即 int 类型不是可迭代的。而字符串(string)类型是可迭代的,同样地,列表、元组、字典等类型,都是可迭代的。那怎么判断一个对象是否可迭代呢?为什么它们是可迭代的呢?怎么让一个对象可迭代呢?要使一个对象可迭代,就要实现可迭代协议,即需要实现__iter__() 魔术方法,换言之,只要实现了这个魔术方法的对象都是可迭代对象。那怎么判断一个对象是否实现了这个方法呢?除了上述的 for 循环外,我知道还有四种方法:# 方法1:dir()查看__iter__dir(2) # 没有,略dir(“abc”) # 有,略# 方法2:isinstance()判断import collectionsisinstance(2, collections.Iterable) # Falseisinstance(“abc”, collections.Iterable) # True# 方法3:hasattr()判断hasattr(2,”iter") # Falsehasattr(“abc”,"iter") # True# 方法4:用iter()查看是否报错iter(2) # 报错:‘int’ object is not iterableiter(“abc”) # <str_iterator at 0x1e2396d8f28>### PS:判断是否可迭代,还可以查看是否实现__getitem__,为方便描述,本文从略。这几种方法中最值得一提的是 iter() 方法,它是 Python 的内置方法,其作用是将可迭代对象变成迭代器 。这句话可以解析出两层意思:(1)可迭代对象跟迭代器是两种东西;(2)可迭代对象能变成迭代器。实际上,迭代器必然是可迭代对象,但可迭代对象不一定是迭代器。两者有多大的区别呢?如上图蓝圈所示,普通可迭代对象与迭代器的最关键区别可概括为:一同两不同 ,所谓“一同”,即两者都是可迭代的(iter),所谓“两不同”,即可迭代对象在转化为迭代器后,它会丢失一些属性(getitem),同时也增加一些属性(next)。首先看看增加的属性 next , 它是迭代器之所以是迭代器的关键,事实上,我们正是把同时实现了 iter 方法 和 next 方法的对象定义为迭代器的。有了多出来的这个属性,可迭代对象不需要借助外部的 for 循环语法,就能实现自我的迭代/遍历过程。我发明了两个概念来描述这两种遍历过程(PS:为了易理解,这里称遍历,实际也可称为迭代):它遍历 指的是通过外部语法而实现的遍历,自遍历 指的是通过自身方法实现的遍历。借助这两个概念,我们说,可迭代对象就是能被“它遍历”的对象,而迭代器是在此基础上,还能做到“自遍历”的对象。ob1 = “abc"ob2 = iter(“abc”)ob3 = iter(“abc”)# ob1它遍历for i in ob1: print(i, end = " “) # a b cfor i in ob1: print(i, end = " “) # a b c# ob1自遍历ob1.next() # 报错: ‘str’ object has no attribute ‘next’# ob2它遍历for i in ob2: print(i, end = " “) # a b c for i in ob2: print(i, end = " “) # 无输出# ob2自遍历ob2.next() # 报错:StopIteration# ob3自遍历ob3.next() # aob3.next() # bob3.next() # cob3.next() # 报错:StopIteration通过上述例子可看出,迭代器的优势在于支持自遍历,同时,它的特点是单向非循环的,一旦完成遍历,再次调用就会报错。对此,我想到一个比方:普通可迭代对象就像是子弹匣,它遍历就是取出子弹,在完成操作后又装回去,所以可以反复遍历(即多次调用for循环,返回相同结果);而迭代器就像是装载了子弹匣且不可拆卸的枪,进行它遍历或者自遍历都是发射子弹,这是消耗性的遍历,是无法复用的(即遍历会有尽头)。写了这么多,稍微小结一下:迭代是一种遍历元素的方式,按照实现方式划分,有外部迭代与内部迭代两种,支持外部迭代(它遍历)的对象就是可迭代对象,而同时还支持内部迭代(自遍历)的对象就是迭代器;按照消费方式划分,可分为复用型迭代与一次性迭代,普通可迭代对象是复用型的,而迭代器是一次性的。2、迭代器切片前面提到了“一同两不同”,最后的不同是,普通可迭代对象在转化成迭代器的过程中会丢失一些属性,其中关键的属性是 getitem 。在《Python进阶:自定义对象实现切片功能》中,我曾介绍了这个魔术方法,并用它实现了自定义对象的切片特性。那么问题来了:为什么迭代器不继承这个属性呢?首先,迭代器使用的是消耗型的遍历,这意味着它充满不确定性,即其长度与索引键值对是动态衰减的,所以很难 get 到它的 item ,也就不再需要 getitem 属性了。其次,若强行给迭代器加上这个属性,这并不合理,正所谓强扭的瓜不甜……由此,新的问题来了:既然会丢失这么重要的属性(还包括其它未标识的属性),为什么还要使用迭代器呢?这个问题的答案在于,迭代器拥有不可替代的强大的有用的功能,使得 Python 要如此设计它。限于篇幅,此处不再展开,后续我会专门填坑此话题。还没完,死缠烂打的问题来了:能否令迭代器拥有这个属性呢,即令迭代器继续支持切片呢?hi = “欢迎关注公众号:Python猫"it = iter(hi)# 普通切片hi[-7:] # Python猫# 反例:迭代器切片it[-7:] # 报错:‘str_iterator’ object is not subscriptable迭代器因为缺少__getitem__ ,因此不能使用普通的切片语法。想要实现切片,无非两种思路:一是自己造轮子,写实现的逻辑;二是找到封装好的轮子。Python 的 itertools 模块就是我们要找的轮子,用它提供的方法可轻松实现迭代器切片。import itertools# 例1:简易迭代器s = iter(“123456789”)for x in itertools.islice(s, 2, 6): print(x, end = " “) # 输出:3 4 5 6for x in itertools.islice(s, 2, 6): print(x, end = " “) # 输出:9# 例2:斐波那契数列迭代器class Fib(): def init(self): self.a, self.b = 1, 1 def iter(self): while True: yield self.a self.a, self.b = self.b, self.a + self.bf = iter(Fib())for x in itertools.islice(f, 2, 6): print(x, end = " “) # 输出:2 3 5 8for x in itertools.islice(f, 2, 6): print(x, end = " “) # 输出:34 55 89 144itertools 模块的 islice() 方法将迭代器与切片完美结合,终于回答了前面的问题。然而,迭代器切片跟普通切片相比,前者有很多局限性。首先,这个方法不是“纯函数”(纯函数需遵守“相同输入得到相同输出”的原则,之前在《来自Kenneth Reitz大神的建议:避免不必要的面向对象编程》提到过);其次,它只支持正向切片,且不支持负数索引,这都是由迭代器的损耗性所决定的。那么,我不禁要问:itertools 模块的切片方法用了什么实现逻辑呢?下方是官网提供的源码:def islice(iterable, *args): # islice(‘ABCDEFG’, 2) –> A B # islice(‘ABCDEFG’, 2, 4) –> C D # islice(‘ABCDEFG’, 2, None) –> C D E F G # islice(‘ABCDEFG’, 0, None, 2) –> A C E G s = slice(*args) # 索引区间是[0,sys.maxsize],默认步长是1 start, stop, step = s.start or 0, s.stop or sys.maxsize, s.step or 1 it = iter(range(start, stop, step)) try: nexti = next(it) except StopIteration: # Consume iterable up to the start position. for i, element in zip(range(start), iterable): pass return try: for i, element in enumerate(iterable): if i == nexti: yield element nexti = next(it) except StopIteration: # Consume to stop. for i, element in zip(range(i + 1, stop), iterable): passislice() 方法的索引方向是受限的,但它也提供了一种可能性:即允许你对一个无穷的(在系统支持范围内)迭代器进行切片的能力。这是迭代器切片最具想象力的用途场景。除此之外,迭代器切片还有一个很实在的应用场景:读取文件对象中给定行数范围的数据。在《给Python学习者的文件读写指南(含基础与进阶,建议收藏)》里,我介绍了从文件中读取内容的几种方法:readline() 比较鸡肋,不咋用;read() 适合读取内容较少的情况,或者是需要一次性处理全部内容的情况;而 readlines() 用的较多,比较灵活,每次迭代读取内容,既减少内存压力,又方便逐行对数据处理。虽然 readlines() 有迭代读取的优势,但它是从头到尾逐行读取,若文件有几千行,而我们只想要读取少数特定行(例如第1000-1009行),那它还是效率太低了。考虑到文件对象天然就是迭代器 ,我们可以使用迭代器切片先行截取,然后再处理,如此效率将大大地提升。# test.txt 文件内容’‘‘猫Python猫python is a cat.this is the end.‘‘‘from itertools import islicewith open(’test.txt’,‘r’,encoding=‘utf-8’) as f: print(hasattr(f, “next”)) # 判断是否迭代器 content = islice(f, 2, 4) for line in content: print(line.strip())### 输出结果:Truepython is a cat.this is the end.3、小结好啦,今天的学习就到这,小结一下:迭代器是一种特殊的可迭代对象,可用于它遍历与自遍历,但遍历过程是损耗型的,不具备循环复用性,因此,迭代器本身不支持切片操作;通过借助 itertools 模块,我们能实现迭代器切片,将两者的优势相结合,其主要用途在于截取大型迭代器(如无限数列、超大文件等等)的片段,实现精准的处理,从而大大地提升性能与效率。切片系列: 《Python进阶:切片的误区与高级用法》《Python进阶:自定义对象实现切片功能》相关链接: 《官网的itertools模块介绍》《来自Kenneth Reitz大神的建议:避免不必要的面向对象编程》《给Python学习者的文件读写指南(含基础与进阶,建议收藏)》—————–本文原创并首发于微信公众号【Python猫】,后台回复“爱学习”,免费获得20+本精选电子书。 ...

December 30, 2018 · 3 min · jiezi

Flask之扩展flask-migrate

flask-migrate一个用来做数据迁移的falsk扩展,一般都是结合flask-sqlalchemy使用,在上一篇文章中我也介绍了这个扩展,需要的小伙伴可以看一下,后续我会将flask-sqlalchemy更深层的写出来。【config.py】SQLALCHEMY_DATABASE_URI=‘mysql://root:mysql@127.0.0.1:3306/test’ //数据库连接SQLALCHEMY_TRACK_MODIFICATIONS=False【data_migrate.py】from flask import Flaskfrom flask_sqlalchemy import SQLAlchemyfrom flask_script import Manager #这是一个做脚本调式的库,有时间我也会总结from flask_migrate import Migrate,MigrateCommandapp = Flask(name)app.config.from_envvar(‘config.py’)db = SQLAlchemy(app) migrate = Migrate(app, db)manager = Manager(app)manager.add_command(‘db’, MigrateCommand)class User(db.Model): #创建一个模型类,用于做数据迁移 id = db.Column(db.Integer, primary_key=True) name = db.Column(db.String(32)) if name == ‘main’: manager.run()【控制台测试】>>>python data_migrate.py db init //创建迁移存储库>>>python data_migrate.py db migrate -m ‘版本名后缀’ //生成初始迁移>>>python data_migrate.py db upgrade //将迁移应用于数据库//若有修改,可重复执行2/3这两条命令>>>python 文件 db history //显示整个历史版本记录【其他命令】python data_migrate.py db –help //帮助,查找所有命令python data_migrate.py db current //显示当前版本python data_migrate.py db upgrade 版本号 //升级版本,不指定版本为最新版本python data_migrate.py db downgrade 版本号 //降级数据库,不指定版本则是最老版本 ...

December 28, 2018 · 1 min · jiezi

Flask扩展之flask-sqlalchemy(上)

flask-sqlalchemy是flask的一个ORM扩展框架,这个扩展在sqlalchemy的进行的扩展,更方便的结合Flask.什么是ORM?其是Object Relational Mapping的缩写,中文:对象关系映射,说白了就是程序中的实体类通过ORM可以映射成为数据库中的表,方便我们通过程序的方式操作数据表,这里就包括数据表的生成、删除、关系创建及表记录的增删改查。【config.py】SQLALCHEMY_DATABASE_URI=‘mysql://root:mysql@127.0.0.1:3306/test’ //数据库连接SQLALCHEMY_TRACK_MODIFICATIONS=False上面两项是必配置的属性,否则程序将不能正常运行:所有配置键见最后【create_sur.py】from flask import Flaskfrom flask_sqlalchemy import SQLAlchemyapp = Flask(name)db = SQLAlchemy(app)app.config.from_object(‘config.py’)class User(db.Model): tablename = ‘user’ #指定表名,默认模型类小写 id = db.Column(db.Integer, primary_key=True) username = db.Column(db.String(32)) def repr(self): return ‘Role:%s’ % self.nameif name == “main”: db.create_all() app.run()这样子在test数据库下就生成了一张user表,是不是很简单。在进行数据的增删改查之前,我们先来看一下常用的操作语句:常见操作语句db.session.add(obj) 添加对象db.session.add_all([obj1,obj2,..]) 添加多个对象db.session.delete(obj) 删除对象db.session.commit() 提交会话db.session.rollback() 回滚db.session.remove() 移除会话增加数据>>>from create_sur import db,User>>>user1=User(name=‘jim’)>>>db.session.add(user1)>>>db.session.commit() //添加一条数据>>>user2=User(name=‘sam’)>>>user3=User(name=‘alice’)>>>db.session.add_all([user2,user3]) //批量添加数据>>>db.session.commit()查询数据>>>from create_sur import db,User>>>user_all=User.query.all() //查询所有数据>>>user=User.query.filter_by(name=‘jim’).all() //查询name为jim的数据>>>user=User.query.filter(User.name=‘sam’).first() //查询name为sam的数据常用过滤函数:常用查询函数:删除数据//在查询数据的基础上>>>db.session.delete(obj) //obj为查询后的数据对象>>>db.session.commit()更新数据//在查询数据的基础上通过修改对象的属性然后再添加达到更新的作用>>>user=User.query.filter(User.name=‘sam’).first() //查询数据>>>user.name=‘sam_two’>>>db.session.add(user)>>>db.session.commit()上面是一些简单的小例子,接下来我们写一个有外键关系的两个表的例子:【create_sur2.py】from flask import Flaskfrom flask_sqlalchemy import SQLAlchemyapp = Flask(name)db = SQLAlchemy(app)app.config.from_object(‘config.py’)class Role(db.Model): # 定义表名 tablename = ‘roles’ # 定义列对象 id = db.Column(db.Integer, primary_key=True) name = db.Column(db.String(64), unique=True) #设置关系属性,方便查询使用 us = db.relationship(‘User’, backref=‘role’) #重写__repr__方法,方便查看对象输出内容 def repr(self): return ‘Role:%s’% self.name class User(db.Model): tablename = ‘users’ id = db.Column(db.Integer, primary_key=True) name = db.Column(db.String(64), unique=True, index=True) password = db.Column(db.String(64)) role_id = db.Column(db.Integer, db.ForeignKey(‘roles.id’)) #定义外键 def repr(self): return ‘User:%s’%self.name上述有两个重点:使用ForeignKey义外键属性使用relationship方法定义两表的关系接下来我们测试一下这两个方法的作用:添加一些数据:>>>from create_sur2 import db,User,Role>>>ro1 = Role(name=‘admin’)>>>ro2 = Role(name=‘user’)>>>db.session.add_all([ro1,ro2])>>>db.session.commit()>>>us1 = User(name=‘wang’, password=‘123456’, role_id=ro1.id)>>>us2 = User(name=‘zhang’, password=‘201512’, role_id=ro2.id)>>>us3 = User(name=‘chen’, password=‘987654’, role_id=ro2.id)>>>us4 = User(name=‘zhou’, password=‘456789’, role_id=ro1.id)>>>db.session.add_all([us1,us2,us3,us4])>>>db.session.commit()测试一下查询:>>>from create_sur2 import User,Role>>>role=Role.query.get(1)>>>role.User.all()[User:wang,User:zhou] 这里之所以可以找到就是因为relationship通过外键作用实现的>>>user=User.query.get(3)>>>user.role[Role:user] 这里之所以可以找到就是因为relationship的backref参数值实现的所有配置清单SQLALCHEMY_DATABASE_URI 用于连接的数据库 URI SQLALCHEMY_BINDS 一个映射 binds 到连接 URI 的字典SQLALCHEMY_ECHO 如果设置为Ture, SQLAlchemy 会记录所有 发给 stderr 的语句,这对调试有用。(打印sql语句)SQLALCHEMY_RECORD_QUERIES 可以用于显式地禁用或启用查询记录。查询记录 在调试或测试模式自动启用。更多信息见get_debug_queries()。SQLALCHEMY_NATIVE_UNICODE 可以用于显式禁用原生 unicode 支持。当使用 不合适的指定无编码的数据库默认值时,这对于 一些数据库适配器是必须的(比如 Ubuntu 上 某些版本的 PostgreSQL )。SQLALCHEMY_POOL_SIZE 数据库连接池的大小。默认是引擎默认值(通常 是 5 )SQLALCHEMY_POOL_TIMEOUT 设定连接池的连接超时时间。默认是 10 。SQLALCHEMY_POOL_RECYCLE 多少秒后自动回收连接。这对 MySQL 是必要的, 它默认移除闲置多于 8 小时的连接。注意如果 使用了 MySQL , Flask-SQLALchemy 自动设定 这个值为 2 小时。所有数据类型 ...

December 27, 2018 · 1 min · jiezi

Flask之扩展flask-session

在使用flask写应用程序的时候,我们会使用session来控制用户会话状态,但是我们无法确定session的保存位置,默认的flask保存session是模糊的,那我们可不可以控制session的存储位置呢,这就是flask-session:增加了服务器端支持会话到您的应用程序.安装flask-sessionpip install flask-session使用from flask import Flask,sessionfrom flask_session import Sessionfrom redis import StrictRedisapp=Flask(name)app.config[‘DEBUG’]=Trueapp.config[‘SESSION_TYPE’]=‘redis’ //设置会话接口app.config[‘SESSION_REDIS’]=StrictRedis(host=‘localhost’, port=6379,decode_responses=True) //配置连接redisSession(app) //装载app到Session@app.route("/set/") def set(): session[‘key’]=‘value’ return ‘save session'@app.route("/get/")def get(): return session.get(‘key’,’not set’) if name="main": app.run(debug=app.config[‘DEBUG’])会话接口及对应的连接配置一般情况下我们配置好SESSION_TYPE,然后把连接配置配置一下就可以。null:NullSessionInterface(默认)redis:RedisSessionInterface连接配置:SESSION_REDIS:一个redis.Redis例如,默认连接到 127.0.0.1:6379memcached:MemcachedSessionInterface连接配置:SESSION_MEMCACHED:一个memcache.Client实例,默认连接到127.0.0.1:11211filesystem:FileSystemSessionInterface连接配置:SESSION_FILE_DIR:存储会话文件的目录。默认使用当前工作目录下的flask_session目录。连接配置:SESSION_FILE_THRESHOLD:会话在开始删除之前存储的最大项目数,默认值为500连接配置:SESSION_FILE_MODE:存会话文件所需的文件模式,默认为0600mongodb:MongoDBSessionInterface连接配置:SESSION_MONGODB:一个pymongo.MongoClient实例,默认连接到127.0.0.1:27017连接配置:SESSION_MONGODB_COLLECT:您要使用的MongoDB集合,默认为“sessions”SQLAlchemy:SqlAlchemySessionInterface连接配置:SESSION_SQLALCHEMY:flask_sqlalchemy.SQLAlchemy实例,其数据库连接URI是使用配置SQLALCHEMY_DATABASE_URI参数连接配置:SESSION_SQLALCHEMY_TABLE:要使用的SQL表的名称,默认为“sessions”

December 26, 2018 · 1 min · jiezi

Flask之请求钩子

什么是请求钩子?说白了,就是在执行视图函数前后你可以进行一些处理,Flask使用装饰器为我们提供了注册通用函数的功能。before_first_request:在处理第一个请求前执行before_request:在每次请求前执行after_request:每次请求之后调用,前提是没有未处理的异常抛出teardown_request:每次请求之后调用,即使有未处理的异常抛出from flask import Flaskapp = Flask(name)@app.before_first_requestdef before_first_request(): print(‘before_first_request’)@app.before_requestdef before_request(): print(‘before_request’)@app.after_requestdef after_request(resp): print(‘after_request’) return resp@app.teardown_requestdef teardown_request(e): print(’teardown_request’)@app.route("/")def view_fn(): return “view_fn” if name == “main”: app.run()第一次请求:页面输出:view_fn控制台输出: before_first_request before_request after_request teardown_request第一次请求:页面输出:view_fn控制台输出: before_request after_request teardown_request

December 22, 2018 · 1 min · jiezi

Flask动态路由

在通常我们写路由的时候都是这样子的:@app.route("/")def hello(): return “Hello World"但是我们会有这样的需求,判断字符串的类型或长度来决定使用哪个视图函数或者返回404,那我们就可以这样做:@app.route("/int:requirt>")def fn_int(requirt): return “<h1>"+str(requirt)+"</h1>” #http://127.0.0.1:5000/123 返回”<h1>123</h1>"#http://127.0.0.1:5000/12 发生404错误@app.route("/<float:requirt>")def fn_float(requirt): return “<h1>"+str(requirt)+"</h1>” #http://127.0.0.1:5000/1.2 返回"<h1>1.2</h1>"#http://127.0.0.1:5000/12 发生404错误@app.route_path("/<requirt>")def fn(requirt): return “<h1>"+requirt+"</h1>” #http://127.0.0.1:5000/1.2 返回"<h1>1.2</h1>"#http://127.0.0.1:5000/12 返回<h1>12</h1>#http://127.0.0.1:5000/hello 返回<h1>hello</h1>上面写了常用的三种动态路由限制类型,当然我们还可以自定义类型,继承BaseConverter,然后就可以写我们的规则了from werkzeug.routing import BaseConverterclass MyConverter(BaseConverter): def init(self,map,regex): super().init(map) self.regex=regex app.url_map.converters[‘rule’]=MyConverter@app.route_path("/<rule("\w{3}"):requirt>")def fn_rule(requirt): return “<h1>"+requirt+"</h1>"#http://127.0.0.1:5000/hello 发生404#http://127.0.0.1:5000/123 返回”<h1>123</h1>"#http://127.0.0.1:5000/12.3 发生404

December 22, 2018 · 1 min · jiezi

Flask内置命令行工具—CLI

应用发现flask命令在Flask库安装后可使用,使用前需要正确配置FLASK_APP环境变量以告知用户程序所在位置。不同平台设置方式有所不同。Unix Bash (Linux, Mac, etc.):$ export FLASK_APP=hello$ flask runWindows CMD:> set FLASK_APP=hello> flask runWindows PowerShell:> $env:FLASK_APP=“hello”> flask runFLASK_APP环境变量分三部分:一个设置当前工作目录的可选路径,一个python文件或带".“的导入路径,一个可选的应用实例或工厂函数变量名。如果是工厂函数,可以后跟括号传入要接收的参数。FLASK_APP=src/hello设置src为当前工作目录,并导入helloFLASK_APP=hello.web导入hello.webFLASK_APP=hello:app2使用hello中的app2实例FLASK_APP=hello:create_app(‘dev’)使用hello中的工厂函数create_app并传入参数dev如果未设置FLASK_APP,flask命令会查找wsgi.py或app.py文件并探测应用实例或工厂函数。flask命令在给定的导入内寻找一个名为app或者application的应用实例,如果找不到会继续寻找任意应用实例。如果找不到任何实例,会接着寻找名为create_app或者make_app的工厂函数,使用该函数返回的实例。flask run运行开发服务器。不消说,不要在生产环境使用flask shell开启一个交互式的python shell,用来访问或处理应用数据。该指令默认激活应用上下文,并导入应用实例。只有应用实例是默认导入的,如果需要导入其他对象,使用shell_context_processor装饰函数,返回一个字典对象,键值对表示额外导入的对象。from exts import db, models@app.shell_context_processordef make_shell_context(): return dict(models=models, db=db)flask db执行数据库迁移相关操作。flask db指令不能直接使用,需要获取Migrate实例。from flask_migrate import Migratemigrate = Migrate(app, db)环境通过FLASK_ENV变量设置Flask应用的运行环境。可选值包括production(默认)和development。如果设置为development,flask命令将激活debug模式,执行flask run会启用交互式调试器和代码自动重载。DEBUG模式设置FLASK_ENV为development时,会自动激活debug模式。也可以通过FLASK_DEBUG变量单独控制,1表示启用,0表示禁用通过dotenv设置环境变量略通过virtualenv设置环境变量略自定义命令flask命令基于Click库实现。下列代码演示添加带有name参数的print-user命令:import clickfrom flask import Flaskapp = Flask(name)@app.cli.command()@click.argument(“name”)def print_user(name): print(“this is”, name)运行方式:flask print-user root。注意这里的指令是print-user,使用print_user会提示没有这个指令,除非显示地在app.cli.command()中传入"print_user”。下列代码演示了和上面相同的功能,但将打印用户的命令添加到了user命令组。命令组可用于管理多个相关的命令:import clickfrom flask import Flaskfrom flask.cli import AppGroupapp = Flask(name)user_cli = AppGroup(“user”)@user_cli.command(“print”)@click.argument(“name”)def print_user(name): print(“this is”, name)app.cli.add_command(user_cli)运行方式:flask user print root要测试自定义命令,参考Testing CLI Commands应用上下文使用Flask应用的.cli.command()装饰器添加的命令在执行时自动推入应用上下文。如果使用Click的command()装饰器添加命令,执行时不会自动推入应用上下文,要想达到同样的效果,增加with_appcontext装饰器:import clickfrom flask import Flask, current_appfrom flask.cli import with_appcontextapp = Flask(name)@click.command()@with_appcontextdef do_work(): print(“do work”) print(current_app)app.cli.add_command(do_work)如果命令不需要在应用上下文中执行,可以显示地禁用:@app.cli.command(with_appcontext=False)def do_work(): pass插件略(在setup.py中配置entry_points->flask.commands的值)自定义脚本略(针对使用工厂函数生成应用实例的情况,在setup.py中配置entry_points->console_scripts的值)PyCharm 集成略 ...

December 19, 2018 · 1 min · jiezi