Python函数基础
函数
1.什么是函数
函数是组织好的,可重复使用的,用来实现单一,或相关联功能的代码段。让繁琐的事情更加简洁。
def func(): print('这就是一个函数')
2.函数的定义和调用
定义:def 关键词开头,空格之后接函数名称和圆括号( ),最后还有一个":"。
def 是固定的,不能变,他就是定义函数的关键字(用来表示一个函数。)。
空格 为了将def关键字和函数名分开,必须空(四声),当然你可以空2格、3格或者你想空多少都行,但正常人还是空1格。
函数名:函数名只能包含字符串、下划线和数字且不能以数字开头。虽然函数名可以随便起,但我们给函数起名字还是要尽量简短,并能表达函数功能
括号:是必须加的,先别问为啥要有括号,总之加上括号就对了!
注释:每一个函数都应该对功能和参数进行相应的说明,应该写在函数下面第一行。以增强代码的可读性。
调用:就是 函数名() 要记得加上括号。
def func(): #def(define)要和函数名之间有空格 print('这就是一个函数') #这是函数内的架构 func() #调用函数
3.函数的返回值
没有返回值:不写return的情况下,会默认返回一个None:没有写return,这就是没有返回值的一种情况。
- 遇到return函数就会结束,不会继续进行
def jianh(): print('今天是') print('2018年-10月-30日') return '我就不告诉你时间' #return可以返回多个结果 jianh() #今天是 \n 2018年-10月-30日 print(jianh()) #今天是 \n 2018年-10月-30日 \n 我就不告诉你时间(没有print就不会执行return)
有一个返回值
有多个返回值
4.函数的参数
- 实参 :调用时传递的参数为实际参数
- 形参 :变量定义的参数为形式参数
- 位置参数
- 默认参数
#默认参数 这时只用传一个参数就可以了,默认参数已被定义 def defult(a,b=10): print(a,b) defult(1)
- 动态参数(*args,**kwargs)都必须在参数的最后定义
#args 重复取参数 def sum(*args): s=0 for i in args: s+=i return s print(sum(1,2,3,4,5,6,7,8,9,))
# **kwargs 重复取可定义的参数 def duocan(**kwargs): print(kwargs) duocan(a=1,b=2,c=3)
参数陷阱
5.命名空间和作用域
1. 名称空间, 局部名称空间, 全局名称空间, 作用域, 加载顺序.
- 在Python解释器开始执行之后, 就会在内存中开辟一个空间, 每当遇到一个变量的时候, 就 把变量名和值之间的关系记录下来,
- 当遇到函数定义的时候, 解释器只是把函数名读入内存, 表示这个函数存在了, 至于函数内部的变量和逻辑, 解释器是不关心的.
- 一开始的时候函数只是加载进来, 仅此而已, 只有当函数被调用和访问的时候, 解释器才会根据函数内部声明的变量来进行开辟变量的内部空间.
- 函数执行完毕, 这些函数内部变量占用的空间也会随着函数执行完毕而被清空
(1)命名空间 :存放名字和值的关系的空间,变量在存储的时候就是存储在这片空间中的.
- 全局命名空间-- 直接在py文件中, 函数外声明的变量都属于全局命名空间
- 局部命名空间-- 在函数中声明的变量会放在局部命名空间
- 内置命名空间-- 存放python解释器为我们提供的名字, list, tuple, str, int这些都是内置命名空间
(2)加载顺序&取值顺序:
- 加载顺序: 内置命名空间>全局命名空间 >局部命名空间(函数被执行的时候)
- 取值顺序: 局部命名空间 >全局命名空间 >内置命名空间
(3)作用域 就是作用范围, 按照生效范围来看分为全局作用域和局部作用域
- 全局作用域: 全局命名空间 + 内置命名空间 通过globals()函数来查看全局作用域中的内容
- 局部作用域: 局部命名空间 通过locals()来查看局部作用域中的变量和函数信息
2.函数的嵌套
def outer(): def inner(): return inner return outer
1 def mysql(): 2 print('in mysql') 3 def mysql2(): 4 print('in mysql2') 5 mysql2() 6 mysql()
nonlocal关键字
1.外部必须有这个变量
2.在内部函数声明nonlocal变量之前不能再出现同名变量
3.内部修改这个变量如果想在外部有这个变量的第一层函数中生效
6.闭包(第一类对象???)
定义:闭包是由函数及其相关的引用环境组合而成的实体(即:闭包=函数+引用环境)
(想想Erlang的外层函数传入一个参数a, 内层函数依旧传入一个参数b, 内层函数使用a和b, 最后返回内层函数)。
函数可以作为另一个函数的参数或返回值,接受另一个函数作为输入并调用他。
def foo(): a = [1] def bar(): a[0] = a[0] + 1 return a[0] return bar
7.装饰器warpper
装饰器是一个函数,其主要用途是包装一个函数或类。这种包装的首要目的是透明的修改或增强被包装对象的行为。表示装饰器的语法是特殊符号@。
最简单的装饰器
def a(func): print(1) def b(): print(2) func() print(3) return b @a #语法糖Syntactic sugar 调用装饰器 def c(): print(4) c()
'''
1
2
4
3
'''
装饰器模板(加入参数的装饰器)
def a(func): print(1) def b(*args,**kwargs): print(2) func(*args,**kwargs) #传所有的参数,让别人说去吧 print(3) return b #ruturn b是不能带括号 @a def c(a,b): print(a+b) c(3,4) ''' 1 2 7 3 '''
基本搞懂
装饰器为什么不带括号???
8.生成器与yield
函数使用yield关键字可以定义生成器对象。生成器是一个函数,它生成一个值的序列。以便在迭代中使用。生成器的本质就是迭代器。
def countdown(n): print('Counting down from %d'% n ) while n>0: yield n n -= 1 return c=countdown(10) #如果调用函数,就会发现其中的代码不会执行。 #这是为什么呢,老李捋了捋他的胡子说:这就告诉你。
它会返回一个生成器的对象。接着,该生成器对象就会在__next__()被调用时执行函数。
调用__next__()时,生成器将不断执行语句,直至遇到yield为止。yield语句在函数执行停止的地方生成一个结果,直到再次调用__next__()。然后继续执行yield之后的语句。
通常不会在生成器上直接调用__next__()方法,而是在for语句、sum()或一些使用序列其他操作中使用。
send方法 send和__next__()一样都可以让生成器执行到下一个yield
def eat(): print("我吃什什么啊") a = yield "馒头" print("a=",a) b = yield "⼤大饼" print("b=",b) c = yield "⾲菜盒⼦" print("c=",c) yield "GAME OVER" gen = eat() # 获取生成器 ret1 = gen.__next__() print(ret1) #我吃什什么啊 馒头 ret2 = gen.send("胡辣汤") print(ret2) #a= 胡辣汤 ⼤大饼 ret3 = gen.send("狗粮") print(ret3) #b= 狗粮 ⾲菜盒⼦ ret4 = gen.send("猫粮") print(ret4) #c= 猫粮 GAME OVER
send和__next__()区别:
- 1. send和next()都是让生成器向下走1次
- 2. send可以给上⼀个yield的位置传递值, 不能给后⼀个yield发送值. 在第⼀次执行生成器代码的时候不能使用send()
推导式——
(1) 列表推导式 [结果 for循环 if筛选]
#列表中取出偶数 list1=[1,2,3,4,5,6,7,8,9,10,11,12,13,1,4] res=[i for i in list1 if i%2==0] print(res)
(2)字典推导式 {key: value for循环 if 筛选}
#与zip功能相似 lst1 = ["东北", "陕西", "山西", "开封", "杭州", "广东", "济南"] lst2 = ['大拉皮', "油泼面", "老陈醋", "灌汤包", "西湖鲤鱼", "早茶", "胶东一锅鲜"] dic = {lst1[i]:lst2[i] for i in range(len(lst1))} print(dic)
(4)集合推导式
lst = ["2","4","5","3","4","4","2","3"] s = {el for el in lst} print(s) #起到去重的作用