函数是一段具有特点功能的、可重用的语句组。(将代码封装起来)
定义:def(定义一个函数)关键词开头,空格之后接函数名称和圆括号(),最后还有一个“:”。
def是固定的,不能变,必须是连续的def三个字母,不能分开。
空格 为了将def关键字和函数名称分开,必须空。
函数名:函数名只能包含字符串、下划线和数字且不能以数字开头。虽然函数名可以随便起,但我们给函数起名字还是要尽量简短,并表达函数功能。
括号:必须要有;
注释:每一个函数都应该对功能和参数进行相应的说明,应该写在函数下面第一行。以增强代码的可读性。
调用:就是函数名()一定要加上括号。
#函数定义 def my_len(): """计算s1的长度""" s1 = "hello world" length = 0 for i in s1: length = length+1 print(length) #函数调用 my_len() 函数的定义和调用
注:只定义函数而不调用,函数就不执行。
函数的返回值
在调用python自带的len函数时,必须用一个变量来接收这个值。
str_len=len('hello,word')
使用自己写的函数也可以做到这一点
# 函数定义 def my_len(): s1='hello world' length=0 for i in s1: length=length+1 print(length) str_len=my_len() #函数调用 print('str_len:%s'%str_len) # 11 # str_len:None 说明这段代码什么都没有返回。
在写函数的时候,要尽量以功能为向导,结果最好不要直接在函数中打印出来。
关键字return的作用
1、返回一个值
2、终止一个函数的继续
def my_len(): # 函数名的定义 s1='hello world' length=0 for i in s1: length=length+1 return length # 函数的返回值 str_len=my_len() #函数的调用以及返回值的接收 print(str_len) # 11
在没有返回值的时候:
1、不写return与写入return None的效果相同,返回的只都是None
2、只写一个return后面不加任何东西的时候与写return None的效果一样
返回多个值:
1、当用一个变量接收返回值的时候,收到的是一个元组。这是因为在python中把用逗号分割的 多个值认为是一个元组。
2、当返回值有多个变量接收,那么返回值的个数应该和接收变量的个数完全一致。
3. return还有一个特殊的用途,一旦执行到return,后面的语句就不在执行了(结束一个函数)。(和break类似但有区别,break是跳出循环,如果循环后有代码则继续执行。return是结束整个函数)
4.如果在函数中有多个return,只执行第一个return。
########### def func(): return "a" , "b" #返回多个值时接收到的是一个元组 c = func() # c接收的是一个元组 print(c) ##('a', 'b')
#返回多个值,用多个变量接收(接收的变量数与返回值的个数要一致) def func(): return "a" , "b" , "c" d , e , f = func() print(d , e , f) ##a b c def func(): return [1,2,3] a , b , c = func() #列表和元组是可以解包的 print(a,b,c) ##1 2 3
返回的字典类型有点意外:
def func():
return {"name":"span"}
dic = func() #需要字典类型来接收,而不能直接用k,v,字典解包解出来的只是键
print(dic)
##{'name': 'span'}
##return扩展 def f(L): #L为形式参数,接收参数 if len(L)>4: return True else: return False s=[1,2,3,4] dic={5,6,7,8,9} print(f(s)) #s为实际参数,给参数的过程就是传参(argument) print(f(dic)) # False # True
函数的参数:
#函数定义 def fun(s): count=0 for i in s: count+=1 return count #函数调用 str=fun('jshdjkshkdhsk') print(str) #13
在上述代码中,告诉了fun函数要计算的字符串是谁,这个过程就是传递参数,简称传参;在调用函数时传递的这个'jshdjkshkdhsk'和定义函数时的s就是参数。
实参与形参:
在调用函数时传递的'jshdjkshkdhsk'被称为实际参数,因为这是实际要交给函数的内容,简称实参
定义s的时候,s只是一个变量的名字,被称为形式参数,因为在定义函数的时候它只是一个形式,表示这里有一个参数,简称形参。
在传递多个参数:多个函数分别可以使用任意数据类型
按照关键字传参数和按照位置传参数是可以混用的,但是首先都是按位置传,之后再是按关键字传的;按照位置传完该接收的参数只能接收一个值,不接受或者重复接收。
###传递两个参数的情况
def my_sum(a,b):#有两个参数就该传两个参数,1对应a;2对应b (这样的参数就称为位置参数) res = a + b return res ret = my_sum(1,2) print(ret) #3
#站在传参(实参)的角度
#按照位置传参
#按照关键字传参
#混着用也可以,但必须是先按照位置传参,再按照关键字传参
#不能给一个变量传多个值
def my_sum(a,b): print(a,b) res = a + b return res ret = my_sum(1,b=2) print(ret) ## 1 2 3
def my_sum(a,b): print(a,b) res = a + b return res ret = my_sum(b=2,a=1) print(ret) ## 1 2 3
###站在形参的角度上:
#位置参数:必须传(有几个参数就传几个,不能多传和少传)
#默认参数,可以不传,如果不传就按默认的参数,如果传了就按传了的值
####在调用函数的时候:
#若按照位置传,直接写参数的值
#若按照关键字传,关键字=值
###定义函数的时候
#位置参数
#默认参数,关键字参数。 参数名=值
#在定义参数时必须先定义位置参数再定义默认参数
#动态参数(*args),可以接收任意多个参数,组织成一个元组
#动态参数(**kwargs),接收的是按照关键字传参的值,组织成一个字典
#args必须在kwargs之前
######接收参数的先后顺序:位置参数、*args、默认参数、**kwargs#############
###默认参数:是可以不传的参数,在不传参数的情况下可以使用默认值;如果传了,就会使用传的值
def classmate(name,sex='男'): print('姓名:%s,性别:%s' %(name,sex)) classmate('张三') classmate('李四') classmate('翠花','女') # 姓名:张三,性别:男 # 姓名:李四,性别:男 # 姓名:翠花,性别:女
默认参数 魔性用法:默认参数尽量避免使用可变数据类型(默认参数的陷阱)
def func(L=[]): # 相当于在def之前先定义了一个str=[],再将str赋值给L,如果不传参数就共用这个数据类型 L.append(2) print(L) func() func() func() func() # [2] # [2, 2] # [2, 2, 2] # [2, 2, 2, 2]
def fun(L=[]): # 相当于在def之前先定义了一个str=[],再将str赋值给L L.append(2) print(L) fun([]) fun([]) fun([]) fun([]) #[2] # [2] # [2] # [2]
##################################
def func(k,d={}): #参数有可变数据类型如列表、字典等,如果不给可变数据类型赋值,则一直用的是同一个列表或字典 d[k] = "V" print(d) func(1) func(2) func(3) ### {1: 'V'} {1: 'V', 2: 'V'} {1: 'V', 2: 'V', 3: 'V'}
动态参数:
1、*args:接收所有按照位置传的参数。
#动态参数def func(*args): # 在参数前面加个*,这个参数就变成了动态参数 def func(*args): print(args) # 使用的时候,所有接收过来的参数都被组织成一个元组的形式 func(2,3,4,'span',[1,2,3]) ##(2, 3, 4, 'span', [1, 2, 3])
###*args只能接收按位置传参,按照关键字传参就接收不到
def func(*args,L=[]): print(args,L) # 使用的时候,所有接收过来的参数都被组织成一个元组的形式 func(2,3,4,'span',L=[1,2,3]) ##(2, 3, 4, 'span') [1, 2, 3]
#位置参数 动态参数 默认参数 def func(a,b,c,*args,key='key'): # 在参数前面加个*,这个参数就变成了动态参数 print(a,b,c) print(key) print(args) # 使用的时候,所有接收过来的参数都被组织成一个元组的形式 func(2,3,4,'span',[1,2,3],'xiaoming') ## 2 3 4 key ('span', [1, 2, 3], 'xiaoming')
##累加器
def sum(*args): ##多个动态参数累加 res = 0 for i in args: res += i return res ret = sum(3,4,5) print(ret) #12
2、**kwargs:按照所有接收关键字传的参数。
def func(**kwargs): print(kwargs) func(a=1,b=2,c=3) func(a=100) ## {'a': 1, 'b': 2, 'c': 3} {'a': 100}
##无敌用法:
def func(*args,**kwargs): #能接收无限多个位置参数和关键字参数 print(args,kwargs) func(1,2,3,a=1,b=2,c=3) #必须先传位置参数再传关键字参数 ## (1, 2, 3) {'a': 1, 'b': 2, 'c': 3}
######接收参数的先后顺序:位置参数、*args、默认参数、**kwargs#############
## *args的魔性用法:
def sum(*args): # 站在形参的角度上,*args的作用是聚合,将传入的参数转变成元组的形式 print(args) sun=0 for i in args: sun+=i return sun L=[1,2,34,5] print(sum(*L)) # 站在实参的角度上,*L是将列表L按顺序打散之后变成将列表中的每个元素分别输出 ## (1, 2, 34, 5) 42
##**kwargs的魔性用法:
def func(**kwargs):#聚合 print(kwargs)
func(c=3,d=5) dic={'a':1,'b':4} func(**dic) #打散 # {'c': 3, 'd': 5} # {'a': 1, 'b': 4}
####三元运算符
三元运算符 a=1 b=7 c=0 if a>b: c=a else: c=b print(c) #与以下的代码的作用效果相同(三元运算表达式) c=0 a=1 b=7 c=a if a>b else b #如果a>b成立,则c=a,否则c=b print(c)
NAMESPACE 命名空间,名称空间
局部命名空间:定义函数都拥有自己的命名空间(局部命名空间可以有多个)。(函数内部定义的变量名,当调用函数的时候才会产生这个名字空间,随着函数的结束这个名字空间又消失)
##多个函数应该拥有多个局部的名字空间,且不互相共享。
def func1(): a = 1 def func2(): print(a) func2() ## NameError: name 'a' is not defined
全局命名空间:写在函数外面的变量名和函数名(全局命名空间,是在程序从上到下执行的过程中依次加载进内存里的,放置了全局变量名和函数名)
内置命名空间:python解释器启动之后就可以使用的名字(Python解释器一启动就能认识的函数,这些函数就存放在内置命名空间里,这些函数(内置的名字)在解释器启动时加载到内存里)
########################################################################################################################################
作用域:
全局作用域:-----作用在 全局-------内置和全局名字空间中的名字都属于全局作用域
局部作用域:-------作用在局部-------函数(局部名字空间中的名字)
#######################################################################################################################################
Python的解释器要运行起来
加载顺序:
先加载所有内置命名空间中的名字,然后按照顺序加载全局命名空间中的名字。
局部命名空间中的名字:在调用函数的时候产生,并且随着调用的结束而消失。
###1.在局部可以使用全局和内置命名空间中的名字
###2.在全局可以使用内置命名空间中的名字,但不能使用局部命名空间中的名字(当定义的函数执行完后局部空间就被释放了,消失了)
###3.在内置不能使用全局和局部的名字
(依赖倒置原则,上层模块只能依赖下层模块。反之则不可!)
##例:
例如:max()本身是个内置函数
print(max([1,2,3])) #3
如果定义一个和内置函数相同名字的函数,则执行定义的函数(全局名字空间的函数,当自己有的时候就不找上级要,如果自己没有就找最近的上级要,如果在没有就找更上级要!)
def max(L): print("In max function!") print(max([1,2,3])) ## In max function! #执行的是定义的函数(虽然和内置函数同名) None #由于没有返回值,所以打印的是None
作用域:一个名字可以使用的区域。局部作用域可以使用全局作用域中的变量,而全局作用域不能使用局部作用域中的变量;在局部作用域中还可以嵌套更小的局部作用域。
全局作用域:内置名字空间和全局名字空间中的名字属于全局作用域
局部作用域:局部名字空间中的名字属于局部作用域(对于不可变数据类型,在局部只能查看全局作用域的变量,但是不能修改,如果要修改要在变量前加global)
作用域链:小范围作用域可以使用大范围的变量,但作用域链是单向的,不能反向应用。
globals():保存了在全局作用域中的名字和值;
globals和locals方法:小范围可以使用大范围的,但是不能修改,如果想要修改,可以使用global关键字,但是要尽量避免使用,这是因为使用global之后会将全局变量修改掉,这样可能会使其他的要使用原来的全局变量的函数发生变化。(globals()永远打印全局,locals()输出什么要看它出现在什么位置)
locals():保存了当前作用域中的变量,其中的内容会根据执行的位置来决定作用域中的内容,如果是在全局执行,打印的结果会与globals打印的结果一致。
n=1 def func(): global n # 在加入这句话之前函数func中没有定义n,会报错。 n += 1 func() print(n) #如果在一个局部内声明了一个global变量,这个变量在全局有效 #2
PS:是要尽量避免使用,这是因为使用global之后会将全局变量修改掉,这样可能会使其他的要使用原来的全局变量的函数发生变化。
##locals()
a = 1 b = 2 def func(): x = "aaa" y = "bbb" print(locals()) #使用locals()可以查看局部空间里的所有名字 func()
print(globals()) #使用globals()不仅能够查看全局名字空间里的名字,还可以查看内置名字空间里的名字 ## {'y': 'bbb', 'x': 'aaa'}
{'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x00000224A85F8828>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, '__file__': 'C:/Users/Dell/PycharmProjects/s9/day10/函数进阶.py', '__cached__': None, 'a': 1, 'b': 2, 'func': <function func at 0x00000224A84F2E18>}
##locals();globals()
a = 1 b = 2 def func(): x = "aaa" y = "bbb" func() print(globals()) print(locals())#本地的 (输出的结果一样,如果locals()放在全局,打印的就是全局,如果放在局部就是局部) ### {'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x000002A1B06F8828>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, '__file__': 'C:/Users/Dell/PycharmProjects/s9/day10/函数进阶.py', '__cached__': None, 'a': 1, 'b': 2, 'func': <function func at 0x000002A1B06A2E18>} {'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x000002A1B06F8828>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, '__file__': 'C:/Users/Dell/PycharmProjects/s9/day10/函数进阶.py', '__cached__': None, 'a': 1, 'b': 2, 'func': <function func at 0x000002A1B06A2E18>}
## nonlcoal 变量名 修改最近的拥有该变量的外层函数的变量,不会影响全局。
n=0 def func(): n=1 def func2(): nonlocal n # 调用上一级函数中的n n+=1 func2() print(n) # n=2 func() print(n) # 使用全局变量中的n=0 # 2 # 0