文章目录

  • 一、函数的递归调用
  • (一)什么是函数的递归调用
  • (二)递归的两个阶段
  • 1.回溯:
  • 2.递推:
  • (三)递归的应用:二分法
  • 二、三元表达式
  • (一)什么是三元表达式
  • (二)如何使用三元表达式
  • 三、匿名函数
  • (一)什么是匿名函数
  • (二)如何使用匿名函数
  • 四、模块介绍
  • (一)什么是模块
  • 1.模块的定义:
  • 2.模块的三个来源:
  • 3.模块的四个类型:
  • (二)为何使用模块
  • 1.使用别人的模块:
  • 2.使用自定义模块:
  • 五、模块的使用
  • (一)模块使用之import语句
  • 1.import的使用
  • 2.首次导入模块发生了三件事
  • 3.被导入的模块有独立的名称空间
  • 4.为模块起别名
  • 5.可以一行导入多个模块
  • (二)模块使用之from-import 语句
  • 1.from...import...的使用
  • 2.from...import 与import的对比
  • 3.支持as为模块起别名
  • 4.支持一行导入多个名字
  • 5.from...import * 的是使用
  • 大部分情况下我们的python程序不应该使用这种导入方式,因为*你不知道你导入什么名字,很有可能会覆盖掉你之前已经定义的名字。而且可读性极其的差,在交互式环境中导入时没有问题。


一、函数的递归调用

(一)什么是函数的递归调用

就是函数在调用过程中,直接或间接的调用了自己。为防止内存溢出,在python中函数的递归调用默认最大1000层。所以函数递归不应无限进行,应在满足某条件时结束,然后返回。

# 直接调用自己
def func():
    print('from func')
    func()
    
func()

# 间接调用自己
def foo():
    print('from foo')
    bar()
def bar():
    print('from bar')
    foo()
    
foo()

(二)递归的两个阶段

1.回溯:

一层层地回溯下去。

2.递推:

在满足某条件的情况下结束回溯,然后一层层向上返回。

# 案例:
# 某公司四个员工坐在一起,问第四个人薪水,他说比第三个人多1000,问第三个人薪水,第他说比第二个人多1000,问第二个人薪水,他说比第一个人多1000,最后第一人说自己每月5000,请问第四个人的薪水是多少?
# 思路解析:
# 要知道第四个人的月薪,就必须知道第三个人的,第三个人的又取决于第二个人的,第二个人的又取决于第一个人的,而且每一个员工都比前一个多一千,数学表达式即:

salary(4)=salary(3)+1000 
salary(3)=salary(2)+1000 
salary(2)=salary(1)+1000 
salary(1)=5000
总结为: 
salary(n)=salary(n-1)+1000 (n>1) 
salary(1)=5000 (n=1) 

# 代码实现
def salary(n):
    if n==1:
        return 5000
    return salary(n-1) + 1000

s = salary(4)
print(s)  # 8000
# 打印列表里的所有值:
nums = [111, [222, [333, [444, [5555, [6666, [777, [888, [9999]]]]]]]]]

def func(l):
    for x in l:
        if type(x) is list:
            # 把自身再调用一遍
            func(x)
        else:
            print(x)
func(nums)

(三)递归的应用:二分法


# 从小到大排列的一个数字列表,找出某数字是否在列表中
nums = [11, 13, 32, 47, 53, 73, 84, 91, 101, 111, 222, 333, 444, 5555]

def binary_search(l, find_num):
    print(l)
    if len(l) == 0:
        print('find_num not exists')
        return
    mid_index = len(l) // 2
    if find_num > l[mid_index]:
        right_l = l[mid_index + 1:]
        binary_search(right_l, find_num)
    elif find_num < l[mid_index]:
        left_l = l[:mid_index]
        binary_search(left_l, find_num)
    else:
        print('find it')

binary_search(nums, 85)
# [11, 13, 32, 47, 53, 73, 84, 91, 101, 111, 222, 333, 444, 5555]
# [11, 13, 32, 47, 53, 73, 84]
# [53, 73, 84]
# [84]
# []
# find_num not exists

二、三元表达式

二、三元表达式

(一)什么是三元表达式

三元表达式是一种可以简化代码的方式,在不丧失可读性的情况下,精简代码。

# 表达式1 if 条件  else  表达式2
# 将三元表达式赋值给一个变量,可以得出结果

(二)如何使用三元表达式

# 案例
x = 111
y = 222

def max2(x, y):
    if x > y:
        return x
    else:
        return y

res = x if x > y else y  # 一行代码搞定一个简单的比较取值过程

print(res)  # 222

三、匿名函数

(一)什么是匿名函数

匿名函数就是没有名字的函数。

没有名字意味着只能使用一次,用完之后就是垃圾,所以匿名函数只能用于临时使用一次的场景。

匿名函数的表达式:lambda 参数:表达式 ===>>> 进而得出结果

表达式 lambda parameters: expression 会产生一个函数对象。 
该未命名对象的行为类似于用以下方式定义的函数:
def <lambda>(parameters):
    return expression

(二)如何使用匿名函数

# 错误示范:给匿名函数命名(没有意义)
res = lambda x, y: x + y
print(res(1, 2))

# 正确用法:
res = (lambda x, y: x + y)(1, 2)
print(res)
# 用于使用一次就没作用的场景,如:为其他函数作为一个辅助参数
salaries = {
    'egon': 4.4,
    "lqz": 3.3,
    'yj': 2.2
}

# max()、min()、sorted()函数对字典默认针对key进行排序,若要对值进行排序并返回key,可以加一个参数key,作为实际比较的依据。
res = max(salary, key=lambda k: salary[k])
print(res)  # egon

res = min(salary, key=lambda k: salary[k])
print(res)  # yj

res = sorted(salary, key=lambda k: salary[k], reverse = True)
print(res)  # ['egon', 'lqz', 'yj']

res = sorted(salary, key=lambda k: salary[k])
print(res)  # ['yj', 'lqz', 'egon']

四、模块介绍

(一)什么是模块

1.模块的定义:

模块就是一个功能的集合体,不是用来直接运行的,而是用于被导入使用的。

2.模块的三个来源:

(1)python内置模块;(2)第三方模块;(3)自定义模块。

3.模块的四个类型:

(1)一个py文件就是一个模块;(2)一个文件夹(包)也是一个模块;(3)已被编译为共享库或DLL的DLL的C或C++扩展:(4)使用C语言编写并链接到python解释器的内置模块。

(二)为何使用模块

1.使用别人的模块:

拿来主义,提升开发效率。

2.使用自定义模块:

为了减少代码冗余,方便程序的组织、管理。

五、模块的使用

(一)模块使用之import语句

1.import的使用

# 以spam.py为例
print('from the spam.py')

money=1000

def read1():
    print('spam模块:',money)

def read2():
    print('spam模块')
    read1()

def change():
    global money
    money=0
# test.py
import spam #只在第一次导入时才执行spam.py内代码,此处的显式效果是只打印一次'from the spam.py'(spam.py文件中的可执行语句),当然其他的顶级代码也都被执行了,只不过没有显示效果.
import spam
import spam
import spam

'''
执行结果:
from the spam.py
'''

2.首次导入模块发生了三件事

若重复导入会直接引用内存中已加载好的结果。

(1)为源文件(如:spam模块)创建新的名称空间,在spam中定义的函数和方法都在这个名称空间中,如果发生全局调用,则使用的数据也是该名称空间中的。

(2)在新创建的名称空间的同时执行模块中包含的代码,即在import spam 代码写完的同时触发运行。

(3)在当前名称空间中创建名字spam来引用该名称空间中的内容。

3.被导入的模块有独立的名称空间

每个模块都是一个独立的名称空间,定义在这个模块中的函数,把这个名称空间当作全局名称空间,这样就不会让被导入的模块与使用者的全局变量冲突。

(1)测试一:money与spam.money不冲突

# test.py
import spam 
money=10
print(spam.money)

'''
执行结果:
from the spam.py
1000
'''

(2)测试二:read1与spam.read1不冲突

# test.py
import spam
def read1():
    print('========')
spam.read1()

'''
执行结果:
from the spam.py
spam->read1->money 1000
'''

(3)测试三:执行spam.change()操作的全局变量money仍然是spam中的

# test.py
import spam
money=1
spam.change()
print(money)

'''
执行结果:
from the spam.py
1
'''

4.为模块起别名

可以为导入的模块起别名,简化调用的复杂程度。

import spam as sm
print(sm.money)

有两种sql模块mysql和Oracle,根据用户的输入,选择不同的sql功能

#mysql.py
def sqlparse():
    print('from mysql sqlparse')
#oracle.py
def sqlparse():
    print('from oracle sqlparse')

#test.py
db_type=input('>>: ')
if db_type == 'mysql':
    import mysql as db
elif db_type == 'oracle':
    import oracle as db

db.sqlparse()

5.可以一行导入多个模块

import sys,os,re

(二)模块使用之from-import 语句

1.from…import…的使用

from spam import read1, read2

2.from…import 与import的对比

不同:使用from…import…语句,是将spam中的名字直接导入当前名称空间,使用名字的时候不用加spam.进行调用了。

优势:使用起来更方便。

劣势:容易与当前执行文件中的名字冲突,相同名字的数据有覆盖效果。

(1)验证一:当前位置直接使用read1和read2就好了,执行时,仍然以spam.py文件全局名称空间

#测试一:导入的函数read1,执行时仍然回到spam.py中寻找全局变量money
#test.py
from spam import read1
money=1000
read1()
'''
执行结果:
from the spam.py
spam->read1->money 1000
'''

#测试二:导入的函数read2,执行时需要调用read1(),仍然回到spam.py中找read1()
#test.py
from spam import read2
def read1():
    print('==========')
read2()

'''
执行结果:
from the spam.py
spam->read2 calling read
spam->read1->money 1000
'''

(2)验证二:如果当前有重名read1或者read2,那么会有覆盖效果。

#测试三:导入的函数read1,被当前位置定义的read1覆盖掉了
#test.py
from spam import read1
def read1():
    print('==========')
read1()
'''
执行结果:
from the spam.py
==========
'''

(3)验证三:导入的方法在执行时,始终是以源文件为准的

from spam import money,read1
money=100 #将当前位置的名字money绑定到了100
print(money) #打印当前的名字
read1() #读取spam.py中的名字money,仍然为1000

'''
from the spam.py
100
spam->read1->money 1000
'''

3.支持as为模块起别名

from spam import read1 as read

4.支持一行导入多个名字

from spam import read1,read2,money

5.from…import * 的是使用

# from spam import * 把spam中所有的不是以下划线(_)开头的名字都导入到当前位置

# 大部分情况下我们的python程序不应该使用这种导入方式,因为*你不知道你导入什么名字,很有可能会覆盖掉你之前已经定义的名字。而且可读性极其的差,在交互式环境中导入时没有问题。
from spam import * #将模块spam中所有的名字都导入到当前名称空间
print(money)
print(read1)
print(read2)
print(change)

'''
执行结果:
from the spam.py
1000
<function read1 at 0x1012e8158>
<function read2 at 0x1012e81e0>
<function change at 0x1012e8268>
'''

可以使用__ all __来控制*(用来发布新版本),在spam.py中新增一行:

__all__=['money','read1'] #这样在另外一个文件中用from spam import *就这能导入列表中规定的两个名字

)开头的名字都导入到当前位置

大部分情况下我们的python程序不应该使用这种导入方式,因为*你不知道你导入什么名字,很有可能会覆盖掉你之前已经定义的名字。而且可读性极其的差,在交互式环境中导入时没有问题。

```python
from spam import * #将模块spam中所有的名字都导入到当前名称空间
print(money)
print(read1)
print(read2)
print(change)

'''
执行结果:
from the spam.py
1000
<function read1 at 0x1012e8158>
<function read2 at 0x1012e81e0>
<function change at 0x1012e8268>
'''

可以使用__ all __来控制*(用来发布新版本),在spam.py中新增一行:

__all__=['money','read1'] #这样在另外一个文件中用from spam import *就这能导入列表中规定的两个名字