函数先定义函数,后调用
一、定义函数:
def 函数名(param1,param2……): """ 函数功能的描述信息 :param1:描述 :param2:描述 :return:返回值 """ code ... return 返回值
1、无参函数
def register(): """注册功能""" username = input('username: ').strip() pwd = input('password: ').strip() with open('32.txt', 'a', encoding='utf8') as fa: fa.write(f"{username}:{pwd}\n") fa.flush()
2、有参函数
def sum_self(x, y): """求和""" res = x+y print(res) sum_self(1,2) # 3
3、空函数
你只知道你需要实现某个功能,但不知道该如何用代码实现时,你可以暂时写个空函数
def func(): pass
二、调用函数及返回值
函数名(param1、param2……)
如:
register()
1、函数运行完毕所有代码,如果函数体不写return,则会返回None。
def foo(): pass print(foo()) #None
2、函数可以返回一个或多个值
def func(): name = 'nick' age = 19 hobby_list = ['read', 'run'] return name, age, hobby_list name, age, hobby_list = func() print(name,age,hobby_list) #('nick', 19, ['read', 'run'])
三、函数的参数
1、普通参数
(1)位置形参
在函数定义阶段,按照从左到右的顺序依次定义的形参,称之为位置形参。
特点:按照位置定义的形参,都必须被传值,多一个不行,少一个也不行。
def func(x, y): print(x) print(y)
(2)位置实参
在函数调用阶段,按照从左到右的顺序依次定义的实参,称之为位置实参。
特点:按照位置为对应的形参依次传值。
func(1, 2)
(3)关键字实参
在调用函数时,按照key=value的形式为指定的参数传值,称为关键字实参。
特点:可以打破位置的限制,但仍能为指定的形参赋值。
func(y=2, x=1)
- 可以混用位置实参和关键字实参,但是位置实参必须在关键字实参的左边。
- 可以混用位置实参和关键字实参,但不能对一个形参重复赋值。
func(x, y=2) func(y=2, x) # SyntaxError: positional argument follows keyword argument func(x, x=1) # NameError: name 'x' is not defined
(4)默认形参
在定义阶段,就已经被赋值。意味着在调用时可以不用为其赋值。位置形参必须放在默认形参的左边。
def func(x, y=10): print(x) print(y) func(2)
默认形参的值只在定义阶段赋值一次,也就是说默认参数的值在函数定义阶段就已经固定了。
m = 10 def foo(x=m): print(x) m = 111 foo() # 10
默认参数的值通常应该是不可变类型。
def register(name, hobby, hobby_list=None): hobby_list = [hobby] print(f"{name} prefer {hobby_list}") register('nick', 'read') # ['read'] register('tank', 'zuipao') # [ 'zuipao'] register('jason', 'piao') # ['piao'] # 演示形参是可变类型,(列表是可变类型) def register(name, hobby, hobby_list=None): hobby_list = [hobby] print(f"{name} prefer {hobby_list}") register('nick', 'read') # nick prefer ['read'] register('tank', 'zuipao') # tank prefer ['zuipao'] register('jason', 'piao') # jason prefer ['piao']o']
2、可变长参数
(1)、可变长形参之*
形参中的*会将溢出的位置实参全部接收,然后存储元组的形式,然后把元组赋值给*后的参数。需要注意的是:*后的参数名约定俗成为args。
def sum_self( *args): res = 0 for num in args: res += num return res res = sum_self(1, 2, 3, 4) print(res) # 10
(2)、可变长实参之*
实参中的*,*会将*后参数的值循环取出,打散成位置实参。以后但凡碰到实参中带*的,它就是位置实参,应该马上打散成位置实参去看。
def func(x, y, z, *args): print(x, y, z, args) func(1, *(1, 2) , 3, 4) # 1 1 2 (3, 4)
(3)、可变长形参之**
形参中的**会将溢出的关键字实参全部接收,然后存储字典的形式,然后把字典赋值给**后的参数。需要注意的是:**后的参数名约定俗成为kwargs。
def func( **kwargw): print(kwargw) func(a=5) # {'a': 5}
(4)、可变长实参之**
实参中的**,**会将**后参数的值循环取出,打散成关键字实参。以后但凡碰到实参中带**的,它就是关键字实参,应该马上打散成关键字实参去看。
def func(x, y, z, **kwargs): print(x, y, z, kwargs) func(1, 3, 4, **{'a': 1, 'b': 2} ) # 1 3 4 {'a': 1, 'b': 2}
(5)、可变长参数应用
def index(name, age, sex): print(f"name: {name}, age: {age}, sex: {sex}") # name: nick, age: 19, sex: male def wrapper(*args, **kwargs): print(f"args: {args}") # args: () print(f"kwargs: {kwargs}") # kwargs: {'name': 'nick', 'sex': 'male', 'age': 19} index( *args, **kwargs) wrapper(name='nick', sex='male', age=19)
(6)、命名关键字形参
命名关键字则是在「位置参数」和「命名关键字参数」中使用,*,隔开,后面的即为命名关键字。
def student(name, age, *, city, gender): print(name, age, city, gender) student('xiaoming', 6, city='beijing', gender='male') # xiaoming 6 beijing male student('xiaoming', 6, 'beijing', 'male') #TypeError: student() takes 2 positional arguments but 4 were given
特点:在传值时,必须按照key=value的方式传值,并且key必须命名关键字参数的指定的参数名。
def register(x, y, *, name, gender='male', age): print(x) print(name) print(age) register(1, 2, name1='nick', age=19) # TypeError: register() got an unexpected keyword argument 'name1'
四、函数对象
函数是第一类对象,即函数可以被当做数据处理。
def func(): print('from func') print(func) #
1、函数当作参数传给一个另一函数
def func(): print('from func') def foo(m): m() foo(func) # from func
2、函数当作另一函数的返回值
def func(): print('from func') def foo(x): return x res = foo(func) print(res) #
3、函数可以当作容器类型的元素
def func(): print('from func') function_list = [func] function_list[0]() # from func
五、名称空间和作用域
- 内置名称空间:存放内置的名字,如
len/eval/enumerate/bytes/max/min/sorted/map/filter....
- 全局名称空间:除了内置与局部,其他的名字都存放在全局名称空间内
- 局部名称空间:函数内部的名字都是局部名称空间,不同函数内部的名字互不干涉
- 查找顺序:从当前开始往上寻找,如果当前是局部名称空间,查找顺序为:局部--》全局--》内置
- 执行顺序:先内置(Python解释器启动的时候才会生成)--》全局(文件执行的时候才会生成)--》局部(函数调用的时候才会生成)
作用域:全局名称空间和局部名称空间中可能会存在名字相同的变量,但是这两个变量互不影响。
全局作用域:全局有效,全局存活,包含内置名称空间和全局名称空间。
局部作用域:局部有小,临时存储,只包含局部名称空间。
x = 1 def func(): print(x) #10 x = 10 func()
作用域关系在函数定义阶段就固定死了,与函数的调用无关。
# 作用域注意点 x = 1 def f1(): # 定义阶段x=1 print(x) #1 def f2(): x = 2 f1() f2()
1、引用另一函数
def f1(): print('from inner') f = f1 f() # from inner
2、函数对象+作用域应用
def f1(): def inner(): print('from inner') return inner f = f1() # from inner 。把局部定义的函数inner()放在全局之中 def bar(): f() bar()
3、global关键字修改全局作用域中的变量
x = 1 def f1(): x = 2 def f2(): global x # 修改全局 x = 3 f2() f1() print(x) # 3
4、nonlocal关键字修改局部作用域中的变量。
x = 1 def f1(): x = 2 def f2(): nonlocal x x = 3 f2() print(x) # 3 f1()