函数进阶


闭包

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
实现闭包步骤:
- 1: 你要先来一个函数嵌套
- 外函数里包装一个内函数
- 2: 外函数作用域有一个局部数据对象
- 3: 内部函数对于外部函数作用域里非全局变量的引用
- 4: 外函数返回内部函数
def wai():
a = 1 #a是属于wai函数作用域里的
def nei(): #nei函数就是一个载体了
print(a)
return nei #返回的是一个函数对象
func = wai() #func -> None
#func == nei
func() # a
#返回的nei函数可以在wai函数执行完成之后,继续不报错执行
#证明了在nei函数,也就是此时的func中,保留一个a变量。

1: 闭包内部创建载体,可以使一个局部变量续命
2:变量私有化:
每一次闭包函数执行后的返回值都是一个新的,这是因为函数运行首先开辟的都 是一块新的函数临时空间
每一次闭包函数执行后,都是返回了一个不一样的闭包函数载体,
那么这个载体里的变量,也是不一样的
3: 变量状态维持:
闭包函数载体(返回值),只要存活着,那么它其中的变量也将会一直维护
4: 闭包会一直保存变量,所以呢,本该死去的局部变量现在无法及时得到释放,
消耗内存
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
mylist = [1,2,3]
def func():
a = 1 #局部变量 只能存活在函数运行期间
#mylist[0] += 1 #在函数内部引用全局可变变量,那么其实你们都是全局的
mylist = ['a','b'] #局部变量
return a
func #函数对象 函数名
func() #函数调用
print(mylist)

#让一个局部变量存活下来:
#1: 返回值
#2: global
#3: 列表保存数据 全局共享同一个(只在函数内部做修改) 可变数据的引用
#4: 闭包也可以给一个局部变量续命

#对于可变数据对象:
#赋值:我为自己创建了一个新得 找了新欢
#直接创建了一个新的 : 局部变量
#修改:影响咱俩共有的女朋友
#全局被局部所使用 : 全局

装饰器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 装饰器可以在函数运行前添加功能,并且不影响原有函数内容

函数三要素
def func(arg): #定义
var = arg ** 2
return var
函数名: func
函数参数(形参):arg
函数返回值: arg
默认的,如果没有函数内的return语句,函数默认返回None

res = func(10) #执行
函数名:func
返回值:res
参数(实参):10

- func:函数对象
- func() :函数调用

普通装饰器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
def wai(b_func): #外函数的参数是一个函数对象
def nei():
print("嘿嘿嘿") #在nei函数里写的代码基本上都是添加的功能
return b_func() #这个是nei函数返回值,用来返回之前在wai函数所接收的参数
#b_func()
#return ''
return nei #返回了wai函数里的这个定义好的闭包容器

@wai #语法糖
def func():
print('哈哈哈哈')
return 'func'

res = func() #调用一个被装饰函数 其实相等于 wai(func)()
print('func的返回值:',res)
#func() 传统函数调用
#1: print('哈哈哈')
#2: return None
#func() 被装饰时调用 @wai
#被装饰函数: func
#装饰器函数: wai
#1: wai(func) -> return nei
#2: nei() ->
#1: print("嘿嘿嘿")
#2: return b_func()
#1: b_func() -> 'func' #内部调用被装饰函数
#2: return 'func' #内部闭包函数的返回值 其实是被装饰函数的返回值

被装饰函数带参数

1
2
3
4
5
6
7
8
9
10
11
12
13
def wai(func): #装饰器
def nei(var1,var2):
var1 = 100 #在闭包函数内部对被装饰函数参数进行干预
var2 = 99
return func(var1,var2)
return nei

@wai
def work(a,b):
return a + b

res = work(20,30) #wai(work)(a,b)
print(res)

装饰器函数也可以有参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
#商品
mind = True
zhekou = 0.9 #折扣的数值
youhuiquan = 5
# 当你的装饰器函数也需要参数的时候,只需要多一层包装即可
def dazhe(con,zk,yhq):
def wai(func):
def nei(money):
print('-----------')
if con:
print('打折!')
money = money * zk
elif yhq:
print("优惠券!")
money = money - yhq
return func(money)
return nei
return wai
#dazhe -> wai
#wai -> nei
#nei -> func()
@dazhe(mind,zhekou,youhuiquan)
def apple(money):
print('苹果的价钱是:%d' % money)
return money

@dazhe(mind,zhekou,youhuiquan)
def pants(money):
print('裤子的价钱是:%d' % money)
return money

@dazhe(mind,zhekou,youhuiquan)
def skirt(money):
print('裙子的价钱是:%d' % money)
return money

apple(10)
pants(50)
skirt(100)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
def wai(b_func): #外函数的参数是一个函数对象
def nei():
print("嘿嘿嘿") #在nei函数里写的代码基本上都是添加的功能
return b_func() #这个是nei函数返回值,用来返回之前在wai函数所接收的参数
#b_func()
#return ''
return nei #返回了wai函数里的这个定义好的闭包容器

@wai #语法糖
def func():
print('哈哈哈哈')
return 'func'

res = func() #调用一个被装饰函数 其实相等于 wai(func)()
print('func的返回值:',res)


#func() 传统函数调用
#1: print('哈哈哈')
#2: return None
#func() 被装饰时调用 @wai
#被装饰函数: func
#装饰器函数: wai
#1: wai(func) -> return nei
#2: nei() ->
#1: print("嘿嘿嘿")
#2: return b_func()
#1: b_func() -> 'func' #内部调用被装饰函数
#2: return 'func' #内部闭包函数的返回值 其实是被装饰函数的返回值
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import random
def func_1():
print('穿裙子')
def func_2():
print('穿大衣')
def func_3():
print('穿牛仔')
func_dict = {
'裙子':func_1,
'大衣':func_2,
'牛仔':func_3,
}
choice = random.choice(list(func_dict.keys()))

def _wai(choice):
def wai(func):
def nei():
func_dict[choice]()
return func()
return nei
return wai

@_wai(choice)
def girl():
print('上班了!')

girl() #装饰这个函数

异常捕获

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
常见错误:
IndentationError: unexpected indent 缩进错误
ZeroDivisionError: division by zero 除数为0
NameError: name 'b' is not defined 访问未声明变量
IndexError: list index out of range 访问越界(超过了原有数据的长度)变量
KeyError: 2 访问字典中(JSON类似)不存在key值
AttributeError: module 'socket' has no attribute 'create'
访问一个对象(变量,函数,类,模块)不存在的属性

异常捕获的作用:
- 异常捕获可以提高代码的健壮性
- 让我们的代码在不同情况下,可以让程序继续向下,而不是直接中断
- 有一些异常错误,需要额外导入模块才可以使用,切记。

异常的类型:
SystemExit(系统中断异常)
KeyboardInterrupt(ctrl+c)
Exception(内建异常类)
Python中的异常都是继承自这个Exception而来的

try:
print('打开文件') #捕获异常
fp = open('1.txt')
except FileNotFoundError :
print('这个文件不存在') #捕获到异常后要做的事情
except Exception as e:
#捕获所有错误,一般不用
finally:
#不管错误是否发生,都执行这里的代码,比如关闭文件
else:
print("关闭文件")
fp.close()
#else 分支可以在没有异常出现的时候 执行

手动抛出异常

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#手动抛出异常,就是直接raise		raise MyError('你能不能好好传参')

try:
raise TypeError('我心情不好,我就是要报错')
#raise语句去将一个合法的异常
except TypeError:
print('这是我刚才自己要抛出来的异常,现在我要捕获他')
print("哈哈哈哈哈哈")

def func(name,age):
if type(name) == str and type(age) == int:
print('%s:%d' % (name,age,abc)) #定义了传入参数的类型,类型不对就报错
else:
raise TypeError('你能不能好好传参')
#%d只能接受数字
name = '小明'
age = '18'
func(name,age)

自定义异常

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class MyError(Exception): #面向对象的继承方法
pass #什么都不干,过!

#继承已又的异常基类,从这个基础来创建自己的异常
# 现在已经创建出来了一个自己的错误

def func(name,age):
#name str
#age int
if type(name) == str and type(age) == int:
print('%s:%d' % (name,age,abc))
else:
raise MyError('你能不能好好传参')

name = '小明'
age = '18'
func(name,age)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#coding: utf-8
#2,3版本是两个Python
try:
from urllib.request import urlopen
from urllib.error import URLError
except ImportError:
from urllib2 import urlopen,URLError

url = 'https://www.asdaskljdklasjdsajdlk.com'
try:
res = urlopen(url)
print(res.read())
except URLError:
print('[+] 链接异常,无法访问')
1
2
3
4
5
6
7
8
9
10
11
12
13
human = {
'name':'小明',
'company':'小红',
#'gays':'小暗'
}

try:
print('%s的老婆是%s' % (human['name'],human['company'],human['gays']))
except KeyError as e:
if 'company' in str(e):
print('他没老婆')
elif 'gays' in str(e):
print('他没老公')

时间模块time

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
- time.time()  -> float_time
- linux时间:从197011日, 00:00:00
- 获取从Unix时间到现在经过的秒数 -> 浮点数
- time.localtime(float_time) -> 时间元组
- 如果不传递参数,那么默认返回本地时间的一个时间元组

res = time.localtime() -> 时间结果是具备时区的
time.struct_time(
tm_year=2018, 年
tm_mon=6, 月
tm_mday=9, 日
tm_hour=10, 时
tm_min=17, 分
tm_sec=9, 秒
tm_wday=5, 星期几 星期一数字是00开始 表示星期几
tm_yday=160, 今年第几天
tm_isdst=0 夏令时
)

res.tm_year
res.tm_mon
通过返回值的内置属性来获取到对应的值

time.sleep(seconds) --> 程序休眠,让程序挂起
time.clock() ---> 一般衡量程序的耗时
- win:
- 第一次调用:返回当前的CPU时间
- 第二次调用:返回距离上一次调用所花费的时间
- linux:
- 直接返回程序运行到执行这个函数所花费的时间
time.asctime(时间元组)
time.strftime(格式,时间元组) -> 良好可读性的字符串
将时间元组根据你指定的格式来成为一个良好可读性的字符串
time.strptime(良好可读性的字符串,格式) -> 时间元组
把一个字符串变成时间元组。

%Y: 年份 Year
%m: 月份 month
%d: 天数 day
#年月日,时分秒中,只有月和天是小写的,其他都是大写的
%H:时 Hour
%M:分 Minute
%S:秒 Seconds
#以上六个死记硬背
'%Y-%m-%d %H:%M:%S'

%x 月/日/年
%A: 星期的全称英语
%a:星期的缩写英语

%F 年/月/日
%T 时/分/秒

- strf 将时间元组变成字符串
- strp 将字符串变成时间元组
- 时间元组用来被处理,时间字符串只是一个展示的
1
2
3
4
5
6
>>> time.time()
1530412385.3557775
>>> time.localtime()
time.struct_time(tm_year=2018, tm_mon=7, tm_mday=1, tm_hour=10, tm_min=33, tm_sec=14, tm_wday=6, tm_yday=182, tm_isdst=0)
>>> time.asctime(time.localtime())
'Sun Jul 1 10:33:39 2018'

datetime模块

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
datetime.datetime
- 处理年月日,时分秒的
- datetime.datetime.now()
- datetime.datetime.today()
- 获取当前的时间
- 返回值:datetime.datetime类型
- 返回值类型支持差值运算,用来求出两个时间区间的秒数,或者说你指定的时间单位

datetime.date
- 处理年月日
- datetime.date.today()
- 年月日返回当前时间
datetime.time
- 只能处理时分秒
- 多用来创建时分秒的时间

创建时间
datetime.datetime(2018,6,6,10,10,59) 创建年月日,时分秒
datetime.date(2018,6,6) 创建年月日
datetime.time(10,50,59) 创建时分秒

#我想求出再过20天是啥时候
>>> de = datetime.timedelta(days=25)
>>> now = datetime.datetime.now()
>>> now + de
datetime.datetime(2018, 7, 4, 11, 20, 2, 286059)
#结果中,会自动把天数,月数向上换算
1
2
3
4
5
6
7
8
9
10
11
>>> datetime.datetime.now()
datetime.datetime(2018, 7, 1, 10, 30, 36, 190926)

>>> de=datetime.timedelta(days=20) #求20天后日期时间
>>> now=datetime.datetime.now()
>>> now+de
datetime.datetime(2018, 7, 21, 10, 31, 53, 726784)

>>> de=datetime.timedelta(days=-20) #使用-20求的是20天之前的日期时间
>>> now+de
datetime.datetime(2018, 6, 11, 10, 31, 53, 726784)

随机模块random

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
- random.randrange(stop)
- 生产出从0到stop区间内的一个随机整数
- 不包含stop
- random.randrange(start,stop,step)
- 生产出从0到stop区间内以步长为step的一个随机整数
- 不包含stop
- random.randint(start,stop)
- 返回start和stop区间内的一个随机整数
- 起点和终点都可能被取到
- random.getrandbits(num)
- 传入一个num值,0-2**num次方,去一个随机的整数
- 如果传递的num是10 - 2 ** 1 0 -2
- 不能取到终点:如果你传递的是2**3,那么取不到8
- 随机浮点数
- random.random() -> <1
- 返回介于01之间的浮点数
- random.uniform(start,stop)
- 取出一个从start开始到stop结束的一个随机浮点数
- 这里start也可能出现
- stop的值是不取的
- 随机序列
- random.choice(seq)
- 从一个非空序列中随机选择一个元素
- 序列为空则报错
- 序列:字符串,列表,元组 支持索引操作的数据
- random.shuffle(seq)
- 打乱序列的顺序
- random.sample(seq,num)
- num是我要从seq中随机抽取数据的长度,num 一定要小于 len(seq)
- 从seq中 随机抽取num个数据 返回成一个列表
- num代表取几个

SYS模块

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
sys.argv:
获取当前程序的命令行参数
命令行参数 就是你在执行.py脚本时传递的
参数的列表
sys.argv[0]: 当前程序的名字
sys.platform
- 用来输出当前的环境平台
- os.name
sys.exit(0)
直接退出程序
sys.path
- 环境变量列表
IO流处理:
i: in 进去
- 键盘向程序输入内容
- 写文件
- 接受其他服务器反馈
o: out 出来
- print向屏幕打印
- 读文件
- 向其他服务器发送请求
IO是影响程序性能,print操作是IO操作,那么尽量少在代码里print

sys.stdin
- 标准输入 input
sys.stdin.readline()[:-1] == input()
sys.stdout
- 标准输出 print
sys.stdout.write('abc\n') == print('abc')
sys.stderr
- 标准出错
坚持原创技术分享,您的支持将鼓励我继续创作!