四.函数
一.函数
函数的定义:函数是指将一组语句集合通过一个名字(函数名)封装起来,想要执行这个函数,调用函数名就行。
基本形式: def function():
return
函数的优势:1.可以更好的使代码简洁,避免的代码的冗长,可复用强
2.能够更好的维护代码
函数分为:内置函数(built-in function)和自定义函数
定义函数的形式:
1.无参函数:def function( ) 不依赖外部参数传入的函数,不需要返回值。
2.有参函数:def function (x,y ) 依赖外部参数传入的函数,需要返回值
3.空函数:def function( ): pass 作用是用一个整体的概念定义程序功能,使体系结构分明。
4.return: 1.不写return时,返回None值
2.renturn 返回一个值
3.return多个值时,返回一个由多个值构成的元祖
5.函数分为定义阶段是调用阶段:由于python是弱类型语言,在函数定义时,出现错误也不会提示,函数调用阶段必须在定义阶段之后。
二 . 实参和形参
1.形式参数:定义函数时的形式参数。 def function(x, y) → x,y 形式参数
2.实际参数:def function(x ,y) → function(1,2) (1,2)就是函数的实际参数
注意: 在函数的调用阶段,括号内定义时的参数,本质上就是定义的实际值,必须明确是一个不可变的数据类型的值。原因是因为在pyhton中,一切皆对象,将一个变量赋值,只是将一个内存地址指向变量,当实参为可变类型时会出现实参被改变的情况,在程序庞杂时,会出现程序不可控的现象,所有必须使用不可变数据类型(int ,string,float,tuple)
def a (x): x.append(4) x= [1,2,3] a(x) print(x)
【1,2,3,4】
结果发生改变,因为当实参是一个可变数据时,python实际是将一个内存地址指向变量名,当在函数内部,数据发生改变时,会影响外部数据。
3. 位置参数和默认参数:(重点)
1》. 从实参角度分析:
1.按位置传值:function(x,y) → function (1,2)
2.按关键字传值:function(x,y) → function(x=1,y=2)
3.混用:function(1,y = 2)
注意的问题:
1.在混用时:关键字传值必须在位置传值后面,否则会报错:
2.对于一个形参,不能重复赋值。如:function(1,x=2,3) 报错
2》.从形参角度分析:function(x,y)
位置参数:必须传值,若不传则报错:function(1) →报错
默认参数:function(x,y=2)
当有默认参数时,只需要传入一个值,默认参数在函数定义阶段已经确定了,也可传新的值
function(1)
注意:默认参数必须放在位置参数后面
3》.def function(*args **kwargs):
1.*args:*是指所有的意思
按位置参数传入值时,多余的传入的值会交给*args处理成一个元祖形式的数据:
def f (x,y,*args): print(args) f(1,2,3,4)
>>> (3,4)
注意:*args,位置参数,默认参数混用时,顺序为:位置参数,默认参数,*args
2.**kwargs:
按关键字传值时,将除位置参数的其他关键字传的值交给**kwargs处理成字典:
def f (x,y,**kwargs): print(kwargs) f(1,y=2,z=3,d=4) >>>>{'d': 4, 'z': 3}
注意:位置参数,默认参数,*args,**kwargs混合使用时,位置的顺序为:位置参数,默认参数,*args,**kwargs
3.*args,**kwargs的混用:
def function(*args,**kwargs):可以接受任何值的传入。
def f (*args,**kwargs): print(args) print(kwargs) f(1,2,3,4,5,y=2,z=3,d=4) >>>>> (1, 2, 3, 4, 5) {'y': 2, 'z': 3, 'd': 4}
三.名称空间与作用域:
1>.内置的名称空间与作用域:built-in
2>.全局的名称空间与作用域:globals
3>.局部的名称空间与作用域:locas
函数调用时,先从局部空间找变量,再从全局找变量,再从内置找变量。
四.函数的嵌套与嵌套作用域:
1.函数嵌套的定义与调用:在函数内部定义一个新的函数。
def function():
def function():
pass
2.函数嵌套的作用域名:从最内部函数开始找变量名字。
3.函数是第一类对象:可以当成变量,参数,函数的返回值,也可以当成一个可变容器内的一个元素。
在函数定义中再定义一个函数,由于函数是第一类对象,可以将函数返回,可以在外部调用函数内部定义的函数而不受函数定义时层级的影响
4. 闭包:一定是内部函数包含对外部作用域而不是全局作用域名字的引用。
如:f()就是一个闭包,在f1()函数定义的里面有对x的引用,f()返回的是f1函数加上和包含在f1函数外的对x的引用的一个状态,在运行f2函数时,保存的是在f1函数内部的引用状态而不是对全局变量X的引用。
x = 10000 def f(): x = 1 def f1(): print(x) return f1 f2 = f() print(f2) f2() >>>>> <function f.<locals>.f1 at 0x000001BEAE7CF950> 1
5.装饰器 可参考 https://blog.csdn.net/u013471155/article/details/68960244
定义:在不修改函数的源代码和调用方式的前提下,给函数加上新功能的方式。
装饰器的实现方式是基于闭包函数。
有参装饰器和无参装饰器:
1.无参装饰器:
@func ## index=func(index)
def index():
pass
index()
2.有参装饰器:
@func(args) ## index = func(index)
def index():
pass
index()
当定义有参装饰器时,相当于要在index()函数外面再包一层数据状态,相当于再做一个闭包函数,而未传参数之前的装饰器可以作为内部函数。
如:有参装饰器的基本形式
def timeer(args):
def func(f):
def wrapper(*args,**kwargs):
res = f(*args,**kwargs)
return res
return wrapper
return func
3.如何显示函数的注释信息:
import time from functools import wraps def timmer(func): @wraps(func) def wrapper(*args,**kwargs): '内部函数' start_time = time.time() stop_time = time.time() print('run time is %d' % (stop_time - start_time)) return wrapper @timmer def index(): '函数的注释信息' time.sleep(3) print('welcome!') print(index.__doc__) index() >>>>> welcome! 函数的注释信息 run time is 3
6.迭代器:迭代器出现的意义就是为了遍历那些不能依靠索引遍历的对象而产生的。
可迭代的对象:只要对象本身有__iter__方法,那么它就是可迭代的。
di = {'a':1,'b':2,'c':3,'d':4} i = iter(di) while True: try: print(i.__next__()) except StopIteration: break >>>> c d a b
迭代器的概念:对象.__iter__()得到的结果就是迭代器
迭代器的特性:
迭代器.__next__() 取下一个值
优点:1.提供了一种统一的迭代对象的方式,不依赖索引
2.迭代器与列表比较,迭代器是惰性计算的,更节省内存
缺点:1.无法获取迭代器的长度
2.一次性的,只能往后取值,不能往前退,不能像索引取值那样取特定位置的值
哪些地方需要用迭代器:
for i in object:就是将objec变成迭代器,将object.__next__()赋给i,然后在对i进行循环。
#查看可迭代对象与迭代器对象:
from collections import Iterable,Iterator s = 'aaa' list = [1,23,4,5,6] t = (1,2,3,4) d = {'a':1,'b':2} set = {1,2,34,4} f1 = open('a.txt') print(isinstance(s,Iterable)) print(isinstance(list,Iterable)) print(isinstance(t,Iterable)) print(isinstance(d,Iterable)) print(isinstance(set,Iterable)) print(isinstance(f1,Iterable)) print('>>>>>>>>>') print(isinstance(s,Iterator)) print(isinstance(list,Iterator)) print(isinstance(t,Iterator)) print(isinstance(d,Iterator)) print(isinstance(set,Iterator)) print(isinstance(f1,Iterator)) >>>>>>>>>> True True True True True True >>>>>>>>> False False False False False True Process finished with exit code 0
可知所有的数据类型都是可迭代的,只有文件是迭代器.
7.生成器
函数内带有yield关键字,那么这个函数执行的结果就是生成器。
生成器的本质就是迭代器:
def func():
n = 0
while True:
yield n
n += 1
g = func( ) :g就是一个生成器。
next(g) 执行生成器
总结yield的功能:
1.相当于将__iter__( ) 和 __next__( )方法封装带函数内部
2.与return比,return只能返回一次然后函数结束,而yield能返回多次
3.函数暂停已经继续运行的状态是通过yield保存的
实现tail - f 命令的脚本:
#/usr/bin/env python import time def tail(filename): with open(filename,mode='r') as f: f.seek(0,2) while True: line = f.readline() if not line: time.sleep(0.5) print('#######') continue else: yield line f = tail('liang.txt') for i in f: print(i)
实现tail - f | grep 'string'命令的脚本
import time def tail(filename): with open(filename,mode='r') as f: f.seek(0,2) while True: line = f.readline() if not line: time.sleep(0.5) print('#######') continue else: yield line f = tail('liang.txt') #print(next(f)) for i in f: print(i) ~
yeild的表达式形式: (food = yield吃东西程序实例:)
def func(parrent):
print('现在开始点餐!')
message_list = []
while True:
message = yield message_list
print('%s先生您好,您的菜单是 %s' % (parrent,message))
message_list.append(message)
excut = func('Roy')
next(excut)
print(excut.send('鸡腿'))
print(excut.send('咖啡'))
print(excut.send('汉堡'))
>>>>>>
现在开始点餐!
Roy先生您好,您的菜单是 鸡腿
['鸡腿']
Roy先生您好,您的菜单是 咖啡
['鸡腿', '咖啡']
Roy先生您好,您的菜单是 汉堡
['鸡腿', '咖啡', '汉堡']
#e.send 和next(e)的区别:
1.如果函数内yield是表达式形式,那么必须先next( e )一次,才能对生成器使用 .send()
2.二者的共同之处都可以让函数在上次暂停的位置继续运行,不一样的地方在于send在触发下一次代码的执行时,会顺便给yield传一个值。
#由于yield不接受一个非None值,所在在send用之前必须先next下,所以可以在生成器函数前封装个装饰器,完成这个步骤:
def next1(func): def warpper(*args,**kwargs): res = func(*args,**kwargs) next(res) return res return warpper @next1 def func(parrent): print('现在开始点餐!') message_list = [] while True: message = yield message_list print('%s先生您好,您的菜单是 %s' % (parrent,message)) message_list.append(message) excut = func('Roy') print(excut.send('鸡腿')) print(excut.send('咖啡')) print(excut.send('汉堡')) >>>>>>>>> 现在开始点餐! Roy先生您好,您的菜单是 鸡腿 ['鸡腿'] Roy先生您好,您的菜单是 咖啡 ['鸡腿', '咖啡'] Roy先生您好,您的菜单是 汉堡 ['鸡腿', '咖啡', '汉堡']
用协程函数 和 send()取爬网页:
from urllib.request import urlopen def get(): while True: url = yield res = urlopen(url).read() return res f = get() next(f) f.send('http://www.baidu.com')
五.面向过程的程序设计:
用协程函数制作 grep -rl ’string' /路径 命令:
#!/usr/bin/env python # editor/roy # grep -rl 'string' /c.txt import os,time def init(func): 'next()装饰器' def warpper(*args,**kwargs): res = func(*args,**kwargs) next(res) return res return warpper @init def seach(target): '找到文件的绝对路径' while True: dir_name = yield time.sleep(2) f = os.walk(dir_name) for i in f: for j in i[-1]: file_path = '%s\%s' % (i[0],j) target.send(file_path) @init def opener(target): '打开文件' while True: file_path = yield time.sleep(2) with open(file_path) as f: target.send((file_path,f)) @init def cat(target): '读取文件的内容' while True: file_path,f = yield time.sleep(2) for line in f: target.send((file_path,line)) @init def shuaixuan(pattren,target): '帅选文件的内容' while True: file_path,line = yield time.sleep(2) if pattren in line: target.send(file_path) @init def printer(): '打印符合内容的文件的绝对路径' while True: file_path = yield time.sleep(0.3) print(file_path) g = seach(opener(cat(shuaixuan('python',printer())))) g.send(r'c:\deng')
import os :
os.work(‘文件路径’) →会得到一个生成器:
面向过程程序设计的思想:流水线式的编程思想,在程序设计时,需要把整个流程设计出来
优点:1.体系结构更加清晰
2.简化程序的复杂度
3.程序调用阶段
缺点:1.可扩展性及其的差,所以应用场景是不需要经常变化的软件
六.列表解析和生成器表达式:
1.列表生成式:
l =[] for i in range(100): l.append('egg%s' % i ) print(l) l1 = ['egg%s' % i for i in range(100)] print(l1) >>>>>>> 结果一样
2.生成器表达式:可以一个把值一个一个取出来,在内存中只要一个值,而列表会将列表内的所有元素都加载到内存中
list = [] f = open('a.txt') for i in f: line = i.strip() list.append(line) print(list) f.seek(0) g = [i.strip() for i in f ] print(g) f.seek(0) g1 = (i.strip() for i in f) print(g1) print(next(g1)) >>>>>>> ['asdasdasd', 'asdsadasd', 'asdasdasdasdas sadas', 'sadasd', 'asdasda', 'asd', 'dasaddddddddddddddddd', 'dsaaaaaaaaaaaaaaa', 'asddddddddddddddddd'] ['asdasdasd', 'asdsadasd', 'asdasdasdasdas sadas', 'sadasd', 'asdasda', 'asd', 'dasaddddddddddddddddd', 'dsaaaaaaaaaaaaaaa', 'asddddddddddddddddd'] <generator object <genexpr> at 0x0000014DB94D2A98> asdasdasd
由于生成器是一个可迭代的对象,所有可以同过list(g)将生成器转化为以个列表
3.声明式编程:声明式编程就是将代码声明给一个变量,上面的g=( i.strip() for i in f ) 就是一个声明,相当于声明式编程
apple 10 3
tesla 1000000 1
mac 3000 2
lenvo 3000 2
chicken 10 3
1.求商品价格的和
l1 = ((int(i.split()[-1])*int(i.split()[1])) for i in f) print(sum(l1) >>>>>> 1012060 f = open('a.txt') l = [] for i in f: i = i.split() m = int(i[1]) * int(i [-1]) l.append(m) print(sum(l)) >>>>>>> 1012060
2.将TXT做成字典放入列表内:并加入筛选功能
with open('a.txt') as f: l = (line.split()for line in f) g = ({'name':i[0],'price':i[1],'count':i[2] } for i in l if float(i[1]) > 10000) print(next(g)) print(next(g)) >>>>>>>>>> {'name': 'tesla', 'count': '1', 'price': '1000000'} {'name': 'lenvo', 'count': '2', 'price': '30000'}
七.内置函数:
1 dir():看的是对象下面与哪些可以调用的方法,与.类似 2 divmod(10,3): >>>>> 取整数和余数 3 ,1 3 enumerate() :将对象元素加入前标并放在元组中 4 eval():将字符串里面的表达式提取出来 5 hash():得到哈希值 特点:用来校验数据的完整性,字符串改变,则哈希值也对应改变 6 hex():十六进制 7 bin():二进制 8 id():身份标志 9 isinstance():验证数据类型 10 iter():把一个可迭代的对象变成迭代器 11 oct():十进制转换成八进制 12 char():将字母转换成对应的ascii码 13 ord():将ascii码转换成对应的字母 14 max():取最大值 15 min():取最小值 16 zip(*args,**kwargs):拉链 如将列表中的元素与字符串中的字符一一对应 17 sorted():排序 默认是升序 sorted(reverse= True) 降序 不改变原来的对象排列顺序
#重点:
18.map() 映射功能并且得到新的数据 map(lambda x: x**2,interable)
19.reduce(): 合并功能并且得到新的数据 from functools import reduce
20.filter():过滤功能 过滤掉不满足过滤条件的数据,将满足条件的数据过滤出来 得到的是一个迭代器
21.pow(x,y,z):x**y%y
22.reversed():翻转
23.sorted():排序
24.round():四舍六入五留双
25.vars():无参数传入时相当于locals
26.slice():切片
27.__import__():通过字符串导入模块
28.complex():复数 num = i+j
匿名函数:lambda: x,y :x+y return x+y 只能用一次 通常是跟内置函数用,如max,min,reduce,map,filter,sorted.....
八.递归
递归的条件:递归的效率低
1.必须要有一个明确的结束条件
2.每次进入更深一层递归时,问题规模必须必上一次递归都应有减少
3.递归效率不高,递归层数过多会导致栈溢出
二分法:
l = [1,2,3,4,5,6,7,8,9,13,14,15,19,22,26,28,32,34,56,65,66,72,88,99,101,102,121,131,212,213] def search(num,date): if int(len(date)) > 1: mid_index = int(len(date)/2) mid_value = date[mid_index] if num > mid_value: date = date[mid_index:] search(num,date) elif num < mid_value: date = date[:mid_index] search(num,date) else: print('find it ') return else: if num == date[0]: print('in zhe list!') else: print('not exist') search(1,l)
九.面向对象的程序设计:
面向对象的程序设计的优缺点:
1.可扩展性强
2.缺点是可控性差,无法预知程序运行的结果
类:
使用类的方式:
1.实例化
2.引用名字(类名.变量名,类名.函数名)
定义类时内部定义的是函数:
当调用类的功能时候,调用的是函数,而当类的对象调用改功能时是调用绑定的方法,直接调用使用,不用输入参数
使用实例:引用名字(实例名.类的变量,实例名.绑定方法,实例名.实例自己的变量名)