内置函数(补)
1、float()
上一课中,我们可以使浮点数变为整数;相反的也可以把整数变为浮点数。看示例:
>>> a=10>>> b=float(10)>>> b10.0
2、max()
在一系列数中取最大的一个。看示例:
>>> max(1,2,3,4,5)5
3、min()
在一系列数中取最小的一个。看示例:
>>> min(1,2,3,4,5)1
4、help()
查看说明。看示例:
>>> help(int)
大家自己试试看int的说明。
5、迭代器
上一课我们介绍了iter()这个内置函数。这里再详细介绍一下。
5.1 可以直接作用于for循环的对象统称为可迭代对象(Iterable)。
5.2 可以被next()函数调用并不断返回下一个值的对象称为迭代器(Iterator)。
5.3 所有的Iterable均可以通过内置函数iter()来转变为Iterator。
5.4 对迭代器来讲,有一个__next()就够了。在你使用for 和 in 语句时,程序就会自动调用即将被处理的对象的迭代器对象,然后使用它的next__()方法,直到监测到一个StopIteration异常。
>>> a=[1,2,3]>>> b=iter(a)>>> next(b)1>>> next(b)2>>> next(b)3>>> next(b)Traceback (most recent call last): File "", line 1, in next(b)StopIteration
a是一个列表,也就是可迭代对象。b是一个迭代器。
6、yield()
6.1 使用了yield的函数叫生成器,即生成器函数。还可以用生成器表达式来创建生成器:
>>> a=[x**2 for x in range(10)]>>> a[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]>>> b=(x**2 for x in range(10))>>> bat 0x000001FDA3A7D780>
分析一下:a是一个列表,a所有的值都储存在计算机的内存中;b是一个生成器。
要想打印生成器中的内容,有2中方法:
1)next()
>>> g.next()0>>> g.next()1>>> g.next()4>>> g.next()9>>> g.next()16>>> g.next()25>>> g.next()36>>> g.next()49>>> g.next()64>>> g.next()81>>> g.next()Traceback (most recent call last): File "", line 1, in StopIteration
当b中的值调出完毕后,再执行next(),系统会报错。
2)for循环
>>> b=(x**2 for x in range(10))>>> for i in b: print(i,end=' ') 0 1 4 9 16 25 36 49 64 81
>>> for i in b:
print(i,end=' ')
>>>
这里可以看到b作为一个生成器,完成一个循环后就无法再次使用。
6.2 生成器函数
生成器也是一个函数,返回一个迭代器。简单想生成器也是一个迭代器。
在调用生成器时,一旦遇到 yield 函数会暂停并保存当前所有的运行信息(也就是函数内所有变量的状态会被保留,同时函数代码执行到的位置会被保留,感觉就像函数被暂停了一样),并返回 yield 的值,在下一次执行 next() 方法时从当前位置继续运行。
看示例:
>>> def pingfang_2(a): b=1 while b<=a: yield b**2 b=b+1 >>> for i in pingfang_2(5): print(i) 1491625
分析一下:
1)函数pingfang_2中使用yield来返回一个值而非return返回值,所以这个函数是一个生成器,调用这个函数就会返回一个迭代器(即pingfang_2(5))。
2)生成器的好处是延迟计算,一次返回一个结果。也就是说,它不会一次生成所有的结果,这对于大数据量处理,将会非常有用。举个栗子-_-!!
sum(i for i in range(10000000000))
虽然range的范围很大,到10的10次方。但是由于使用的是(),所以i实在一个生成器中循环,每次生成器只返回一个数字:0,1,2,3...占用内存极小。
反例也有一个:
sum([i for i in range(10000000000)])
执行之前请先检查是否为64位操作系统,内容是否为16GB以上,否则你还没等看明白怎么回事你的电脑就死机啦。。。
因为使用的是[],意味着首先生成一个列表,从0到10的10次幂-1。列表越大占用内存越多,会显著降低电脑的运行速度甚至死机。每次也是取一个数。
另外,我碰到这样一个问题:
电脑配置:AMD1600,win10,使用节能模式,显示CPU主频1.32GHz,内存16GB@3000MHz。
此时我执行下列代码:
>>> def time1(): import time a=time.time() sum([i for i in range(10000000)]) b=time.time() return b-a>>> time1()1.9722506999969482>>> def time2(): import time a=time.time() sum(i for i in range(10000000)) b=time.time() return b-a>>> time2()2.1406807899475098
生成器虽说节省内存,但是速度竟然比列表慢!!!
然后我把电源模式改为了“高性能”,再次执行上述2个函数:
>>> time1()0.8594393730163574>>> time2()0.8438072204589844
这一次结果相反!!!生成器即节省内存,又节省时间。
同样是高性能下,把range增加10倍,看结果:
>>> def time3(): import time a=time.time() sum([i for i in range(100000000)]) b=time.time() return b-a>>> def time4(): import time a=time.time() sum(i for i in range(100000000)) b=time.time() return b-a>>> time3()8.884530067443848>>> time4()8.421962976455688
时间差距不明显。内存消耗差距大。
内置函数先讲这些,今后碰到没讲的,随时补充。下面介绍下自定义函数。
自定义函数
介绍了这么多内置的函数,那我们可以自己编写函数吗?可以的,称之为自定义函数。无论是内置的函数,还是我们自定义的函数,都是可以重复使用的。
1、函数的格式:
>>> def add_1(a,b): return(a+b)>>> add_1(10,15)25
看看格式:
1.1 def开头 ,空格后是自定义函数的函数名,这个函数名的命名不要与系统内置的函数名重复,且命名与变量一样开头字符可以是下划线或者英语字母,不能使数字开头。
1.2 函数名后面是括号,里面是参数,可以没有参数,看示例:
>>> def _print(): print('I wanna learn Python.') >>> _print()I wanna learn Python.
1.3 自定义函数内部,依然可以添加说明,与之前的用法一致;
1.4 注意缩进。
1.5 看下取值逻辑:
>>> def mianji(chang,kuan): s=chang*kuan print('长:',chang) print('宽:',kuan) print("面积是:") return s>>> mianji(20,50)长: 20宽: 50面积是:1000
函数mianji中,定义了2个参数:chang和kuan。引用函数时,也提供了这2个参数对应的值。可以看到函数参数和调用时提供的参数,是有对应顺序的:chang对应的是20,kuan对应的是50。
1.6 因为函数mianji中定义了2个参数,调用时如果只传入一个,会怎么样呢?看示例:
>>> mianji(20)Traceback (most recent call last): File "", line 1, in mianji(20)TypeError: mianji() missing 1 required positional argument: 'kuan'
会报错!提示缺少一个参数值,这个参数值对应的参数是kuan。
1.7 再一个问题:调用自定义函数时,是否可以改变参数的顺序呢?看示例。
>>> mianji(kuan=50,chang=20)长: 20宽: 50面积是:1000
答案是可以的。无非就是在调用自定义函数时,在括号中把函数的参数和值一并写入。
1.8 默认参数
>>> def person(name,age,salary=10000): print(name) print(age) print(salary) >>> person('zhaoyun',23)zhaoyun2310000>>> person('zhaoyun',23,5000)zhaoyun235000>>> person(age=23,name='zhaoyun',8000)SyntaxError: positional argument follows keyword argument>>> person(age=23,name='zhaoyun',salary=8000)zhaoyun238000
在自定义函数person中有3个参数,其中salary给出了默认值10000。调用时,如果不提供salary这个参数的值,就会按默认值打印出来;如果给出新的值,就会打印新的值。
再就是如果通过参数名给出对应值,那么所有参数都要标出,否则会报错。
1.9 如果想自定义一个函数,且该函数的参数个数不固定,要如何处理呢?看示例:
>>> def buguding1(*args): print(args) >>> buguding1(1,2,3,45,5)(1, 2, 3, 45, 5)>>> buguding1([1,2,4,43,5,7])([1, 2, 4, 43, 5, 7],)
当参数个数不固定,可以使用*args作为参数。当然‘args’并不是一个固定的名称,说必须使用*args作为不固定参数,使用*a或者*_12a也可以。不过既然多数人都使用*args,那么你也这么用。
上面调用时,我们传入了元祖和列表,都可以正常打印。
还可以这么用:
>>> def buguding_2(a,*args): print(a) print(args) >>> buguding_2(1,2,3,4,5)1(2, 3, 4, 5)
参数有2部分,注意不是2个。第一部分是一个参数a,第二部分是一个不固定个数的参数*args。调用时,1赋值给了a,其余的2,3,4,5赋值给了args。
还可以这样使用:
>>> def buguding_2(a,*args,b): print(a) print(args) print(b) >>> buguding_2(1,2,3,4,5)Traceback (most recent call last): File "", line 1, in buguding_2(1,2,3,4,5)TypeError: buguding_2() missing 1 required keyword-only argument: 'b'>>> buguding_2(1,2,3,4,b=5)1(2, 3, 4)5
有3部分参数,a、*args和b。调用时如果不指定b的值就会报错,以为*args包含了除第一个参数外所有值,b就没有传入值。指定b的值之后,就没问题了。
1.10 如果传入的参数是字典,就需要使用另一种方式:
>>> def buguding_3(**kwargs): print(kwargs) >>> buguding_3(name='zhaoyun',age=23,salary=10000){ 'name': 'zhaoyun', 'age': 23, 'salary': 10000}
如果传入值是字典类型且不固定长度,就可以使用**kwargs作为函数的参数。当然**a也可以。道理同上。
1.11 *args和**kwargs可以混用。看示例:
>>> def buguding_4(*args,**kwargs): print(args) print(kwargs)
这样一来,这个自定义函数就能打印传入的元祖、列表和字典。看示例:
>>> buguding_4(1,2,3,5,6)(1, 2, 3, 5, 6){}
调用时只传入了一个元祖,并未传入字典,此时程序默认传入的是一个空字典。反之亦然。看示例:
>>> buguding_4(name='zhaoyun',age=23,height=200)(){ 'name': 'zhaoyun', 'age': 23, 'height': 200}
调用时如果只传入一个字典,那么程序默认你也传入了一个元祖,不过是空元组罢了。
>>> buguding_4(1,2,3,5,6,name='zhaoyun',age=23,height=200)(1, 2, 3, 5, 6){ 'name': 'zhaoyun', 'age': 23, 'height': 200}
同时传入元祖和字典的结果。
>>> buguding_4(name='zhaoyun',age=23,height=200,1,2,3,5,6,)SyntaxError: positional argument follows keyword argument
调用时如果颠倒参数顺序,结果就是报错。
1.12 不用定义的函数,称之为‘匿名函数’,格式也有所不同。看示例:
>>> a=lambda x,y:x+y>>> a(10,100)110
使用lambda定义一个函数,lambda后面是变量,用逗号隔开,然后是冒号,最后是变量的算法也就是返回值。调用时输入参数即可。
1.13 变量的作用域。
所谓变量的作用域,就是指变量的有效范围。先看示例:
>>> a=10>>> b=2>>> c=a**b>>> c100
上面这段程序中,c的值由a和b计算得来,说明在计算c的值过程中,a和b都起了作用。那么在这一过程,都是a和b的作用域。再看另一个例子:
>>> def pingfang(a): return a**2>>> pingfang(10)100>>> aTraceback (most recent call last): File "", line 1, in aNameError: name 'a' is not defined
这段代码中,函数pingfang中有一个参数a,a也是一个变量。a这个变量的作用域就是函数pingfang内,一旦出了这个函数,a就不存在了。