本文讨论python中的函数。主要内容如下:
函数的定义
函数的参数和函数的重载
函数的嵌套
函数的全局变量与局部变量
函数的递归
函数的作用域
匿名函数和lamda表达式
函数式编程
函数常见的内置函数
1.函数的定义
1 def test(): 2 print('这是我的第一个函数') 3 return 0 4 5 f=test 6 f1=test() 7 print(f) 8 print(f1)
函数一般包=包含两部分:函数的定义和函数的调用。上面的代码中1-3行定义了一个函数,后面几行是测试这个函数。首先看函数的定义。函数是用def关键字来标志的。函数的标准定义是:def 函数名(形参列表):第二行和第三行是这个函数的内容,其中第三行定义了函数的返回值。每一个函数都有一个返回值返回值的数据由return标志。函数执行了return语句之后,函数的内容中return后面的语句不会再执行,就像while循环中的break关键字,一旦执行了break,while循环就立马终止。函数一旦执行了return语句,函数调用立马结束。return语句后面的语句将不再执行。因此可以理解为函数是以return作为结束标志的,如果定义的函数没有return语句,那么python解释器会默认return None.
需要注意,python中,函数定以后,程序代码执行时,会把函数名作为一个变量存放在内存中,此时不会去执行函数体中语句,直到遇到函数的调用语句,此时才会去执行函数体中的内容。
第五行和第六行看似都是定义了一个变量来吧函数赋给这个变量,但这两个语句有着本质区别。上一段说过,在python中,会把定义的函数的函数名作为一个变量存放在内存中。因此第五句f=test相当于把函数作为一个变量赋予了f,此时f存放的是test函数在内存中存放的地址,这一条语句并不是调用该函数,因此函数体中的内容不会执行,用c语言来说,就是f是一个指向该函数的指针。而第六句f1=test(),这是一个标准的函数调用语句,变量f1接收的是函数return 语句返回的值。这句代码会执行函数体中的内容,并把return的值返回给f1,若没有return语句,则返回None.
2.函数的参数和函数的重载
函数的参数分为形参和实参,其中形参指的是在定义函数时()中的参数列表,实参指的是在调用函数时传给函数的参数,形参是一种抽象的意义,而实参则是我们在使用这个函数时传给函数的关键数据。
函数的形参分为三种形式:关键字参数,位置参数,参数组,下面分别来讨论。
(1)位置参数
def test(x,y,z): s=x+y+z #计算三个数的和 return s f1=test(1,2,3) print(f1)
位置参数是我们很熟悉的一种参数形式,无论在java还是在c中,形参都是可以这样表示的,只是会在参数前面加上数据类型。该例子的函数是实现三个数值的相加,x,y,z是函数的三个关键字参数,在调用该函数时,实参会传入1,2,3三个数字。因此在调用该函数后,形参的x=1,y=2,z=3.注意,形参和实参是一一对应的,因此在本例中,一定是x=1,y=2,z=3,而不会是其他的对应关系。这一点,在所有编程语言中都是如此。
(2)关键字参数
1 def test(x,y,z=2): 2 return x+y+z 3 4 s1=test(1,2) 5 s2=test(1,2,3) 6 print(s1) 7 print(s2)
关键字参数类似在c语言中很熟悉的缺省参数。关键字参数的定义形式是“z=2”,即给某些形参附上一个默认值。观察第4行和第5行的函数调用可以知道,对于有关键字参数的函数,一般有多种调用方式,第一种就是正常的给所有形参都传一个实参,就如第5行,这样的话,实参的值会覆盖掉定义函数时给关键字形参赋予的值。其他调用方式就是对于那些关键字形参,不传实参,这样函数在执行时,相应的关键字参数就会用其默认值来参与函数的计算。这个概念跟缺省参数类似。缺省参数的意思就是调用函数,传入实参时,这个函数的实参可传可不传,如果传了实参,就用实参参与函数的计算,如果没有传入实参,则用定义函数时给该缺省参数的默认值参与计算。
需要注意的是,关键字参数必须要在位置参数的后面,即不能这么定义:
1 def test(x,y=2,z):#错误,关键字参数不能在位置参数前面
(3)参数组 *代表列表,**代表字典
1 def test(x,*args): 2 print(x) 3 print(args) 4 def test1(x,**kwargs): 5 print(x) 6 print(kwargs) 7 test(1,*[2,'hekki',[12,43]]) 8 test(1,[2,'hekki',[12,43]]) 9 test(1,2,3,4,) 10 test1(1,**{'key':'value'})
列表参数组的形参一般用*args来表示,字典的的形参一般用**kwargs来表示。这里主要说明一下实参调用的方式,对于列表参数组,实参的调用方式比较灵活。第7,8,9行都可以表示为列表的调用方式,其中第7行在列表前面加了一个*,第8行没有加,这两种方式是一致的,第9行传了了多个实参,没有用列表表示,这样在调用时,会根据形参的分布情况,把多的实参合成一个列表。而对于字典的调用,则只能用第10行的形式进行调用,即用**标志实参是一个字典。
函数的重载指的是在定义函数时,可能会定义多个相同名字的函数,但是每一个函数的形参个数和形参形式不一致,这样在调用函数时,就可以利用实参传入的参数的个数和形式把不同的函数区分开。
3.函数的嵌套
1 def test1(): 2 print('form test1') 3 def test2(): 4 print('form test2') 5 return 1 6 return 2 7 8 f=test1() 9 print(f)
函数的嵌套,意思就是在一个函数中定义了另外一个函数,请注意与在一个函数中调用另一个函数的区别。对于函数的嵌套,有一点需要记住,那就是python对于函数的处理是把函数作为一个变量存放在内存中,直到遇到该函数的调用语句,才会执行函数体中的语句。根据这一原则,简单分析一下上面的代码。
在上面的代码中,定义了一个函数test1(),并在test1中定义了一个test2()函数。然后我们在下面调用了test1()函数。现在分析调用test1()这个函数会发生的事情。前面说过,python是把函数作为一个变量存放在内存中的,直到遇到该函数的调用语句,才会执行函数体中的内容。因此在第8行调用test1()函数后,python解释器会返回存放该函数变量的地方,开始执行函数体中的内容。函数体中第一句会执行第2行,这一行会打印一条语句。打印完成后,继续向下执行第三行,这一行定义了一个新的函数test2(),根据前面说的,python会把函数当做一个变量存放在内存中,直到遇到该函数的调用语句才执行函数体中的内容,因此执行完第三行,python会把第三行的函数名字作为一个变量存放在内存中,然后跳过第4行和第5行的函数体内容,跳到了第六行的return语句。上文曾说过,函数遇到return语句,就会将return语句后面的数据作为函数的返回值,并结束该函数的调用。因此执行完第6行,test1()函数执行完成。程序转到第9行,继续执行。
通过上面的分析,我们可以总结,执行了第8行的语句后,发生了这几件事:第一,执行了第三行语句,向控制台打印出了‘form test1’这条语句,第二,把函数test2作为一个变量存放在了内存中,第三件事:将2作为函数的返回值赋给了变量f.因此这段代码的结果如图所示:
分析完上面的代码,我们发现一个问题就是函数test2中的内容并没有被执行,因为我们上面的程序并没有出现调用test2()的语句,因此python只是把test2()作为一个变量存放在内存中,那么现在的问题是要如何调用test2(),使这个程序能执行test2()中的代码呢?
这个问题有两个解决方案,第一个是在调用函数的部分加入一个调用test2()的语句,如下面的代码所示:
def test1(): print('form test1') def test2(): print('form test2') return 1 return 2 f=test1() f1=test2() print(f) print(f1)
我们希望这段代码能执行test2()语句,输出form test2,并把1赋值给f1,然后把f1打印出来,但是实际结果却是如下:
报错了。报错提示test2未定义。这是为什么呢?因为test2是定义在函数test1中的,他属于函数test1的局部变量,而我们上面的语句是调用的全局变量,因为局部变量的生命周期只在本语句块内,因此会报这个错。关于局部变量和全局变量的区别,下面马上就会讨论。现在先来看看第二种解决方案。
调用一个函数只有两种方式:在函数调用部分去调用和在函数定义的部分去调用。上面使用的是在函数调用部分调用,事实证明这种方案是不行的,那么就只剩下在函数定义的部分去调用了:
1 def test1(): 2 print('form test1') 3 def test2(): 4 print('form test2') 5 return 1 6 res=test2() 7 return res 8 9 f=test1() 10 print(f)
上面的代码,我们把test2函数的调用语句定义在了函数test1的内部,这样就相当于调用了局部变量,这样就不会有问题了,结果如图:
通过上面分析可知,要想实现嵌套函数的调用,必须在定义的函数内部去调用,不能在函数调用部分去调用。
4.函数的全局变量和局部变量
(1)全局变量与局部变量的区别
全局变量指的是这个变量的作用域是整个程序,而局部变量指的是这个变量的作用域只有某一特定的代码块,比如函数的形参就是一个局部变量,他的作用范围仅限于这个函数。
1 name='我是全局变量' 2 def test(): 3 name='我是局部变量' 4 print('test函数中输出name:%s' % name) 5 return 0 6 7 test() 8 print("在函数外面输出name:%s" % name)
上面的代码定义了两个name变量,第一行的name变量是一个全局变量,因为它不存在于任何代码块中,他是一个相对独立的成分,第3行也定义了一个name变量,但是因为该变量是定义在test函数内部中的,因此它属于test函数的局部变量。通过上面的分析,可以简单的总结为,任何局部变量,都必然要依附于某一个代码块(函数,for循环,while循环,if语句,类),而全局变量则依附于任何代码块,他在程序中相对独立,与这些代码块是处于同一个层级。因为python是一个严格控制缩进的语言,正好可以借助这一特性帮我们分析代码的层级关系,然后通过层级关系来判断局部变量和全局变量。
明白了全局变量和局部变量的区别,下面来分析如果存在全局变量和局部变量同名,那么该如何取值的问题。对于这个问题,记住一个原则,一条代码语句如果需要使用某一个变量的值,那么它会首先在同一级找,如果在同一级内找到了这个变量,那么就使用这个变量,如果同一层级没有这个变量,那么就会向上一层级找,如果上一层级找到了该变量,就用该变量的值,如果上一层没有,就再往上找。也就是最近原则。因此如果在一个程序中,存在全局变量和局部变量同名的情况,那么会优先寻找局部变量,如果局部变量不存在,再去找上一层的变量。
上面的代码执行结果如图:
因为在函数test内部定义了局部变量,因此第四行使用的name是局部变量name,而因为局部变量的作用域只限于他所处的层级,第8行的代码的层级是和函数定义同一层级(函数体的层级比函数定义层级低一个层级),因此第8行调用的是全局变量name。
(2)global关键字
前面讲过,局部变量的作用域仅限于其申明的代码块中。代码块中针对该变量的所有操作都是对局部变量进行的,那么现在有一个问题是,有没有办法在代码块中对全局变量进行操作呢?答案是肯定的,只需要使用global关键字声明该变量是全局变量即可:
1 name='我是全局变量' 2 def test(): 3 global name 4 name='猜猜我是谁' 5 print('test函数中输出name:%s' % name) 6 return 0 7 8 test() 9 print("在函数外面输出name:%s" % name)
上面的代码首先定义了一个全局变量,然后在函数的定义中,用global关键字表明了在函数中会对全局变量进行操作。如果没有第三行代码,那么第四行就相当于定义了一个局部变量,这样的话就无法做到改变全局变量。因为在4行改变了全局变量的值,因此第九行的输出和第5行相同:
需要注意,在代码块中如果使用global关键字表明全局变量,那么在global关键字之前就不能再定义同名的局部变量,如果定义了,则会出现全局变量与局部变量冲突,就会报错:
1 name='我是全局变量' 2 def test(): 3 name='ja' #不能在global关键字之前定义同名的局部变量,系统会报错 4 global name 5 name='猜猜我是谁' 6 print('test函数中输出name:%s' % name) 7 return 0 8 9 test() 10 print("在函数外面输出name:%s" % name)
(3)全局变量与局部变量命名规则
前面讨论了局部变量和全局变量的作用域问题,并讨论了局部变量和全局变量同名该如何区分的问题。但是在实际开发中,一般不会存在全局变量和局部变量同名的情形。在实际开发中,为了更好的区分全局变量和局部变量,一般用大写表示全局变量,小写表示局部变量。
5函数的递归
函数的递归,即函数自己调用自己下面仅通过一个简单的例子说明递归的形式,具体讨论后续讨论数据结构系列时在细论
1 def cal(n): 2 print(n) 3 if int(n/2)==0: 4 return n 5 else: 6 return cal(int(n/2)) 7 l=cal(10) 8 print(l)
在上面代码中,第6行出现了函数自己调用自己,这便是递归。写一个递归函数需要注意两点:第一:递归函数必须有明确的结束递归的条件,没有这个条件,递归就会一直执行下去,直到系统崩溃,第二:每一次递归后,问题的规模应该是逐渐减少的。
6函数的作用域和高阶函数
1 def foo(): 2 name='foo1' 3 print('from foo %s' %name) 4 def bar(): 5 name='badd' 6 print('from bar %s' % name) 7 return bar 8 9 f=foo() #调用foo函数,返回bar函数在内存中的地址,把该地址赋给变量f 10 print(f) 11 s=f() #调用bar函数 12 print(s)
函数的作用域指的是函数的生命周期,前面说过在python中,是把函数当做一个变量存放在内存中,直到遇到该代码的调用语句才会执行。前面也说过,对于嵌套函数,要想执行嵌套函数的函数体,必须在函数定义里面调用,那么有没有一种办法可以在全局调用嵌套函数呢?答案是肯定的,上述的代码就实现了在全局调用嵌套函数bar.之所以能这样实现,关键在于第7行的return 语句。因为此处的return语句返回的不是一个常规的数据,而是一个函数。即定义的函数的返回值也是一个函数。如果一个函数返回的是另外一个函数,那么我们就可以使用变量来接收这个函数,然后再加上括号就可以调用该函数了。
对于这种返回值也是一个函数的函数,我们称为高阶函数。高阶函数的定义是:如果一个函数的形参或者返回值是函数,那么这样的函数就称为高阶函数。
7 匿名函数和lambda表达式
匿名函数,即没有函数名的函数,在python中,要实现匿名函数,需要借助lambda表达式。首先来看一段代码:
1 def cal(x): 2 return x+1 3 4 res=cal(10) 5 print(res)
这是一个简单的使传入的参数值加一的函数,这个函数如果用匿名函数来改写,可以写成如下的形式:
1 res=lambda x:x+1 2 print(res(10))
在这段代码中,第一行的代码就代替了cal函数,这段代码执行效果跟上面的代码执行效果一致。通过这两段代码可以看出,使用匿名函数更加简洁,而且匿名函数一般是配合其他函数使用,这样就免去了为函数专门定义一个变量,使得内存占用更少。下面来分析lambda表达式的写法。
在lambda x:x+1这句代码中,lambda是关键字,表明后面的语句是一条lambda表达式后面x:x+1是lambda的内容。这个表达式用:分隔成了两部分,前面部分x是参数,相当于定义函数的形参,后面部分是返回对该参数的操作,相当于函数中的return语句。因此lambda的意义就是,根据:前面的形参执行:后面的运算,并把该运算结果作为lambda的返回值。因为函数的参数可以有多个,因此在lambda表达式中,:前面的参数部分可以有多个参数,但是:后面的返回语句则只能有一条,因为一个函数只会有一个最终的返回值。
8函数式编程
函数式编程同面向对象编程,面向过程编程一样,是一种比较重要的编程思想,此处先略过,后面会开辟专章来讨论这三种编程思想的区别。
9python常见的内置函数
(1)map,reduce,filter函数
a map函数
在讨论map函数之前,先看一个简单的问题:将列表l=[1,2,3,4,5,6]中的每一个元素的值加1。这个问题的解决方案很简单,只需要几行代码就可以实现:
1 l=[1,2,3,4,5,6] 2 ret=[] 3 for i in l: 4 ret.append(i+1) 5 print(ret)
上面这个代码只能处理列表l,为了让这段代码复用性更高,能处理更多的列表,现在我们将这段代码改成函数的形式,这样就可以处理其他列表了:
1 def map_test(array): 2 ret=[] 3 for i in array: 4 ret.append(i+1) 5 return ret 6 li=[1,2,3,4,5,6] 7 s=map_test(li) 8 print(s) 9 print(li)
通过定义函数,现在这段代码就可以处理所有的列表元素加1的操作了。那么现在新增一个需求,上面的代码只能处理将列表元素加1的操作,我现在需要新增一个可以把所有元素减一的操作。针对这个问题,最直观的想法就是在map_test中添加代码。但是这样有一个问题就是,如果后续还需要有新的操作需求就意味着每一次都要在,map_test中添加新的代码。这样做存在两个问题:第一个问题,在实际开发中,一般是不会去动已经写好的代码,因为这样动可能会对其他地方造成影响。第二个:这样不断的向一个函数中添加代码,就会使一个函数变得很臃肿,这就有违函数应该简洁,一个函数解决某一个特定问题的特性了。因此为了能让这个函数处理其他的操作(比如将列表每个元素减一),我们可以考虑将这些操作作为一个参数传入map_test中,这样以后添加新的操作就不用去动map_test的内部操作了。这样就提高了程序的可扩展性:
1 def add_one(x): 2 return x+1 3 4 def redecu_one(x): 5 return x-1 6 7 def map_test(fun,array): 8 ret=[] 9 for i in array: 10 res=fun(i) 11 ret.append(res) 12 return ret
在这段代码中,我们将map_test函数的形参改为了传入一个函数和一个列表,这样就可以处理对任意列表的任意操作了。如果以后还需要添加其他操作,只需要在外面再定义其他操作的实现函数就好,然后传入map_test中即可。
因为map_test在python中经常会使用到,因此python就将其封装为python的内置函数——map().map函数的实现原理就是map_test的代码。
1 l1=map(add_one,l) 2 print(list(l1))
第一行的代码就实现了map_test的操作。现在来分析map函数的参数,第一个参数传入的是一个操作方法(函数),第二个是需要处理的数据集。map函数会针对传入的数据集中的元素逐一用操作方法去处理。map函数的返回值是一个内存地址,如果要输出,需要指明输出的形式。
因为map函数的第一参数是一个函数,因此我们可以把lambda表达式作为参数传入:
1 l1=map(add_one,l) 2 l2=map(lambda x:x+1,l) 3 print(list(l1)) 4 print(list(l2))
lambda表达式的效果与add_one的效果一致,这样我们就可以不用特意去声明一个函数,这对于大型项目是很能提高效率的。
b.reduce函数
同样,通过一个简单的例子开始对reduce函数的讨论。现在有一个需求:需要求一个列表中的元素的和。为了日后方便对程序进行扩展,现在将这个功能写为函数形式:
1 li=[1,2,3,4,5] 2 def reduce_test(array): 3 s=0 4 for i in array: 5 s=s+i 6 return s 7 res=reduce_test(li) 8 print(res)
这段代码很容易实现了上述的需求,现在就要开始加需求了,现在需要新增一个求列表中所有元素的积的操作。为了更好的拓展该函数的功能,现在需要把这些操作以函数的形式传给reduce_test函数。
1 li=[1,2,3,4,5] 2 def add_one(x,y): 3 return x+y 4 def reduce_test(fun,array): 5 s=0 6 for i in array: 7 s=fun(s,i) 8 return s 9 res=reduce_test(add_one,li) 10 print(res)
这段代码也可以用lambda表达式来改写:
1 li=[1,2,3,4,5] 2 def reduce_test(fun,array): 3 s=0 4 for i in array: 5 s=fun(s,i) 6 return s 7 res=reduce_test(lambda x,y:x+y,li) 8 print(res)
现在来考虑另外一个问题,我们之前的计算都是默认给s赋了一个初值,但是在实际开发中,这样并不妥当,初始值应该由用户来提供,如果用户不提供初值,那么就使用列表中的第一个元素,这样可以使代码可用性更高,因为如果是我们给s赋初值,那么对于加法,我们要赋0,而对于乘法则需要赋1,这样就使得程序代码在处理不同的操作时发生冲突,因此为了使程序兼容性更好,现在对初始值做一下改动,以列表第一个元素作为初始值,如果用户有指定初始值,则以用户指定的值作为初始值:
1 li=[1,2,3,4,5] 2 def reduce_test(fun,array,init=None):#指定关键字参数,该参数作为用户输入的初始值 3 if init==None: 4 s=array.pop(0) #如果没有指定初始值,则取出列表的第一个元素,作为s的初始值 5 else: 6 s=init 7 for i in array: 8 s=fun(s,i) 9 return s 10 res=reduce_test(lambda x,y:x+y,li,10) 11 print(res)
上面的reduce_test函数就是python内置函数reduce函数的实现原理,即:
1 from functools import reduce #reduce函数需要从functools中导入 2 li=[1,2,3,4,5] 3 def reduce_test(fun,array,init=None):#指定关键字参数,该参数作为用户输入的初始值 4 if init==None: 5 s=array.pop(0) #如果没有指定初始值,则取出列表的第一个元素,作为s的初始值 6 else: 7 s=init 8 for i in array: 9 s=fun(s,i) 10 return s 11 res=reduce_test(lambda x,y:x+y,li,10) 12 print(res) 13 res1=reduce(lambda x,y:x+y,li,10) #reduce函数执行结果与reduce_test执行结果相同 14 print(res1)
需要注意,reduce函数是定义在functools模块中定义,因此需要从functools模块导入。
c.filter函数
同样,以一个例子开始,现在有一个列表li=['sb_ha','sb_he','sb_ni','zhengchangren'],要求将这个列表中所有以'sb'开头的元素过滤掉。这个需求很容易做到,用一个简单的函数就可以完成:
1 li=['sb_ha','sb_he','sb_ni','zhengchangren'] 2 def filter_test(array): 3 res=[] 4 for i in li: 5 if not i.startswith('sb'): 6 res.append(i) 7 return res 8 9 print(list(filter_test(li)))
现在要对这个函数进行扩展了,要它不但能实现过滤掉以sb开头的,还可以过滤掉以sb结束的,而且后面还可能会增加其他需求,为了满足这样的需求,现在把这些操作作为函数传给filter_test.
1 li=['sb_ha','sb_he','sb_ni','zhengchangren'] 2 def start_with(x): 3 return x.startswith('sb') 4 def filter_test(func,array): 5 res=[] 6 for i in li: 7 if not func(i): 8 res.append(i) 9 return res 10 11 s=filter_test(start_with,li) 12 print(list(s))
这样就成功完成了对该函数的扩展,上面的操作方法可以用lambda表达式来简化:
1 li=['sb_ha','sb_he','sb_ni','zhengchangren'] 2 3 def filter_test(func,array): 4 res=[] 5 for i in li: 6 if not func(i): 7 res.append(i) 8 return res 9 10 s=filter_test(lambda x:x.startswith('sb'),li) 11 print(list(s))
明白了上述代码的功能,就可以使用python内置的函数filter来代替filter_test函数了:
1 def filter_test(func,array): 2 res=[] 3 for i in li: 4 if not func(i): 5 res.append(i) 6 return res 7 8 s=filter_test(lambda x:x.startswith('sb'),li) 9 s1=filter(lambda x:not x.startswith('sb'),li) #filter函数的功能与filter_test的功能一致 10 print(list(s)) 11 print(list(s1))
这里需要注意,filter函数的作用是把符合条件的元素保留下来,因此如果是要删除掉符合条件的元素,需要在lambda表达式中加入not
d.三个函数的总结
map函数处理序列中的每一个元素,得到的结果是一个‘列表’,该列表元素个数及位置与原来序列一致,即map函数的处理结果不会改变序列的长度
reduce函数处理一个序列,然后把序列进行合并操作,reduce函数需要从functools模块中导入
filter遍历序列中的每个元素,判断每个元素得到一个布尔值,如果是True就将该元素保留下来
(2)abs:求绝对值
1 a=-1 2 b=abs(a) 3 print(b)
(3)all:判断序列中的每一个元素的bool值,如果所有元素的bool值都为true,则返回true,否则返回false
1 print(all([1,2,''])) 2 print(all([1,2,3])) 3 print(all([1,2,False]))
(4)bin,hex,oct十进制转为二进制,十六进制,八进制
1 print(bin(99)) #十进制转为二进制 2 print(oct(99)) #十进制转为八进制 3 print(hex(99)) #十进制转为十六进制
(5)bool:返回一个数据的布尔值,0,‘ ’空列表,元组,字典返回False,其他返回true
1 print(bool(0)) 2 print(bool('')) 3 print(bool([]))
(6)bytes:将字符串按照指定的编码方式转为字节
1 name='你好' 2 print(bytes(name,encoding='utf-8')) #encoding指定编码方式 3 print(bytes(name,encoding='utf-8').decode('utf-8')) #decode指定解码方式
(7)chr(int),ord(str)输出int在ASCII马中代表的字符,输出ASCII表中str的序号
print(chr(46)) #46在ASCII码中代表.
(8)dir(obj)输出obj类在pythin中的所有内置方法
1 print(dir(dict)) #输出dict(字典)的所有内置方法
(9)divmod(a,b)返回a除以b的商和余数,结果以元组形式展示
1 print(divmod(10,3)) #返回10除3的商和余数
(10)hash(a)求a的hash值
1 name='hah' 2 print(hash(name)) 3 name='hehh' 4 print(hash(name)) #重新定义了name变量,因此hash值会改变
hash函数的参数要求该变量可hash,前面说过,可hash代表着该变量是不可变的数据类型(包括数字,字符串,元组)。每一个变量一旦定义了变量值,就会有一个唯一的hash值与之对应。如果后面改变了该变量的值,相当于重新定义了一个变量,因此此时hash也会改变。
(11)isinstance(a,b)判断数据a是否是属于类型b,如果是返回True,否则返回False
1 print(isinstance(1,int)) #1是int类型,返回true 2 print(isinstance(1,str)) #1不是str,返回false 3 print(isinstance(1,list)) 4 print(isinstance(1,tuple)) 5 print(isinstance(1,dict))
(12)globals() locals()返回所有的全局变量,局部变量
(13)zip,max,min
a.zip(a,b):将序列a中的元素与序列b中的元素一一配对
1 print(list(zip(('a','b','c'),(1,2,3)))) #将('a','b','c')与(1,2,3)一一配对 2 print(list(zip(('a','b','c'),(1,2,3,4)))) #如果两个元组中的元素不一致,则会从左往右一一配对,没有配对的元素丢掉 3 print(list(zip(('a','b','c','d'),(1,2,3))))
zip函数中的序列a和序列b的元素个数不一定要相等,如果有一个序列的的元素比另一个序列的元素个数多,那么zip会按照从左往右的顺序将两个序列的元素一一匹配,多余的元素自动舍弃。上述三行代码执行效果一样:
zip除了可以把两个序列的元素一一配对外,还可以将一个字典中的键值对进行一一配对:
1 p={'key1':'value1','key2':'value2','key3':'value3'} 2 print(list(zip((p.keys()),(p.values()))))
b.max(li):返回序列li中的最大值
1 p={'key1':'value1','key2':'value2','key3':'value3'} 2 print(list(zip((p.keys()),(p.values())))) 3 4 s=max(p) #默认比较的是字典的key值,返回的是字典中key值最大的键值对对应的key值,在本例中,返回key3 5 print(s)
在上面的例子中可以看到,如果序列是一个字典,那么默认是对字典的key值进行比较,但是在实际开发中,我们往往需要对字典的value值进行比较,当然,我们可以自己写代码来实现,但是如果借助zip函数,就可以很快的得出结果:
1 p={'key1':'value1','key2':'value2','key3':'value3'} 2 print(list(zip((p.keys()),(p.values())))) 3 4 s=max(p) #默认比较的是字典的key值,返回的是字典中key值最大的键值对对应的key值,在本例中,返回key3 5 print(s) 6 s1=list(max(zip((p.values()),(p.keys())))) #这样将返回value值最大的键值对并将value和key值以列表形式返回。返回值为['value3','key3'] 7 print(s1)
另外,max还可以通过关键字参数key来指定比较的方式:
1 p=[ #将字典元素作为列表的元素 2 {'name':'nihao','gender':'man','age':12}, 3 {'name':'hello','gender':'faman','age':34}, 4 {'name':'nihao','gender':'man','age':56}, 5 {'name':'nihao','gender':'man','age':45}, 6 {'name':'nihao','gender':'man','age':567}, 7 {'name':'nihao','gender':'man','age':4567}, 8 {'name':'nihao','gender':'man','age':909}, 9 {'name':'nihao','gender':'man','age':12789} 10 ] 11 res=max(p,key=lambda dic:dic['age'] ) #指定max比较的是字典的age关键字对应的value值 12 print(res)
注意,这种方式,需要将字典转为列表中元素
需要注意:不同类型的元素不可以进行比较,因此如果一个序列中含有不同的数据类型,则不同使用max
c.min(li)min函数返回序列li中的最小值,该函数用法与max一致
(14)pow(a,n,d=None)求a的n次幂:
1 s=pow(2,3) #求2的3次方 2 s1=pow(4,5,6) #先计算4的5次方,再求该值除以6的余数(求模) 3 print(s) 4 print(s1)
(15)round(a):四舍五入
1 s1=round(3.5) #返回4 2 s2=round(3.3) #返回3 3 print(s1, s2)
(16)slice(a,b,step=None)指定切片的范围,其中step用来指定切片的步长,即每取一个元素的间隔是多少.
1 l='helloword' 2 s1=slice(3,5) #指定切片范围是[3,5) 3 s2=slice(2,7,2) #指定切片范围是[2,7),并且是间隔2位取一位 4 print(l[3:5]) #返回lo 5 print(l[s1]) #返回lo 6 print(l[s2]) #返回loo 7 8 print(s2.start) #返回切片的开始位置 9 print(s2.step) #返回切片的步长 10 print(s2.stop) #返回切片的结束位置
(17)sorted()排序
1 li=[100,34,23,556,45,67,32,109,3] 2 li2=[19,'se',[12,54]] 3 print(sorted(li)) #将列表按升序排序 4 print(sorted(li,reverse=True)) #将列表按照降序排序 5 print(sorted(li2)) #错误,一个序列中存在不同的数据类型,不能进行排序
注意,排序的本质是比较大小,因此如果一个序列中含有不同的数据类型,则不能进行排序。此外,同max函数一样,对于字典,可以通过指定key参数来指明排序的标准:
1 p=[ #将字典元素作为列表的元素 2 {'name':'nihao','gender':'man','age':12}, 3 {'name':'hello','gender':'faman','age':34}, 4 {'name':'nihao','gender':'man','age':56}, 5 {'name':'nihao','gender':'man','age':45}, 6 {'name':'nihao','gender':'man','age':567}, 7 {'name':'nihao','gender':'man','age':4567}, 8 {'name':'nihao','gender':'man','age':909}, 9 {'name':'nihao','gender':'man','age':12789} 10 ] 11 print(list(sorted(p,key=lambda dic:dic['age'])))