20 二分法 面向过程 函数式编程 模块

二分法 面向过程 函数式编程 模块

1 二分查找法

算法是高效解决问题的方法。

二分查找法 Binary Search
前提:列表中的元素按照升序排列
具体步骤:

  1. 如果带查找的列表为空,则说明列表中不存在目标元素;
  2. 将列表中间位置的元素与目标值进行比较,如果两者相等,则查找成功;
  3. 否则根据这个中间元素将列表分为前后两个子列表;
  4. 根据中间位置的元素与目标值的大小关系确定带查找的子列表,如果中间位置的元素大于目标值,则对后面的子列表进行进一步查找,否则对前面的子列表进行进一步查找;
  5. 重复上面的查找过程,直到找到目标元素,或者待查找的子列表为空,即表示列表中不存在目标元素。
def binary_search(lst, target_ele):
    if len(lst) == 0:
        print('目标元素不存在。')
        return False
    mid_index = len(lst) // 2
    mid_ele = lst[mid_index]
    if target_ele == mid_ele:
        print('目标元素存在。')
        return True
    elif target_ele > mid_ele:
        return binary_search(lst[mid_index + 1: ], target_ele)
    else:
        return binary_search(lst[: mid_index], target_ele)

lst = [0, 1, 2, 3, 4, 5]
print(binary_search(lst, 2))  # 目标元素存在。True
print(binary_search(lst, 20))  # 目标元素不存在。False

2 面向过程的编程思想

2.1 介绍

编程思想,也可以称为编程范式。
面向过程(Procedure Oriented)的编程思想是以过程为核心的编程思想,其中过程指的是做事的步骤流程

其它编程思想本质上都是基于面向过程的编程思想的,只是在此面向过程的基础上进行了进一步封装。

优点:将复杂的问题流程化处理,变得简单。
缺点:扩展性比较差。面向过程的编程思想中,所有的流程都是联系在一起的,互相影响,修改某个部分很可能会影响整体,因此不便于维护和扩展。

2.2 应用场景解析:
  1. 不是所有的软件程序都需要频繁更迭;
    例如脚本程序 或者 进行数据分析的相关操作
  2. 即便软件程序需要频繁更迭,但这并不代表代码的所有组成部分都需要一起更迭。

3 函数式编程思想

3.1 介绍

函数式编程思想是将计算机的运算视为数学运算,即将数学上对函数的定义搬到了程序中。整体上,函数式编程与python的设计思想相悖。python提供了一些具有函数式编程思想的语法,它们可以将代码精简又不丧失可读性与间接性。

3.2 lambda 匿名函数
3.2.1 定义匿名函数
lambda [arg1 [,arg2,.....argn]]: expression

表达式只能是单行

对比:
定义有名函数

def func(x, y):
	return x + y

print(func)  # <function func at 0x地址>

定义匿名函数

lambda x, y: x + y

print(lambda x, y: x + y)  # <function <lambda> at 0x地址>
3.2.2 调用匿名函数

方式1:内存地址加括号

res = (lambda x, y: x + y)(1, 2)
print(res)  # 3

方式2:通过赋值,使用有名函数的方式调用

func = lambda x, y: x + y
print(func(1, 2))  # 3

匿名函数的核心是不需要函数名,因此上面的独立地调用匿名函数的方式不推荐使用。

方式3:
匿名函数一般用于需要临时调用一次的场景
之后由于没有函数名,即执行后无法再次通过函数名来访问函数体代码所在的内存空间,因此会被视为垃圾而回收掉。
更多的使用场景是将匿名函数与其它函数进行配合使用,即调用某个函数使临时为这个函数服务一次。

3.2.3 常用使用场景

max(iterable, key=None)
取出传入的可迭代对象中的最大值,可选参数key为比较的基准。
max函数的工作原理是将可迭代对象转换为迭代器对象进行迭代,返回的是迭代器产生的元素中的一个。
如果指定key,max函数会将每一个迭代器产生的元素当作参数传入key指定的函数中,将函数的返回值作为比较依据,根据返回值的大小取出最大的返回值对应的元素。

lst = [1, -2]
print(max(lst))  # 1

def func(x):
	return x ** 2
print(max(lst, key=func))  # -2

print(max(lst, key=lambda x: x ** 2))  # -2
dic = {1: 30, 2: 20, 3: 10}
# 默认比较的是字典的键
print(max(dic))  # 3

# 指定了key,此时比较的是字典的值
print(max(dic, key=lambda each_key: dic[each_key]))  # 1

min函数使用方法相同。

sorted(iterable, key=None, reverse=False)
排序规则,reverse = True 降序,reverse = False 升序(默认)

dic = {1: 30, 2: 20, 3: 10}
res1 = sorted(dic)
print(res1)  # [1, 2, 3]

res2 = sorted(dic, key=lambda each_key: dic[each_key])
print(res2)  # [3, 2, 1]

filter(function, iterable)
filter() 函数用于过滤掉不符合条件的元素,Python 3 中返回迭代器对象。
第一个参数 function 函数会接受迭代器产生的每一个元素,经过判断后根据返回的真/假结果来判断这个元素是否保留/去除。

dic = {1: 30, 2: 20, 3: 10}
res = filter(lambda each_key: dic[each_key] > 10, dic)
print(res)  # <filter object at 0x地址>
print(list(res))  # [1, 2]

reduce(function, sequence [, initial] )
python 3 中reduce函数已不是内置函数。
reduce函数会依次从sequence中取一个元素,将这个元素和上一次调用function的返回值作为参数传入function函数,并再次调用function函数。

from functools import reduce
res = reduce(lambda x, y: x * y, [1, 2, 3], 10)
print(res)  # 60

map(function, iterable, …)
根据提供的函数 function 作为处理规则来对序列做映射操作。
序列的每一个元素作为参数传入 function 函数,返回迭代器,其生成的值是每次调用 function 函数的返回值。

res = map(lambda x: x ** 3, list(range(5)))
print(res)  # <map object at 0x地址>
print(list(res))  # [0, 1, 8, 27, 64]

4 模块

4.1 什么是模块

模块是一系列功能的集合体。

分为三类:内置模块第三方模块自定义模块

模块分为四种形式:

  1. 使用python语言编写的后缀名为.py的文件;
    一个python文件可以存放很多功能,其本身就是一个模块。
    文件名:func.py
    模块名:func
  2. 已被编译成共享库或DLL的C或C++扩展;
  3. 存放一系列模块的文件夹(文件夹下存在名为__init__.py的文件),该文件夹也可以称为包;
  4. 使用C语言编写的链接到python解释器的内置模块。
4.2 为什么使用模块
  1. 内置模块与第三方模块可以拿来就用,无需定义,这种拿来主义可以极大地提升开发效率;
  2. 自定义模块
    可以将程序的各部分功能提取出来存放到一个公共的模块中,供大家来共享使用。这样做可以减少代码冗余,使组织结构更加清晰。
4.3 如何使用模块
4.3.1 导入

import func
func 是一个名字,属于本文件的全局名称空间。
首次导入模块会发生:

  1. 执行 func.py;
  2. 产生 func.py 的名称空间,将 func.py 执行过程中产生的名字存放到此名称空间中;
  3. 在当前文件中产生一个名字 func,指向2中产生的名称空间。

之后导入相同的模块都会直接引用首次导入时产生的名称空间,不会重复执行相同的模块。

import 模块名 as 模块别名
如果原模块名比较长,可以为其起一个简短的别名,方便使用。

4.3.2 引用

模块名.名字

  1. 指名道姓地向模块索要名字对应的值,不会与当前名称空间中的名字产生冲突;
  2. 无论是查看还是修改,操作的都是模块本身,与调用位置无关。

导入模块规范

  1. 可以以逗号为分隔符,在同一行中导入多个模块,但不推荐;
  2. 按顺序分类导入
    python内置模块
    第三方模块
    自定义模块
4.3.3 补充
  1. 函数是第一类对象,即函数可以当作变量来使用,包括用于赋值,作为函数的参数以及返回值,作为容器类型数据的元素等。
    模块也是第一类对象。
  2. 自定义模块的命名风格:纯小写字母+下划线
  3. 可以在函数内导入模块,导入的模块名字处于函数的局部名称空间。

5 练习

5.1

文件内容如下
egon male 18 3000
alex male 38 30000
wupeiqi female 28 20000
yuanhao female 28 10000
要求:

  1. 从文件中取出每一条记录放入列表中,
    每个元素形式为{‘name’:‘egon’,‘sex’:‘male’,‘age’:18,‘salary’:3000};
  2. 取出薪资最高的人的信息;
  3. 取出最年轻的人的信息。
with open(r'./test.txt', mode='rt', encoding='utf-8') as f:
    key_list = ['name', 'sex', 'age', 'salary']
    info_dict = [
        {
            key_list[0]: each_list[0],
            key_list[1]: each_list[1],
            key_list[2]: each_list[2],
            key_list[3]: each_list[3]
        } for each_list in (each_line.strip().split() for each_line in f)
    ]
print(info_dict)

# 取出薪资最高的人的信息
max_salary_info = max(info_dict, key=lambda each_dict: int(each_dict['salary']))
print(max_salary_info)  # {'name': 'alex', 'sex': 'male', 'age': '38', 'salary': '30000'}

# 取出最年轻的人的信息
min_age_info = min(info_dict, key=lambda each_dict: int(each_dict['age']))
print(min_age_info)  # {'name': 'egon', 'sex': 'male', 'age': '18', 'salary': '3000'}
5.2

将names=[‘egon’,‘alex_sb’,‘wupeiqi’,‘yuanhao’]中的名字全部变大写。

names = ['egon', 'alex_sb', 'wupeiqi', 'yuanhao']
upper_list = [each_name.upper() for each_name in names]
print(upper_list)  # ['EGON', 'ALEX_SB', 'WUPEIQI', 'YUANHAO']
5.3

将names=[‘egon’,‘alex_sb’,‘wupeiqi’,‘yuanhao’]中以sb结尾的名字过滤掉,然后保存剩下的名字长度。

from functools import reduce

names = ['egon', 'alex_sb', 'wupeiqi', 'yuanhao']
no_sb_list = filter(lambda each_name: not each_name.endswith('sb'), names)  # ['egon', 'wupeiqi', 'yuanhao']
len_list = [len(each_ele) for each_ele in no_sb_list]
print(len_list)  # [4, 7, 7]
total_len = reduce(lambda x, y: x + y, len_list)
print(total_len)  # 18
5.4

求文件a.txt中最长的行的长度(长度按字符个数算,需要使用max函数)

with open(r'./a.txt', mode='rt', encoding='utf-8') as f:
    max_len_line = max(len(each_line) for each_line in f)
print(max_len_line)
5.5

求文件a.txt中总共包含的字符个数?思考为何在第一次之后的n次sum求和得到的结果为0?(需要使用sum函数)

with open(r'./a.txt', mode='rt', encoding='utf-8') as f:
    sum_len = sum(len(each_line) for each_line in f)
    print(sum_len)
    sum_len1 = sum(len(each_line) for each_line in f)
    print(sum_len1)  # 0

第一次读取文件后指针指向了文件末尾,所以再次读取文件时读到的内容为空。

5.6
with open('a.txt') as f:
    g = (len(line) for line in f)
print(sum(g))  # 为何报错?
  1. g是一个生成器,本身不存储值,只有在需要时才会执行并生成值;
  2. with open语句会在其内部代码块执行完毕后关闭文件;
  3. 结合以上两点,当调用sum(g)时,生成器会执行并产生值,但此时文件已经关闭,无法读取文件,因此报错。
5.7

文件shopping.txt内容如下
mac,20000,3
lenovo,3000,10
tesla,1000000,10
chicken,200,1

  1. 求总共花了多少钱?
  2. 打印出所有商品的信息,格式为[{‘name’:‘xxx’,‘price’:333,‘count’:3},…]
  3. 求单价大于10000的商品信息,格式同上
with open(r'./shopping.txt', mode='rt', encoding='utf-8') as f:
    key_list = ['name', 'price', 'count']
    shop_dict = [
        {
            key_list[0]: each_list[0],
            key_list[1]: each_list[1],
            key_list[2]: each_list[2]
        } for each_list in (each_line.strip().split(',') for each_line in f)
    ]

print(shop_dict)

# 总共消费
total_consume = sum(int(each_dict['price']) * int(each_dict['count']) for each_dict in shop_dict)
print(total_consume)  # 10090200

# 求单价大于10000的商品信息
target_list = [each_dict for each_dict in shop_dict if int(each_dict['price']) > 10000]
print(target_list)
5.8

题目1:

  1. 应该将程序所有功能都扔到一个模块中,然后通过导入模块的方式引用它们;
    答:不应该,应该看情况处理,对于一些文件独有的功能,无需将其共享到模块中。
  2. 应该只将程序各部分组件共享的那一部分功能扔到一个模块中,然后通过导入模块的方式引用它们。
    答:可以

题目2:

  1. 运行python文件与导入python文件的区别是什么?
    运行python文件仅仅生成本文件的名称空间,导入时还会生成导入文件的名称空间。
  2. 运行的python文件产生的名称空间何时回收,为什么?
    该文件运行完毕时回收名称空间。因为文件运行过程中产生的名字存放于全局名称空间中,全局名称空间在文件运行结束时销毁。
  3. 导入的python文件产生的名称空间何时回收,为什么?
    所有导入这个文件的文件全部运行完毕时回收导入文件的名称空间。因为导入时会产生导入文件的名称空间,并在当前文件中产生一个名字去指向导入文件的名称空间,该名字处于源文件的全局名称空间中,在原文件运行结束时随着全局名称空间一起销毁。
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值