三元运算符、列表解析、迭代器、生成器、装饰器

三元运算符和列表解析

三元运算符

格式:

真值结果 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函数等)使用迭代器协议访问对象。

比如:

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__())         # 报错
View Code

例子中的 b = a.__iter__(),是遵循迭代器协议,生成可迭代对象。

像for中自动做了捕捉异常终止迭代的动作

为什么要用迭代器

对于序列类型:字符串、列表、元组,我们可以使用索引的方式迭代取出其包含的元素。但对于字典、集合、文件等类型是没有索引的,若还想取出其内部包含的元素,则必须找出一种不依赖于索引的迭代方式,这就是迭代器

 迭代器的使用

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
View Code

python用强大的for循环帮我们解决上面的麻烦的写法

#基于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,结束循环
View Code

 迭代器优缺点

优点:
  - 提供一种统一的、不依赖于索引的迭代方式
  - 惰性计算,节省内存
缺点:
  - 无法获取长度(只有在next完毕才知道到底有几个值)
  - 一次性的,只能往后走,不能往前退

生成器

什么是生成器

可以理解为一种数据类型,这种数据类型自动实现了迭代器协议(其他的数据类型需要调用自己的__iter__方法,而生成器自身就有__next__方法),所以生成器就是可迭代对象。

生成器分类

1、生成器函数:常用函数定义,但是使用yield语句而不是return语句返回结果,yield语句一次返回一个结果,在每个结果中间,挂起函数状态,以便下次从它离开的地方继续执行

2、生成器表达式:将列表解析的[]换成()得到的就是生成器表达式。它有个好处就是延时计算,一次返回一个结果也就是说,它不会一次生成所有结果。这对大量数据处理的时候非常有用。比如:sum([i for i in range(10000000)]) 内存占用大,机器容易卡死,而sum((i for i in range(10000000))) 因为sum也是迭代计算,里面是迭代生成,几乎不占用内存空间。

 比如:

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
View Code

yield特性 

yield有两个作用:

返回一个参数

比如:

def demo():
    yield 1

res = demo()
print(res.__next__())       # 1
View Code

接收一个参数

def demo():
    a = yield 1
    print(a)                # 11
    yield 2

res = demo()
print(res.__next__())       # 1
res1 = res.send(11)
print(res1)                 # 2
View Code

send的时候是给生成器中函数停留位置的yield。send接收的值是下一个yield的返回值。

下面是根据这个特性做到生产者和消费者的模型。

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()
View Code

常用的三种方式唤醒生成器

  • next(生成器对象)
  • 生成器对象.__next__()
  • 生成器对象.send('一些参数')

装饰器

在说装饰器之前要先说明一些关于函数的用法

详细的看我的另一篇博客:https://www.cnblogs.com/kuxingseng95/articles/9050305.html

其中在装饰器中要知道的就是函数对象是可以被当作参数传递的,返回值可以是函数。

比如:

def foo():
    print('foo')

def bar():
    print('bar')

dic={
    'foo':foo,
    'bar':bar,
}
while True:
    choice=input('>>: ').strip()
    if choice in dic:
        dic[choice]()
View Code

装饰器的初级形态及升级

其实装饰器的产生是源于一种需求。当我们写完了一个函数,很多时候不是完善的,是需要进行修改的,而很多时候我们不想修改原来的代码,而是想在原来的代码之上,给它增加一些额外的功能。装饰器就可以满足这个需要,我们可以在不修改原函数代码不修改原函数调用方式的基础上给这个函数加上一些功能。

下面是一个简单的例子:

定义了一个A函数,我现在想在不改变源代码,不改变调用方式的基础上看看运行这个函数用了多长时间。

import time
def A():
    time.sleep(3)
    print('我是A函数')

if __name__ == '__main__':
    A()

先想想,再看下面的代码

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()
View Code

上面是运行之后的结果,可以看到,要加功能的那个函数运行了两次,这不行,所以要继续进行修改。

 这次的修改增加了函数的嵌套这个方式。

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()函数
View Code

 到这里就完成了在不改变源代码,不改变调用方式为函数增加功能,但是还存在这样几个问题:

  1. 在调用函数之前加入了我们不想要的赋值操作
  2. 被装饰的函数有返回值怎么办
  3. 被装饰的函数有参数怎么办

解决第一个问题

而python为我们提供了一个简便的方法:使用‘@’,我们可以用‘@show_time’给想要加入这个计算函数运行时间的函数添加计时的功能。

我们可以认为’@show_time‘等于'A=show_time(A)'

 修改之后: 

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()函数
View Code

 解决第二个问题

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())
View Code

 解决第三个问题

解决第三个问题,我们需要修改装饰器函数中嵌套的函数wrapper,因为我们在主函数中执行A就相当于执行执行wrapper。

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'))
View Code

延伸

有时候我们要往装饰器里面传入参数,然后根据参数选择的操作,具体格式是:

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

例子如下:

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'))
View Code

 

其他的在这里推荐一个博客:https://www.cnblogs.com/cicaday/p/python-decorator.html

 

posted @ 2018-05-22 15:10  苦行僧95  阅读(315)  评论(0编辑  收藏  举报