函数最重要的目的是方便我们重复使用相同的一段程序。将一些操作隶属于一个函数,以后你想实现相同的操作的时候,只用调用函数名就可以,而不需要重复敲所有的语句。
def my_len(): def 关键字 li=[1,2,3,4,5,6] count = 0 for i in li: count+=1 return count return 关键字 set=my_len() 函数的调用以及返回值的接收 print(set)
写函数时最好不要在函数中打印,要尽量以功能为导向。
返回值:
一 没有返回值的情况:
1.不写return
二 有返回值的情况:
return 的作用:1。返回一个或多个值
2. 终止一个函数的继续。 这点很重要好多人都没有注意到这一点,有必要举个例子
举个例子:
def my_len(): print(1) print(2) return None #函数运行到return这里会直接跳出函数 print(33) print(44) print(my_len())
结果:
44 ###为什么会先打印出44,因为程序第一步先运行到def这行,接着运行print(44)这行,而不是运行print(1),因为函数还没有调用。 1 2 None
1.返回一个值:什么数据类型都可以返回,返回什么就接收什么。
2.返回多个值:
如果用一个变量接收返回值,它会返回一个元祖,为什么会返回 一个元组,这里用到了python解析包的概念。python有一种一次给多个变量赋值的方法称为序列解包。
范例1
>>> 1,3 输入 (1, 3) 结果 >>> 2,3,6 输入 (2, 3, 6) 结果
x,y,z=1,2,3 print(x,y,z) 两个变量之间调换 x,y=y,x print(x,y,z)
它允许函数返回一个以上的值并且打包成元组,然后通过一个赋值语句很容易进行访问。所解包的序列中的元素数量必须和放置在赋值符号(=)左边的变量数量完全一致,否则Python会在赋值时引发异常,
范例2:
def my_len(): return 2, 3,4 set1,set2,set3=my_len() print(set1,set2 ,set3)
结果:
(2, 3,4)
返回值用多个变量接收,那么返回值的个数应该和接收值的个数一样,否则会报错。
范例一:
def my_len(): return 2, 3,4 set1,set2,set3=my_len() print(set1,set2 ,set3)
结果:
2 3 4
参数
def func(lst): 接收参数叫形参 count=0 for i in lst: count+=1 return count l=[1,2,3,4,5] print(func(l)) 传入参数叫实参
传函数可以传任意数据类型,并且传什么接收什么。
传参方式
站在传参数的角度上,一共有两种传参数的方式。
第一种:按照位置传参数。
第二种:按照关键字传参数。
传参顺序,这两种方式可以混用,但是注意位置传参不可以放在关键字传参之后。。一个位置只能接受一个参数,不允许接收多个参数。
接收参数方式
站在接收参数的角度上形参有4中参数
方式一:位置参数 。这是必传的
方式二:默认参数。可以传可以不传,不传就用自己的。
方式三:*args. 可以接收多个按位置传参
方式四:**kwargs可以接收多个按关键字传参
同时*args和**kwargs,又叫做动态参数
接收参数顺序:位置参数 ,*args,默认参数,**kwargs
参数的接收和传入的顺序:位置参数,*args, 默认参数,**kwargs 注意不要把默认参数想象成关键字参数了。
def f(a,*args,b=8,**kwargs): print(a) print('*args',args) print('b',b) print('**kwargs',kwargs) f(5,6,7,b=8,c=9,d=10)
结果:
5 *args (6, 7) b 8 **kwargs {'c': 9, 'd': 10}
默认参数
默认参数:python为了简化函数的调用,提供了默认参数机制。
注意:1.默认参数必须放在必选参数之后,否则python会报错。
2.默认参数必须指向的是不变的对象。看下边的默认参数的坑
范例
def classmate(name,sex="男"): print("姓名:%s,性别:%s"%(name,sex)) return classmate("小红") classmate("小花") classmate("小蓝","女")
结果:
姓名:小红,性别:男
姓名:小花,性别:男
姓名:小蓝,性别:女
默认参数的坑:(默认参数尽量不要用可变参数)
如果默认参数是可变对象时,每次调用函数,都是用的一个参数也就是一个id,所以只要你修改了默认参数的值,就相当于全部修改了,解决方法:把L射程[ ]
范例:
def add_end(L=[]): L.append('we') return L print(add_end()) print(add_end()) print(add_end([1]))#重新给L赋值了,相当于又另外开辟了一块内存空间. print(add_end())#还是原来的L的内存空间
结果
['we'] ['we', 'we'] [1, 'we'] ['we', 'we', 'we']
纳尼,为什么不一直是['we'].
解答这个问题的方法:;
其实上面这个式子就等同于下面这个式子
li=[] def add_end(L=li): L.append('we') return L print(id(add_end()),add_end()) print(id(add_end()),add_end()) print(id(add_end([1])),add_end([1])) #重新给L赋值了,相当于又另外开辟了一块内存空间. print(id(add_end()),add_end())#还是原来的L的内存空间
结果:
2142204139528 ['we', 'we'] 2142204139528 ['we', 'we', 'we', 'we'] 2142204163016 [1, 'we'] 2142204139528 ['we', 'we', 'we', 'we', 'we', 'we']
动态参数
python中*和**的作用:
*args: 接收所有按位置传的参数。中的*要求python创建一个名为args的空元祖,然后将接受到的值封装到这个元组中.
def f(*w): print(w) f(1,3,4,5)
结果:
(1, 3, 4, 5)
**kwargs 接受所有按关键字传的参数,它中的**要求python创建一个名为kwargs的空字典,然后将所有的值都封装到这个字典中.
def f(**w): print(w) f(a=1,b=2,c=3)
结果:
{'a': 1, 'b': 2, 'c': 3}
分为两种用法:
1.定义函数时的应用,把传进来的参数放在元组中,注意:如果你传进来一个列表[1,2,3,4,5],print(args )后你会得到元组([1,2,3,4,5], )而不是元组(1,2,3,4,5),它并不会迭代的添加,所以for循环的时候只能循环1个数。
def f(*w): print(w) l=[1,2,3,4] f(l)
结果:
([1, 2, 3, 4],)
2.调用函数时使用,把传进来的迭代的参数拆开也就是加上*号,这也就是所谓的解包(Unpacking)
def f(*w): print(w) l=[1,2,3,4] f(*l)
结果:
(1, 2, 3, 4)
解包:把序列或映射中的每个元素提取出来。注意*后面跟序列,**后面跟映射。
打包: *args 或**kwargs把*解包传进来的值,打包成一个元组或字典.
例子:
l=[1,2,3,4,5] print("list",*l) d = {"hello": "world", "python": "rocks"} print("dic1",*d) print({**d}["python"])
结果:
list 1 2 3 4 5
dic1 hello python
rocks
谓的解包(Unpacking)实际上可以看做是去掉()
的元组或者是去掉{}
的字典。这一语法也提供了一个更加 Pythonic 地合并字典的方法:
user = {'name': "Trey", 'website': "http://treyhunner.com"} defaults = {'name': "Anonymous User", 'page_name': "Profile Page"} print({**defaults, **user})
{'page_name': 'Profile Page', 'name': 'Trey', 'website': 'http://treyhunner.com'
范例要求:写一个函数用来模拟len()的用法:
加*的情况:
def my_len(*args):
print("*args1",*args)
count=0
for i in args:
print("*args2",args)
count+=1
print(count)
my_len(*[1,2,3,4])
结果:
*args1 1 2 3 4 其实这里是一个元组(1,2,3,4) *args2 (1, 2, 3, 4) *args2 (1, 2, 3, 4) *args2 (1, 2, 3, 4) *args2 (1, 2, 3, 4) 4
不加*的情况:
def my_len(*args): print("*args",*args) count=0 for i in args: print("*args",args) count+=1 print(count) my_len([1,2,3,4])
结果:
*args [1, 2, 3, 4] 其实这里得到的是一个元组([1, 2, 3, 4] ,)
*args ([1, 2, 3, 4],)
1
**kwargs: 接收所有按关键字传的参数。返回的是一个字典。注意在函数中用的时候不用加**,只需要kargs.
命名空间
我们给这个“存放名字与值的关系”的空间起了一个名字——叫做命名空间
代码在运行伊始,创建的存储“变量名与值的关系”的空间叫做全局命名空间,在函数的运行中开辟的临时的空间叫做局部命名空间, 内边的东西只能在局部使用
命名空间的本质:存放名字与值的绑定关系的一个区域
局部命名空间(局部变量):当函数运行起来时创建的存放函数中变量和值关系的空间。
全局命名空间(全局变量):存放定义在函数外边的的变量和函数名的空间。这个全局命名空间必须在调用函数()的前面出现,否则就不是一个全局变量,会报错。
内置命名空间:存放python解释器刚启动就能运行的变量和函数名的空间。不用你自己定义,直接就能用,比如len(),int(),但是import os 不是内置命名函数
各个变量的加载顺序; python解释器运行起来》内置变量》全局变量》局部变量(在调用函数的时候产生,并且随着调用的结束而消失)。
作用域
作用域:作用范围。
全局作用域:内置变量和全局变量的使用的区域叫做全局作用域 也就是可以在全局使用.。
局部作用域:局部变量可以使用的区域仅能在局部使用。
注意:全局变量不可以使用局部变量,局部变量可以使用全局变量,但是不能修改全局变量(后边这句话有歧义)。
如果你传入的参数对象或引用的全局变量是不可变的对象:数字,元组,字符串。那么源对象是不会改变的
例子1:
d = [2,3] def f(): d.append(66) f() print(d)
结果:
[2, 3, 66]
例子二:
dic = [2,3] def f(d): d.append(66) f(dic) print(dic)
结果:
[2, 3, 66]
列子3
dic = 5 def f(d): d = 6 f(dic) print(dic)
结果:
5
局部作用域还可以嵌套更小的局部作用域。
作用域链:小范围作用域可以使用大范围的作用域,但是作用域链是单向的。
如何用一个全局变量函数调用一个局部变量函数?
运用函数嵌套可以解决这个问题,看范例
范例一
def func1(): def func2(): n=8 print(n) func2() func1() #全局函数来调用局部变量
结果;
8
globals():显示保存在全局作用域中的变量名和值。不管位置在哪里。
locals():local中的内容会根据它的位置来决定作用域中的内容,在局部作用域时,显示局部作用域中的变量名和值,在全局作用域中等于globasl。
范例
n=1 m=10 def func(): a=10 b=20 print("locals",locals()) print("globals",globals()) func()
结果:
globals {'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x000002015AEBB278>,
'__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, '__file__':
'C:/Users/stickerzhang/PycharmProjects/untitled3/text1/tt/text2.py', '__cached__': None, 'foo': <function foo at 0x000002015AF8E488>,
'outer': <function outer at 0x000002015AF8E510>, 'n': 1, 'm': 10, 'func': <function func at 0x000002015AF8E7B8>}
locals {'b': 20, 'a': 10}
范例二:
n=1 m=10 def func(): a=10 b=20 print("globals",globals()) print("global",locals()) func()
结果:
globals '__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x0000014D5456A1D0>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, '__file__': 'C:/Users/张守业/PycharmProjects/untitled/day9/global.py', '__cached__': None, 'n': 1, 'm': 10, 'func': <function func at 0x0000014D54486048>} locals {'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x0000014D5456A1D0>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, '__file__': 'C:/Users/张守业/PycharmProjects/untitled/day9/global.py', '__cached__': None, 'n': 1, 'm': 10, 'func': <function func at 0x0000014D54486048>}
global:我们知道在局部作用域中可以使用全局作用域中的变量和值,但是不能修改,如果要修改的话要用,global声明变量的作用域为全局作用域。注意这里的global修改的是全局的变量,不是局部的。
n=4 def f(): global n n=9 f() print(n)
结果:
9
例子二:
n =0
def f(): n=5 def f2(): global n n=n+1 f2() print('内层的',n) f() print('最外层的',n)
结果:
内层的 5 #没有修改最外层局部的n 最外层的 1 #修改的是全局的n
那如果要修改局部的n怎么,只能用nonlocal函数
nonlocal:闭包函数修改嵌套函数外边的函数中的变量值。python3中新添加的功能。
def f(): n=5 def f2(): n=n+1 f2() print(’外层的',n) f()
结果:
会报错
解决方法:
def f(): n=5 def f2(): nonlocal n n=n+1 f2() print('内层的',n) f()
结果:
内层的 6
函数是第一类对象:
第一类对象共有的特征:
1.函数名可以赋值给别的变量。
2.函数可以作为一个函数的参数。
3.函数可以作为一个函数的返回值
4.函数可以作为一个数据结构的元素。
范例:
def func(): print("in the func") return 10 def func2(arg): print("arg",arg()) ###打印一个函数也是对一个函数的调用 return arg f=func2(func)
结果:
in the func arg 10