一、抽象
抽象是隐藏多余细节的艺术。忽略一个主题中与当前目标无关的东西,专注的注意与当前目标有关的方面( 就是把现实世界中的某一类东西, 提取出来, 用程序代码表示, 抽象出来的一般叫做类或者接口)。在面向对象的概念中,抽象的直接表现形式通常为类。Python基本上提供了面向对象编程语言的所有元素。
抽象并不打算了解全部问题, 而是选择其中的一部分,暂时不用部分细节。抽象包括两个方面,一个数据抽象,二是过程抽象
- 数据抽象 -->表示世界中一类事物的特征,就是对象的属性.比如鸟有翅膀,羽毛等(类的属性)
- 过程抽象 -->表示世界中一类事物的行为,就是对象的行为.比如鸟会飞,会叫(类的方法)
1.自定义函数
函数指将常用的代码以固定的格式封装(包装)成一个独立的模块并可以重复使用它。函数先定义,后调用,即先将函数体代码保存,然后将内存地址赋值给函数名,函数名是对这段代码的引用。
def 函数名(参数1,参数2,...):
"""文档描述"""
函数体
return 值
- def: 定义函数的关键字
- 函数名:函数名指向函数内存地址,是对函数体代码的引用。函数的命名应该反映出函数的功能
- 括号:括号内定义参数,参数可有可无,且无需指定参数的类型
- 冒号:括号后要加冒号,然后在下一行开始缩进编写函数体的代码
- “”“文档描述”“”: 描述函数功能,参数介绍等信息的文档,非必要,但是建议加上,从而增强函数的可读性。放在函数开头的字符串称为文档字符串(docstring),将作为函数的一部分存储起来。
- 函数体:由语句和表达式组成
- return 值:定义函数的返回值,return可有可无
函数的使用分为定义阶段与调用阶段,定义函数时只检测语法,不执行函数体代码。函数名加括号即函数调用,只有调用函数时才会执行函数体代码。
1)定义
1.无参函数
def 函数名():
函数体
2.有参函数
def 函数名(参数,参数...):
函数体
3.空函数
函数体为pass代表什么都不做,称之为空函数。
def 函数名():
pass
2)调用
#定义阶段
def foo():
print('in the foo')
bar()
def bar():
print('in the bar')
#调用阶段
foo()
按照在程序出现的形式和位置,可将函数的调用形式分为三种
def add(a,b):
print(a+b)
return a+b
# 语句的形式:只加括号调用函数
add(1,2)
# 表达式形式
# 1.赋值表达式
res = add(1,2)
# 2.数学表达式
add(1,2)*10
# 函数调用可以当做参数
add(add(1,2),3)
callable()
要判断某个对象是否可调用,可使用内置函数callable。
import math
x = 1
y = math.sqrt
print(callable(x))
# False
print(callable(y))
# True
print(fn) # 其实就是在打印函数对象,也就是内存地址
print(fn()) # 其实就是在打印fn()的返回值
3)函数文档
在def语句后面添加函数字符串描述很有用。放在函数开头的字符串称为文档字符串(docstring),将作为函数的一部分存储起来。下面的代码演示了如何给函数添加文档字符串
__doc__
def square(x):
"""Calculates the square of the number x."""
return x * x
# 访问文档字符串:
print(square.__doc__)
# Calculates the square of the number x.
# __doc__是函数的一个属性。属性将在第7章详细介绍。属性名中的双下划线表示这是一个特殊的属性。特殊(“魔法”)属性将在第9章讨论。
help()
特殊的内置函数help很有用。在交互式解释器中,可使用它获取有关函数的信息,其中包含函数的文档字符串
def square(x):
"""Calculates the square of the number x."""
return x * x
print(help(square))
# Help on function square in module __main__:
#
# square(x)
# Calculates the square of the number x.
#
# None
print(square(x=3))
# 9
4)返回值
- return将函数执行结果返回给调用者。返回值无类型限制,可将多个返回值放到一个元组内。返回多个值时用逗号分隔,被return返回成一个元祖。
- return是函数结束的标志,函数运行到return会立刻终止运行,将return后的值当做本次运行的结果返回。
- 函数体内没有return,或者return后没有任何东西,就是返回一个None
2.函数参数
函数的参数分为形式参数和实际参数,简称形参和实参。
- 形参:在定义函数时,括号内声明的参数。形参本质就是一个变量名,用来接收外部传来的值
- 实参:在调用函数时,括号内传入的值,值可以是常量、变量、表达式或三者的组合
# 实参是常量
res = my_min(1, 2)
# 实参是变量
a = 1
b = 2
res = my_min(a, b)
# 实参是表达式
res = my_min(10 * 2, 10 * my_min(3, 4))
# 实参可以是常量、变量、表达式的任意组合
a = 2
my_min(1, a, 10 * my_min(3, 4))
在调用阶段,实参(变量值)会绑定给形参(变量名),这种绑定关系只能在函数体内使用,实参与形参的绑定关系在函数体调用时生效,函数调用结束后解除绑定关系
1)位置参数
位置参数指按照从左到右的顺序依次定义的参数
位置形参:按照从左到右的顺序直接定义的“变量名”
特点:必须被传值,多一个少一个都不行
位置实参:在函数调用阶段,按照从左到右的顺序依次传入的值
特点:按照顺序与形参一一对应
2)关键字参数
- 关键字实参:在函数调用阶段,按照key=value的形式传入的值
- 特点:指名道姓的给某个形参传值,可以完全不参照顺序,可以指定默认值。
- 与位置实参混合使用时,位置实参必须在关键字实参前,且不能为同一个形参重复传值
# 给参数指定默认值后,调用函数时可不提供它!可以根据需要,一个参数值也不提供、提供部分参数值或提供全部参数值。
def hello_3(greeting='Hello', name='world'):
print('{}, {}!'.format(greeting, name))
print(hello_3())
# Hello, world!
print(hello_3('Greetings'))
# Greetings, world!
print(hello_3('Greetings', 'universe'))
# Greetings, universe!
# 如果提供参数name,必须同时提供参数greeting。如果只想提供参数name,并让参数greeting使用默认值:
print(hello_3(name='Gumby'))
# Hello, Gumby!
通常不应结合使用位置参数和关键字参数。除非必不可少的参数很少,而带默认值的可选参数很多,否则不应结合使用关键字参数和位置参数
# 如函数hello可能要求必须指定姓名,而问候语和标点是可选的。
def hello_4(name, greeting='Hello', punctuation='!'):
print('{}, {}{}'.format(greeting, name, punctuation))
# 调用函数
hello_4('Mars')
# Hello, Mars!
hello_4('Mars', 'Howdy')
# Howdy, Mars!
hello_4('Mars', 'Howdy', '...')
# Howdy, Mars...
hello_4('Mars', punctuation='.')
# Hello, Mars.
hello_4('Mars', greeting='Top of the morning to ya')
# Top of the morning to ya, Mars!
# 如果给参数name也指定了默认值,最后一个调用就不会引发异常
hello_4()
# 报错
3)默认参数
默认形参:在定义阶段,就已经被赋值的形参,称之为默认参数
特点:在定义阶段就已经被赋值,意味着在调用阶段可以不用为其赋值
注意:与位置形参混用时,位置形参必须在默认形参前
# 默认形参的值是在函数定义阶段被赋值的,准确的说被赋予的是值的内存地址
# 示范1:
m = 2
def func(x, y=m): # y绑定了2的内存地址
print(x, y)
m = 3
func(1)
# 结果还是 1 2
# 示范2:
m = [11, ]
def func(x, y=m):
print(x, y)
m.append(333)
func(1)
# 结果为:1 [11, 333]
# 虽然默认值可以被指定为任意数据类型,但是不推荐使用可变类型(失去了默认的意义,不可预知结果)
4)可变参数
参数长度可变指在调用函数时实参的个数可以不固定。在调用函数时实参的定义常按位置或按关键字两种形式。
-
可变长度的位置实参
*args
*
形参名:用来接收溢出的位置实参,溢出的位置实参会被*
保存成元祖格式,然后赋值给其后的形参名,*后约定俗成是args -
可变长度的关键字实参
**kwargs
**
形参名:用来接收溢出的关键字实参,溢出的关键字实参会被**
保存成字典格式,然后赋值给其后的形参名,**
后约定俗成是kwargs -
*,**
可以用在实参中,实参中带*
,先将*
后的值炸开成位置实参。 -
*
和**
混合使用,*args
必须在**kwargs
之前
def print_params_4(x, y, z=3, *pospar, **keypar):
print(x, y, z)
print(pospar)
print(keypar)
print_params_4(1, 2, 3, 5, 6, 7, foo=1, bar=2)
# 1 2 3
# (5, 6, 7)
# {'foo': 1, 'bar': 2}
print_params_4(1, 2)
# 1 2 3
# ()
# {}
def add(x, y):
return x + y
params = (1, 2)
print(add(*params))
# 3
使用这些拆分运算符来传递参数无需操心参数个数之类的问题,在调用超类的构造函数时特别有用
def foo(x, y, z, m=0, n=0):
print(x, y, z, m, n)
def call_foo(*args, **kwds):
print("Calling foo!")
foo(*args, **kwds)
5)命名关键字参数
定义函数时,* 后定义的参数称之为命名关键字参数
def func(a,b,*args,x,y): # 其中,x,y称之为命名关键字参数
pass
func(1,2,x=3,y=4) # 命名关键字实参必须按照key=value的形式为其传值
6)组合使用
所有参数可任意组合使用,但定义顺序必须是:位置参数、默认参数、*args、命名关键字参数、**kwargs
# 可变参数*args与关键字参数**kwargs通常组合使用,如果一个函数的形参为*args与**kwargs,那么该函数可以接收任何形式、任意长度的参数
# 在该函数内部还可以把接收到的参数传给另外一个函数
def func(x,y,z):
print(x,y,z)
def wrapper(*args,**kwargs):
func(*args,**kwargs)
wrapper(1,z=3,y=2)
#1 2 3
按照上述写法,为函数wrapper传参时,其实遵循的是函数func的参数规则,调用函数wrapper的过程分析如下:
位置实参1被*接收,以元组形式保存并赋值给args,args=(1,),关键字实参z=3,y=2被**接收,以字典形式保存并赋值给kwargs,kwargs={‘y’: 2, ‘z’: 3},执行func(*args,**kwargs)。
即func(*(1,),** {'y': 2, 'z': 3}),等同于func(1,z=3,y=2)。
def story(**kwargs):
return 'Once upon a time, there was a ' \
'{job} called {name}.'.format_map(kwargs)
def power(x, y, *others):
if others:
print('Received redundant parameters:', others)
return pow(x, y)
# def interval(start, stop=None, step=1):
# """Imitates range() for step > 0"""
# result = []
# if stop is None: # 如果没有给参数stop指定值,
# for j in range(start):
# result.append(j)
# else:
# for i in range(start, stop, step):
# result.append(i)
# return result
def interval(start, stop=None, step=1):
"""Imitates range() for step > 0"""
if stop is None: # 如果没有给参数stop指定值,
start, stop = 0, start # 就调整参数start和stop的值
result = []
i = start # 从start开始往上数
while i < stop: # 数到stop位置
result.append(i) # 将当前数的数附加到result末尾
i += step # 增加到当前数和step(> 0)之和
return result
print(story(job='king', name='Gumby'))
# Once upon a time, there was a king called Gumby.
print(story(name='Sir Robin', job='brave knight'))
# Once upon a time, there was a brave knight called Sir Robin.
params = {'job': 'language', 'name': 'Python'}
print(story(**params))
# Once upon a time, there was a language called Python.
del params['job']
print(story(job='stroke of genius', **params))
# Once upon a time, there was a stroke of genius called Python.
print(power(2, 3))
# 8
print(power(3, 2))
# 9
print(power(y=3, x=2))
# 8
params = (5,) * 2
print(power(*params))
# 3125
print(power(3, 3, 'Hello, world'))
# Received redundant parameters: ('Hello, world',)
# 27
print(interval(10))
# [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
print(interval(1, 5))
# [1, 2, 3, 4]
print(interval(3, 12, 4))
# [3, 7, 11]
print(power(*interval(3, 7)))
# Received redundant parameters: (5, 6)
# 81
3.函数使用
1)被引用
def add(x,y):
return x+y
func = add
res = func(1,2)
print(res)
2)作为元素
def add(x,y):
return x+y
dic={'add':add,'max':max}
print(dic)
# {'add': <function add at 0x100661e18>, 'max': <built-in function max>}
res = dic['add'](1,2)
print(res)
# 3
3)作为参数
def add(x,y):
return x+y
def foo(x,y,func):
return func(x,y)
res = foo(1,2,add)
print(res)
# 3
4)作为返回值
def add(x,y):
return x+y
def bar():
return add
func = bar()
res = func(1, 2)
print(res)
# 3
4.闭包函数
- 闭包函数 = 名称空间和作用域+函数嵌套+函数对象(闭包 = 内部函数+定义函数时的环境)
- 核心:名字的查找关系是以函数定义阶段为准
#闭的意思是,该函数是内嵌函数,在别的函数内部定义出来的函数
def index():
def foo():
pass
#包的意思指的是该函数包含对外层函数(不是对全局作用域)作用域名字的引用
def index():
x = 1
def foo():
print(x)
#函数对象
def f1():
x = 333
def f2():
print(x)
return f2
在一个内部函数里,对在外作用域(但不是在全局作用域)的变量进行引用,那么内部函数就被认为是闭包
def f():
x = 10
def g():
print(x)
return g
f()() # f()相当于g f()()相当于g()
h = f()
h()
# 不是闭包函数,因为y属于g,没有对外部函数进行引用
def f():
x = 10
def g():
y = 5
print(y)
return g
f()() # f()相当于g f()()相当于g()
h = f()
h()
# 也是闭包函数
def f(x):
def g():
print(x)
return g
h = f(1)
h()
得到两种为函数体传参的方式
# 方式1:直接把函数体需要的参数定义成形参
def index(x):
print(x)
# 闭包的方式为函数传参
def f1(x):
def f2():
print(x)
return f2
命名空间:存放与对象映射/绑定关系的名字的地方。如x=3,Python会申请内存空间存放对象3,然后将名字x与3的绑定关系存放于命名空间中
不同的空间可存放相同的名字,是对栈区的划分,有命名空间可以在栈区中存放相同的名字
5.函数递归
def recursion():
return recursion()
这个函数中的递归称为无穷递归,理论上永远不会结束。正常的递归函数包含下面两部分:
- 基线条件(针对最小的问题):满足这种条件时函数将直接返回一个值。
- 递归条件:包含一个或多个调用,这些调用旨在解决问题的一部分。
假设计算数字n的阶乘。可使用循环:
def factorial(n):
result = n
for i in range(1, n):
result *= i
return result
使用函数来实现:
def factorial(n):
if n == 1:
return 1
else:
return n * factorial(n - 1)
计算幂:
def power(x, n):
if n == 0:
return 1
else:
return x * power(x, n - 1)
函数的递归调用是函数嵌套调用的特殊形式,指在调用一个函数的过程中又直接或间接的调用本身
递归函数就是函数内部自己调用自己
递归的本质就是循环,最重要的就是找到出口(停止的条件),因为python会一直开辟内存空间
循环运行的方案有两种:while循环,for循环
def f1(n):
if n<5:
print(n)
n +=1
f1(n)
f1(0)
# 0
# 1
# 2
# 3
# 4
# 递归最大的层级是1000层
import sys
1.查看层级
res = sys.getrecursionlimit()
print(res)
2.设置层级
sys.setrecursionlimit(1000) # 虽然可以设置,但不建议去设置
强调:递归调用不应该无限的调用下去,必须在满足某种条件下结束递归调用
递归调用的阶段:
1.两种工作原理
回溯:一层一层调用下去
递推:满足某种结束条件,结束递归调用,然后一层一层返回
2.阶段
data = [1, [2, [3, [4, [5]]]]]
# 通常情况
for x in data:
if type(x) is list: #如果是列表,应该先循环再判断,即重新运行本身的代码
pass
else:
print(x)
# 1
# 用递归
def f1(data):
for x in data:
if type(x) is list:
# 如果是列表,应该再循环,在判断,即重新运行本身的代码
f1(x)
else:
print(x)
f1(data)
# 1
# 2
# 3
# 4
# 5
二、作用域
作用域就是一个变量可以使用的范围,主要分为全局作用域和函数作用域(局部作用域),全局作用域是最外层的作用域,函数作用域是通过函数创建的一个独立作用域,函数可以嵌套,所以作用域也可以嵌套。作用域也称命名空间。
- 内置命名空间
- 全局命名空间
- 局部命名空间
1.分类
1)内置命名空间
第一个被加载的命名空间,用来存放内置的名字,比如内建函数名
print(max)
# <built-in function max> # built-in内建
存放的名字:存放的python解释器内置的名字
存活周期:伴随python解释器的启动/关闭而产生/回收,解释器启动则产生,关闭则销毁
2)全局命名空间
第二个被加载的命名空间,文件执行过程中产生的名字都会存放于该命名空间中
import sys #模块名sys
x=1 #变量名x
if x == 1:
y=2 #变量名y
def foo(x): #函数名foo
y=1
Class Bar: #类名Bar
pass
存放的名字:只要不是函数内定义,也不是内置的,剩下的都是全局命名空间的名字
存放周期:伴随python文件的开始执行/执行完毕而产生/回收,文件执行则产生,运行完毕后销毁
3)局部命名空间
函数的形参、函数内定义的名字都会被存放于该命名空间中
def foo(x):
y=3 #调用函数时,才会执行函数代码,名字x和y都存放于该函数的局部命名空间中
存放的名字:在调用函数时,运行函数体代码过程中产生的函数内的名字
存活周期:伴随函数的调用/结束而临时产生/回收,在调用函数时存活,函数调用完毕后则销毁
2.顺序
1)加载顺序
内置命名空间>全局命名空间>局部命名空间
2)销毁顺序
局部命名空间>全局命名空间>内置命名空间
3)查找优先级
当前所在的位置向上一层一层查找
查找一个名字,必须从三个命名空间之一找到,
查找顺序为:局部命名空间->全局命名空间->内置命名空间
3.查找
- 内置命名空间——》全局作用域
- 全局命名空间——》全局作用域
- 局部命名空间——》局部作用域
全局作用域范围内的名字全局存活(除非被删除,否则在整个文件执行过程中存活)、全局有效(在任意位置都可以使用)
局部作用域中的名字属于局部范围。该范围内的名字临时存活(即在函数调用时临时生成,函数调用结束后就释放)、局部有效(只能在函数内使用)
1)局部查找
起始位置是局部作用域,先查找局部命名空间,再去全局作用域查找,最后都没有找到就会抛出异常
x=100 #全局作用域的名字x
def foo():
x=300 #局部作用域的名字x
print(x) #在局部找x
foo()#结果为300
2)全局查找
locals()
globals()
起始位置是全局作用域,先查找全局命名空间,再查找内置命名空间,最后都没有找到就会抛出异常
x=100
def foo():
x=300 #在函数调用时产生局部作用域的名字x
foo()
print(x) #在全局找x,结果为100
提示:可以调用内建函数locals()和globals()来分别查看局部作用域和全局作用域的名字,查看的结果都是字典格式。在全局作用域查看到的locals()的结果等于globals()
3)内嵌函数查找
# 在内嵌函数内查找名字时,会优先查找自己局部作用域的名字,然后由内而外层层查找外部嵌套函数定义的作用域,没有找到,则查找全局作用域
x=1
def outer():
x=2
def inner(): # 函数名inner属于outer这一层作用域的名字
x=3
print('inner x:%s' %x)
inner()
print('outer x:%s' %x)
outer()
#结果为
# outer x:1
# inner x:3
# 实参为可变类型时返回原值
num_list=[1,2,3]
def foo():
num_list.append(5)
foo()
print(num_list)
#结果为
# [1, 2, 3, 5]
global
#函数内无论嵌套多少层,都可查看全局作用域的名字,若在函数内修改全局命名空间中名字的值,当值为不可变类型时,需用到global关键字
x=1
def foo():
global x #声明x为全局命名空间的名字
x=2
foo()
print(x) #结果为2
nonlocal
# 嵌套多层的函数使用nonlocal关键字可将名字声明为来自外部嵌套函数定义的作用域(非全局)
def f1():
x=2
def f2():
nonlocal x
x=3
f2() #调用f2(),修改f1作用域中名字x的值
print(x) #在f1作用域查看x
f1() # 结果 3
# nonlocal x会从当前函数的外层函数开始一层层去查找名字x,若是一直到最外层函数都找不到,则会抛出异常
四、装饰器
- 器指的是工具,可以定义成函数
- 装饰指的是为其他事物添加额外的东西
- 装饰器:定义一个函数,该函数是用来为其他函数添加额外的功能
# ’装饰’指为被装饰对象添加新的功能,’器’指器具/工具,装饰器与被装饰的对象均可是任意可调用对象。
# 装饰器的作用是在不修改被装饰对象源代码和调用方式的前提下为被装饰对象添加额外的功能。
# 装饰器经常用于有切面需求的场景,如:插入日志、性能测试、事务处理、缓存、权限校验等应用场景,有了装饰器,就可抽离大量与函数功能本身无关的雷同代码并继续重用
- **软件设计遵循开放封闭原则:对扩展开放,对修改封闭。**对扩展开放指有新需求或变化时,可对现有代码进行扩展,以适应新的情况。对修改封闭指对象设计完成就可独立完成其工作,无需进行修改。
- **软件包含的所有功能的源代码以及调用方式,都应避免修改。**一旦改错,极可能产生连锁反应而导致程序崩溃。上线后的软件新需求或变化层出不穷,所以必须为程序提供扩展的可能性。
- 装饰器就是在不修改被装饰对象源代码以及调用方式的前提下为被装饰对象添加新功能
函数装饰器分为无参装饰器和有参装饰。二者实现原理都是’函数嵌套+闭包+函数对象’
1.无参装饰器
- 若为下述函数添加统计其执行时间的功能
import time
def index():
time.sleep(3)
print('Welcome to the index page')
return 200
index() #函数执行
- 遵循不修改被装饰对象源代码的原则
import time
def index():
time.sleep(3)
print('Welcome to the index page')
return 200
start_time=time.time()
index() #函数执行
stop_time=time.time()
print('run time is %s' %(stop_time-start_time))
- 考虑到可能要统计其他函数的执行时间,可将其做成一个单独的工具,函数体需要外部传入被装饰的函数从而进行调用,可使用参数的形式传入
import time
def index():
time.sleep(3)
print('Welcome to the index page')
return 200
def wrapper(func): # 通过参数接收外部的值
start_time=time.time()
res=func()
stop_time=time.time()
print('run time is %s' %(stop_time-start_time))
return res
- 但之后函数的调用方式都需要统一改成
wrapper(index) # wrapper(其他函数)
- 这违反了不能修改被装饰对象调用方式的原则,于是换一种为函数体传值的方式将值包给函数,如下
import time
def index():
time.sleep(3)
print('Welcome to the index page')
return 200
def timer(func):
def wrapper(): # 引用外部作用域的变量func
start_time=time.time()
res=func()
stop_time=time.time()
print('run time is %s' %(stop_time-start_time))
return res
return wrapper
- 这样便可在不修改被装饰函数源代码和调用方式的前提下为其加上统计时间的功能,只需事先执行一次timer将被装饰的函数传入,返回一个闭包函数wrapper重新赋值给变量名 /函数名index,如下
index=timer(index) # 将timer(index)赋值给变量名index,调用index等同于执行以上两个函数
index() # 调用timer(index)这个函数
- 至此便实现了一个无参装饰器timer,可在不修改被装饰对象index源代码和调用方式的前提下为其加上新功能。但若被装饰的函数是一个有参函数,便会抛出异常。
import time
def timer(func):
def wrapper(): # 引用外部作用域的变量func
start_time=time.time()
res=func()
stop_time=time.time()
print('run time is %s' %(stop_time-start_time))
return res
return wrapper
def home(name):
time.sleep(5)
print('Welcome to the home page',name)
home=timer(home)
home('谢大哥')
# TypeError: wrapper() takes 0 positional arguments but 1 was given
- 之所以会抛出异常,是因为home(‘谢大哥’)调用的是wrapper(‘谢大哥’),而函数wrapper没有参数。wrapper函数接收的参数是给最原始的func用的,为了能满足被装饰函数参数的所有情况,便用上*args+**kwargs组合,于是修正装饰器timer如下
import time
def index():
time.sleep(3)
print('Welcome to the index page')
return 200
def timer(func):
def wrapper(*args,**kwargs):
start_time=time.time()
res=func(*args,**kwargs)
stop_time=time.time()
print('run time is %s' %(stop_time-start_time))
return res
return wrapper
def home(name):
time.sleep(5)
print('Welcome to the home page',name)
home=timer(home)
home('谢大哥')
- 此时就可用timer来装饰带参数或不带参数的函数了,但是为了简洁而优雅地使用装饰器,Python提供了专门的装饰器语法来取代index=timer(index)的形式,需要在被装饰对象的正上方单独一行添加@timer,当解释器解释到@timer时就会调用timer函数,且把它正下方的函数名当做实参传入,然后将返回的结果重新赋值给原函数名
import time
def timer(func):
def wrapper(*args,**kwargs):
start_time=time.time()
res=func(*args,**kwargs)
stop_time=time.time()
print('run time is %s' %(stop_time-start_time))
return res
return wrapper
@timer # home=timer(home)
def home(name):
time.sleep(5)
print('Welcome to the home page',name)
home('谢大哥')
- 若有多个装饰器,可以叠加多个
@deco3
@deco2
@deco1
def index():
pass
- 叠加多个装饰器也无特殊之处,上述代码语义如下
# 无参装饰器
def deco(func):
def wrapper(*args, **kwargs):
res = func(*args, **kwargs)
return res
return wrapper
2.有参装饰器
def outer(mode):
def deco(func):
def wrapper(*args, **kwargs):
if mode == 'QQ':
res = func(*args, **kwargs)
return res
elif mode =='微信':
print('无法登录')
return wrapper
return deco
@outer('QQ')
def test(name):
print(f'{name}登录成功')
test('老tou')
让装饰器和原函数一样,包含函数说明(了解即可)
from functools import wraps
def deco(func):
@wraps(func)
def wrapper(*args, **kwargs):
res = func(*args, **kwargs)
return res
return wrapper
@deco
def test(name):
"""
这是登录的函数
:param name:
:return:
"""
print(f'{name}登录成功')
print(help(test))
闭包:
闭包就是能够读取其它函数内部变量的函数
1.python闭包的作用:
保存外部函数的变量,不会随着外部函数调用完而销毁
2.python闭包的形成条件:
- 函数嵌套
- 内部函数必须使用了外部函数的变量或者参数
- 外部函数返回内部函数 这个使用了外部函数变量的内部函数就称为闭包
装饰器:
实际上也是一个闭包函数,也是一个函数嵌套
1.装饰器的作用:
在不改变原函数的情况下,对已有的函数进行额外的功能扩展
2.装饰器的形成条件:
- 不修改已有函数的源代码
- 不修改已有函数的调用方式
- 给已有函数增加额外的功能
#注意与闭包的区别:装饰器实质上是一个闭包函数,但是装饰器这个闭包函数的参数有且只有一个并且是函数类型才是装饰,否则就是闭包函数
五、常用内置函数
1.匿名函数
lambda 参数1,参数2,...: expression
f = lambda x: x * x
print(f(5))
# 25
# 1、定义
lambda x,y,z:x+y+z
#等同于
def func(x,y,z):
return x+y+z
# 2、调用
# 方式一:
res=(lambda x,y,z:x+y+z)(1,2,3)
# 方式二:
func=lambda x,y,z:x+y+z # “匿名”的本质就是要没有名字,所以此处为匿名函数指定名字是没有意义的
res=func(1,2,3)
总结:
1.lambda并不会带来程序运行效率的提高,只会使代码更加的简洁
2.lambda可读性不好,lambda内不要有循环,有就使用标准函数来完成(目的是为了代码有可重用性和可读性)
3.lambda是为了减少单行函数的定义而存在。如果一个函数只有一个返回值,一句代码,就可以使用lambda
匿名函数与有名函数有相同的作用域,但匿名函数引用计数为0,即使用一次就释放,所以匿名函数用于临时使用一次的场景,匿名函数通常与其他函数配合使用
2.abs()
3.sum()
4.round()
abs() 求绝对值
print(abs(-1))
sum() 求和
print(sum([1, 2, 3, 4, 5],2)) # 给列表求和后再加2
print(sum((1, 2, 3, 4, 5),1)) # 给元组求和后再加1
round() 四舍五入
def ab_sum(a,b,f):
return f(a)+f(b)
print(ab_sum(-1, -4,abs))
print(ab_sum(-1, -4.3,round))
def xfs(x):
return -x
def ab_sum(a,b,f):
return f(a)+f(b)
# print(ab_sum(-1, -4,abs))
# print(ab_sum(-1, -4.3,round))
print(ab_sum(-1, -4,xfs)) # 求两个相反数的和
3.map()
map(func,seq)第一个参数是给一个函数,第二个参数是给一个序列类型
list1 = [-1,-2,-3,-4,-5]
list2 = []
for i in list1:
list2.append(i+1)
print(list2)
由于map返回的是一个迭代器,我们想看结果就需要转为list,转成元组也可以
list1 = [-1,-2,-3,-4,-5]
print(list(map(abs,list1)))
list1 = [1,2,3,4,5]
def add1(x):
return x+1
print(list(map(add1,list1)))
list1 = [1,2,3,4,5]
print(list(map(lambda x:x+1,list1)))
总结:
1.map内置函数的作用就是操作序列中的所有元素,并且返回一个迭代器,迭代器要转列表
2.咱们的lambda表达式可以专门配合我们的高阶内置函数来做简单实现
4.filter()
filter(func,seq)第一个参数是给一个函数,第二个参数是给一个序列类型,用于过滤序列,过滤掉不符合条件的元素,结果可以通过list转换
# 保留一个序列中所有的偶数
普通写法:
list1 = [1,2,3,4,5,6,7,8,9,10]
for i in list1:
if i % 2 !=0: # 说明i是一个奇数
list1.remove(i)
print(list1)
高阶写法:
def fn(x):
return x%2==0
print(list(filter(fn,list1)))
lambda写法:
print(list(filter(lambda x:x%2==0,list1)))
5.sorted
sorted(可迭代对象,key=,reverse=True)
排序,返回一个排序后的序列
list1 = [2,4,1,3,5,6,9,7]
print(sorted(list1))
print(sorted(list1,reverse=True))
list1 = ["七零_69","久违_79","乃荣_89","小川_59","阿飞_100"]
def f(x):
arr = x.split("_")
return int(arr[1])
print(sorted(list1,key=f))
print(sorted(list1,key=lambda x:int(x.split("_")[1])))
max(可迭代对象,key=函数):根据函数获取可迭代对象的最大值
min(可迭代对象,key=函数):根据函数获取可迭代对象的最小值
list1 = ["七零_69","久违_79","乃荣_89","小川_59","阿飞_100"]
def f(x):
arr = x.split("_")
return int(arr[1])
print(min(list1,key=f))
print(max(list1,key=lambda x:int(x.split("_")[1])))
python提供的,可以直接拿来使用的
常用 | 计算 | 进制转换 | 判断 |
---|---|---|---|
type | sum | bin | all |
查看类型 | 求和 | 十进制转二进制 | 是否全部为True |
id | max | oct | any |
查看内存地址 | 最大值 | 十进制转八进制 | 是否存在True |
range | min | hex | |
可创建一个整数列表,一般用在 for 循环中 | 最小值 | 十进制转十六进制 | |
enumerate | divmod | ord | |
可以将索引与元素组合为一个元组。 | 求商和余数 | 获取字符对应的unicode码点(十进制) | |
round | chr | ||
小数点后n位(四舍五入) | 根据码点(十进制)获取对应字符 |