Python基础-第四天
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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基础-第四天的主要内容,如果未能解决你的问题,请参考以下文章