三元运算符、列表解析、迭代器、生成器、装饰器
三元运算符和列表解析
三元运算符
格式:
真值结果 if 判断语句 else 假值结果
比如:
res = "大于0" if 10 > 0 else "小于0" print(res) # 大于0
列表解析
l = ["数字%s" % i for i in range(10)] ll = ["数字%s" % i for i in range(10) if i > 5] print(l) # ['数字0', '数字1', '数字2', '数字3', '数字4', '数字5', '数字6', '数字7', '数字8', '数字9'] print(ll) # ['数字6', '数字7', '数字8', '数字9']
第二种写法类似三元。
迭代器
迭代器协议和迭代器对象
迭代器协议是指:对象必须提供一个next方法,执行该方法要么返回迭代中的下一个,要么引起一个stoplteration异常,用来终止迭代。
迭代对象:实现了迭代器协议的对象(如果实现:对象内部定义了一个__iter__()方法,而__iter__又内置有__next__方法)
协议是一种约定,可迭代对象实现了迭代器协议,python的内部工具(如for循环,sum,min,max函数等)使用迭代器协议访问对象。
比如:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
a = "hello" b = a.__iter__() print(b) # <str_iterator object at 0x000001C1541CF860> print(b.__next__()) # h print(b.__next__()) # e print(b.__next__()) # l print(b.__next__()) # l print(b.__next__()) # o print(b.__next__()) # 报错
例子中的 b = a.__iter__(),是遵循迭代器协议,生成可迭代对象。
像for中自动做了捕捉异常终止迭代的动作
为什么要用迭代器
对于序列类型:字符串、列表、元组,我们可以使用索引的方式迭代取出其包含的元素。但对于字典、集合、文件等类型是没有索引的,若还想取出其内部包含的元素,则必须找出一种不依赖于索引的迭代方式,这就是迭代器
迭代器的使用
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
dic={'a':1,'b':2,'c':3} iter_dic=dic.__iter__() #得到迭代器对象,迭代器对象即有__iter__又有__next__,但是:迭代器.__iter__()得到的仍然是迭代器本身 iter_dic.__iter__() is iter_dic #True print(iter_dic.__next__()) #等同于next(iter_dic) print(iter_dic.__next__()) #等同于next(iter_dic) print(iter_dic.__next__()) #等同于next(iter_dic) # print(iter_dic.__next__()) #抛出异常StopIteration,或者说结束标志 #有了迭代器,我们就可以不依赖索引迭代取值了 iter_dic=dic.__iter__() while 1: try: k=next(iter_dic) print(dic[k]) except StopIteration: break
python用强大的for循环帮我们解决上面的麻烦的写法
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#基于for循环,我们可以完全不再依赖索引去取值了 dic={'a':1,'b':2,'c':3} for k in dic: print(dic[k]) #for循环的工作原理 #1:执行in后对象的dic.__iter__()方法,得到一个迭代器对象iter_dic #2: 执行next(iter_dic),将得到的值赋值给k,然后执行循环体代码 #3: 重复过程2,直到捕捉到异常StopIteration,结束循环
迭代器优缺点
优点: - 提供一种统一的、不依赖于索引的迭代方式 - 惰性计算,节省内存 缺点: - 无法获取长度(只有在next完毕才知道到底有几个值) - 一次性的,只能往后走,不能往前退
生成器
什么是生成器
可以理解为一种数据类型,这种数据类型自动实现了迭代器协议(其他的数据类型需要调用自己的__iter__方法,而生成器自身就有__next__方法),所以生成器就是可迭代对象。
生成器分类
1、生成器函数:常用函数定义,但是使用yield语句而不是return语句返回结果,yield语句一次返回一个结果,在每个结果中间,挂起函数状态,以便下次从它离开的地方继续执行
2、生成器表达式:将列表解析的[]换成()得到的就是生成器表达式。它有个好处就是延时计算,一次返回一个结果也就是说,它不会一次生成所有结果。这对大量数据处理的时候非常有用。比如:sum([i for i in range(10000000)]) 内存占用大,机器容易卡死,而sum((i for i in range(10000000))) 因为sum也是迭代计算,里面是迭代生成,几乎不占用内存空间。
比如:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
def demo(): print("测试体") yield "第一次返回" yield "第二次返回" yield "第三次返回" res = demo() print(res) # <generator object demo at 0x000001F86A77A4C0> print(res.__next__()) # 测试体 # 第一次返回 print(res.__next__()) # 第二次返回 ------------------------------------------------------------------------- l = ("数字%s" % i for i in range(3)) print(l) # <generator object <genexpr> at 0x000001CB0B9DA4C0> print(l.__next__()) # 数字0 print(l.__next__()) # 数字1 print(l.__next__()) # 数字2 print(l.__next__()) # 报错,StopIteration
yield特性
yield有两个作用:
返回一个参数
比如:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
def demo(): yield 1 res = demo() print(res.__next__()) # 1
接收一个参数
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
def demo(): a = yield 1 print(a) # 11 yield 2 res = demo() print(res.__next__()) # 1 res1 = res.send(11) print(res1) # 2
send的时候是给生成器中函数停留位置的yield。send接收的值是下一个yield的返回值。
下面是根据这个特性做到生产者和消费者的模型。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
import time def consumer(name): print('我是[%s],我准备开始吃包子了' %name) while True: baozi=yield time.sleep(1) print('%s 很开心的把【%s】吃掉了' %(name,baozi)) def producer(): c1=consumer('张三') c2=consumer('赵四') c1.__next__() c2.__next__() for i in range(10): time.sleep(1) c1.send('包子 %s' %i) c2.send('包子 %s' %i) producer()
常用的三种方式唤醒生成器
- next(生成器对象)
- 生成器对象.__next__()
- 生成器对象.send('一些参数')
装饰器
在说装饰器之前要先说明一些关于函数的用法
详细的看我的另一篇博客:https://www.cnblogs.com/kuxingseng95/articles/9050305.html
其中在装饰器中要知道的就是函数对象是可以被当作参数传递的,返回值可以是函数。
比如:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
def foo(): print('foo') def bar(): print('bar') dic={ 'foo':foo, 'bar':bar, } while True: choice=input('>>: ').strip() if choice in dic: dic[choice]()
装饰器的初级形态及升级
其实装饰器的产生是源于一种需求。当我们写完了一个函数,很多时候不是完善的,是需要进行修改的,而很多时候我们不想修改原来的代码,而是想在原来的代码之上,给它增加一些额外的功能。装饰器就可以满足这个需要,我们可以在不修改原函数代码,不修改原函数调用方式的基础上给这个函数加上一些功能。
下面是一个简单的例子:
定义了一个A函数,我现在想在不改变源代码,不改变调用方式的基础上看看运行这个函数用了多长时间。
import time def A(): time.sleep(3) print('我是A函数') if __name__ == '__main__': A()
先想想,再看下面的代码
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
import time def A(): time.sleep(3) print('我是A函数') def show_time(func): start_time = time.time() func() stop_time = time.time() print("函数的运行时间为%s", (stop_time - start_time)) return func if __name__ == '__main__': A = show_time(A) A()
上面是运行之后的结果,可以看到,要加功能的那个函数运行了两次,这不行,所以要继续进行修改。
这次的修改增加了函数的嵌套这个方式。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
import time def A(): time.sleep(3) print('我是A函数') def show_time(func): def wrapper(): start_time = time.time() func() stop_time = time.time() print("函数的运行时间为%s", (stop_time - start_time)) return wrapper if __name__ == '__main__': A = show_time(A) # 返回的是wrapper的地址 A() # 执行的是wrapper()函数
到这里就完成了在不改变源代码,不改变调用方式为函数增加功能,但是还存在这样几个问题:
- 在调用函数之前加入了我们不想要的赋值操作
- 被装饰的函数有返回值怎么办
- 被装饰的函数有参数怎么办
解决第一个问题
而python为我们提供了一个简便的方法:使用‘@’,我们可以用‘@show_time’给想要加入这个计算函数运行时间的函数添加计时的功能。
我们可以认为’@show_time‘等于'A=show_time(A)'
修改之后:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
import time def show_time(func): def wrapper(): start_time = time.time() func() stop_time = time.time() print("函数的运行时间为%s", (stop_time - start_time)) return wrapper @show_time def A(): time.sleep(3) print('我是A函数') if __name__ == '__main__': # A = show_time(A) # 返回的是wrapper的地址 A() # 执行的是wrapper()函数
解决第二个问题
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
import time def show_time(func): def wrapper(): start_time = time.time() res = func() stop_time = time.time() print("函数的运行时间为%s", (stop_time - start_time)) return res return wrapper @show_time def A(): time.sleep(3) print('我是A函数') return '我是A的运行结果' if __name__ == '__main__': print(A())
解决第三个问题
解决第三个问题,我们需要修改装饰器函数中嵌套的函数wrapper,因为我们在主函数中执行A就相当于执行执行wrapper。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
import time def show_time(func): def wrapper(*args, **kwargs): start_time = time.time() res = func(*args, **kwargs) stop_time = time.time() print("函数的运行时间为%s", (stop_time - start_time)) return res return wrapper @show_time def A(name, age): time.sleep(3) print('我是老A,真名是%s,年龄是%s' % (name, age)) return '我是A的运行结果' if __name__ == '__main__': print(A(name='老王', age='18'))
延伸
有时候我们要往装饰器里面传入参数,然后根据参数选择的操作,具体格式是:
def 函数1(参数): def 参数2(参数): def wrapper(*args, **kwargs): pass return wrapper return 参数2 @函数1(参数="XXX") # 由于装饰器在外层又加了一个函数1,又将下一层的函数2作为返回值,所以和之前的装饰器一样,获取到了函数2的地址。 def demo(): pass if __name__ == '__main__': demo()
例子如下:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
import time def user_choice(choice): def show_time(func): def wrapper(*args, **kwargs): if choice == '1': start_time = time.time() res = func(*args, **kwargs) stop_time = time.time() print("函数的运行时间为%s", (stop_time - start_time)) return res else: print("操作失败") return wrapper return show_time @user_choice(choice = '1') def A(name, age): time.sleep(3) print('我是老A,真名是%s,年龄是%s' % (name, age)) return '我是A的运行结果' if __name__ == '__main__': print(A(name='老王', age='18'))
其他的在这里推荐一个博客:https://www.cnblogs.com/cicaday/p/python-decorator.html