Python函数进阶

一、 函数嵌套

1 函数的嵌套调用

在调用函数的过程中,又调用了其他函数

实例1-1 :

def foo():
    print('from foo')

def bar():
    print('from bar')
    foo()
bar()

实例1-2  

#求四个数的最大值
def max2(x,y):
    if x > y:
        return x
    else:
        return y

def max4(a,b,c,d):
    res1 = max2(a,b)
    res2 = max2(res1,c)
    res3 = max2(res2,d)
    return res3

print(max4(5,4,3,2))

2 函数的嵌套定义

在一个函数的内部,又定义另外一个函数

函数内部的代码,在调用函数时生效,调用结束时失效

def f1():
    def f2():
        def f3():
            print('from f3')
        f3()
    f2()
f1()

f3()                                #报错

二、名称空间与作用域

1 名称空间

名称空间:名称(标识符)到对象的映射,可以理解为一个容器。不同容器中的同名的标识符是不会相互冲突的

名称空间分为三种:内置名称空间、全局名称空间、局部名称空间

名称空间

内置名称空间(builtins)

Python自带的名字,如print、len等

Python解释器启动就生效,关闭就失效

全局名称空间(globals)

文件级别定义的名字(没有缩进的所有定义的名字)

执行文件时就生效,执行完毕就失效

局部名称空间(locals)

定义在函数内部的名字

调用函数时就生效,调用结束就失效

加载顺序

内置名称空间-->全局名称空间-->局部名称空间

取值顺序

局部名称空间-->全局名称空间-->内置名称空间

实例:

print(max)                          #打印结果:<built-in function max>

max = 1
def f1():
    max = 2
    def f2():
        max = 3
        print(max)                  #打印结果:3
    f2()
    print(max)                      #打印结果:2
f1()

print(max)                          #打印结果:1

2 作用域

作用域决定了再哪一部分程序可以访问哪个特定的变量名称

查找规则:以 L –> E –> G –>B 的规则查找,在局部找不到,便会去局部外的局部找(例如闭包),再找不到就会去全局找,再者去内建中找

作用域

L (Local) 局部作用域

E (Enclosing) 闭包函数外的函数中

G (Global) 全局作用域

B (Built-in) 内建作用域

查找规则

以 L –> E –> G –>B 的规则查找,在局部找不到,便会去局部外的局部找(例如闭包),再找不到就会去全局找,再者去内建中找

参考:

x =0                                    #全局作用域

def f1():
    a = 1                               #闭包函数外的函数中
    def f2():
        z = 2                           #局部作用域
        print(locals())                 #查看局部名称空间
    f2()
f1()

print(globals())                        #查看全局名称空间
print(dir(globals()['__builtins__']))   #查看内置名称空间

3 global和nonlocal关键字

1、global:局部作用域改掉全局作用域的变量

实例:

x = 1

def foo():
    global x                            #注意:当x为可变类型(例如字典)时,可以不用global
    x = 2
foo()

print(x)

#输出结果如下:
2

2、nonlocal:修改嵌套作用域(enclosing 作用域,外层非全局作用域)中的变量

实例:

def f1():
    x = 1
    def f2():
        nonlocal x
        x = 2
    f2()
    print(x)
f1()

#打印结果如下:
2

4 附加知识

实例:

x = 1

def f1():
    def f2():
        print(x)
    return f2                       #返回f2的内存地址
    f2()
f1()

func = f1()                         #f1就是f2的内存地址
print(func)                         #打印

#执行结果如下:
<function f1.<locals>.f2 at 0x0000000001E9B8C8>

#结论:通过函数对象的概念(函数可以当做另一个函数的返回值),打破了函数原有的层级限制,使此函数可以在任意位置调用
x = 1

def f1():
    def f2():
        print(x)
    return f2                       #返回f2的内存地址

def foo(func):
    x = 1000
    func()                          #等于执行f2(),回到定义的位置找x的值
foo(f1())                           #f1()就是f2的内存地址

#执行结果如下:
1

#结论:作用域关系,在函数定义时就已经固定,与调用位置无关

三、闭包函数

1 闭包函数的定义

内部函数包含对外部作用域而非全局作用域的引用

实例:

def f1():
    x = 1
    def f2():
        print(x)
    return f2

func = f1()
func()

2 闭包函数的应用

##爬网页-函数传参
import requests         #pip3 install requests

def get(url):
    return  requests.get(url).text

print(get('https://www.baidu.com'))

 
##爬网页-闭包函数
import requests         #pip3 install requests

def index():
    url = 'https://www.baidu.com'
    def get():
        return requests.get(url).text
    return get

baidu_web = index()
print(baidu_web())

 
##爬网页-闭包函数+函数传参
import requests         #pip3 install requests

def index(url):
    # url = 'https://www.baidu.com'
    def get():
        return requests.get(url).text
    return get

baidu_web = index('https://www.baidu.com')
python_web = index('https://www.python.org')
print(baidu_web())
print(python_web())

四、装饰器

1 装饰器的定义

装饰器其实就是一个闭包,把一个函数当做参数然后返回一个替代版函数

装饰器可以让其他函数在不需要做任何代码变动的前提下增加额外功能,装饰器的返回值也是一个函数对象

装饰器遵循的原则:不修改被装饰对象的源代码、不修改被调用对象的调用方式

2 无参装饰器

实例:

#爬网页
import requests

def index():
    print(requests.get('https://www.baidu.com').text)
index()


#加了一个统计消耗时间的功能,问题是动了源代码啊???
import time
import requests

def index():
    start = time.time()
    print(requests.get('https://www.baidu.com').text)
    stop = time.time()
    print('run time is %s' %(stop - start))
index()


#不动源代码,用装饰器实现
import time
import requests

def index():
    print(requests.get('https://www.baidu.com').text)

def timmer(func):
    def wrapper():
        start = time.time()
        func()
        stop = time.time()
        print('run time is %s' %(stop - start))
    return wrapper

index = timmer(index)
index()


#更简洁的实现方法,使用‘@ 装饰器名’
import time
import requests

def timmer(func):
    def wrapper():
        start = time.time()
        func()
        stop = time.time()
        print('run time is %s' %(stop - start))
    return wrapper

@timmer                         #等同于index = timmer(index),必须写在被装饰对象的正上方,并且是单独一行
def index():
    print(requests.get('https://www.baidu.com').text)
index()                         #index其实就是wrapper

上面的例子,装饰器是无参形式的,那有参装饰器如何处理呢?

3 有参装饰器

import time
import requests

def timmer(func):
    def wrapper(url):
        start = time.time()
        func(url)
        stop = time.time()
        print('run time is %s' %(stop - start))
    return wrapper

@timmer
def index(url):
    print(requests.get(url).text)
index('https://www.baidu.com')


#再改进一下,可以传任何长度和格式的参数,就要用到(*args,**kwargs)
import time
import requests

def timmer(func):
    def wrapper(*args,**kwargs):
        start = time.time()
        func(*args,**kwargs)
        stop = time.time()
        print('run time is %s' %(stop - start))
    return wrapper

@timmer
def index(url):
    print(requests.get(url).text)
index('https://www.baidu.com')

4 装饰器的应用

用装饰器实现认证功能

#先写框架
def auth(func):
    def wrapper(*args,**kwargs):
        pass
    return wrapper

@auth
def index():
    pass

index()

 
#进一步修改,实现基本认证
def auth(func):
    def wrapper(*args,**kwargs):
        name = input('name:').strip()
        password = input('password:').strip()
        if name == 'yim' and password == '123':
            res = func(*args,**kwargs)
            return res
        else:
            print('login fail')
    return wrapper

@auth
def index():
    print('login success')

index()

 

# 从文件中取出用户信息来验证(支持多用户验证)
# db文件内容如:{'zhangsan':'123','lisi':'456'}
def auth(func):
    def wrapper(*args,**kwargs):
        name = input('name:').strip()
        password = input('password:').strip()
        with open('db', 'r', encoding='UTF-8') as f:
            data = eval(f.read())                   # 从文件中取出来的实际上是字符串,用eval函数转换成字典
        if name in data and password == data[name]:
            res = func(*args,**kwargs)
            return res
        else:
            print('login fail')
    return wrapper

@auth
def index():
    print('login success')

index()

 
#记住登录状态
current_user = {'user':None}
def auth(func):
    def wrapper(*args,**kwargs):
        if current_user['user']:
            res = func(*args,**kwargs)
            return res
        name = input('name:').strip()
        password = input('password:').strip()
        with open('db', 'r', encoding='UTF-8') as f:
            data = eval(f.read())                   # 从文件中取出来的实际上是字符串,用eval函数转换成字典
        if name in data and password == data[name]:
            res = func(*args,**kwargs)
            current_user['user'] = name
            return res
        else:
            print('login fail')
    return wrapper

@auth
def index():
    print('login success')

@auth
def info():
    print('from info')

index()
info()

5 装饰器其他

查看函数注释信息

import time
from functools import wraps
import requests

def timmer(func):
    @wraps(func)
    def wrapper():
        start = time.time()
        func()
        stop = time.time()
        print('run time is %s' %(stop - start))
    return wrapper

@timmer
def index():
    '''这是index函数'''
    print(requests.get('https://www.baidu.com').text)
print(index.__doc__)                  #显示函数的注释信息,加了装饰器就看不到了。加了wraps装饰器就能看到了

# index()

五、迭代器

1 迭代器的定义

迭代的概念:重复的过程称为迭代,每次重复即一次迭代,并且每次迭代的结果是下一次迭代的初始值

迭代器是访问集合元素的一种方式。迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问结束。迭代器只能往前不能后退

迭代器有两个基本的方法:

  iter():返回迭代器对象本身

  next():返回迭代器的下一个元素

可迭代对象:只要是实现了__iter__()或__getitem__()方法的对象,就是可迭代对象

       序列:字符串、列表、元组

       非序列:字典、文件

       自定义类:用户自定义的类实现了__iter__()或__getitem__()方法的对象

可迭代对象(Iterable) 可以通过内置函数iter()来转变为迭代器(Iterator),再通过next()函数调用并不断返回下一个值

在使用for 和 in 语句时,程序就会自动调用即将被处理的对象的迭代器对象,然后使用它的next__()方法,直到监测到一个StopIteration异常

实例:

list = ['a','b','c']

i = list.__iter__()                       #或iter(list)
print(i.__next__())                     #或print(next(i))
print(i.__next__())
print(i.__next__())
print(i.__next__())

#执行结果如下:
a
b
c
Traceback (most recent call last):
  File "F:/Python/迭代器.py", line 7, in <module>                   #下面是提示信息,表示迭代器没值了
    print(i.__next__())
StopIteration


#处理StopIteration异常
dict = {'name':'Yim','age':25,'sex':'male'}
i = iter(dict)

while True:
    try:
        key = next(i)
        value = dict[key]
        print(key,value)
    except StopIteration:
        break

2 迭代器的优缺点

优点:

  1. 提供统一的且不依赖于索引的迭代方式
  2. 惰性计算,不要求事先准备好整个迭代过程中所有的元素。仅仅是在迭代至某个元素时才计算该元素,而在这之前或之后,元素可以不存在或者被销毁。这个特点使得它特别适合用于遍历一些巨大的或是无限的集合。(节省内存)

缺点:

  1. 无法预知什么时候取值结束,即无法预知长度
  2. 只能往前不能后退,不如索引取值灵活

3 判断可迭代对象与迭代器对象

判断是否为可迭代对象

from collections import Iterable,Iterator

s='hello'
l=[1,2,3,4]
t=(1,2,3)
d={'a':1}
set={1,2,3}
f=open('a.txt')

print(isinstance(s,Iterable))
print(isinstance(l,Iterable))
print(isinstance(t,Iterable))
print(isinstance(d,Iterable))
print(isinstance(set,Iterable))
print(isinstance(f,Iterable))

#执行结果如下:
True
True
True
True
True
True

判断是否为迭代器对象

from collections import Iterable,Iterator

s='hello'
l=[1,2,3,4]
t=(1,2,3)
d={'a':1}
set={1,2,3}
f=open('a.txt')

print(isinstance(s,Iterator))
print(isinstance(l,Iterator))
print(isinstance(t,Iterator))
print(isinstance(d,Iterator))
print(isinstance(set,Iterator))
print(isinstance(f,Iterator))

#执行结果如下:
False
False
False
False
False
True                                #文件是迭代器对象

六、生成器

1 生成器的定义

在Python中,使用了yield的函数被称为生成器(generator)

跟普通函数不同的是:生成器是一个返回迭代器的函数,只能用于迭代操作,更简单点理解生成器就是一个迭代器

在调用生成器运行的过程中,每次遇到yield时函数会暂停并保存当前所有的运行信息,返回yield的值。并在下一次执行next()方法时从当前位置继续运行

实例:

def func():
    print('first')
    yield 11111
    print('second')
    yield 22222
    print('third')
    yield 33333

print(type(func()))

g = func()
print(next(g))                      #打印返回值
print(next(g))
print(next(g))

#返回的结果
<class 'generator'>                 #类型是生成器
first
11111
second
22222
third
33333

 
#for
def func():
    print('first')
    yield 11111
    print('second')
    yield 22222
    print('third')
    yield 33333

g = func()

for i in g:                     #i = iter(g), next(g)
    print(i)

#返回的结果
first
11111
second
22222
third
33333

2 生成器的应用

def func(n):
    while True:
        yield n
        n += 1

g = func(0)

for i in g:
    print(i)
def my_range(start,stop):
    while True:
        if start == stop:
            raise StopIteration
        yield start
        start += 1

for i in my_range(1,100):
    print(i)
# python3 tail.py -f access.log | grep 'error'
import time

def tail(filepath):
    with open(filepath, 'r') as f:
        f.seek(0, 2)
        while True:
            line = f.readline()
            if line:
                yield line
            else:
                time.sleep(0.2)

def grep(pattern,lines):
    for line in lines:
        if pattern in line:
            print(line,end='')

grep('error',tail('access.log'))

七、三元表达式

1 三元表达式格式

格式:

  为真时的结果 if 判定条件 else 为假时的结果

2 三元表达式的应用

实例:

x = 10

def foo() :
    if x > 3:
        return 'ok'
    else:
        return 'no'
res = foo()
print(res)
 

#三元表达式
x = 10

res = 'ok' if x > 3 else 'no'
print(res)
def max(x,y):
    if x > y:
        return x
    else:
        return y
print(max(1,2))

 
#三元表达式
def max(x,y):
   return x if x > y else y
print(max(1,2))

八、列表解析

1 列表解析的定义

根据已有列表,高效创建新列表的方式

列表解析是Python迭代机制的一种应用,它常用语实现创建新的列表。因此用在[]中

语法:

       [expression for iter_val in iterable]

     [expression for iter_val in iterable if cond_expr]

2 列表解析的应用

实例:

#要求:列出1-10所有数字的平方
#普通方法
list = []

for i in range(1,11):
    list.append(i ** 2)
print(list)

 
#列表解析
list = [i **2 for i in range(1,11)]
print(list)
#要求:列出1-10中大于等于4的数字的平方
#普通方法
list = []

for i in range(1,11):
    if i >= 4:
        list.append(i ** 2)
print(list)


#列表解析
list = [i ** 2 for i in range(1,11) if i >= 4]
print(list)
#要求:列出1-10所有数字的平方除以2的值
#普通方法
list = []

for i in range(1,11):
    list.append(i ** 2 / 2)
print(list)

 
#列表解析
list = [i ** 2 / 2 for i in range(1,11)]
print(list)

九、生成器表达式

1 生成器表达式的定义

生成器表达式并不真正的创建数字列表,而是返回一个生成器对象,此对象在每次计算出一个条目后,把这个条目"产生"(yield)出来。生成器表达式使用了"惰性计算"或称作"延时求值"的机制

序列过长,并且每次只需要获取一个元素时,应该考虑生成器表达式而不是列表解析

2 生成器表达式的应用

实例:

N = (i **2 for i in range(1,11))

print(N)                                #返回的是一个生成器的地址
print(next(N))
print(next(N))
print(next(N))
print(next(N))
print(next(N))
print(next(N))
print(next(N))
print(next(N))
print(next(N))
print(next(N))
print(next(N))                          #所有元素遍历完后,会抛出StopIteration异常
N = (i **2 for i in range(1,11))

for i in N:
    print(i)
#有一个文件内容如下
aaa
bbbb
ccc
ddddd

#统计文件中最长的那行的长度
with open('a.txt','r',encoding='UTF-8') as f:
  print(max((len(line) for line in f)))

十、声明式编程

#有一个文件,里面的商品信息如下:
apple 10 3
tesla 100000 1
mac 3000 2
lenovo 30000 3
chiken 10 3

#要求1:求一共花了多少钱
with open('shop.txt','r',encoding='UTF-8') as f:
    l = []
    for line in f:
        goods = line.split()
        price = float(goods[1])
        count = int(goods[2])
        cost = price * count
        l.append(cost)
    print(sum(l))

#改进
with open('shop.txt','r',encoding='UTF-8') as f:
    l = [float(line.split()[1]) * int(line.split()[2]) for line in f ]
print(sum(l))


#要求2:把商品信息做成字典类型,再加入到列表,如:[{'name': 'apple', 'prcie': 10.0, 'count': 3}, {'name': 'tesla', 'prcie': 100000.0, 'count': 1}]
with open('shop.txt','r',encoding='UTF-8') as f:
    list = []
    for line in f:
        list.append({'name':line.split()[0],'prcie':float(line.split()[1]),'count':int(line.split()[2])})
print(list)

#改进
with open('shop.txt','r',encoding='UTF-8') as f:
    list = [{'name':line.split()[0],'prcie':float(line.split()[1]),'count':int(line.split()[2])} for line in f]
    print(list)
原文地址:https://www.cnblogs.com/yanmj/p/7239502.html