函数是什么:
函数是指将一组语句的集合,通过一个名字(函数名)封装起来,想要执行这个函数,只需要调用具体函数名即可(函数名+())
特征:
1.减少重复代码
2.使程序变得可扩展
3.是程序变得易维护
语法定义:
def sayhi(): #函数名 print(‘Hello!‘) sayhi() #调用函数
带参数的函数
a,b = 5,8 c = a**b print(c) #用函数实现以上代码 def calc(x,y):#x,y为形参 res = x**y return res #将函数执行结果返回 c = calc(5,8) #将函数结果赋值给c,5,8为实参 print(c)
默认参数
#默认参数 def stu_register(name,age,country,course): print("----注册学生信息------") print("姓名:",name) print("age:",age) print("国籍:",country) print("课程:",course) stu_register("王山炮",22,"CN","python_devops") stu_register("张叫春",21,"CN","linux") stu_register("刘老根",25,"CN","linux") def stu_register(name,age,course,country="CN"): #country="CN" 就是默认参数,如调用时不指定,就默认为CN,如调用时指定,就使用指定值 print("registriation info...") print(name, age, course, country) stu_register("alex", 22, "python") stu_register("kris", 23, "python", "CN") stu_register("shanshan", 18, "python", "Korean")
关键参数 (必须放在位置参数之后)
正常情况下,给函数传参数要按顺序,不想按顺序就可以用关键参数,只需指定参数名即可(指定了参数名的参数就叫关键参数),但记住一个要求就是,关键参数必须放在位置参数(以位置顺序确定对应关系的参数)之后
def stu_register(name, age, course=‘PY‘ ,country=‘CN‘): print("----注册学生信息------") print("姓名:", name) print("age:", age) print("国籍:", country) print("课程:", course) stu_register("王山炮",course=‘PY‘, age=22,country=‘JP‘ ) #调用可以是这样 #但绝不可以是这样 stu_register("王山炮",course=‘PY‘, 22,country=‘JP‘ ) stu_register("王山炮", 22,age=2,5,country=‘JP‘ ) #这样相当于给age赋值2次
非固定参数(*args,**kwarge)
def stu_register(name, age, *args): #*args会把多传入的参数按元祖方式储存 print(name,age,args) stu_register("王山炮", 22 ) #输出 #王山炮 22()#后面这个()就是args,只是因为没有传值,所以为空 stu_register("Jack", 22, "CN", "Python") #输出 #Jack 22(‘CN‘ ‘Python‘)
def stu_register(name, age, *args, **kwargs): #**kwargs会把多传入的参数按dict方式储存 print(name,age,args,kwargs) stu_register("王山炮", 22 ) #输出 #王山炮 22(){}#后面这个{}就是kwargs,只是因为没有传值,所以为空 stu_register("Jack", 22, "CN", "Python", sex = "Male", province = "ShanDong") #输出 #Jack 22(‘CN‘ ‘Python‘){‘sex‘:‘Male‘, ‘province‘:‘ShanDong‘}
返回值 (return代表函数的终值)
外部的代码要想获取函数的执行结果,就可以在函数里用return语句把结果返回;return语句退出函数,并返回一个表达式。不带参数值的return语句返回None
全局与局部变量
定义在函数外部一级代码的变量,叫全局变量,全局能用; 局部变量就是指定义在函数里的变量,只能在局部生效; 在函数内部,可以引用全局变量;
如果全局和局部都有一个变量叫name,函数查找变量的顺序是由内而外的。
里边可以调用外边,但是不能修改。外边不能调用里边的变量。 局部只能引用全局,是修改不了的
在函数里修改全局变量(整体改,要加global)
name = "Black girl" def change_name(): global name #改为全局 name = "黑色的姑娘" age = 25 print("在" ,name ,"里面...", id(name)) change_name() print(name, id(name) )
在函数里修改列表数据 字典、列表、集合、类,不可以修改的有字符串
names = [‘alex‘ , ‘black girl‘ , ‘peiqi‘ ] def change_name(): del names[2] names[1] = "黑姑娘" #整体不可以修改,但里边内部可以改 print(names) change_name() print(names)
在函数中定义的变量称为局部变量,在程序的一开始定义的变量称为全局变量
全局变量作用域是整个程序,局部变量作用域是定义该变量的函数
当局部变量与全局变量同名时,在定义局部变量的函数内,局部变量起作用,在其他地方全局变量起作用
嵌套函数
#嵌套函数 #1 def func1(): print(‘alex‘) def func2(): print(‘eric‘) #它没有被调用所以就不会输出eric func1() #输出alex #2 def func1(): print(‘alex‘) def func2(): print(‘eric‘) func2() func1() #输出alex eric # ====>1.函数内部可以再次定义函数。 2.执行需要被调用
#3 def func1(): age = 73 print(age) def func2(): print(age) #它会去父级找 func2() func1() #输出 73 73
自己没有,就去父级找,没有再去爷爷级。由内向往,一层层的找。局部变量之间也是有等级关系的。
age = 19 def func1(): age = 73 def func2(): print(age) #输出 73 func2() func1() age = 19 def func1(): def func2(): print(age) #输出73 age = 73 #程序是从上往下读的,先读age,再去调用func2() func2() func1() age = 19 def func1(): global age #把age=19拿回了 def func2(): print(age) #19 func2() #当程序走到这的时候,调用func2()然后往上走,输入age=19,再往下走age=73 age = 73 #73 它已经是全局了 func1() print(age) #73 age = 19 def func1(): global age #把全局拿过来了 def func2(): print(age) #73 age = 73 #把全局的给改了,然后再往下走去执行func2() func2() func1() print(age) #73
匿名函数
匿名函数就是不需要显示的指定函数名
#这段代码 def calc(x,y): return x**y print(calc(2,5)) #32 #换成匿名函数 calc = lambda x,y:x**y print(calc(2,5))
data = list(range(10)) print(data) #[0, 1, 2, 3, 4, 5, 6, 7, 8, 9] #for index,i in enumerate(data): # data[index] = i * i #print(data) #[0, 1, 4, 9, 16, 25, 36, 49, 64, 81] def f2(n): return n *n print(list(map(f2, data))) #map函数 #[0, 1, 4, 9, 16, 25, 36, 49, 64, 81] print(list(map(lambda x:x*x, data))) #[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
高阶函数
变量可以指向函数,函数的参数能够接收变量,那么一个函数就可以接收另一个函数作为参数,这种函数就称之为高阶函数
def calc(x): return x*x #f = lambda x:x*x #变量指向匿名函数 f = calc #变量不光可以赋值,可以指向函数,什么都可以赋值; f() def calc(x): return x*x n = 10 calc(n) #函数的参数能接收变量 print(calc(n)) #100 def func(x,y): return x+y def calc(x): return x #x()可以执行这个函数; x=func n = func calc(n) #一个函数可以接收另一个函数作为参数 def func(x,y): return x+y def calc(x): #把func当做参数传给了x,又返回了;不返回pass也是高阶函数 return x f = calc(func) print(f(5,9)) //14; 上一步执行calc这个函数的时候把x返回了,也就是把func这个函数给返回了,返回给外部使用;相当于执行的是func(5,9) def func2(x,y): return abs,x,y #返回另外一个函数 res = func2(3,-10) print(res)
只需要满足以下任意一个条件,即是高阶函数:1.接受一个或多个函数作为输入,2.return返回另一个函数
def func(x,y): return x+y def calc(x): return x f = calc(func) print(f(5,9)) //14
def func2(x,y): return abs,x,y res = func2(3,-10) print(res)
递归
在函数内部,可以调用其他函数。如果一个函数在内部调用自身本身,这个函数就是递归函数。有最大递归深度限制,递归调用的次数过多,会导致栈溢出,所以限制。
def calc(n): print(n) #10 5 2 1 if int(n/2) ==0: #加一个判断条件就不用再往下递归了 return n return calc(int(n/2)) calc(10)
def calc(n): v = int(n/2) print(v) #5 2 1 if v > 0: calc(v) print(n)#0 1 2 5 10 它会一层层再出来,从内到外 calc(10)
递归执行过程
递归特性:
- 必须有一个明确的结束条件
- 每次进入更深一层递归时,问题规模相比上次递归都应有所减少
- 递归效率不高,递归层次过多会导致栈溢出(在计算机中,函数调用是通过栈(stack)这种数据结构实现的,每当进入一个函数调用,栈就会加一层栈帧,每当函数返回,栈就会减一层栈帧。由于栈的大小不是无限的,所以,递归调用的次数过多,会导致栈溢出)
二分查找
data_set = [1,3,4,6,7,8,9,10,11,13,14,16,18,19,21] def mid_search(n,low,high,data_set): mid = int((low+high)/2) if low == high: print(‘错误‘) return elif data_set[mid] > n: print(‘从左侧找‘,n,low,high,data_set[mid]) return mid_search(n,low,mid,data_set) elif data_set[mid] < n: print(‘从右侧找‘,n,low,high,data_set[mid]) return mid_search(n,mid+1,high,data_set) else: print(‘找到‘,data_set[mid],mid) mid_search(21,0,len(data_set),data_set)
函数进阶
命名空间
又名name space, 顾名思义就是存放名字的地方,存什么名字呢?举例说明,若变量x=1,1存放于内存中,那名字x存放在哪里呢?名称空间正是存放名字x与1绑定关系的地方
名称空间共3种,分别如下
- locals: 是函数内的名称空间,包括局部变量和形参
- globals: 全局变量,函数定义所在模块的名字空间
- builtins: 内置模块的名字空间
不同变量的作用域不同就是由这个变量所在的命名空间决定的。
作用域即范围
- 全局范围:全局存活,全局有效
- 局部范围:临时存活,局部有效
查看作用域方法 globals(),locals()
作用域查找顺序
LEGB:
L:locals ; E:enclosing相邻的 ; G:globls ; B:builtins
n = 10 def func(): n = 20 print(‘func:‘ ,n) #func:20 def func2(): n = 30 print(‘ func2‘ ,n) #func2:30 def func3(): print(‘func3:‘ , n) #func3:30 func3() func2() func()
LEGB 代表名字查找顺序: locals -> enclosing function -> globals -> __builtins__
- locals 是函数内的名字空间,包括局部变量和形参
- enclosing 外部嵌套函数的名字空间
- globals 全局变量,函数定义所在模块的名字空间
- builtins 内置模块的名字空间
闭包
在函数里边又套了一层子函数,在子函数被返回了,就是当外层函数执行的时候子函数被返回了返回了内存地址;然后在外边执行这个子函数的时候它又引用了外边函数的这个变量,相当于这个子函数跟外层函数有某种扯不清的关系,这种关系就叫闭包。
关于闭包,即函数定义和函数表达式位于另一个函数的函数体内(嵌套函数)。而且,这些内部函数可以访问它们所在的外部函数中声明的所有局部变量、参数。当其中一个这样的内部函数在包含它们的外部函数之外被调用时,就会形成闭包。也就是说,内部函数会在外部函数返回后被执行。而当这个内部函数执行时,它仍然必需访问其外部函数的局部变量、参数以及其他内部函数。这些局部变量、参数和函数声明(最初时)的值是外部函数返回时的值,但也会受到内部函数的影响。
装饰器
软件开发中的一个原则“开放-封闭”原则,简单来说,它规定已经实现的功能代码不允许被修改,但可以被扩展,即:
- 封闭:已实现的功能代码块不应该被修改
- 开放:对现有功能的扩展开放
#_*_coding:utf-8_*_ user_status = False #验证标识,用户登录成功了就把这个改成True def login(func): #把要执行的模块从这里传进来 def inner(*args,**kwargs):#再定义一层函数 _username = "alex" #假装这是DB里存的用户信息 _password = "abc!23" #假装这是DB里存的用户信息 global user_status if user_status == False: username = input("user:") password = input("pasword:") if username == _username and password == _password: print("welcome login....") user_status = True else: print("wrong username or password!") if user_status == True: func(*args,**kwargs) # 看这里看这里,只要验证通过了,就调用相应功能 return inner #用户调用login时,只会返回inner的内存地址,下次再调用时加上()才会执行inner函数 def home(): print("---首页----") @login def america(): #login() #执行前加上验证 print("----欧美专区----") def japan(): print("----日韩专区----") # @login def henan(style): ‘‘‘ :param style: 喜欢看什么类型的,就传进来 :return: ‘‘‘ #login() #执行前加上验证 print("----河南专区----") home() # america = login(america) #你在这里相当于把america这个函数替换了 henan = login(henan) # #那用户调用时依然写 america() henan("3p")
#带参数的装饰器 #_*_coding:utf-8_*_ user_status = False #用户登录了就把这个改成True def login(auth_type): #把要执行的模块从这里传进来 def auth(func): def inner(*args,**kwargs):#再定义一层函数 if auth_type == "qq": _username = "alex" #假装这是DB里存的用户信息 _password = "abc!23" #假装这是DB里存的用户信息 global user_status if user_status == False: username = input("user:") password = input("pasword:") if username == _username and password == _password: print("welcome login....") user_status = True else: print("wrong username or password!") if user_status == True: return func(*args,**kwargs) # 看这里看这里,只要验证通过了,就调用相应功能 else: print("only support qq ") return inner #用户调用login时,只会返回inner的内存地址,下次再调用时加上()才会执行inner函数 return auth def home(): print("---首页----") @login(‘qq‘) def america(): #login() #执行前加上验证 print("----欧美专区----") def japan(): print("----日韩专区----") @login(‘weibo‘) def henan(style): ‘‘‘ :param style: 喜欢看什么类型的,就传进来 :return: ‘‘‘ #login() #执行前加上验证 print("----河南专区----") home() # america = login(america) #你在这里相当于把america这个函数替换了 #henan = login(henan) # #那用户调用时依然写 america() # henan("3p")
生成器
列表生成器
直接对列表里边的值修改
>>>a = [ i*i for i in range(10)] >>>a [0, 1, 4, 9, 16, 25, 36, 49, 64, 81] >>>a = list(range(10)) >>>a [0, 1, 2, 3, 4, 5, 6, 7, 8, 9,] >>>a = [ i if i < 5 else i*i for i in a] >>>a [0, 1, 2, 3, 4, 25, 36, 49, 64, 81 ]
yield
语句可以让普通函数变成一个生成器,并且相应的 __next__()
方法返回的是 yield
后面的值。
一种更直观的解释是:程序执行到 yield
会返回值并暂停,再次调用 next()
时会从上次暂停的地方继续开始执行。
>>>a2 = (i for i in range(1000)) >>>a2 <generator object <genexpr> at 0x1014a73b8> ##生成了一个生成器 >>>next(a2) >>>next(a2) >>>next(a2) 用for循环 >>> g = (x * x for x in range(10)) >>> for n in g: ... print(n) ... 1 9 25 49 81
斐波那切数列
def feibo(max): n,a,b = 0,0,1 while n<max: print(b) a, b = b, a + b n = n + 1 return ‘done’ >>> fib(10) ##用函数写生成器 def fib(max): n,a,b = 0,0,1 while n < max: #print(b) yield b a,b = b,a+b n += 1 return ‘done‘ >>> f = fib(6) >>> f <generator object fib at 0x104feaaa0>
生成器调用方法
python2: range = list Python3: range = 生成器
xrange = 生成器 xrange | 没有
生成器send方法
只要函数里边有yield就是生成器了;
迭代器
范围比生成器大
可以直接作用于for
循环的数据类型有以下几种:
一类是集合数据类型,如list
、tuple
、dict
、set
、str
等;
一类是generator
,包括生成器和带yield
的generator function。
这些可以直接作用于for
循环的对象统称为可迭代对象:Iterable
。
凡是可作用于for
循环的对象都是Iterable(可迭代)
类型;凡是可作用于next()
函数的对象都是Iterator(迭代器)
类型,它们表示一个惰性计算的序列;集合数据类型如list
、dict
、str
等是Iterable
但不是Iterator
,不过可以通过iter()
函数获得一个Iterator
对象。
可以使用isinstance()
判断一个对象是否是Iterable
对象:
>>> from collections import Iterable >>> isinstance([], Iterable) True >>> isinstance({}, Iterable) True >>> isinstance(‘abc‘, Iterable) True >>> isinstance((x for x in range(10)), Iterable) True >>> isinstance(100, Iterable) False
而生成器不但可以作用于for循环,还可以被next()函数不断调用并返回下一个值,直到最后抛出StopIteration错误表示无法继续返回下一个值了。
*可以被next()函数调用并不断返回下一个值的对象称为迭代器:Iterator。
可以使用isinstance()判断一个对象是否是Iterator对象:
>>> from collections import Iterator >>> isinstance((x for x in range(10)), Iterator) True >>> isinstance([], Iterator) False >>> isinstance({}, Iterator) False >>> isinstance(‘abc‘, Iterator) False
创建一个迭代器有3种方法,其中前两种分别是:
- 为容器对象添加
__iter__()
和__next__()
方法(Python 2.7 中是next()
);__iter__()
返回迭代器对象本身self
,__next__()
则返回每次调用next()
或迭代时的元素; - 内置函数
iter()
将可迭代对象转化为迭代器; - 生成器,生成器通过
yield
语句快速生成迭代器,省略了复杂的__iter__()
&__next__()
方式。
创建迭代器对象的好处是当序列长度很大时,可以减少内存消耗,因为每次只需要记录一个值即可。
原文地址:https://www.cnblogs.com/bj-mr-li/p/9685075.html