1 什么是函数?
函数就是具备某一个功能的工具,
----需要工具时,需要提前准备好,然后拿来就用且可以重复使用
所以
函数需要先定义,再使用
2 为什么要用函数?
1,代码的组织结构不清晰,可读性差
2,遇到重复的功能只能重复编写实现代码,代码冗余
3,功能需要扩展时,需要找出所有实现该功能的地方修改之,无法统一管理且维护难度极大
3 函数的分类:内置函数与自定义函数
内置函数:python解释器已经为我们定义好了的函数
自定义函数:我们自己根据需求,事先定制好的我们自己的函数,来实现某种功
4 如何自定义函数
语法
def 函数名(参数1,参数2,参数3.......):
"""注释"""
函数体
return 返回的值
# 函数名要能反映其意义
函数使用的原则:先定义,再调用
函数即"变量","变量"必须先定义后引用。未定义而直接引用函数,就相当于在引用一个不存在的变量名
函数的使用,必须遵循原则:先定义,后调用
###我们在使用函数时,一定要明确区分定义阶段和调用阶段###
#定义阶段
def foo():
print('from foo')
bar()
def bar():
print('from bar')
#调用阶段
foo()
定义有参数函数,及有参函数的应用场景
有参:需要根据外部传进来的参数,才能执行相应的逻辑,比如统计长度等
def tell_tag(tag,n): #有参数
print (tag*n)
定义无参数函数,及无参函数的应用场景
无参:应用场景仅仅只是执行一些操作,比如用户交互,打印
def tell_msg(): #无参数
print ('hello world') 定义空函数,及空函数的应用场景
设计代码结构
#结论:
#1,定义是无参,意味着调用时也无需传入参数
#2,定义是有参,意味着调用时则必须传入参数
函数在定义阶段:
只检测语法,不执行代码
--语法错误在函数定义阶段就会检测出来,而代码的逻辑错误只有在执行时才会知道
5 调用函数 如何调用函数
函数的调用:函数名加括号
1,先找到名字
2,根据名字调用代码
函数的返回值
无return--->None
return 1个值->返回1个值
return 逗号分隔多个值 -->返回元组
什么时候该有返回值:
调用函数,经过一系列的操作,最后拿到一个明确的结果,则必须要有返回值
通常有参函数需要有返回值,输入参数,经过计算,得到一个最终的结果
什么时候不需要有返回值:
调用函数,仅仅只是执行一系列的操作,最后不需要得到什么结果,则无需有返回值
通常无参函数不需要有返回值
函数调用的三种形式
1,语句形式:foo()
2,表达式形式:3*len(’hello‘)
3,当中另外一个函数的参数:range(len(’hello‘))
函数参数的应用:
形参和实参
形参即变量名,实参即变量名,函数调用时,将值绑定到变量名上,函数调用结束,解除绑定
位置参数:按照从左到右的顺序定义的参数 位置形参:必选参数 位置实参:按照位置给形参传值
关键字参数:按照key=value的形式定义的实参
无需按照位置为形参传值
注意的问题:
1,关键字实参必须在位置实参后面
2,对同一个形参不能重复传值
默认参数:形参在定义时就已经为其赋值
可以传值也可以不传值,经常需要变的参数定义成位置形参,变化较小的参数定义成默认参数(形参)
注意的问题:
1,只在定义的时候赋值一次
2,默认参数的定义应该在位置形参右边
3,默认参数通常应该定义成不可变类型
可变长参数:
可变长指的是实参值得个数不固定
而实参有按位置和按关键字两种形式定义,针对这两种形式的可变长,形参对应有两种解决方案来完整的存放它们,
分别是 *args,**kwargs(args,kwargs只是约定俗成的表示,没有特殊意义)
===========*args=========== def foo(x,y,*args): print(x,y) print(args) foo(1,2,3,4,5) def foo(x,y,*args): print(x,y) print(args) foo(1,2,*[3,4,5]) def foo(x,y,z): print(x,y,z) foo(*[1,2,3]) ===========**kwargs=========== def foo(x,y,**kwargs): print(x,y) print(kwargs) foo(1,y=2,a=1,b=2,c=3) def foo(x,y,**kwargs): print(x,y) print(kwargs) foo(1,y=2,**{'a':1,'b':2,'c':3}) def foo(x,y,z): print(x,y,z) foo(**{'z':1,'x':2,'y':3}) ===========*args+**kwargs=========== def foo(x,y): print(x,y) def wrapper(*args,**kwargs): print('====>') foo(*args,**kwargs)
命名关键字参数:
* 后定义的参数,必须被传值(有默认值得除外),且必须按照关键字实参的形式传递
可以保证,传入的参数中一定包含某些关键字
def foo(x,y,*args,a=1,b,**kwargs): print(x,y) print(args) print(a) print(b) print(kwargs) foo(1,2,3,4,5,b=3,c=4,d=5) 结果: 1 2 (3, 4, 5) 1 3 {'c': 4, 'd': 5
6 高阶函数(函数对象)
函数是第一类对象,即函数可以当做数据传递
1,函数可以被引用
2,函数可以当做参数传递
3,函数的返回值可以是函数
4,函数可以当做容器类型的元素
利用这些特性,可以取代多分支的if
1 def foo(): 2 print('foo') 3 4 def bar(): 5 print('bar') 6 7 dic={ 8 'foo':foo, 9 'bar':bar, 10 } 11 while True: 12 choice=input('>>: ').strip() 13 if choice in dic: 14 dic[choice]()
7 函数嵌套
函数的嵌套调用
1 def max(x,y): 2 return x if x > y else y 3 4 def max4(a,b,c,d): 5 res1=max(a,b) 6 res2=max(res1,c) 7 res3=max(res2,d) 8 return res3 9 print(max4(1,2,3,4))
8 作用域与名称空间
名称空间:存放名字的地方,三种空间:内置名称空间,全局名称空间,局部名称空间 存放的是名字和内存地址的对应关系
名称空间的加载顺序
python test.py #1、python解释器先启动,因而首先加载的是:内置名称空间 #2、执行test.py文件,然后以文件为基础,加载全局名称空间 #3、在执行文件的过程中如果调用函数,则临时产生局部名称空间
名字的查找顺序
局部名称空间--全局名称空间--内置名称空间
注意:在全局无法查看局部的,在局部可以查看全局的
作用域:作用域即范围
-全局范围:(内置名称空间与全局名称空间属于该范围):全局存活,全局有效
-局部范围:(局部名称空间属于该范围):临时存活,局部有效
查看作用域: globals(),locas()
1 LEGB 代表名字查找顺序: locals -> enclosing function -> globals -> __builtins__ 2 locals 是函数内的名字空间,包括局部变量和形参 3 enclosing 外部嵌套函数的名字空间(闭包中常见) 4 globals 全局变量,函数定义所在模块的名字空间 5 builtins 内置模块的名字空间
闭包函数
内部函数包含对外部作用域而非全局作用域的引用
1 def index(url): 2 def get(): 3 return urlopen(url).read() 4 return get
1 #闭包的意义:返回的函数对象,不仅仅是一个函数对象,在该函数外还包裹了一层作用域,这使得,该函数无论在何处调用,优先使用自己外层包裹的作用域 2 #应用领域:延迟计算(原来我们是传参,现在我们是包起来) 3 from urllib.request import urlopen 4 5 def index(url): 6 def get(): 7 return urlopen(url).read() 8 return get 9 10 baidu=index('http://www.baidu.com') 11 print(baidu().decode('utf-8'))
9 装饰器
为什么要用装饰器?
开放封闭原则:对修改封闭,对扩展开放
什么是装饰器?
装饰器本身是任意可调用对象,被装饰着也可以是任意可调用对象
装饰器的原则:
1,不修改被装饰对象的源代码
2,不修改被装饰对象的调用方式
3,在遵循1和2的前提下,为被装饰对象添加新功能
装饰器的基本语法:(任何装饰器都可以套用)
def foo(func): def wrappers(*args,**kwargs): res = func() return res return wrappers
无参装饰器
import time def timmer(func): def wrapper(*args,**kwargs): start_time=time.time() res=func(*args,**kwargs) stop_time=time.time() print('run time is %s' %(stop_time-start_time)) return res return wrapper @timmer def foo(): time.sleep(3) print('from foo') foo()
有参装饰器
def auth(driver='file'): def auth2(func): def wrapper(*args,**kwargs): name=input("user: ") pwd=input("pwd: ") if driver == 'file': if name == 'egon' and pwd == '123': print('login successful') res=func(*args,**kwargs) return res elif driver == 'ldap': print('ldap') return wrapper return auth2 @auth(driver='file') def foo(name): print(name) foo('egon')
10 迭代器与生成器及协程函数
1,迭代器就是迭代的工具,迭代就是一个重复的过程,每次重复就是一次迭代,
并且每次迭代的结果都是下一次迭代的初始值
2,为什么要有迭代器?
对于没有索引的数据类型,迭代器提供了一种不依赖索引的迭代方式
3,可迭代对象
内置有__iter__方法的对象,即obj.__iter__
可迭代对象:字符串,列表,元组,字典,集合,文件
4,迭代器对象
可迭代对象执行__iter__得到的结果就是迭代器对象
迭代器对象指的是既内置有__iter__又内置有__next__方法的对象
文件类型是迭代器对象
dic={'a':1,'b':2,'c':3} iter_dic=dic.__iter__() #可迭代对象执行__iter__方法得到的结果就是迭代器对象 #迭代器对象内置有__iter__方法和__next__方法 iter_dic.__iter__() id iter_dic # 迭代器执行__iter__()得到的仍然是迭代器本身 print(iter_dic.__next__()) print(iter_dic.__next__()) print(iter_dic.__next__()) #等同于next(iter_dic) #迭代对象有几个值就需要执行几次,直到没有值可取 print(iter_dic.__next__()) #抛出异常StopIteration,标志迭代对象的值已经全部取出
基于for循环,我们可以实现不依赖索引取值 1 dic={'a':1,'b':2,'c':3} 2 for k in dic: 3 print(dic[k] for循环工作原理 1,执行in后的对象__iter__()方法,得到一个迭代器对象 2,执行__next__方法,将得到的值赋值变量,然后执行循环体代码 3,重复过程2,直到捕捉到异常StopIteration
迭代器的优缺点:
优点:提供了一种不依赖索引的迭代方式
缺点:无法获取长度
只能一直往前取,无法往后取
生成器
函数内包含有yield关键字
再调用函数,就不会执行函数体代码,拿到的返回值就是一个生成器对象
注意::生成器本质就是迭代器,也就是说迭代器的用法就是生成器的用法
11 三元运算,列表解析、生成器表达式
三元运算:
格式:
为真时的结果 if 判断条件 else 为假时的结果
例子: print(1 if 5>3 else 0)
列表解析(列表推导式)
基本形式:[表达式 for 参数 in 可迭代对象]
或者 [表达式 for 参数 in 可迭代对象 if 条件]
实例
1 l = [] 2 for i in range(100): 3 l.append('egg%s' %i) 4 print i 5 6 #不带if条件 7 l = ['agg%s' %i for i in range(100) ] 8 9 #带if条件 10 l = ['agg%s' %i for i in range(100) if i>10]
生成器表达式
将列表推导式的中括号改为小括号即可
l = ('egg%s' %i for i in range(100) if i>10) print(next(l)) 注意:得到的结果为迭代器
12 函数的递归调用
递归调用是函数嵌套调用的一种特殊形式,函数在调用时,直接或间接调用了自身
就是递归调用
递归的两个阶段:
回溯:往前搜索,以达到目标
注意:一定要在满足某个条件的情况下结束回溯,否则就是无限递归
递推:往回推
1 def age(n): 2 if n == 1: 3 return 1 4 else: 5 return age(n-1)+2 6 7 print(age(5))
递归总结:
递归必须有一个明确的结束条件
每次进入更深一层递归时,问题规模相比上次递归都应有所减少
python没有尾递归优化,但是设置了最大递归层数
13 内置函数
内置函数就是python解释器定义好的函数
reduce(func,iterable) 从可迭代系列中循环取值,参与第一个函数参数的计算,最后得到一个结果
reduce(lambda x, y: x+y, [1,2,3,4,5]) # 使用 lambda 匿名函数
bytes()将unicode指定编码后转化为bytes类型
chr()返回值是传入整数对应的ascii字符
divmod()将除数和余数的运算结果结合起来,返回一个包含商和余数的元组(a//b,a%b)
>>>divmod(7, 2)
(3, 1)
>>> divmod(8, 2)
(4, 0)
>>> divmod(1+2j,1+0.5j)
((1+0j), 1.5j)
enumerate()将一个可遍历的数据对象组合为一个索引序列,同时列出数据个数据下标,一般用在for循环
>>>seasons = ['Spring', 'Summer', 'Fall', 'Winter']
>>> list(enumerate(seasons))
[(0, 'Spring'), (1, 'Summer'), (2, 'Fall'), (3, 'Winter')]
eval()执行一个字符串表达式,并返回表达式的值
>>>x = 7
>>> eval( '3 * x' )
21
>>> eval('pow(2,2)')
4
>>> eval('2 + 2')
4
>>> n=81
>>> eval("n + 4")
85
filter(func,iterable)函数用于过滤序列,过滤掉不符合条件的元素,返回由符合条件元素组成的新列表
import math
def is_sqr(x):
return math.sqrt(x) % 1 == 0
newlist = filter(is_sqr, range(1, 101))
print(newlist)
id() 返回参数的内存地址序号
input()获取输入
iter(object)生成迭代器
len()返回参数长度
map(func,iterable)第一个参数 function 以参数序列中的每一个元素调用 function 函数,返回包含每次 function 函数返回值的新列表。
map(lambda x: x ** 2, [1, 2, 3, 4, 5]) # 使用 lambda 匿名函数
[1, 4, 9, 16, 25]
>>> map(lambda x, y: x + y, [1, 3, 5, 7, 9], [2, 4, 6, 8, 10])
[3, 7, 11, 15, 19]
max()返回给定参数的最大值,参数可以是系列
egon male 18 3000
alex male 38 30000
wupeiqi female 28 20000
yuanhao female 28 10000
with open('db.txt') as f:
items=(line.split() for line in f)
info=[{'name':name,'sex':sex,'age':age,'salary':salary}
for name,sex,age,salary in items]
print(max(info,key=lambda dic:dic['salary']))
max(iterable, key, default) 求迭代器的最大值,其中iterable 为迭代器,max会for i in … 遍历一遍这个迭代器,然后将迭代器的每一个返回值当做参数传给key=func 中的func(一般用lambda表达式定义) ,然后将func的执行结果传给key,然后以key为标准进行大小的判断
min()返回给定参数的最小值,参数可以是系列
ord()返回给定参数的十进制数
>>>ord('a')
97
>>> ord('b')
98
>>> ord('c')
99
sorted()对所有可迭代的对象进行排序操作。
sort 与 sorted 区别:
sort 是应用在 list 上的方法,sorted 可以对所有可迭代的对象进行排序操作。
list 的 sort 方法返回的是对已经存在的列表进行操作,而内建函数 sorted 方法返回的是一个新的 list,而不是在原来的基础上进行的操作。
iterable -- 可迭代对象。
cmp -- 比较的函数,这个具有两个参数,参数的值都是从可迭代对象中取出,此函数必须遵守的规则为,大于则返回1,小于则返回-1,等于则返回0。
key -- 主要是用来进行比较的元素,只有一个参数,具体的函数的参数就是取自于可迭代对象中,指定可迭代对象中的一个元素来进行排序。
reverse -- 排序规则,reverse = True 降序 , reverse = False 升序(默认)。
>>>a = [5,7,6,3,4,1,2]
>>> b = sorted(a) # 保留原列表
>>> a
[5, 7, 6, 3, 4, 1, 2]
>>> b
[1, 2, 3, 4, 5, 6, 7]
>>> L=[('b',2),('a',1),('c',3),('d',4)]
>>> sorted(L, cmp=lambda x,y:cmp(x[1],y[1])) # 利用cmp函数
[('a', 1), ('b', 2), ('c', 3), ('d', 4)]
>>> sorted(L, key=lambda x:x[1]) # 利用key
[('a', 1), ('b', 2), ('c', 3), ('d', 4)]
>>> students = [('john', 'A', 15), ('jane', 'B', 12), ('dave', 'B', 10)]
>>> sorted(students, key=lambda s: s[2]) # 按年龄排序
[('dave', 'B', 10), ('jane', 'B', 12), ('john', 'A', 15)]
>>> sorted(students, key=lambda s: s[2], reverse=True) # 按降序
[('john', 'A', 15), ('jane', 'B', 12), ('dave', 'B', 10)]
>>>
14 面向过程编程
#1、首先强调:面向过程编程绝对不是用函数编程这么简单,面向过程是一种编程思路、思想,而编程思路是不依赖于具体的语言或语法的。言外之意是即使我们不依赖于函数,也可以基于面向过程的思想编写程序 #2、定义 面向过程的核心是过程二字,过程指的是解决问题的步骤,即先干什么再干什么 基于面向过程设计程序就好比在设计一条流水线,是一种机械式的思维方式 #3、优点:复杂的问题流程化,进而简单化 #4、缺点:可扩展性差,修改流水线的任意一个阶段,都会牵一发而动全身 #5、应用:扩展性要求不高的场景,典型案例如linux内核,git,httpd #6、举例 流水线1: 用户输入用户名、密码--->用户验证--->欢迎界面 流水线2: 用户输入sql--->sql解析--->执行功能