首页 > 代码库 > Python基础-第四天

Python基础-第四天


本篇内容:

1.装饰器

2.列表生成式与生成器

3.可迭代对象与迭代器

4.Python内建函数



一、装饰器

1.装饰器的介绍

装饰器本质是函数,它是为其它函数添加附加功能(装饰其它函数)。

装饰器遵循的原则有:

  ●不能修改被装饰函数的源代码;

  ●不能修改被装饰函数的调用方式;

  ●不能修改被装饰函数的运行结果;


2.装饰器的实现方式

①函数即变量

定义函数就相当于定义变量

>>> variable1 = "python"  # 定义一个变量variable1
>>> variable2 = variable1  # 将变量variable1的值赋给变量variable2
>>> print(variable2)  # 调用变量variable2
python

>>> def test():  # 定义一个test函数
        print("in the test")

>>> test1 = test
>>> test1()  # 调用test1函数,就相当调用test函数
in the test

上面通过def定义test函数,等同于test = 函数体


②高阶函数

●把函数名当作实参传给另外一个函数

>>> import time


>>> def bar():
        print("in the bar")
        time.sleep(1)


>>> def test1(func):
        start_time = time.time()
        func()  # run bar
        stop_time = time.time()
        print("the bar run time is %s second" % (stop_time - start_time))

>>> test1(bar)
in the bar
the bar run time is 1.0150017738342285 second

上面的例子,实现了在不修改bar函数源代码情况下,给bar函数加上了记录函数运行所耗时间的功能,但是修改了bar函数的调用方式。


●函数的返回值中包含函数名

>>> import time


>>> def bar():
        print("in the bar")
        time.sleep(1)


>>> def test2(func):
        start_time = time.time()
        func()  # run bar
        stop_time = time.time()
        print("the bar run time is %s second" % (stop_time - start_time))
        return func

>>> bar = test2(bar)
in the bar
the bar run time is 1.0002121925354004 second

>>> bar()
in the bar

上面的例子中,通过return返回bar函数的内存地址,实现了不修改bar函数的调用方式,但是好像bar()仅仅只是调用了bar函数,没有调用test2函数。

注意上面的bar = test2(bar),接下来例子中就能见证它的作用了。


③函数嵌套

>>> import time


>>> def timer(func):  # func = test1(将test1函数的内存地址传递进来)

        def decorator():
            start_time = time.time()
            result = func()  # func() = test1(),将test1函数运行结果赋给result
            stop_time = time.time()
            print("the test1 run time is %s second" % (stop_time - start_time))
            return result  # 返回test1函数运行结果

        return decorator
        
# @timer
>>> def test1():
        time.sleep(1)
        print("in the test1")
        return "from test1"

>>> test1 = timer(test1)

>>> test1()
in the test1
the test1 run time is 1.0140018463134766 second
from test1  # test1函数运行结果

上面的例子中,在没修改test1函数的源代码、test1函数的调用方式、test1函数的运行结果情况下,给test1函数加上了记录它自己运行耗时的功能。

到这步装饰器基本上就出来了,上面的例子中将test1 = timer(test1)去掉,在test1函数上面顶格写上@timer后,装饰器就真正的成了。其实@timer效果等同于test1 = timer(test1) = decorator。

上面是一个无参装饰器、被装饰函数也无参的例子,下面我们来说说装饰器加上参数的情况。


3.装饰器和被装饰函数有参数的情况

①无参装饰器,被装饰函数有参数

>>> import time


>>> def timer(func):

        def decorator(*args, **kwargs):
            start_time = time.time()
            result1 = func(*args, **kwargs)
            stop_time = time.time()
            print("the test1 run time is %s second" % (stop_time - start_time))
            return result1

        return decorator


>>> @timer
>>> def test1(name, age):
        time.sleep(1)
        print("my name is %s" % name)
        print("my age is %s" % age)
        return "from test1"

>>> print(test1("路人甲", 20))
my name is 路人甲
my age is 25
the test1 run time is 1.0000572204589844 second
from test1

被装饰的test1函数加上参数后,为什么是传给了decorator函数了?那是因为:

test1("路人甲", 20) = timer(test1)("路人甲", 20) = decorator("路人甲", 20)。

为什么这里要写*args, **kwargs了?因为这样写即能接收位置参数,也能接收关键参数,并且参数数量没有限制,程序一下变的很活了。


②有参装饰器,被装饰函数参数不固定

>>> import time


>>> def timer(timeout):

        def wrapper(func):

            def decorator(*args, **kwargs):
                start_time = time.time()
                result1 = func(*args, **kwargs)
                stop_time = time.time()
                print("the test1 run time is %s second" % (stop_time - start_time))
                print(timeout)
                return result1
            

            return decorator

        return wrapper


>>> @timer(10)
>>> def test1(name):
        time.sleep(1)
        print("my name is %s" % name)
        return "from test1"

>>> print(test1("路人甲"))
my name is 路人甲
the test1 run time is 1.012411117553711 second
10
from test1

当装饰器也带上参数后,就需要在装饰器函数中再加上一层函数嵌套。为什么还需要再加上一层了,我们来推导下:test1("路人甲") = timer(10)(test1)("路人甲") = wrapper(test1)("路人甲") = decorator("路人甲")


③总结

  ●函数中通过return返回的是函数名(函数的内存地址),在函数名后面加上小括号后就是调用该函数了;

  ●调用函数时,如果小括号中写有值,代表给被调用函数传参;


4.多个装饰器共存的情况

>>> def dec2(func):  # func = wra1
        def wra2():
            print("wra2开始")
            res2 = func()
            print("wra2结束")
            return res2
        return wra2


>>> def dec1(func):  # func = test
        def wra1():
            print("wra1开始")
            res1 = func()
            print("wra1结束")
            return res1
        return wra1


>>> @dec2
>>> @dec1
>>> def test():
        print("test开始")
        print("test结束")
        return 1

>>> print(test())
wra2开始
wra1开始
test开始
test结束
wra1结束
wra2结束
1

先不管@dec2,只看@dec1,它的意思是:test = dec1(test) = wra1

再来看加上@dec2后的情况,可以推导成:test = dec2(dec1(test)) = dec2(wra1) = wra2

有没有看明白?其实给dec2函数传的参数是wra1函数的内存地址,给dec1函数传的参数是test函数的内存地址。

总结一下就是,装饰顺序从被装饰函数开始由内向外,调用函数时的执行顺序由外向内,装饰顺序和执行顺序相反。

多个装饰器共存情况下的传参问题就不说了,看懂上面第3节内容后,细想下就明白了。



二、列表生成式与生成器

1.列表生成式

现有一个需求要生成一个列表,列表中的十个元素是0到9每个数乘以2的积,可以这么做:

list1 = []

for number in range(10):
    list1.append(number*2)

print(list1)
[0, 2, 4, 6, 8, 10, 12, 14, 16, 18]

但是,有没有觉得上面方法的代码太多,其实还可以这样做:

list2 = [i * 2 for i in range(10)]

print(list2)
[0, 2, 4, 6, 8, 10, 12, 14, 16, 18]

意思是将每次循环得到的值乘以2,计算出的积就是列表的元素,一条代码就搞定了,这就是列表生成式。


2.生成器

动过列表生成式可以直接创建一个列表。但是,受到内存限制,列表容量肯定是有限的。而且,创建一个包含100万个元素的列表,不仅占用很大的存储空间,如果仅仅需要访问前面几个元素,那后面绝大多数元素占用的空间都白白浪费了。

如果列表元素可以按照某种算法推算出来,那可以在循环的过程中不断推算出后续的元素,这样就不必创建完整的list,从而节省大量的空间。

在Python中,这种一边循环一边计算的机制,称为生成器(generator)。注意,生成器只有在被调用时才会生成相应的数据。

生成器不支持通过切片方式获取值,唯一方法只能一个一个的取。


①将列表生成式变成生成器(generator)

将列表生成式最外层的[]换成()后,列表生成式就变成了生成器。

generator1 = (i * 2 for i in range(10))

print(generator1)
<generator object <genexpr> at 0x0000000000B1EF10>

print(generator1.__next__())  # 通过__next__()函数一个一个的取值
0

print(generator1.__next__())  # 每次调用只会生成当前值
2

for i in generator1:  # 推荐方法通过for循环取值
    print(i)
4
6
8
10
12
14
16
18


②使用yield把函数变成生成器(generator)

如果一个函数中包含yield关键字,那么它就不再是函数,而是一个生成器(generator)。

def fib(max):  # 斐波那契数列(Fibonacci),除第一个和第二个数外,任意一个数都可由前两个数相加得到
    n, a, b = 0, 0, 1

    while n < max:
        yield b  # 使用yield把函数变成生成器
        a, b = b, a + b
        n += 1

    return ‘done‘

f = fib(8)

print(f)
<generator object fib at 0x000000000067EF10>

print(f.__next__())
1

print(f.__next__())
1

print("==做点别的事==")
==做点别的事==

print(f.__next__())
2

for number in f:  # 推荐方法通过for循环取值
    print(number)
3
5
8
13
21

a, b = b, a + b意思是:

a, b = 1, 2

t = (b, a + b)

a = t[0]

b = t[1]


③StopIteration错误

创建了一个生成器(generator)后,如果通过for循环来迭代它,不管循环多少次,都不会有StopIteration的错误,所以就不需要关心StopIteration的错误。

如果调用__next__()函数,当调用次数超过生成器(generator)总共可调用次数后,会有StopIteration的错误提示。

如果想要拿到生成器函数中的返回值,必须捕获StopIteration错误,因为返回值包含在StopIteration的value中。

def fib(max):  # 斐波那契数列(Fibonacci),除第一个和第二个数外,任意一个数都可由前两个数相加得到
    n, a, b = 0, 0, 1

    while n < max:
        yield b  # 使用yield把函数变成生成器
        a, b = b, a + b
        n += 1

    return "---done---"  # return后面写的是错误信息,即返回值。返回值包含在StopIteration的value中;

f = fib(6)  # 把fib函数变成生成器

while True:
    try:  # 当调用次数小于生成器总共可调用次数,执行try部分
        x = f.__next__()  # 执行生成器,当执行到yield语句处返回b的值
        print("斐波那契数列值: ", x)

    except StopIteration as e:  # 把异常对象命名为e,可以随便命名。当调用次数超过生成器总共可调用次数后,执行except部分的代码
        print("生成器return的值: ", e.value)  # 返回值包含在StopIteration的value中
        break
斐波那契数列值:  1
斐波那契数列值:  1
斐波那契数列值:  2
斐波那契数列值:  3
斐波那契数列值:  5
斐波那契数列值:  8
生成器return的值:  ---done---



三、可迭代对象与迭代器

1.可迭代对象(Iterable)

可以直接作用于for循环的数据结构有以下几种:

  ●一类是集合数据类型,如list、tuple、dict、set、str、bytes等;

  ●一类是生成器(generator),包括生成器和带yield的生成器函数。

这些可以直接作用于for循环的对象统称为可迭代对象(Iterable)。


判断一个对象是否是可迭代对象(Iterable):

isinstance():可以判断一个对象是否是可迭代对象(Iterable);

from collections import Iterable


print(isinstance([], Iterable))
True

print(isinstance((), Iterable))
True

print(isinstance({}, Iterable))
True

print(isinstance("abcd", Iterable))
True

print(isinstance("abcd", Iterable))
True

print(isinstance(10, Iterable))
False


2.迭代器(Iterator)

可以被__next__()函数调用并不断返回下一个值的对象称为迭代器(Iterator)。


判断一个对象是否是迭代器(Iterator)对象:

isinstance():可以判断一个对象是否是迭代器(Iterator)对象;

from collections import Iterator


print(isinstance((x for x in range(10)), Iterator))
True

print(isinstance([], Iterator))
False

print(isinstance({}, Iterator))
False

print(isinstance(10, Iterator))
False


3.把list、dict、str等,由可迭代对象(Iterable)变成迭代器(Iterator)

生成器都是迭代器(Iterator)对象,但list、dict、str虽然是可迭代对象(Iterable),却不是迭代器(Iterator)。

把list、dict、str等,由可迭代对象(Iterable)变成迭代器(Iterator)可以使用iter()函数。

from collections import Iterable, Iterator


variable1 = [1, 2, 3]

print(isinstance(variable1, Iterable))  # 判断variable1是不是可迭代对象(Iterable)
True

print(isinstance(variable1, Iterator))  # 判断variable1是不是迭代器(Iterator)
False

variable2 = iter(variable1)

print(isinstance(variable2, Iterator))  # 判断variable2是不是迭代器(Iterator)
True

print(variable2.__next__())
1

print(variable2.__next__())
2



四、Python内建函数

abs(x):返回数字的绝对值;

print(abs(-1))
1

print(abs(2))
2

print(abs(-1.2))
1.2


all(iterable):如果可迭代对象(iterable)的所有元素都为true,或者可迭代对象(iterable)为空,则返回True。

print(all([0, 1]))
False

print(all([-1, 1]))
True

print(all([1.1]))
True

print(all({}))
True


any(iterable):如果可迭代对象(iterable)有为true的元素,则返回True。如果可迭代对象(iterable)为空,则返回False。

print(any({}))
False

print(any([0, 1]))
True

print(any([0]))
False

print(any([1, 2, "abcd"]))
True


bin(x):将整数转换为二进制;

print(bin(1))
0b1

print(bin(2))
0b10

print(bin(3))
0b11

print(bin(255))
0b11111111


hex(x):将整数转换为以“0x”为前缀的小写十六进制字符串;

print(hex(10))
0xa

print(hex(15))
0xf

print(hex(255))
0xff


oct(x):将整数转换为以“0o”为前缀的小写八进制字符串;

print(oct(5))
0o5

print(oct(8))
0o10

print(oct(9))
0o11

print(oct(15))
0o17

print(oct(16))
0o20


bool([x]):返回一个布尔值,即True或False。如果x为false或省略不写,则返回False,否则返回True。空列表、空元组、空字典等都返回False,只要有元素存在都返回True。对于数字类型来说,非零的数都为真,浮点数为真,负数也为真。

print(bool(0))
False

print(bool(-1))
True

print(bool(1.1))
True

print(bool([]))
False

print(bool([0]))
True

print(bool(["abcd"]))
True


callable(object):如果对象参数可调用,则返回True,否则返回False。

def function():
    pass

print(callable(function))
True

print(callable([]))
False


exec(object):对象必须是字符串或代码对象。如果它是一个字符串,则将该字符串解析为一组Python语句,然后执行该语句(除非发生语法错误)。如果它是一个代码对象,它只是被执行。

code = """
def function(number):
    print("第%s次传递值" % number)

for i in range(1, 3, 1):
    function(i)
"""
exec(code)
第1次传递值
第2次传递值


eval(expression):将字符串str当成有效的表达式来求值并返回计算结果,参数只能是字符串。

x = "1+2+3"
print(eval(x))
6

variable1 = """{"name": "zhonglu"}"""
print(type(variable1))
<class ‘str‘>

variable1 = eval(variable1)
print(type(variable1))
<class ‘dict‘>

variable2 = """[1, 2]"""
print(type(variable2))
<class ‘str‘>

variable2 = eval(variable2)
print(type(variable2))
<class ‘list‘>


dir([object]):如果没有写具体的对象,返回当前整个文件作用域中的名称列表。使用参数时,返回该对象的有效属性列表。


enumerate(iterable, start=0):iterable必须是一个序列,一个迭代器,或者其他支持迭代的对象。__next__()方法返回的迭代器的 enumerate()返回一个元组,其中包含一个count(start,默认从零开始)和一个从迭代获得的值。

variable = ["a", "b", "c"]

for index, element in enumerate(variable):
    print(index, element)
0 a
1 b
2 c

print(list(enumerate(variable)))
[(0, ‘a‘), (1, ‘b‘), (2, ‘c‘)]

print(list(enumerate(variable, start=1)))
[(1, ‘a‘), (2, ‘b‘), (3, ‘c‘)]


globals():返回一个字典,字典中包含整个程序中所有的变量,变量名是key,变量的内容是value。


locals():返回一个字典,字典中包含所有本地(局部)的变量,变量名是key,变量的内容是value。可以判断文件中是否存在某一变量。


hash(object):返回对象的哈希值。哈希值是整数。

print(hash(1))
1

print(hash(1.0))
1

print(hash("abcd"))
-6905759380937986042

print(hash("a"))
-4127621861757105051

print(hash("好的"))
4063688330910289507


help([object]):调用内置的帮助系统。


id(object):返回对象的内存地址。


input([prompt]):该函数从输入读取一行,将其转换为字符串(剥离尾随的换行符),并返回。


int(x=0):返回一个由数字构造的整型对象。如果没有给出参数,则返回0。对于浮点数,将截断小数点后面的部分,取整。


len(s):返回一个对象的长度(项目数)。参数可以是序列(如字符串,字节,元组,列表或范围)或集合(如字典,集合或冻结集合)。

list1 = [1, 2, 3, "a", "b"]
print(len(list1))
5


max(iterable):返回一个可迭代对象中最大的项;

variable1 = [1, 2, 3, 5, 4]
print(max(variable1))
5


min(iterable):返回一个可迭代对象中最小的项;


reversed(seq):返回一个反向迭代器。

variable1 = [1, 2, 3]

variable2 = reversed(variable1)

print(variable2)
<list_reverseiterator object at 0x00000000006CA5F8>

print(variable2.__next__())
3

print(variable2.__next__())
2

print(variable2.__next__())
1


round(number[, ndigits]):四舍五入保留小数点后指定的位数。如果省略了ndigits,或者没有,它将去掉小数位,返回整数位。

print(round(1.235, 2))
1.24

print(round(12.235))
12


sorted(iterable):从可迭代对象(Iterable)中返回一个新的排序列表。

variable = {-5: "i", 2: "a", 6: "e", 4: "c", 8: "g"}

print(variable)
{8: ‘g‘, 2: ‘a‘, -5: ‘i‘, 4: ‘c‘, 6: ‘e‘}

print(variable.items())
dict_items([(8, ‘g‘), (2, ‘a‘), (-5, ‘i‘), (4, ‘c‘), (6, ‘e‘)])

print(sorted(variable.items()))  # 根据key来排序
[(-5, ‘i‘), (2, ‘a‘), (4, ‘c‘), (6, ‘e‘), (8, ‘g‘)]

print(sorted(variable.items(), key=lambda x: x[1]))  # 根据value来排序
[(2, ‘a‘), (4, ‘c‘), (6, ‘e‘), (8, ‘g‘), (-5, ‘i‘)]


sum(iterable):从左到右依次迭代,返回他们的总和。可迭代对象(Iterable)的项目通常是数字,起始值不允许为字符串。

variable = [1, 2, 3]

print(sum(variable))
6


type(object):返回对象的数据类型;


zip(*iterables):创建一个迭代器,从每个迭代器中聚合元素,返回一个元组的迭代器。当最短输入的可迭代对象(Iterable)耗尽时,迭代器就停了止。

variable1 = [1, 2, 3]
variable2 = ["a", "b", "c"]
print(zip(variable1, variable2))
<zip object at 0x0000000000D5D408>

for i in zip(variable1, variable2):
    print(i)
(1, ‘a‘)
(2, ‘b‘)
(3, ‘c‘)

variable3 = [5, 6, 7, 8, 9]
variable4 = ["e", "f", "g"]
print(zip(variable3, variable4))
<zip object at 0x0000000000D5D408>

for j in zip(variable3, variable4):
    print(j)  # 默认是以最短的为准
(5, ‘e‘)
(6, ‘f‘)
(7, ‘g‘)




























本文出自 “12031302” 博客,谢绝转载!

Python基础-第四天