Python编程-函数进阶

一、函数对象

函数是第一类对象,即函数可以当作数据传递

1 可以被引用
2 可以当作参数传递
3 返回值可以是函数
4 可以当作容器类型的元素
def foo():
    print('from foo')

func=foo    #将函数赋值给func

print(foo)  #打印函数,将返回函数在内存中的地址
print(func) #此时func = foo
func()      #加上大括号才能引用函数
运行结果:
<function foo at 0x00000000007E00D0>
<function foo at 0x00000000007E00D0>
from foo
def foo():
    print('from foo')

def bar(foo):   #给函数bar一个参数foo
    print(foo)  #打印foo,其实就是打印foo函数在内存中的地址
    foo()       #引用foo函数

bar(foo)
运行结果:
<function foo at 0x00000000006D00D0>
from foo
def foo():
    print('from foo')

def bar(foo):
    return foo         #返回值是foo函数在内存中的地址

f=bar(foo)

print(f)               #取bar函数的返回值

f()
运行结果:
<function foo at 0x0000000000D700D0>
from foo
def foo():
    print('from foo')
dic={'func':foo}

print(dic['func'])     #打印字典key名为func的值foo,也就是foo函数在内存中的地址

dic['func']()          #调用foo函数
运行结果:
<function foo at 0x00000000007C00D0>
from foo

应用

def select(sql):
    print('========>select')

def insert(sql):
    print('========>add')

def delete(sql):
    print('=======>delete')

def update(sql):
    print('-=---->update')


func_dic={
    'select':select,
    'update':update,
    'insert':insert,
    'delete':delete
}

def main():
    while True:
        sql = input('>>: ').strip()
        if not sql:continue
        l = sql.split()
        cmd=l[0]
        if cmd in func_dic:
            func_dic[cmd](l)

main()
def main():
    sql = input('>>: ')
    l = sql.split()
    print(l)
    if l[0] == 'select':
        select(l)
    elif l[0] == 'insert':
        insert(l)
    elif l[0] == 'delete':
        delete(l)
    elif l[0] == 'update':
        update(l)

二、函数的嵌套

1.函数的嵌套调用
def max2(x,y):                #取最大值
    return x if x > y else y

def max4(a,b,c,d):
    res1=max2(a,b)            #两两比较,取最大值
    res2=max2(res1,c)
    res3=max2(res2,d)
    return res3

print(max4(10,99,31,22))
运行结果:
99
name = "Alex"

def change_name():                    #先调用这层 
    name = "Alex2"

    def change_name2():               #再调用这层 
        name = "Alex3"
        print("第3层打印", name)

    change_name2()  # 调用内层函数    
    print("第2层打印", name)

change_name()
print("最外层打印", name)             #最后到这层
运行结果:
第3层打印 Alex3
第2层打印 Alex2
最外层打印 Alex
2.函数的嵌套定义

注意:定义函数在运行程序时不会执行,仅判断语法是否有错误

def f1():

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

f1()
运行结果:
from f2
from f3

三、名称空间与作用域

1.定义名字的方法
import time

name='egon'

def func():
    pass

class Foo:
    pass
2.三种名称空间
(1)内置名称空间

随着python解释器的启动而产生,內建函数属于内置名称空间内

print(sum)
print(max)
print(min)

print(max([1,2,3]))

import builtins
for i in dir(builtins):      #查看內建函数
    print(i)
(2)全局名称空间

文件的执行会产生全局名称空间,指的是文件级别定义的名字都会放入该空间

x=1
if x ==1 :
    y=2
import time

name='egon'

def func():
    pass

class Foo:
    pass
x=1

def func():
    money=2000
    x=2
    print('func')
print(x)
print(func)
func()
print(money)       #money属于func函数内的局部命名空间

func()
print(x)
报错信息:
Traceback (most recent call last):
1
  File "E:/s17/day04/名称空间与作用域.py", line 59, in <module>
    print(money)
<function func at 0x00000000007B00D0>
NameError: name 'money' is not defined
func
(3)局部名称空间

调用函数时会产生局部名称空间,只在函数调用时临时绑定,调用结束解绑定

x=10000          #全局
def func():
    x=1          #局部
    def f1():
        pass

3.作用域

(1)全局作用域:内置名称空间,全局名层空间
(2)局部作用:局部名称空间

名字的查找顺序:局部名称空间---》全局名层空间---》内置名称空间

x=1
def func():
    x=2
    print(x)
    sum=123123
    print(sum)
func()
运行结果:
2
123123
def func():
    x=2

func()
print(x)     #此时调用不到x
报错信息:
Traceback (most recent call last):
  File "E:/s17/day04/名称空间与作用域.py", line 94, in <module>
    print(x)
NameError: name 'x' is not defined

查看全局作用域内的名字:gloabls()

查看局局作用域内的名字:locals()

x=1000
def func():
    x=2

print(globals())

print(locals())
print(globals() is locals())
运行结果:
{'func': <function func at 0x00000000007D00D0>, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x0000000000626DA0>, '__spec__': None, 'x': 1000, '__cached__': None, '__doc__': '\n作用域:\n    1. 全局作用域:内置名称空间,全局名层空间\n    2. 局部作用:局部名称空间\n', '__builtins__': <module 'builtins' (built-in)>, '__package__': None, '__file__': 'E:/s17/day04/名称空间与作用域.py', '__name__': '__main__'}
{'func': <function func at 0x00000000007D00D0>, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x0000000000626DA0>, '__spec__': None, 'x': 1000, '__cached__': None, '__doc__': '\n作用域:\n    1. 全局作用域:内置名称空间,全局名层空间\n    2. 局部作用:局部名称空间\n', '__builtins__': <module 'builtins' (built-in)>, '__package__': None, '__file__': 'E:/s17/day04/名称空间与作用域.py', '__name__': '__main__'}
True
x=1000
def func(y):
    x=2
    print(locals())
    print(globals())

func(1)
运行结果:
{'y': 1, 'x': 2}
{'__file__': 'E:/s17/day04/名称空间与作用域.py', '__doc__': '\n作用域:\n    1. 全局作用域:内置名称空间,全局名层空间\n    2. 局部作用:局部名称空间\n', '__package__': None, 'func': <function func at 0x00000000007E00D0>, '__name__': '__main__', '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x0000000000646DA0>, '__builtins__': <module 'builtins' (built-in)>, 'x': 1000, '__cached__': None, '__spec__': None}

全局作用域:全局有效,在任何位置都能被访问到,除非del删掉,否则会一直存活到文件执行完毕

局部作用域的名字:局部有效,只能在局部范围调用,只在函数调用时才有效,调用结束就失效

x=1

def f1():
    print(x)

def foo():
    print(x)

def f(x):
    # x=4
    def f2():
        # x=3
        def f3():
            # x=2
            print(x)

        f3()
    f2()

f(4)
运行结果:
4

四、闭包

  1. 定义在内部函数
  2. 包含对外部作用域而非全局作用域的引用,该内部函数就成为闭包函数
def f1():
    x = 1
    def f2():
        print(x)
    return f2

f=f1()
print(f)

x=100000000000000000000000000
f()
运行结果:
<function f1.<locals>.f2 at 0x00000000006B01E0>
1

闭包应用:惰性计算

抓取网页

from urllib.request import urlopen

def index(url):
    def get():
        return urlopen(url).read()

    return get

oldboy=index('http://crm.oldboyedu.com')

print(oldboy().decode('utf-8'))
print(oldboy.__closure__[0].cell_contents)

res=urlopen('http://crm.oldboyedu.com').read()

print(res.decode('utf-8'))
x=1
y=2
def f1():
    x=1
    y=2
    def f2():
        print(x,y)
    return f2

f=f1()
print(f.__closure__[0].cell_contents)
运行结果:
1

五、装饰器

装饰器:修饰别人的工具,修饰添加功能,工具指的是函数

装饰器本身可以是任何可调用对象,被装饰的对象也可以是任意可调用对象

为什么要用装饰器:

  • 开放封闭原则:对修改是封闭的,对扩展是开放的
  • 装饰器就是为了在不修改被装饰对象的源代码以及调用方式的前提下,为期添加新功能

在修改代码时,为了不对源代码产生影响而使用装饰器

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 wrapper

@timmer                           #对源代码进行装饰,index=timmer(index)
def index():

    time.sleep(3)
    print('welcome to index')

f=timmer(index)
print(f)
f()                              #wrapper()---->index()

index=timmer(index)              #index==wrapper

index() #wrapper()----->

流程分析

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                          #装饰器 index=timmer(index)
def index():
    time.sleep(3)
    print('welcome to index')
    return 1

@timmer
def foo(name):
    time.sleep(1)
    print('from foo')


res=index()                      #wrapper()
print(res)

res1=foo('egon')                 #res1=wrapper('egon')
print(res1)
运行结果:
welcome to index
run time is 3.012805223464966
1
from foo
run time is 1.0140018463134766
None

应用

login_user={'user':None,'status':False}
def auth(func):
    def wrapper(*args,**kwargs):
        if login_user['user'] and login_user['status']:
            res=func(*args,**kwargs)
            return res
        else:
            name=input('>>: ')
            password=input('>>: ')
            if name == 'egon' and password == '123':
                login_user['user']='egon'
                login_user['status']=True
                print('\033[45mlogin successful\033[0m')
                res=func(*args,**kwargs)
                return res
            else:
                print('\033[45mlogin err\033[0m')
    return wrapper

@auth
def index():
    print('welcome to index page')
@auth
def home(name):
    print('%s welcome to home page' %name)

index()
home('egon')

有参装饰器

import time
def timmer(func):
    def wrapper(*args,**kwargs):
        print('====>timmer.wrapper')
        start_time=time.time()
        res=func(*args,**kwargs)                         #auth_wrapper
        stop_time=time.time()
        print('run time is %s' %(stop_time-start_time))
        return res
    return wrapper

login_user={'user':None,'status':False}
def auth(driver='file'):
    def auth2(func):
        def wrapper(*args,**kwargs):
            print('=======>auth.wrapper')
            time.sleep(5)
            if driver == 'file':
                if login_user['user'] and login_user['status']:
                    res=func(*args,**kwargs)
                    return res
                else:
                    name=input('>>: ')
                    password=input('>>: ')
                    if name == 'egon' and password == '123':
                        login_user['user']='egon'
                        login_user['status']=True
                        print('\033[45mlogin successful\033[0m')
                        res=func(*args,**kwargs)
                        return res
                    else:
                        print('\033[45mlogin err\033[0m')
            elif driver == 'ldap':
                print('==========ldap的认证')
            elif driver == 'mysql':
                print('==========mysql的认证')
                return func(*args,**kwargs)
            else:
                print('=========未知的认证来源')
        return wrapper
    return auth2

@auth('file')                  #@auth2====>index=auth2(index)===>index=auth_wrapper
@timmer                        #index=timmer(auth_wrapper) #index=timmer_wrapper
def index():
    time.sleep(3)
    print('welcome to index page')
@auth(driver='mysql')
def home(name):
    print('%s welcome to home page' %name)
index()                       #timmer_wrapper()
home('egon') #wrapper('egon')

六、迭代器

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

迭代器是访问集合元素的一种方式。迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束。迭代器只能往前不会后退,不过这也没什么,因为人们很少在迭代途中往后退。另外,迭代器的一大优点是不要求事先准备好整个迭代过程中所有的元素。迭代器仅仅在迭代到某个元素时才计算该元素,而在这之前或之后,元素可以不存在或者被销毁。这个特点使得它特别适合用于遍历一些巨大的或是无限的集合,比如几个G的文件

特点:

  • 访问者不需要关心迭代器内部的结构,仅需通过next()方法不断去取下一个内容
  • 不能随机访问集合中的某个值 ,只能从头到尾依次访问
  • 访问到一半时不能往回退
  • 便于循环比较大的数据集合,节省内存
while True: #只满足重复,因而不是迭代
    print('====>')

真正的迭代如下:

l = [1, 2, 3]
count = 0
while count < len(l):  
    print('====>', l[count])
    count += 1

l = (1, 2, 3)
count = 0
while count < len(l):  
    print('====>', l[count])
    count += 1

s='hello'
count = 0
while count < len(s):
    print('====>', s[count])
    count += 1

为什么要有迭代器?对于没有索引的数据类型,必须提供一种不依赖索引的迭代方式

可迭代的对象:内置__iter__方法的,都是可迭代的对象

[1,2].__iter__()
'hello'.__iter__()
(1,2).__iter__()

{'a':1,'b':2}.__iter__()
{1,2,3}.__iter__()

迭代器:执行__iter__方法,得到的结果就是迭代器,迭代器对象有__next__方法

i=[1,2,3].__iter__()

print(i)

print(i.__next__())
print(i.__next__())
print(i.__next__())
print(i.__next__()) #抛出异常:StopIteration
dic={'a':1,'b':2,'c':3}
i=dic.__iter__()
while True:
    try:
        key=i.__next__()
        print(dic[key])
    except StopIteration:
        break
如何判断一个对象是可迭代的对象,还是迭代器对象

from collections import Iterable,Iterator

'abc'.iter()
().iter()
[].iter()
{'a':1}.iter()
{1,2}.iter()

f=open('a.txt','w')
f.iter()

下列数据类型都是可迭代的对象,只有文件是迭代器对象

print(isinstance('abc',Iterable))
print(isinstance([],Iterable))
print(isinstance((),Iterable))
print(isinstance({'a':1},Iterable))
print(isinstance({1,2},Iterable))
print(isinstance(f,Iterable))

可迭代对象:只有__iter__方法,执行该方法得到的迭代器对象

迭代协议:

  • 对象有__next__
  • 对象有__iter__,对于迭代器对象来说,执行__iter__方法,得到的结果仍然是它本身

迭代器的优点和缺点

优点:

  • 提供了一种不依赖下标的迭代方式
  • 就跌迭代器本身来说,更节省内存

缺点:

  • 无法获取迭代器对象的长度
  • 不如序列类型取值灵活,是一次性的,只能往后取值,不能往前退

七、生成器

生成器函数:只要函数体包含yield关键字,该函数就是生成器函数
生成器就是迭代器

一个函数调用时返回一个迭代器,那这个函数就叫做生成器(generator);如果函数中包含yield语法,那这个函数就会变成生成器;

def func():
    yield 1
    yield 2
    yield 3
    yield 4

上述代码中:func是函数称为生成器,当执行此函数func()时会得到一个迭代器。

运行结果:
>>> temp = func()
>>> temp.__next__()
1
>>> temp.__next__()
2
>>> temp.__next__()
3
>>> temp.__next__()
4
>>> temp.__next__()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration
def foo():
    print('first')
    yield 1
    print('second')
    yield 2
    print('third')
    yield 3
    print('fourth')
    yield 4
    print('fifth')

g=foo()
for i in g:
    print(i)
print(next(g))      #触发迭代器g的执行,进而触发函数的执行
运行结果:
first
1
second
2
third
3
fourth
4
fifth

yield的功能:

  • 相当于为函数封装好__iter__和__next__
  • return只能返回一次值,函数就终止了,而yield能返回多次值,每次返回都会将函数暂停,下一次next会从上一次暂停的位置继续执行

应用:tail -f a.txt | grep 'python'

import time
def tail(filepath):
    with open(filepath,encoding='utf-8') as f:
        f.seek(0,2)
        while True:
            line=f.readline().strip()
            if line:
                yield line
            else:
                time.sleep(0.2)

t=tail('a.txt')

for line in t:
    print(line)
				
def grep(pattern,lines):
    for line in lines:
        if pattern in line:
            yield line
            
g=grep('python',tail('a.txt'))
print(g)

for i in g:
    print(i)

八、递归

在函数内部,可以调用其他函数。如果在调用一个函数的过程中直接或间接调用自身本身

def calc(n):
    print(n)
    if int(n/2) ==0:
        return n
    return calc(int(n/2))
 
calc(10)
运行结果:
10
5
2
1

应用:递归问路

#_*_coding:utf-8_*_
__author__ = 'Linhaifeng'
import time

person_list=['alex','wupeiqi','yuanhao','linhaifeng']
def ask_way(person_list):
    print('-'*60)
    if len(person_list) == 0:
        return '没人知道'
    person=person_list.pop(0)
    if person == 'linhaifeng':
        return '%s说:我知道,老男孩就在沙河汇德商厦,下地铁就是' %person
    print('hi 美男[%s],敢问路在何方' %person)
    print('%s回答道:我不知道,但念你慧眼识猪,你等着,我帮你问问%s...' %(person,person_list))
    time.sleep(3)
    res=ask_way(person_list)
    # print('%s问的结果是: %res' %(person,res))
    return res



res=ask_way(person_list)

print(res)

递归特性:

  • 必须有一个明确的结束条件

  • 每次进入更深一层递归时,问题规模相比上次递归都应有所减少

  • 递归效率不高,递归层次过多会导致栈溢出(在计算机中,函数调用是通过栈(stack)这种数据结构实现的,每当进入一个函数调用,栈就会加一层栈帧,每当函数返回,栈就会减一层栈帧。由于栈的大小不是无限的,所以,递归调用的次数过多,会导致栈溢出)

九、内置函数

内置参数详解 https://docs.python.org/3/library/functions.html?highlight=built#ascii

posted @ 2017-09-26 11:13  BXBZ—边学边做  阅读(212)  评论(0编辑  收藏  举报