Python开发【第六篇】:迭代器、生成器、高阶函数、装饰器

 迭代器

  迭代器是访问集合元素的一种方式,迭代器从对象的第一个元素开始访问,知道所有元素被访问完成。迭代器只能往前访问,不能通过索引访问。

  类型内部使用__iter__()方法转为迭代器,使用__next__()取值。

特点:

  1. 访问者不需要关心迭代器内部的结构,仅需通过next()方法不断去取下一个内容
  2. 不能随机访问集合中的某个值 ,只能从头到尾依次访问
  3. 访问到一半时不能往回退
  4. 便于循环比较大的数据集合,节省内存

迭代器协议

  对象必须提供一个__next__方法执行该方法要么返回迭代中的下一项,要么返回一个Stopiteration异常以终止迭代

迭代对象

  实现了迭代协议(for循环机制就基于可迭代协议)

s_str = "hello world!"
# 使用__iter__()方法转变为可迭代对象
s_str_iter = s_str.__iter__()
print(s_str_iter)
# 使用可迭代对象里的next方法取值 (一直取到报异常:StopIteration)
print(s_str_iter.__next__())
# print(s_str_iter.__next__())
# print(s_str_iter.__next__())
# print(s_str_iter.__next__())
# print(s_str_iter.__next__())
# print(s_str_iter.__next__())
# print(s_str_iter.__next__())
# print(s_str_iter.__next__())
# print(s_str_iter.__next__())
# print(s_str_iter.__next__())
# print(s_str_iter.__next__())
# print(s_str_iter.__next__())
# print(s_str_iter.__next__())
# print(s_str_iter.__next__())
# print(s_str_iter.__next__())

# for 循环本质 1:先执行__iter__()转化为可迭代对象(遵循迭代协议) 2:然后依次执行__next__()方法 3:最后捕捉StopIteration异常

dic = {"name": "张三", "age": 18, "sex": "男"}
dic_iter = dic.__iter__()
print(dic_iter.__next__())
print(dic_iter.__next__())

# 模拟for循环输出
str = "1232456"
str_iter = str.__iter__()

while True:
    try:
        # print(str_iter.__next__())
        # 系统next()放和可迭代对象内部的__next__方法是一样的
        print(next(str_iter))
    except Exception as e:
        print(e)
        break

  

生成器

  ·一个函数调用时返回一个迭代器,那么这个函数叫做生成器,如果函数中包含yield,那么这个函数就叫做生成器(自动生成或实现了可迭代协议),生成器的本质就是迭代器

  1:生成器函数:内部使用了yield关键字 2:生成器表达式:列表推导式,返回的就是生成器

yield关键字

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

  

# 定义生成器
def my_rang(start, stop, step=0):
    while start <= stop:
        yield start + step
        start += 1

# 获取生成器
number = my_rang(1, 7)
# 依次获取值
# print(number.__next__())
# print(number.__next__())
# print(number.__next__())
# print(number.__next__())
# print(number.__next__())
# print(number.__next__())
# print(number.__next__())
# 便利获取值
for i in my_rang(1, 7):
    print(i)

  

print("========================生成100个包子案例===========================")


def product_baozi():
    li = []
    for i in range(100):
        li.append("包子%s" % i)
    return li


l = product_baozi()
print(list(l))


def product_pro():
    for i in range(100):
        print('正在生产包子.....')
        yield '以产包子%d' % i
        print('正在售卖包子')


p_pro = product_pro()
print(p_pro.__next__())
print(p_pro.__next__())
print(p_pro.__next__())

# 生成器效率高 内存占用小
#  yield总结:
#   1、把函数做成迭代器
#   2、对比return,可以返回多次值,可以挂起/保存函数的运行状态

print("=========================人口普查案例=======================")


# def get_population():
#     res =[]
#     with open("../files/population", "r", encoding="utf-8") as f:
#         for i in f:
#             res.append(i)
#     return res
#
# p_gen = get_population()
# print(p_gen)

def get_population():
    with open("../files/population", "r", encoding="utf-8") as f:
        for i in f:
            yield i


# 获取生成器
p_gen = get_population()

# 统计总人口数
count = sum(eval(i)["population"] for i in p_gen)
print(count)

ppp = get_population()
for i in ppp:
    print("%s 占统计人口的 %d %%" % (eval(i)['city'], count // eval(i)['population']))

print('=========================跑步案例=======================')


def run():
    i = 0
    while True:
        time.sleep(1)
        print('from run')
        yield i
        i += 1


f = run()
t = 0;
while t <= 1:
    print(f.__next__())
    t += 1
else:
    print("运行完成")

# 几乎不占内存
su = sum(i for i in range(100))
print(su)

# 三元表达式
age = 10
t = True if age > 10 else False

# 列表推导式
lit = [i for i in range(100) if i % 2 == 0]
print(lit)


def test():
    for i in range(4):
        yield i


t = test()
t1 = (i for i in t)
t2 = (i for i in t1)
print(list(t1))
# t2为空,因为t2取自t1 ,在上面t1已经打印并且释放完了,所以t2为空
print(list(t2))

生产者和消费者 

import time

# 生产包子
def consumer(name):
    print("我是【%s】,我准备开始吃包子了" % name)
    while True:
        baozi = yield
        time.sleep(1)
        print("%s 很开心把【%s】吃掉了" % (name, baozi))

# 消费包子
def producer():
    c1 = consumer("张三")
    c2 = consumer("李四")
    c2.__next__()
    c1.__next__()
    for i in range(10):
        time.sleep(1)
        c1.send('包子 %s' % i)
        c2.send('包子 %s' % i)


producer()

  

装饰器

  本质就是函数,为其它函数添加附加功能。2个原则,1:不修改被修饰的源代码,2:不修改被修饰函数的调用方式。

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

函数对象

     #1 可以被引用

  ·   #2 可以当作参数传递

     #3 返回值可以是函数

       #3 可以当作容器类型的元素

函数嵌套

     在函数内部再次定义函数     

def text1():
    def text2():
        print('我是在text1函数内部定义的text2函数。')
    text2()
text1()

  

高阶函数

  函数接收的参数是一个函数名称或者函数的返回值是一个函数名

def text1():
    def text2():
        print('我是在text1函数内部定义的text2函数。')

    return text2  # 返回text2函数内存地址


# 调用

# text1()()
# text1() 是调用text返回的都是text2的地址,
t = text1()
print(t)
# 真正调用函数
t()

  

 闭包

  在第五篇函数中有讲到.......

装饰器代码案例

   为一个函数添加一个运行时间计算的方法

def text(func):
    def text1():
        start_time = time.time()
        func()
        end_time = time.time()
        print("foo函数运行时间为:%s" % (end_time - start_time))

    return text1
 
def foo():
    print('来自foo函数运行模块....')

 
foo = text(foo)
foo()
 

  

@装饰器名称  ----------》python语法糖

def text(func):
    def text1():
        start_time = time.time()
        func()
        end_time = time.time()
        print("foo函数运行时间为:%s" % (end_time - start_time))

    return text1


@text  # foo = text(foo)
def foo():
    print('来自foo函数运行模块....')


# @text 相当于foo = text(foo)
# foo = text(foo)
# foo()
# 为改变调用方式
foo()

  

 装饰器带参数、返回值

from 数据库操作.MySqlHelper import MySqlHelper
from 数据库操作.RedisHelper import RedisHelper
from hashlib import sha1


## 装饰器返回值
# def text1(func):
#     def text2():
#         func()
#         return "text2返回值"
#     return text2
#
# @text1
# def text():
#     print('这是text1')
#
# t = text()
# print(t)

# # 装饰器带参数
# def text1(func):
#     def text2(a,b):
#         func(a,b)
#         return "text2返回值"
#     return text2
#
# @text1
# def text(a,b):
#     print('这是text1')
#     print("a+b=%s"%(a+b))
#
# t = text(13,14)
# print(t)


# 装饰器带参数
def text1(func):
    def text2(*args, **kwargs):
        func(*args, **kwargs)
        return "text2返回值"

    return text2


@text1
def text(a, b):
    print('这是text1')
    print("a+b=%s" % (a + b))


text(15, 18)
# t = text(13, 14)
# print(t)

  

装饰器综合案例

 不同数据库的登录验证

# -*- coding: utf-8 -*-

# 声明字符编码
# coding:utf-8
from 数据库操作.MySqlHelper import MySqlHelper
from 数据库操作.RedisHelper import RedisHelper
from hashlib import sha1

print("========================验证功能========================")

# 定义登录返回的消息格式
result_msg = {"success": False, 'message': ''}


# 定义文件读取生成器
def file_yeid():
    with open("../files/users", 'r', encoding='utf-8') as f:
        for row in f:
            yield row


# file登录验证
def file_login(user_name, user_pwd):
    # 获取文件生成器
    users = file_yeid()
    for row in users:
        try:
            user = eval(row)
            print(user)
            if user['user_name'] == user_name:
                if user['user_pwd'] == user_pwd:
                    result_msg['success'] = True
                    result_msg['message'] = '登录成功'
                else:
                    result_msg['success'] = False
                    result_msg['message'] = '密码错误'
                break
        except Exception as e:
            print('账号文件内容异常。。。。')
    else:
        result_msg['success'] = False
        result_msg['message'] = '用户名不存在'
    return result_msg


# redis登录验证
def redis_login(user_name, user_pwd):
    redis = RedisHelper(host="118.24.53.196", port=6379)
    name_value = redis.get(user_name)
    print("redis用户名:%s"%name_value)
    if name_value is None:
        result_msg['success'] = False
        result_msg['message'] = '用户名不存在'
    else:
        name_value = name_value.decode("utf8")
        if name_value == user_pwd:
            result_msg['success'] = True
            result_msg['message'] = '登录成功'
        else:
            result_msg['success'] = False
            result_msg['message'] = '密码错误'
    return result_msg


# MySql登录验证
def mysql_login(user_name, user_pwd):
    # 从数据库查询
    sql_helper = MySqlHelper(host='127.0.0.1', user='admin', password='123456', db='python')
    sql = "SELECT * FROM users WHERE uname = %s"
    params = [user_name]
    user_info = sql_helper.fetchall(sql, params)
    print(user_info)
    if user_info is None:
        result_msg['success'] = False
        result_msg['message'] = '用户名不存在'
    elif user_info[0][2] == user_pwd:
        result_msg['success'] = True
        result_msg['message'] = '登录成功'

    else:
        result_msg['success'] = False
        result_msg['message'] = '密码错误'

    return result_msg


def login_verification(db_type):
    def login_gener(func):
        # 接收用户输入
        user_name = input("请输入用户名:")
        user_pwd = input("请输入密码:")
        # 用户密码加密操作
        s1 = sha1()
        s1.update(user_pwd.encode('utf-8'))
        pwd_sha = s1.hexdigest()
        print("密码加密后:%s" % pwd_sha)

        def login(*args, **kwargs):

            resut = ""
            if db_type == 'file_db':
                resut = file_login(user_name, pwd_sha)
                print('file登录信息:%s' % resut)
            elif db_type == 'redis_db':
                resut = redis_login(user_name, pwd_sha)
                print('redis登录信息:%s' % resut)
            elif db_type == 'mysql_db':

                resut = mysql_login(user_name, pwd_sha)
                print('mysql登录信息:%s' % resut)
            else:
                print('暂无当前数据库类型的验证!!')
                return
            if resut['success'] == True:
                func(user_name)
            else:
                func("游客")

        return login

    return login_gener


@login_verification(db_type='redis_db')
def index(name):
    print("欢迎[%s]来到首页。。。。。。。。。" % name)

@login_verification(db_type='redis_db')
def home(name):
    print("欢迎[%s]来到家目录" % name)


# index("hehe")
home()

# 单个测试
# print(file_login('lisi', 'c3be7cf2877085fed38e0ec93b009e547e2929e0'))
# print(redis_login('admin1', 'c3be7cf2877085fed38e0ec93b009e547e2929e'))
# print(mysql_login('admin','c3be7cf2877085fed38e0ec93b009e547e2929e0'))# 密码为:123

MySqlHelper: 

# -*- coding: utf-8 -*-

# 声明字符编码
# coding:utf-8
import pymysql

class MySqlHelper(object):

    def __init__(self, host, user, password, db, charset='utf8', port=3306):
        """ 初始化 """
        self.host = host ,  # 服务器名称
        self.port = port # 3306, 3306
        self.user = user ,  # 用户名
        self.password = password ,  # 密码
        self.db = db #'python',  # 数据库名称
        self.charset = charset #'utf8'  # 字符编码
        self.conn = None
        self.cursor = None

    def connect(self):
        """链接数据库"""
        db_config = {
            'host': self.host,  # 服务器名称
            'port': self.port,  # 3306
            'user': self.user,  # 用户名
            'password': self.password,  # 密码
            'db': self.db,  # 数据库名称
            'charset': self.charset  # 字符编码
        }
        self.conn = pymysql.connect(**db_config)
        self.cursor = self.conn.cursor()

    def close(self):
        """关闭数据库"""
        self.cursor.close()
        self.conn.close()

    def execute(self, sql, params=[]):
        """执行数据库 增删改"""
        try:
            self.connect()
            self.cursor.execute(sql, params)
            self.conn.commit()
            self.close()
            print("OK")
        except Exception as e:
            print("异常信息如下:")
            print("%s" % e)

    def fetchall(self, sql, params=[]):
        """数据查询"""
        try:
            self.connect()
            self.cursor.execute(sql, params)
            result = self.cursor.fetchall()
            self.close()
            return result
        except Exception as e:
            print('%s' % e)

 

RedisHelper

# -*- coding: utf-8 -*-

# 声明字符编码
# coding:utf-8

from redis import *
# 这种连接不需要指定数据库及密码
# = StrictRedis(host="127.0.0.1", port=6379)

class RedisHelper():
    def __init__(self, host, port):
        self.host = host
        self.port = port
        self.__redis = StrictRedis(host, port)

    def set(self, key, value):
        self.__redis.set(key, value)

    def get(self, key):
        return self.__redis.get(key)

  

 

猜你喜欢

转载自www.cnblogs.com/wendj/p/Python_generator.html