进阶第二课 Python内置函数(补)及自定义函数

内置函数(补)

1、float()

上一课中,我们可以使浮点数变为整数;相反的也可以把整数变为浮点数。看示例:

>>> a=10
>>> b=float(10)
>>> b
10.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 "<pyshell#17>", line 1, in <module>
    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))
>>> b
<generator object <genexpr> at 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 "<stdin>", line 1, in <module>

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)

    
1
4
9
16
25

分析一下:

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 "<pyshell#14>", line 1, in <module>
    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)
zhaoyun
23
10000
>>> person('zhaoyun',23,5000)
zhaoyun
23
5000
>>> person(age=23,name='zhaoyun',8000)
SyntaxError: positional argument follows keyword argument
>>> person(age=23,name='zhaoyun',salary=8000)
zhaoyun
23
8000

在自定义函数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 "<pyshell#55>", line 1, in <module>
    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
>>> c
100

上面这段程序中,c的值由a和b计算得来,说明在计算c的值过程中,a和b都起了作用。那么在这一过程,都是a和b的作用域。再看另一个例子:

>>> def pingfang(a):
      return a**2

>>> pingfang(10)
100>>> a
Traceback (most recent call last):
  File "<pyshell#11>", line 1, in <module>
    a
NameError: name 'a' is not defined

这段代码中,函数pingfang中有一个参数a,a也是一个变量。a这个变量的作用域就是函数pingfang内,一旦出了这个函数,a就不存在了。

posted @ 2018-03-28 18:19  驼背蜗牛  阅读(357)  评论(0编辑  收藏  举报