Python函数高级

主宰稳场 提交于 2019-12-05 19:35:26

一、闭包函数

闭包:闭是封闭(函数内部函数),包是包含(该内部函数对外部作用域而非全局作用域的变量的引用)。闭包指的是:函数内部函数对外部作用域而非全局作用域的引用。

def outter(x):
    x = 1

    def inner():
        print(x)

    return inner


f = outter(2)
f()  # 1
f()  # 1
f()  # 1
# 查看闭包的元素
print(f.__closure__[0].cell_contents)  # 1

闭包的意义:返回的函数对象,不仅仅是一个函数对象,在该函数外还包裹了一层作用域,这使得,该函数无论在何处调用,优先使用自己外层包裹的作用域。

1、应用领域:

延迟计算(原来我们是传参,现在我们是包起来)、爬虫领域。

import requests


def outter(url):
    def get():
        response = requests.get(url)
        print(f"done: {url}")

    return get


baidu = outter('https://www.baidu.com')
python = outter('https://www.python.org')

baidu()
baidu()

python()
python()

二、装饰器

装饰器指的是为被装饰器对象添加额外功能。因此定义装饰器就是定义一个函数,只不过该函数的功能是用来为其他函数添加额外的功能。装饰器的实现必须遵循两大原则:

  1. 不修改被装饰对象的源代码
  2. 不修改被装饰对象的调用方式

装饰器其实就是在遵循以上两个原则的前提下为被装饰对象添加新功能。

不改变函数体代码,并且不改变函数调用方式,它本质就是一个函数。

def f1(x):
    def f2():
        print(x)  # 10
    return f2

f2 = f1()
f2()  # f2()

1、无参装饰器

1、装饰器模板

def deco(func):
    def wrapper(*args,**kwargs):
        res = func(*args,**kwargs)
        return res
    return wrapper

2、举例:

import time


def index():
    print('welcome to index')
    time.sleep(1)


def time_count(func):
    # func = 最原始的index
    def wrapper():
        start = time.time()
        func()
        end = time.time()
        print(f"{func} time is {start - end}")  # <function index at 0x102977730> time is -1.0038220882415771

    return wrapper


index = time_count(index)  # index为被装饰函数index的内存地址,即index = wrapper
index()  # wrapper()

3、被装饰函数有返回值:

如果原始的被装饰函数index()有返回值的时候,wrapper()函数的返回值应该和index()的返回值相同,也就是说,我们需要同步原始的index()和wrapper()方法的返回值。

import time


def index():
    print('welcome to index')
    time.sleep(1)
    return 123


def time_count(func):
    # func = 最原始的index
    def wrapper():
        start = time.time()
        res1 = func()
        end = time.time()
        print(f"{func} time is {start - end}")  # <function index at 0x102977620> time is -1.0050289630889893
        return res1

    return wrapper


index = time_count(index)
res = index()
print(f"res: {res}")  #
res: 123

4、被装饰函数需要传参:

如果原始的被装饰函数index()方法需要传参,那么我们之前的装饰器是无法实现该功能的,由于有wrapper()=index(),所以给wrapper()方法传参即可。

import time


def index():
    print('welcome to index')
    time.sleep(1)
    return 123


def home(name):
    print(f"welcome {name} to home page")
    time.sleep(1)
    return name


def time_count(func):
    def wrapper(*args, **kwargs):
        start = time.time()
        res = func(*args, **kwargs)
        end = time.time()
        print(f"{func} time is {start-end}") # <function home at 0x102977378> time is -1.0039079189300537
        return res
    return wrapper


home = time_count(home)

res = home('egon')
print(f"res: {res}") #res: egon

5、装饰器语法糖:

在被装饰函数正上方,并且是单独一行写上@装饰器名

import time


def time_count(func):
    # func = 最原始的index
    def wrapper(*args, **kwargs):
        start = time.time()
        res = func(*args, **kwargs)
        end = time.time()
        print(f"{func} time is {start-end}") #<function home at 0x102977620> time is -1.0005171298980713
        return res
    return wrapper


@time_count  # home = time_count(home)
def home(name):
    print(f"welcome {name} to home page") #welcome egon to home page
    time.sleep(1)
    return name


@time_count  # index = time_count(index)
def index():
    print('welcome to index')
    time.sleep(1)
    return 123


res = home('egon')
print(f"res: {res}") #res: egon


2、 有参装饰器

1、注意无参装饰器只套两层。

import time

current_user = {'username': None}


def login(func):
    # func = 最原始的index
    def wrapper(*args, **kwargs):
        if current_user['username']:
            res1 = func(*args, **kwargs)
            return res1

        user = input('username: ').strip()
        pwd = input('password: ').strip()

        if user == 'nick' and pwd == '123':
            print('login successful')
            current_user['username'] = user
            res1 = func(*args, **kwargs)
            return res1
        else:
            print('user or password error')

    return wrapper


@login
def home(name):
    print(f"welcome {name} to home page")
    time.sleep(1)


@login
def index():
    print('welcome to index')
    time.sleep(1)


res = index()

username: nick
password: 123
login successful
welcome to index

2、我们首先看看三层闭包怎么运用。

def f1(y):
    
    def f2():
        x = 1

        def f3():
            print(f"x: {x}")  # x: 1
            print(f"y: {y}")  # x: 1

        return f3

    return f2


f2 = f1(2)
f3 = f2()
f3()

3、应用:有参三层装饰器:

import time

current_user = {'username': None}


def auth(engine='file'):

    def login(func):

        def wrapper(*args, **kwargs):

            if current_user['username']:
                res = func(*args, **kwargs)

                return res

            user = input('username: ').strip()
            pwd = input('password: ').strip()

            if engine == 'file':
                print('base of file')
                if user == 'nick' and pwd == '123':
                    print('login successful')
                    current_user['username'] = user
                    res = func(*args, **kwargs)

                    return res
                else:
                    print('user or password error')
            elif engine == 'mysql':
                print('base of mysql, please base of file')

        return wrapper

    return login


@auth(engine='mysql')
def home(name):
    print(f"welcome {name} to home page")
    time.sleep(1)


@auth(engine='file')
def index():
    print('welcome to index')
    time.sleep(1)


res = index()

username: nick
password: 123
base of file
login successful
welcome to index

三、迭代器

1、可迭代的对象

Python内置str、list、tuple、dict、set、file都是可迭代对象

内置有__iter__方法的都叫可迭代的对象。

x = 1.__iter__  # SyntaxError: invalid syntax

# 以下都是可迭代的对象

name = 'nick'.__iter__
lis = [1, 2].__iter__
tup = (1, 2).__iter__
dic = {'name': 'nick'}.__iter__
s1 = {'a', 'b'}.__iter__
f = open('49w.txt', 'w', encoding='utf-8')
f.__iter__
f.close()

2、迭代器对象:

执行可迭代对象的__iter__方法,拿到的返回值就是迭代器对象。只有字符串和列表都是依赖索引取值的,而其他的可迭代对象都是无法依赖索引取值的,只能使用迭代器对象。

  1. 内置__next__方法,执行该方法会拿到迭代器对象中的一个值
  2. 内置有__iter__方法,执行该方法会拿到迭代器本身
  3. 文件本身就是迭代器对象。
s = 'hello'
iter_s = s.__iter__()

while True:
    try:
        print(iter_s.__next__())
    except StopIteration:
        break

3、for迭代器循环

for循环称为迭代器循环,in后必须是可迭代的对象。

lis = [1, 2, 3]
for i in lis:
    print(i)


print(range(10))  # range(0, 10)
for i in range(10):
    print(i)

四、三元表达式

语法:条件成立时的返回值 if 条件 else 条件不成立时的返回值

x = 10
y = 20

print(x if x > y else y)  # 20

五、列表推导式

语法:

[expression for item1 in iterable1 if condition1
            for item2 in iterable2 if condition2
             ...
            for itemN in iterableN if conditionN
]

举例:

print([i for i in range(10)])  # [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
print([i ** 2 for i in range(10)])  # [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]


name_list = ['nick', 'sean', 'jason', 'tank']
for n in [name if name == 'nick' else name + '_a' for name in name_list] :
    print(n)  # 'nick', 'sean_a', 'jason_a', 'tank_a'

六、字典生成式

print( {i: i**2 for i in range(10)} )
{0: 0, 1: 1, 2: 4, 3: 9, 4: 16, 5: 25, 6: 36, 7: 49, 8: 64, 9: 81}

zip()方法

keys = ['name', 'age', 'gender']
values = ['nick', 19, 'male']

res = zip(keys, values)
print(res)  # <zip object at 0x0000000002233540>

info_dict = {k: v for k, v in res}
print(info_dict)  # {'name': 'nick', 'age': 19, 'gender': 'male'}

通过解压缩函数生成一个字典返回:

info_dict = {'name': 'nick', 'age': 19, 'gender': 'male'}
print(info_dict.keys())  # dict_keys(['name', 'age', 'gender'])
print(info_dict.values())  # dict_values(['nick', 19, 'male'])

res = zip(info_dict.keys(), info_dict.values())
print(res)  # <zip object at 0x00000000026939C0>

info_dict = {k: v for k, v in res}
print(info_dict)  # {'name': 'nick', 'age': 19, 'gender': 'male'}

七、生成器

1、yield

生成器的本质就是迭代器

def func():
    print(1)
    yield   # 在函数中但凡出现yield关键字,再调用函数,就不会继续执行函数体代码,而是会返回一个值。
    print(2)


g = func()
print(g)  # <generator object func at 0x00000000026617B0>
  1. 提供一种自定义迭代器的方式
  2. yield可以暂停住函数,并提供当前的返回值

yield和return:

  1. 相同点:两者都是在函数内部使用,都可以返回值,并且返回值没有类型和个数的限制
  2. 不同点:return只能返回一次之;yield可以返回多次值

2、自定义range()方法

def my_range(start, stop, step=1):
    while start < stop:
        yield start
        start += 1


g = my_range(0, 3)
print(f"list(g): {list(g)}")

复杂版本:

def range(*args, **kwargs):
    if not kwargs:
        if len(args) == 1:
            count = 0
            while count < args[0]:
                yield count
                count += 1
        if len(args) == 2:
            start, stop = args
            while start < stop:
                yield start
                start += 1
        if len(args) == 3:
            start, stop, step = args
            while start < stop:
                yield start
                start += step

    else:
        step = 1

        if len(args) == 1:
            start = args[0]
        if len(args) == 2:
            start, stop = args

        for k, v in kwargs.items():
            if k not in ['start', 'step', 'stop']:
                raise ('参数名错误')

            if k == 'start':
                start = v
            elif k == 'stop':
                stop = v
            elif k == 'step':
                step = v

        while start < stop:
            yield start
            start += step


for i in range(3):
    print(i)  # 0,1,2

for i in range(99, 101):
    print(i)  # 99,100

for i in range(1, 10, 3):
    print(i)  # 1,4,7

for i in range(1, step=2, stop=5):
    print(i)  # 1,3

for i in range(1, 10, step=2):
    print(i)  # 1,3,5,7,9

3、生成器表达式

把列表推导式的[]换成()就是生成器表达式 。

优点:省内存,一次只产生一个值在内存中

t = (i for i in range(10))
print(t)  # <generator object <genexpr> at 0x00000000026907B0>
print(next(t))  # 0
print(next(t))  # 1

举例:

with open('32.txt', 'r', encoding='utf8') as f:
    nums = [len(line) for line in f]  # 列表推导式相当于直接给你一筐蛋

print(max(nums))  # 2


with open('32.txt', 'r', encoding='utf8') as f:
    nums = (len(line) for line in f)  # 生成器表达式相当于给你一只老母鸡。

print(max(nums))  # ValueError: I/O operation on closed file.

八、函数递归

递归的精髓在于通过不断地重复逼近一个最终的结果。

age(1)=26,age(n)=age(n-1)+2 ,求age(5)的值:

'''
...
age(5) = age(4) + 2
age(4) = age(3) + 2
age(3) = age(2) + 2
age(2) = age(1) + 2
age(1) = 26


age(n) = age(n-1) +2 
age(1) = 26  # n=1
'''


def age(n):
    if n == 1:
        return 26
    res = age(n-1) + 2
    return res


print(f"age(5): {age(5)}")

递归的本质就是干重复的活。

lis = [1, [2, [3, [4, [5, [6, ]]]]]]


def tell(lis):
    for i in lis:
        if type(i) is list:
            tell(i)
        else:
            print(i)


tell(lis)

二分法的思想实现查找数字。

from random import randint

nums = [randint(1, 100) for i in range(100)]
nums = sorted(nums)


def search(search_num, nums):
    print(nums)
    mid_index = len(nums) // 2
    if not nums:
        print('not exists')
        return
    if search_num > nums[mid_index]:
        # in the right
        nums = nums[mid_index + 1:]
        search(search_num, nums)
    elif search_num < nums[mid_index]:
        # in the left
        nums = nums[:mid_index]
        search(search_num, nums)
    else:
        print('find it')


search(7, nums)

九、匿名函数

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!