社区所有版块导航
Python
python开源   Django   Python   DjangoApp   pycharm  
DATA
docker   Elasticsearch  
aigc
aigc   chatgpt  
WEB开发
linux   MongoDB   Redis   DATABASE   NGINX   其他Web框架   web工具   zookeeper   tornado   NoSql   Bootstrap   js   peewee   Git   bottle   IE   MQ   Jquery  
机器学习
机器学习算法  
Python88.com
反馈   公告   社区推广  
产品
短视频  
印度
印度  
Py学习  »  Python

猪行天下之Python基础——5.2 函数(下)

coder-pig • 7 年前 • 672 次点击  
阅读 60

猪行天下之Python基础——5.2 函数(下)

内容简述:

  • 1、迭代器
  • 2、生成器
  • 3、装饰器

1、迭代器

迭代器是Python提供的用于访问集合,是一种可以记住遍历位置的对象,会从第一个元素开始访问,直到结束。可以通过内置的iter()函数来获对应的迭代器对象,然后直接循环遍历这个迭代器对象;或者通过另外一个内置的next()函数,返回容器的下一个元素,不过如果超过结尾会报StopIteration异常,使用代码示例如下:

import sys

a = [1, 2, 3, 4, 5]
it1 = iter(a)

# 直接遍历迭代器对象
for  x in it1:
    print(x, end='\t')
else:
    print()

# 每调用一次next向后访问一个元素,超过元素会报StopIteration异常
it2 = iter(a)
print(next(it2), end='\t')
print(next(it2), end='\t')
print(next(it2), end='\t')
print(next(it2), end='\t')
print(next(it2), end='\t')
print()

# 带异常捕获方法
it3 = iter(a)
while True:
    try:
        print(next(it3), end='\t')
    except StopIteration:
        sys.exit()
复制代码

运行结果如下:

1   2   3   4   5 
1   2   3   4   5 
1   2   3   4   5
复制代码

另外有一点要进行区分:iterable 和 iterator,前者只实现了iter函数,而后者同时实现了iter和next函数。比如List只是一个iterable而不是iterator,所以只支持iter (),不支持next(),需要调用iter()函数获得一个iterator对象。如果调用下type(it1),会发现数据类型是:<class 'list_iterator'>,这是Python为list提供的默认迭代器对象。我们也可以自己来实现一个iterator,代码示例如下:

class MyIterator():
    def __init__(self, names):
        self.names = names
        self.index = 0

    def __iter__(self):
        return self

    def __next__(self):
        if self.index >= len(self.names):
            raise StopIteration("已到达末尾")
        else:
            self.index += 1
            return self.names[self.index - 1]

iterator = MyIterator([1, 2, 3, 4, 5])
for  i in iterator:
    print(i, end='\t')
复制代码

运行结果如下:

1   2   3   4   5
复制代码

2、生成器

叫「生成器函数」会更贴切一些,一种特别的函数,用yield关键字 来返回一个生成器对象,本质上还是迭代器,只是更加简洁,yield对应的值在函数调用时候不会立即返回,只有去调用next()函数的时候才会返回,而使用for xxx in xxx的时候其实调用的还是next()函数,最简单的生成器代码示例如下:

def func(n):
    yield n * n

if __name__ == '__main__':
    print(func(10))
    print(next(func(10)))
    for i in func(10):
        print(i)
复制代码

运行结果如下:

<generator object func at 0x0000015D06436308>
100
100
复制代码

可以看到返回的对象类型是generator,相比起迭代器,生成器显得更加优雅简洁,比如最经典的实现斐波那契数列的例子:




    
def func(n):
    a, b = 0, 1
    while n > 0:
        n -= 1
        yield b
        a, b = b, a + b

for i in func(10):
    print(i, end="\t")
复制代码

运行结果如下:

1 1 2 3 5 8 13 21 34 55
复制代码

3、装饰器

本质上也是「函数」,作用是:帮助其他函数在不改动代码的情况下增加额外的功能,装饰器函数的返回值也是一个函数。

① 一步步了解装饰器

装饰器对于很多初学者来说有点难以理解,举个简单的奶茶例子慢慢引入吧(因为手边刚好有一杯一点点奶茶,另外函数名不要用中文,这里用中文只是想让读者更容易理解,还有命名习惯:类名驼峰命名,函数名下划线分割命名!)

import time

def 波霸奶茶():
    time.sleep(1)
    print("制作一杯波霸奶茶")

if __name__ == '__main__':
    波霸奶茶()
复制代码

运行结果如下:

制作一杯波霸奶茶
复制代码

现在笔者想知道制作一杯波霸奶茶花费的时间,可以改动下波霸奶茶这个函数:




    
def 波霸奶茶():
    start = time.clock()
    time.sleep(2)
    print("制作一杯波霸奶茶")
    end = time.clock()
    print("耗时", end - start)

if __name__ == '__main__':
    波霸奶茶()
复制代码

运行结果如下:

制作一杯波霸奶茶
耗时 1.004335880279541
复制代码

这个时候问题来了,除了波霸奶茶还有四季奶绿这款奶茶:

def 四季奶绿():
    time.sleep(2)
    print("制作一杯四季奶绿")
复制代码

而我也想知道四季奶绿的制作时间,需要把统计时间那部分代码粘贴复制一下,有点繁琐,如果还有一款格雷三兄弟,也是要复制下,好繁琐啊。有没有什么方法能简化这个过程呢?答案肯定是有的:「Python支持函数作为参数」,我们可以把计时部分剥离成一个单独的函数,然后把需要查看制作时间的奶茶函数传入即可,所以有了下面这样的代码:

def 波霸奶茶():
    time.sleep(1)
    print("制作一杯波霸奶茶")

def 四季奶绿():
    time.sleep(2)
print("制作一杯四季奶绿")

def 计时(func):
    start = time.time()
    func()
    end = time.time()
print("耗时", end - start)

if __name__ == '__main__':
    计时(波霸奶茶)
    计时(四季奶绿)
复制代码

运行结果如下:

制作一杯波霸奶茶
耗时 1.0002198219299316
制作一杯四季奶绿
耗时 2.0016701221466064
复制代码

可以,这个时候问题又来了,上面的代码意味着:所有调用到制作奶茶方法的地方都要改成计时(奶茶),如果奶茶有N多种的话,每个调用制作奶茶的函数都要改成这样的形式,有没有更简单的方法啊?答案肯定是有的,Python支持返回一个函数。我们要做的是通过一个内嵌的包装函数,为传入的函数附加功能,然后返回这个函数,具体的代码如下:

def 计时(func):
    def decorator():
        start = time.time()
        func()
        end = time.time()
        print("耗时", end - start)
return decorator

if __name__ == '__main__':
    波霸奶茶 = 计时(波霸奶茶)
    波霸奶茶()
    四季奶绿 = 计时(四季奶绿)
    四季奶绿()
复制代码

运行结果如下:

制作一杯波霸奶茶
耗时 1.0033059120178223
制作一杯四季奶绿
耗时 2.0044848918914795
复制代码

可以,没毛病,而且只有在我们需要查询某种奶茶的制作时间时才需要这样写,平常的函数还是正常使用。嗯,你以为到这里就完了,对于装饰器函数,Python还为我们提供了一枚语法糖,语法糖可以理解成一种便捷的写法吧,在Python中通过一个@就可以简化我们上面的代码,具体代码如下:

def


    
 计时(func):
    def decorator():
        start = time.time()
        func()
        end = time.time()
        print("耗时", end - start)
return decorator

@计时
def 波霸奶茶():
    time.sleep(1)
print("制作一杯波霸奶茶")

@计时
def 四季奶绿():
    time.sleep(2)
print("制作一杯四季奶绿")

if __name__ == '__main__':
    波霸奶茶()
    四季奶绿()
复制代码

一样的运行结果,更加精简的代码,相信读者都体验到了语法糖的便利,但是要注意一点:大部分的语法糖只是减少我们写繁琐代码的时间,让我们把更多的时间花在逻辑流程上,代码写少了不一定就会带来性能上的提升,底层的代码可能还是那些东西!

② 带参数的装饰器

上面演示的是无参数的装饰器的用法,有时我们可能需要传入一些参数,需要在原闭包的基础上再加一层闭包,具体流程与代码示例如下:

import time

def 计时(配料="珍珠"):
    def decorator(func):
        def inner():
            print("加入配料:%s" % 配料 )
            start = time.time()
            func()
            end = time.time()
            print("耗时", end - start)
            return func
        return inner

    return decorator

@计时(配料='波霸')
def 波霸奶茶():
    time.sleep(1)
    print("制作一杯波霸奶茶")

@计时(配料='椰果')
def 四季奶绿():
    time.sleep(2)
    print("制作一杯四季奶绿")

if __name__ == '__main__':
    波霸奶茶()
    print()
    四季奶绿()
复制代码

运行结果如下:

加入配料:波霸
制作一杯波霸奶茶
耗时 1.0022242069244385

加入配料:椰果
制作一杯四季奶绿
耗时 2.0034420490264893
复制代码

③ 多个装饰器的执行顺序

一个函数可以搭配多个修饰器使用,执行的顺序:「从下往上,就近原则,靠近函数定义的先执行」。验证代码示例如下:

def func_a(func):
    def decorator():
        func()
        print("Call func_a")
return decorator

def func_b(func):
     def decorator():
        func()
        print("Call func_b")
return decorator

def func_c(func):
    def decorator():
        func()
        print("Call func_c")
return decorator

@func_a
@func_b
@func_c
def func_t():
pass

if __name__ == '__main__':
    func_t()
复制代码

运行结果如下:

Call func_c
Call func_b
Call func_a
复制代码

④ 四种不同类型的装饰器

Python中有四种不同类型的装饰器,分别是:「函数装饰函数」「函数装饰类」「类装饰函数」
「类装饰类」, 和类有关的装饰器可以等后面学完类和对象再回头看,不用死记,用到的时候
照葫芦画瓢改改就好。另外类装饰函数或类主要依赖类的__call__函数,当使用@形式将装饰器 附加到函数上时,就会调用该函数。

  • 「函数装饰函数」,使用代码示例如下:



    
def func_func(func):
    def decorator(a, b):
        print("函数装饰的函数名:", func.__name__)
        result = func(a, b)
        return result
return decorator

@func_func
def func_add(a, b):
return a + b

if __name__ == '__main__':
    print(func_add(1, 2))
复制代码

运行结果如下:

函数装饰的函数名: func_add
3
复制代码
  • 「函数装饰类」,使用代码示例如下:
def func_class(cls):
    def decorator(name):
        print("函数装饰的类:", cls.__name__, name)
        return cls(name)
return decorator

@func_class
class A:
    def  __init__(self, n):
        self.n = n
    def show(self):
        print("a = " + self.n)

a = A('123')
a.show()
复制代码

运行结果如下:

函数装饰的类: A 123
a = 123
复制代码
  • 「类装饰函数」,使用代码示例如下:
class class_func:
    def __init__(self, _func):
        self._func = _func

    def __call__(self, name):
        print("类装饰的函数名:", self._func.__name__, name)
        return self._func(name)

@class_func
def func(a):
    return a
if __name__ ==  '__main__':
    print(func('123'))
复制代码

运行结果如下:

类装饰的函数名: func 123
123
复制代码
  • 「类装饰类」,使用代码示例如下:
class class_class:
    def __init__(self, _cls):
        self._cls = _cls

    def __call__(self, name):
        print("类装饰的类的类名:", self._cls.__name__, name)
        return self._cls(name)

@class_class
class A:
    def __init__(self, a):
        self.a = a

    def show(self):
        print('self.a = ', self.a)

if __name__ == '__main__':
    a = A('123')
    a.show()
复制代码

运行结果如下:

类装饰的类的类名: A 123
self.a =  123
复制代码

如果本文对你有所帮助,欢迎
留言,点赞,转发
素质三连,谢谢😘~


Python社区是高质量的Python/Django开发社区
本文地址:http://www.python88.com/topic/31241