python之函数的介绍

 

文件处理

1.介绍:html

计算机系统分为:计算机硬件,操做系统,应用程序三部分。python

咱们用python或其余语言编写的应用程序若想要把数据永久保存下来,必需要保存于硬盘中,这就涉及到应用程序要操做硬件,众所周知,应用程序是没法直接操做硬件的,这就用到了操做系统。操做系统把复杂的硬件操做封装成简单的接口给用户/应用程序使用,其中文件就是操做系统提供给应用程序来操做硬盘虚拟概念,用户或应用程序经过操做文件,能够将本身的数据永久保存下来。linux

有了文件的概念,咱们无需再去考虑操做硬盘的细节,只须要关注操做文件的流程:算法

1. 打开文件,获得文件句柄并赋值给一个变量 (打开文件时,须要指定文件路径和以何等方式打开文件,打开后,便可获取该文件句柄,往后经过此文件句柄对该文件操做。)
2. 经过句柄对文件进行操做
3. 关闭文件
#1. 打开文件,获得文件句柄并赋值给一个变量
f=open('a.txt','r',encoding='utf-8') #默认打开模式就为r

#2. 经过句柄对文件进行操做
data=f.read()

#3. 关闭文件
f.close()

 

2.打开文件的模式文件句柄 = open('文件路径', '模式')编程

1. 打开文件的模式有(默认为文本模式):vim

  • r ,只读模式【默认模式,文件必须存在,不存在则抛出异常】
  • w,只写模式【不可读;不存在则建立;存在则清空内容;】
  • x, 只写模式【不可读;不存在则建立,存在则报错】
  • a, 追加模式【可读;   不存在则建立;存在则只追加内容;】

2. "b"表示以字节的方式操做 ,对于非文本文件,咱们只能使用b模式。windows

  • rb  或 r+b
  • wb 或 w+b
  • xb 或 w+b
  • ab 或 a+b

全部文件也都是以字节的形式存储的,使用这种模式无需考虑文本文件的字符编码、图片文件的jgp格式、视频文件的avi格式,以b方式打开时,读取到的内容是字节类型,写入时也须要提供字节类型,不能指定编码 缓存

3. "+" 表示能够同时读写某个文件ruby

  • r+, 读写【可读,可写】
  • w+,写读【可读,可写】
  • x+ ,写读【可读,可写】
  • a+, 写读【可读,可写】
  • x,只写模式【不可读;不存在则建立,存在则报错】
  • x+ 【可读,可写】

4.注意点:数据结构

#强调第一点:
打开一个文件包含两部分资源:操做系统级打开的文件+应用程序的变量。在操做完毕一个文件时,必须把与该文件的这两部分资源一个不落地回收,回收方法为:
一、f.close() #回收操做系统级打开的文件
二、del f #回收应用程序级的变量

其中del f必定要发生在f.close()以后,不然就会致使操做系统打开的文件尚未关闭,白白占用资源,
而python自动的垃圾回收机制决定了咱们无需考虑del f,这就要求咱们,在操做完毕文件后,必定要记住f.close()

虽然我这么说,可是不少同窗仍是会很不要脸地忘记f.close(),对于这些不长脑子的同窗,咱们推荐傻瓜式操做方式:使用with关键字来帮咱们管理上下文
with open('a.txt','w') as f:
    pass
 
with open('a.txt','r') as read_f,open('b.txt','w') as write_f:
    data=read_f.read()
    write_f.write(data)
资源回收
f=open(...)是由操做系统打开文件,那么若是咱们没有为open指定编码,那么打开文件的默认编码很明显是操做系统说了算了,操做系统会用本身的默认编码去打开文件,在windows下是gbk,在linux下是utf-8。
这就用到了上节课讲的字符编码的知识:若要保证不乱码,文件以什么方式存的,就要以什么方式打开。

f=open('a.txt','r',encoding='utf-8')
字符编码

三、文件的操做方法

f.read() #读取全部内容,光标移动到文件末尾
f.readline() #读取一行内容,光标移动到第二行首部
f.readlines() #读取每一行内容,存放于列表中

f.write('1111\n222\n') #针对文本模式的写,须要本身写换行符
f.write('1111\n222\n'.encode('utf-8')) #针对b模式的写,须要本身写换行符
f.writelines(['333\n','444\n']) #文件模式
f.writelines([bytes('333\n',encoding='utf-8'),'444\n'.encode('utf-8')]) #b模式

#了解
f.readable() #文件是否可读
f.writable() #文件是否可读
f.closed #文件是否关闭
f.encoding #若是文件打开模式为b,则没有该属性
f.flush() #马上将文件内容从内存刷到硬盘
f.name

4.文件的修改

方式一:将硬盘存放的该文件的内容所有加载到内存,在内存中是能够修改的,修改完毕后,再由内存覆盖到硬盘(word,vim,nodpad++等编辑器)

import os

with open('a.txt') as read_f,open('.a.txt.swap','w') as write_f:
    data=read_f.read() #所有读入内存,若是文件很大,会很卡
    data=data.replace('alex','SB') #在内存中完成修改

    write_f.write(data) #一次性写入新文件

os.remove('a.txt')
os.rename('.a.txt.swap','a.txt')
View Code

方式二:将硬盘存放的该文件的内容一行一行地读入内存,修改完毕就写入新文件,最后用新文件覆盖源文件

import os

with open('a.txt') as read_f,open('.a.txt.swap','w') as write_f:
    for line in read_f:
        line=line.replace('alex','SB')
        write_f.write(line)

os.remove('a.txt')
os.rename('.a.txt.swap','a.txt')
View Code

 

 

函数的介绍

函数的定义:是组织好的,可重复使用的,用来实现单一,或相关联功能的代码段。函数的特性:避免代码重用,提升代码的可读性,可维护性。

 def  函数名(参数1,参数2)

     ''' 函数注释'''

    print('函数体')

    return 返回值

 定义:def关键字开头,空格以后接函数名和圆括号,最后还要加一个冒号,def是固定的,不能变。

 函数名:函数名是包含字母,数字,下划线的任意组合,可是不能以数字开头。虽然函数名能够随便取名,可是通常尽可能定义成能够表示函数功能的。

def function_name(a,b):  #function_name 为函数名, a b 为参数
print("statement")  #函数体
return something # return不是必须的
View Code

 函数的调用:返回值=函数名(参数1,参数2)#记得函数名后面加括号,原则先定义再调用。

1 #定义阶段
2 def foo():
3     print('from foo')
4     bar()
5 def bar():
6     print('from bar')
7 #调用阶段
8 foo()
View Code

 

函数参数

行参与实参:

#形参即变量名,实参即变量值,函数调用时,将值绑定到变量名上,函数调用结束,解除绑定。

 

一、位置参数:按照从左到右的顺序定义的参数
位置形参:必选参数
位置实参:需按照位置给形参传值

二、关键字参数:按照key=value的形式定义的实参,无需按照位置为形参传值。

注意的问题:
1. 关键字实参必须在位置实参右面
2. 对同一个形参不能重复传值

三、默认参数:形参在定义时就已经为其赋值,能够传值也能够不传值,常常须要变得参数定义成位置形参,变化较小的参数定义成默认参数(形参)

注意的问题:
1. 只在定义时赋值一次
2. 默认参数的定义应该在位置形参右面
3. 默认参数一般应该定义成不可变类型

四、可变长参数:可变长指的是实参值的个数不固定

而实参有按位置和按关键字两种形式定义,针对这两种形式的可变长,形参对应有两种解决方案来完整地存放它们,分别是*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)
View Code

五、命名关键字参数:*后定义的参数,必须被传值(有默认值的除外),且必须按照关键字实参的形式传递
能够保证,传入的参数中必定包含某些关键字

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)
结果:
2
(3, 4, 5)
3
{'c': 4, 'd': 5}
View Code

 

 小结:

 

 

 函数的返回值

1.return的做用:结束一个函数的执行

def func4():
     print (1111111)
     return#结束一个函数的执行
     print (1242451)
func4()  打印结果为 1111111
View Code

2.首先返回值能够是任意的数据类型。

3.函数能够有返回值:若是有返回值,必需要用变量接收才有效果。也能够没有返回值,没有返回值的时候分三种状况:

    1.当不写return的时候,函数的返回值为None

    2.当只写一个return的时候,函数的返回值为None

    3.return None的时候,函数的返回值为None(几乎不用)

4.return返回一个值(一个变量)

5.return返回多个值(多个变量):多个值之间用逗号隔开,以元组的形式返回。接收:能够用一个变量接收,也能够用多个变量接收,返回几个就用几个变量去接收

def func():
    a = 111
    b = [1, 2, 3]
    c = {'a': 15, 'b': 6}
    return a #返回一个值  打印结果为:111
    return a,b,c#返回多个值,变量之间按逗号隔开,以元组的形式返回,打印结果:(111, [1, 2, 3], {'a': 15, 'b': 6})

print(func())
View Code 

6 函数没有返回值的函数

1.不写return时返回None
def  func():
    a=111
    b=[1,2,3]
ret=func()
print(ret)#打印结果:None


2.只写一个return时返回None
def  func():
    a=111
    b=[1,2,3]
    return
ret=func()
print(ret)   #打印结果:None

3.return None的时候返回None
def  func():
    a=111
    b=[1,2,3]
    return  None
ret=func()
print(ret)   #打印结果:None
View Code

 

小结:

 

 

全局变量与局部变量

1.定义:在函数内部的变量拥有一个局部做用域,定义在函数外的拥有全局做用域。

局部变量只能在其被声明的函数内部访问,而全局变量能够在整个程序范围内访问。调用函数时,全部在函数内声明的变量名称都将被加入到做用域中。

##若是函数的内容无global关键字,优先读取局部变量,能读取区部变量,没法对全局变量从新赋值,但对于可变类型,能够对内部元素进行操做。若是函数中有global关键字,变量本质就是全局的那个变量,可读取可复制

gloabal、nonlocal

首先咱们写这样一个代码, 首先在全局声明一个变量, 而后再局部调用这个变量, 并改变这 个变量的值 

a = 100
def func():   
    global a    # 加了个global表示不再局部建立这个变量了. 而是直接使用全局的a   
    a = 28   
print(a)
func()
print(a)
View Code

global表示. 再也不使用局部做用域中的内容了. 而改用全局做用域中的变量

lst = ["麻花藤", "刘嘉玲", "詹姆斯"]
def func():   
    lst.append("⻢云")   
    # 对于可变数据类型能够直接进⾏访问. 可是不能改地址. 说⽩了. 不能赋值 在函数中赋值就是在局部空间建立了一个变量   
   print(lst)
func()
print(lst)
View Code

nonlocal 表示在局部做用域中, 调用父级命名空间中的变量.

若是父级命名空间中没有这个变量名,就继续向上查找.最多找到最外成的函数就结束了

a = 10
def func1():   
    a = 20   
    def func2():
        nonlocal a       
        a = 30       
        print(a)  
    func2()   
    print(a)
func1()
 
结果:
加了nonlocal
30
30
 
不加nonlocal
30
20   
View Code

若是嵌套了不少层, 会是一种什么效果:?

a = 1
def fun_1():   
    a = 2   
    def fun_2():       
        nonlocal a       
        a = 3       
        def fun_3():           
            a = 4           
            print(a)       
        print(a)       
        fun_3()       
        print(a)   
    print(a)   
    fun_2()   
    print(a)
print(a)
fun_1()
print(a)
View Code

 

 

函数的嵌套

 1定义:在一个函数中定义了另一个函数即一个函数里用def语句来建立其它的函数的状况

def outer():


  def inner():


    print('inner')


  print('outer')


  inner()


outer()


inner()    # 此句会出错
View Code

内部函数不能被外部直接使用,会抛NameError异常

2个体会列子

 

 

匿名函数与三元运算 

1、匿名函数:也叫lambda表达式

1.匿名函数的核心:一些简单的须要用函数去解决的问题,匿名函数的函数体只有一行

2.参数能够有多个,用逗号隔开

3.返回值和正常的函数同样能够是任意的数据类型

语法:

请把下面的函数转换成匿名函数
def  add(x,y)
        return x+y
add()

结果:
sum1=lambda x,y:x+y
print(sum1(5,8))
View Code

 与map的应用:

l=[1,2,3,4]
# def func(x):
#     return x*x
# print(list(map(func,l)))

print(list(map(lambda x:x*x,l)))
View Code

filter函数的小应用:

l=[15,24,31,14]
 # def func(x):
 #         return x>20
 # print(list(filter(func,l)))
 
 print(list(filter(lambda x:x>20,l)))
View Code

2、三元运算

python的三元运算格式:

result=值1 if x<y else 值2    这个是什么意思呢,就是结果=值1 if 条件1 else 值2

>>> def f(x,y):                             
    return x - y if x>y else abs(x-y)  
#若是x大于y就返回x-y的值 ,不然就返回x-y的绝对值

>>> f(3,4)      #3<4,不知足if 条件,它返回else里面的绝度值
>>> f(4,3)
>>> def f(x,y):
    return 1 if x>y else -1   
#若是x大于y就返回x-y的值 ,不然就返-1
>>> f(3,4)     #3小于4 , 返回-1
-1
>>> f(4,3)      #4大于3,返回1
>>>
View Code

 

函数的递归

定义:在函数内部,能够调用其余函数。若是一个函数在内部调用自身自己,这个函数就是递归函数。优势是定义简单,逻辑清晰。理论上,全部的递归函数均可以写成循环的方式,但循环的逻辑不如递归清晰。

def fact(n):
    if n==1:
        return 1
    return n * fact(n - 1)
fact(5)  #打印结果为 120
#计算过程:
===> fact(5)
===> 5 * fact(4)
===> 5 * (4 * fact(3))
===> 5 * (4 * (3 * fact(2)))
===> 5 * (4 * (3 * (2 * fact(1))))
===> 5 * (4 * (3 * (2 * 1)))
===> 5 * (4 * (3 * 2))
===> 5 * (4 * 6)
===> 5 * 24
===> 120
计算阶乘

 

递归次数的限制:最大递归层数作了一个限制:997,可是能够经过导入sys模块的方式来修改改变限制的次数(sys模块:全部和python相关的设置和方法)列如:

import sys
sys.setrecursionlimit(1500)#修改递归层数
def recursion(n):
    print(n)
    recursion(n+1)

recursion(1)
#打印结果 1 2 3 .....1497
修改递归的次数

 

递归的特性:必须有一个明确的结束条件(return),要不就变成死循环了,每次进入更深一层递归时,问题规模相比上一次递归都应有所减小,递归的执行效率不高,递归层次过多会致使栈的溢出。列子:

def calc(n):
    v=int(n/2)
    print(v)
    if v==0:
        return
    calc(v)
    
calc(10)
#打印结果 5 2 1 0

*******体会********

def calc(n):
    v=int(n/2)
    if v==0:
        return
    calc(v)
    print(v)
calc(10)
#打印结果 1 2 5 
10除以2

 

递归的做用:求斐波那契数列,汉诺塔,多级评论树,二分查找,求阶乘等

# N! = 1 * 2 * 3 * ... * N
def fact(n):
    if n == 1:
        return 1
    return n * fact(n-1)
 fact(n)
利用递归函数计算阶乘
def move(n, a, b, c):
    if n == 1:
        print('move', a, '-->', c)
    else:
        move(n-1, a, c, b)
        move(1, a, b, c)
        move(n-1, b, a, c)

move(4, 'A', 'B', 'C')
利用递归函数移动汉诺塔

二分查找:

从这个列表中找到55的位置l = 【2,3,5,10,15,16,18,22,26,30,32,35,41,42,43,55,56,66,67,69,72,76,82,83,88】

 

这就是二分查找,从上面的列表中能够观察到,这个列表是从小到大依次递增的有序列表。

按照上面的图就能够实现查找了

l = [2, 3, 5, 10, 15, 16, 18, 22, 26, 30, 32, 35, 41, 42, 43, 55, 56, 66, 67, 69, 72, 76, 82, 83, 88]
def find(l,aim):
    mid=len(l)//2#取中间值,//长度取整(取出来的是索引)
    if l[mid]>aim:#判断中间值和要找的那个值的大小关系
        new_l=l[:mid]#顾头不顾尾
        return find(new_l,aim)#递归算法中在每次函数调用的时候在前面加return
    elif l[mid]<aim:
        new_l=l[mid+1:]
        return find(new_l,aim)
    else:
        return l[mid]
print(find(l,66))
简单的二分法
l = [2, 3, 5, 10, 15, 16, 18, 22, 26, 30, 32, 35, 41, 42, 43, 55, 56, 66, 67, 69, 72, 76, 82, 83, 88]
def func(l, aim,start = 0,end = len(l)-1):
    mid = (start+end)//2#求中间的数
    if not l[start:end+1]:#若是你要找的数不在里面,就return'你查找的数字不在这个列表里面'
        return  '你查找的数字不在这个列表里面'
    elif aim > l[mid]:
        return func(l,aim,mid+1,end)
    elif aim < l[mid]:
        return func(l,aim,start,mid-1)
    elif aim == l[mid]:
        print("bingo")
        return mid

index = func(l,55)
print(index)
# print(func(l,41))
升级版二分法

 

尾递归优化:使用递归函数须要注意防止栈溢出。在计算机中,函数调用是经过栈(stack)(#至关于存放数据的盒子)这种数据结构实现的,每当进入一个函数调用,栈就会加一层栈帧,每当函数返回,栈就会减一层栈帧。因为栈的大小不是无限的,因此,递归调用的次数过多,会致使栈溢出。解决递归调用栈溢出的方法是经过尾递归优化。在函数返回的时候,调用自身自己,而且,return语句不能包含表达式。这样,编译器或者解释器就能够把尾递归作优化,使递归自己不管调用多少次,都只占用一个栈帧,不会出现栈溢出的状况

 

 列子:

def cal(n):
    print(n)
    return cal(n+1)
cal(n)
#不是全部的语言都支持,在python里不支持尾递归优化
View Code

 

小结

使用递归函数的优势是逻辑简单清晰,缺点是过深的调用会致使栈溢出。

针对尾递归优化的语言能够经过尾递归防止栈溢出。尾递归事实上和循环是等价的,没有循环语句的编程语言只能经过尾递归实现循环。

Python标准的解释器没有针对尾递归作优化,任何递归函数都存在栈溢出的问题。

 

 

高阶函数

变量能够指向函数,函数的参数能接受变量,那么一个函数就能够接受另一个函数做为参数,这种函数就称为高阶函数。只须要知足如下任意一个条件,便是高阶函数:

接受一个或多个函数做为输入

return返回另一个函数。 例如:

 

def func1():
        age=83
        def func2():
        return func2
val=func1()
print(val)
View Code  
def func():
    n=10
    def func2():
        print("func2",n)
    return func2
f=func()
print (f)
f()
View Code

 

 

函数的内置方法

http://www.runoob.com/python/python-built-in-functions.html

 

    内置函数    
abs() divmod() input() open() staticmethod()
all() enumerate() int() ord() str()
any() eval() isinstance() pow() sum()
basestring() execfile() issubclass() print() super()
bin() file() iter() property() tuple()
bool() filter() len() range() type()
bytearray() float() list() raw_input() unichr()
callable() format() locals() reduce() unicode()
chr() frozenset() long() reload() vars()
classmethod() getattr() map() repr() xrange()
cmp() globals() max() reverse() zip()
compile() hasattr() memoryview() round() __import__()
complex() hash() min() set()  
delattr() help() next() setattr()  
dict() hex() object() slice()  
dir() id() oct() sorted() exec 内置表达式

 

 

函数的做用域,命名空间(名称空间)

名称空间:存放名字和值的关系的空间起一个名字叫: 命名空间即名称空间. 咱们的变量在存储的时候就 是存储在这片空间中的.  

####在python解释器开始执行以后, 就会在内存中开辟一个空间, 每当遇到一个变量的时候, 就把变量名和值之间的关系记录下来, 可是当遇到函数定义的时候, 解释器只是把函数名读入内存, 表示这个函数存在了,  至于函数内部的变量和逻辑, 解释器是不关心的. 也就是说一开始的时候函数只是加载进来, 仅此而已, 只有当函数被调用和访问的时候, 解释器才会根据函数内部声明的变量来进行开辟变量的内部空间. 随着函数执行完毕, 这些函数内部变量占用的空间也会随着函数执行完毕而被清空. 

def fun():   
    a = 10   
    print(a)
fun()
print(a)    # a不存在了已经
View Code

 命名空间分类:         

    1. 内置命名空间--> 存放python解释器为咱们提供的名字, list, tuple, str, int这些都是内置命名空间 (即builtins 内置模块的名称空间)

            2. 全局命名空间--> 咱们直接在py文件中, 函数外声明的变量都属于全局命名空间   (即globals  全局变量)    

            3. 局部命名空间--> 在函数中声明的变量会放在局部命名空间 (即locals 包含局部变量和形参)

加载顺序:

  1. 内置命名空间

  2. 全局命名空间

       3. 局部命名空间(函数被执行的时候)

取值顺序:

       1. 局部命名空间

       2. 全局命名空间

       3. 内置命名空间

a = 10
def func():   
    a = 20   
    print(a)
 
func()  # 20
View Code

 

做用域:就是做用范围(在python中一个函数就是做用域,全部的局部变量放置在其做用域中,代码定义完成后,做用域已经生成,做用域链向上查找)

按照生效范围来看分为  全局做用域  和   局部做用域   

   全局做用域: 包含内置命名空间和全局命名空间. 在整个文件的任何位置均可以使用(遵循 从上到下逐⾏执行).

   局部做用域: 在函数内部可使用.             

做⽤域命名空间:         

  1. 全局做⽤用域:    全局命名空间 + 内置命名空间       

  2. 局部做⽤用域:    局部命名空间,只能在局部范围内生效

  3.站在全局看:
    使用名字的时候:若是全局有,用全局的
    若是全局没有,用内置的

4.为何要有做用域?为了函数内的变量不会影响到全局

5.globals方法:查看全局做用域的名字【print(globals())】

 locals方法:查看局部做用域的名字【print(locals())】

a = 10
def func():   
    a = 40   
    b = 20   
    def abc():       
        print("哈哈")   
        print(a, b)     # 这⾥里里使⽤用的是局部做⽤用域   
        print(globals())    # 打印全局做用域中的内容   
        print(locals())     # 打印局部做用域中的内容
func()      
View Code

6.nonlocal让内部函数中的变量在上一层函数中生效,外部必须有

# x=1
# def f1():
#     x=2
#     def f2():
#         # x=3
#         def f3():
#             # global x#修改全局的
#             nonlocal x#修改局部的(当用nonlocal时,修改x=3为x=100000000,当x=3不存在时,修改x=2为100000000 )
#                    # 必须在函数内部
#             x=10000000000
#         f3()
#         print('f2内的打印',x)
#     f2()
#     print('f1内的打印', x)
# f1()
# # print(x)
View Code

 7.函数名能够用做参数

def func():
    print('func')

def func2(f):
    f()
    print('func2')
func2(func)
View Code

8.函数名能够做为函数的返回值 

def func():
    def func2():
        print('func2')
    return func2
f2=func()
f2()
#func2=func()
#func2()


2.

def f1(x):
    print(x)
    return '123'

def f2():
    ret = f1('s')  #f2调用f1函数
    print(ret)
f2()


3.
def func():
    def func2():
        return 'a'
    return func2   #函数名做为返回值

func2=func()
print(func2())
View Code

  

函数进阶-闭包

 

def func1():
    name = "alex"
    def func2():
        print(name)
        # 闭包
    func2()
func1()
# 结果: alex
View Code

使用__closure__来检测函数是不是闭包. 使用函数名.__closure__返回cell就是
闭包. 返回None就不是闭包

def func1():
    name = "alex"
    def func2():
        print(name)
        # 闭包
    func2()
    print(func2.__closure__)
func1()
 
结果:
alex
(<cell at 0x0000020077EFC378: str object at 0x00000200674DC340>,)
返回的结果不是None就是闭包
View Code

 

   

函数进阶-装饰器

1.介绍:装饰器本质上是一个Python函数,它可让其余函数在不须要作任何代码变更的前提下增长额外功能,装饰器的返回值也是一个函数对象。它常常用于有切面需求的场景,好比:插入日志、性能测试、事务处理、缓存、权限校验等场景。装饰器是解决这类问题的绝佳设计,有了装饰器,咱们就能够抽离出大量与函数功能自己无关的雷同代码并继续重用

装饰器=高阶函数+函数嵌套+闭包

2.装饰器的框架

from functools import wraps

def deco(func):
    @wraps(func) #加在最内层函数正上方
    def wrapper(*args,**kwargs):
        return func(*args,**kwargs)
    return wrapper
装饰器的固定格式——wraps版
def outer(flag):
    def timer(func):
        def inner(*args,**kwargs):
            if flag:
                print('''执行函数以前要作的''')
            re = func(*args,**kwargs)
            if flag:
                print('''执行函数以后要作的''')
            return re
        return inner
    return timer

@outer(False)
def func():
    print(111)

func()
带参数的装饰器的固定格式
def wrapper1(func):
    def inner():
        print('wrapper1 ,before func')
        func()
        print('wrapper1 ,after func')
    return inner

def wrapper2(func):
    def inner():
        print('wrapper2 ,before func')
        func()
        print('wrapper2 ,after func')
    return inner

@wrapper2
@wrapper1
def f():
    print('in f')

f()
多个装饰器装饰同一个函数

 

3.列子
##********************装饰器的列子************************

import time

def timmer (func) : #func=test  
  def wapper():
    start_time=time.time()
    func()    #就是在运行test()
    stop_time = time.time()
    print("函数的运行时间是%s" %(start_time-stop_time))
  return wapper

@timmer     #test=timeer(test)

def test():
time.sleep(2.5)
print("函数运行完毕")
test()
装饰器的基本框架

##*************************** 加上参数的装饰器的列子 ***********************

def timmer (func):    #func=test
    def wapper(*args,**kwargs):
        start_time=time.time()
        func(*args,**kwargs) #就是在运行test()
        stop_time = time.time()
        print("函数的运行时间是%s" %(start_time-stop_time))

    return wapper

@timmer   #test=timeer(test)
def test(name,age):
    time.sleep(2.5)
    print("test函数运行完毕,名字是(%s),年龄是(%s)" %(name,age,))
    return

test("",18)

@timmer
def test2 (name,age,gender):
    time.sleep(3)
    print("test2函数运行完毕,名字:(%s),年龄:(%s),性别:(%s)"%(name,age,gender))
test2("alex",18,"")
带参数的装饰器框架

##.*************************加上返回值的装饰器的列子*************************

import time
def timer(func):
    def inner(*args,**kwargs):
        start = time.time()
        re = func(*args,**kwargs)
        print(time.time() - start)
        return re
    return inner

@timer   #==> func2 = timer(func2)
def func2(a):
    print('in func2 and get a:%s'%(a))
    return 'fun2 over'

func2('aaaaaa')
print(func2('aaaaaa'))
带返回值的装饰器

4. 防止装饰器失效

from functools import wraps

def deco(func):
    @wraps(func) #加在最内层函数正上方
    def wrapper(*args,**kwargs):
        return func(*args,**kwargs)
    return wrapper

@deco
def index():
    '''哈哈哈哈'''
    print('from index')

print(index.__doc__)
print(index.__name__)
View Code

 

  

函数进阶-生成器

列表的生成:

>>>>L= [x*x for x in range(10)]

>>>>L

[0,1,4,9,16,25,36,49,64,81]

 

1.生成器的定义:在Python中,这种一边循环一边计算的机制,称为生成器:generator。(本质就是一个迭代器)

元素的获取

1.经过 next()打印出generator的每个元素,每次调用next(g),就计算出g的下一个元素的值,直到计算到最后一个元素,没有更多的元素时,抛出StopIteration的错误:
next()next(g)gStopIteration
>>> next(g)
0
>>> next(g)
1
>>> next(g)
4
>>> next(g)
9
>>> next(g)
16
>>> next(g)
25
>>> next(g)
36
>>> next(g)
49
>>> next(g)
64
>>> next(g)
81
>>> next(g)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration
经过next()获取 
2.使用for循环获取元素
for
>>> g = (x * x for x in range(10))
>>> for n in g:
...     print(n)
... 
0
1
4
9
16
25
36
49
64
81
使用for循环得到元素  

 

2.建立:

1.把一个列表生成式的[]改为(),就建立了一个generator

>>> g = (x * x for x in range(10)) >>> g <generator object <genexpr> at 0x1022ef630>
2.经过函数实现
##除第一个和第二个数外,任意一个数均可由前两个数相加获得:1, 1, 2, 3, 5, 8, 13, 21, 34, ...



def fib(max):
    n, a, b = 0, 0, 1
    while n < max:
        yield b
        a, b = b, a + b
        n = n + 1
    return 'done'
 f = fib(6)
f
<generator object fib at 0x104feaaa0>

##若是一个函数定义中包含yield关键字,那么这个函数就再也不是一个普通函数,而是一个generator:在generator的函数,在每次调用next()的时候执行,遇到yield语句返回,再次执行时从上次返回的yield语句处继续执行。

将函数中的return换成yield就是生成器
# 函数
def func():
 
    print('这是函数func')
 
    return '函数func'
 
func()
 
# 生成器
def func1():
 
    print('这是函数func1')
 
    yield '函数func'
 
func1()
斐波那契数
__next__()来执行生成器
def func():
     print("111")
     yield 222
gener = func() # 这个时候函数不会执⾏. ⽽是获取到⽣成器
ret = gener.__next__() # 这个时候函数才会执⾏. yield的做⽤和return⼀样. 也是返回数据
print(ret)
结果:
111
222
View Code
send方法:send和__next__()同样均可以让生成器执行到下一个yield
def eat():
    for i in range(1,10000):
        a = yield '包子'+str(i)
        print('a is',a)
 
        b = yield '窝窝头'
        print('b is', b)
e = eat()
print(e.__next__())
print(e.send('大葱'))
print(e.send('大蒜'))
View Code

send和__next__()区别:

send 和 next()都是让生成器向下走一次

send能够给上一个yield的位置传递值,不能给最后一个yield发送值,在第一次执行生成器的时候不能使用send()

第一次调用的时候使用send()也能够可是send的参数必须是None

def func1():
    print('这是函数func1')
    f1 = yield '你好'
    print(f1)
    f2 = yield '我好'
    print(f2)
f = func1()
f.__next__()
f.send('你们好')
View Code


 

yield from:在python3中提供一种能够直接把可迭代对象中的每个数据做为生成器的结果进行返回
def func():
    lst = ['卫龙','老冰棍','北冰洋','牛羊配']
    yield from lst
g = func()
for i in g:
    print(i)


##有个小坑,yield from 是将列表中的每个元素返回,因此 若是写两个yield from 并不会产生交替的效果

def func():
    lst1 = ['卫龙','老冰棍','北冰洋','牛羊配']
    lst2 = ['馒头','花卷','豆包','大饼']
    yield from lst1
    yield from lst2
     
g = func()
for i in g:
    print(i)
View Code

 

 

函数进阶-迭代器

 

1.可迭代对象(Iterable

 可用于for循环的对象统称为可迭代对象 ,字符串,列表,元组,集合,字典,生成器 都是可迭代的。可使用isinstance()来判断一个对象是不是Iterable对象

 2.迭代器定义(Iterator):

能够被next()函数调用并不断返回下一个值的对象称为迭代器。

理解:实际上,Python中的Iterator对象表示的是一个数据流,Iterator能够被next()函数调用被不断返回下一个数据,直到没有数据能够返回时抛出StopIteration异常错误。能够把这个数据流看作一个有序序列,但咱们没法提早知道这个序列的长度。同时,Iterator的计算是惰性的,只有经过next()函数时才会计算并返回下一个数据。

三、可迭代协议:能够被迭代要知足要求的就叫作可迭代协议。内部实现了__iter__方法

  iterable:可迭代的------对应的标志

  什么叫迭代?:一个一个取值,就像for循环同样取值

        

四、迭代器协议:内部实现了__iter__,__next__方法

  迭代器大部分都是在python的内部去使用的,咱们直接拿来用就好了

  迭代器的优势:若是用了迭代器,节约内存,方便操做

   dir([1,2].__iter__())是列表迭代器中实现的全部的方法,而dir([1,2])是列表中实现的全部方法,都是以列表的方式返回给咱们,为了方便看清楚,咱们把他们转换成集合,而后取差集,然而,咱们看到列表迭代器中多出了三个方法,那么这三个方法都分别是干什么的呢?

 

 

 

 

相关文章
相关标签/搜索