变量
命令a = 1
,解释器会创建整形1和变量a,并把a指向1,
整形1也可以是各种类型的数据.
函数
函数是指将一堆代码通过一个名字(函数名)封装起来,要想执行这个函数,只需调用其函数名即可.
def 函数名(若有参数):
代码
return None\任意对象
作用:减少重复代码,使程序变的可扩展,使程序变得易维护.
函数调用:func()
, 函数名加()
.
内置函数:python解释器自带的, 存储在builtins.py
模块文件中:
函数名命名:
命名要有意义
常规是小写
单词间可以使用下划线隔开
避免与内置函数重名
函数执行:
- 函数先定义后使用(相当于变量一样先定义后使用);
- 函数只有调用才会执行;
- 函数定义载入内存过程中, 只检测语法,不执行代码;
按照参数分类:
- 有参数的函数: 接受外部传进来的值,进行一系列的运算,最后返回处理结果;
- 无参数的函数: 通常只是做一下操作而已;
- 空函数: 什么事情都不做就是一个空函数(即便是一个空函数,也会有基本的属性)
函数返回值:
- 可返回任意类型的值,例如:
return func()
; -
return
代表函数的结束, 后面的代码不会执行; - 未指定
return
, 则默认返回None
;
局部和全局变量:
- 在函数内定义的变量称为局部变量,在程序的一开始定义的变量称为全局变量;
- 全局变量作用域是整个程序,局部变量作用域是定义该变量的函数;
- 当全局变量与局部变量同名时,在定义局部变量的函数内,局部变量起作用;在其它地方全局变量起作用。
- 声明global后,可以在函数内部修改全局变量;
函数参数
形参
定义函数时括号内写的参数,形参在定义阶段是不占内存空间的,在调用的时候接收实参才占用内存空间.实参
可以是常量、变量、表达式、函数等,无论实参是何种类型的量,在进行函数调用时,它们都必须有确定的值,以便把这些值传送给形参.
def f1(a, b): # a, b为形参
pass
f1(12, 10) # 12, 10为实参
默认参数
定义函数def stu_register(name,age,course,country="CN"):
时, country
就是默认参数,这个参数在调用时不指定,那默认就是CN,指定了的话,就用你指定的值。
- 默认参数必须在位置形参后面;
- 默认参数的值只在函数定义阶段生效一次,定义之后的修改不会影响它的值;
- 默认参数不要设置为可变类型的, 就是一个坑!
位置参数
- 按位置定义的形参必须要传值;
- 按位置定义的实参按照位置与形参一一对应的传
关键字参数
按照key=value
的形式定义的实参,传的时候与位置没有关系.
混用位置实参和 关键字实参不能给同一个形参重复赋值,且位置实参必须在关键字实参的前面.
def foo(x,y):
pass
foo(1,2) # 与形参一一对应
foo(y=2, x=1) # 关键字参数,指名道姓的传
foo(1, y=2)
可变参数
若你的函数在定义时不确定用户想传入多少个参数,就可以使用可变长参数,参数*args
接收位置参数,多个时变为元祖形式,**kwargs
接收键值对形式的参数.
def f1(x, y, *args, **kwargs):
print(x, y) # 1 2
print(args) # (3, 4, 5)
print(*args) # 3 4 5
print(kwargs) # {'i': 1, 'j': 3}
print(*kwargs) # a b c
f1(1, 2, 3, 4, 5, a=6, b=7, c=8)
*
会把多出来的值传给args
,并且以元组的形式返回;
**
会把多出来的值传给kwargs
,并且以字典的形式返回;
匿名函数
当我们在传入函数时,有些时候不需要显式地定义函数,直接传入匿名函数更方便.
lambda x: x * x # lambda 参数:返回值;
等价于
def f(x):
return x * x
map_obj = map(lambda x: x * x, [1, 2, 3, 4, 5, 6, 7, 8, 9])
print(list(map_obj)) # [1, 4, 9, 16, 25, 36, 49, 64, 81]
命名空间namespace
存放名字的地方,若声明变量 x = 1
, 值1
存放在内存中, 那变量名x
就存放在命名空间里.
名称空间共3种,分别如下:
-
locals
: 是函数内的名称空间,包括局部变量 和 形参; -
globals
: 全局变量,函数定义所在模块的名字空间; -
builtins
: 内置模块的名字空间;
不同变量的作用域不同就是由这个变量所在的命名空间决定的.
全局范围:全局存活,全局有效;
局部范围:临时存活,局部有效;
查看作用域方法 globals()
,locals()
level = 'L0'
n = 22
def func():
level = 'L1'
n = 33
print(locals()) # {'n': 33, 'level': 'L1'}
def outer():
n = 44
level = 'L2'
print(locals(),n) # {'level': 'L2', 'n': 44} 44
def inner():
level = 'L3'
print(locals(),n) # {'level': 'L3', 'n': 44} 44
inner()
print(locals()) # {'n': 33, 'level': 'L1', 'outer': <function func.<locals>.outer at 0x00000000021EF730>}
outer()
func()
LEGB
代表名字查找顺序: locals
-> enclosing function
闭包 -> globals
-> __builtins__
.
locals
: 是函数内的名字空间,包括局部变量和形参
enclosing
: 外部嵌套函数的名字空间
globals
:全局变量,函数定义所在模块的名字空间
builtins
: 内置模块的名字空间
global声明:强制转换成全局变量,例如:
x=1
def foo():
global x #强制转换x为全局变量
x=10000000000
foo()
print(x) # 10000000000
# 这个方法尽量能少用就少用
Nonlocal声明:让内部函数中的变量在上一层函数中生效,外部必须有,但是不会作用于全局,例如:
x=1
def f1():
x=2
def f2():
# x=3
def f3():
nonlocal x
x=10000000000
f3()
print('f2内的打印',x)
f2()
print('f1内的打印', x)
f1()
print(x)
# 修改x=3 为x=100000000,当x=3不存在时,修改x=2为100000000,# 必须保证x在函数内部存在
闭包(closure)
判断闭包函数: 函数内部print(函数名.__closure__)
结果有cell
元素就是是闭包函数.
def outer():
name = 'kate'
def inner():
print("在inner里打印外层函数的变量", name)
print(inner.__closure__)
return inner
f = outer() # f = inner
f() # 在inner里打印外层函数的变量 kate
闭包的意义:返回的函数对象,不仅仅是一个函数对象,在该函数外还包裹了一层作用域,这使得,该函数无论在何处调用,优先使用自己外层包裹的作用域;
闭包形成的必要条件:
- 闭包函数必须有内嵌函数;
- 闭包函数必须返回内嵌函数;
- 内嵌函数需要引用该嵌套函数上一级
namespace
中的变量;
装饰器decorator
作用:在不修改原函数 及 其调用方式的情况下对原函数功能进行扩展.
本质:就是一个闭包函数,函数名作为参数传入上级函数的命名空间,供内部函数返回掉调用.
无参数装饰器
import time
def wrapper(func):
print('wrapper')
def inner():
print('inner')
start=time.time()
func()
end=time.time()
print(end-start)
return inner
@wrapper
def f():
print('f')
time.sleep(1.2)
f()
# 调用前执行:(f可以理解为中间函数)
1. login = outer(login), 先执行等号右侧
2. login = inner
# 调用后执行:
3. login() >> inner()
4. 返回return login()
带参数的装饰器
import time
def timer(func):
def inner(*args,**kwargs):
print('inner')
start = time.time()
func(*args, **kwargs)
end=time.time()
print(end- start)
return inner
@timer
def f1(a,b):
print('in f1')
print(a,b)
f1(1, 2)
# 定义期间
1. f1 = timer(f1)
2. f1 = inner
# 开始调用
3. f1(1, 2) = inner(*args) # *args接收参数(1, 2)
4. func(*args) = f1(1, 2)
带返回值的装饰器
import time
def timer(func):
def inner(*args,**kwargs):
start = time.time()
re = func(*args,**kwargs)
end=time.time()
print(end - start)
return re
return inner
@timer
def f1(a):
print('in f1 and get a:%s'%(a))
return 'fun2 over'
print(f1('EFG'))
# f1= timer(f1) = inner
# f1('efg') = inner('efg')
多个装饰器装饰一个函数
def f1(fun):
def inner():
print('in f1: before')
ret = fun()
print('in f1: after')
return inner
def f2(fun):
def inner():
print('in f2: before')
fun()
print('in f2: after')
return inner
@f1
@f2
def f3():
print('饿了吗')
f3()
# 分析=================
@f1和@f2的执行顺序:
1. 先执行f1里面的 print('in f2: before');
2. 然后跳到了f2里面的 print('in f2: before'),执行函数f3;
3. f2内部接着执行 print('in f2: after');
4. 回到了f1里面的 print('in f2: after')。
装饰器练习
- 编写3个函数,每个函数执行的时间是不一样的.提示:可以使用time.sleep(2),让程序sleep 2s或更多;
- 编写装饰器,为每个函数加上统计运行时间的功能. 提示:在函数开始执行时加上start=time.time()就可纪录当前执行的时间戳,函数执行结束后在time.time() - start就可以拿到执行所用时间;
- 编写装饰器,为函数加上认证的功能,即要求认证成功后才能执行函数;
- 编写装饰器,为多个函数加上认证的功能(用户的账号密码来源于文件),要求登录成功一次,后续的函数都无需再输入用户名和密码
注意: 以下作业必须独立完成才算掌握装饰器!
参考答案:
# 功能2
from random import randrange
import time
def counter(f):
def inner():
start_time = time.time()
f()
end_time = time.time()
ret = "函数<%s>运行时间<%s>" % (f.__name__,end_time-start_time)
print(ret)
return inner
@counter
def f1():
time.sleep(randrange(1, 4))
@counter
def f2():
time.sleep(randrange(1, 4))
@counter
def f3():
time.sleep(randrange(1, 4))
f1()
f2()
f3()
# 功能3和4
LOGIN_STATUS = False
def login(f):
def inner():
global LOGIN_STATUS
if LOGIN_STATUS:
f()
return
username = input('username>> ')
password = input('password>> ')
if username == 'alex' and password == 'abc123':
LOGIN_STATUS = True
f()
return
else:
print('函数<%s>执行失败, 用户名或密码有误!'% f.__name__)
return
return inner
@login
def f1():
time.sleep(randrange(1, 4))
print('执行f1完成')
@login
def f2():
time.sleep(randrange(1, 4))
print('执行f2完成')
@login
def f3():
time.sleep(randrange(1, 4))
print('执行f3完成')
f1()
f2()
f3()
生成器generator
生成器不会把结果保存在一个系列中,保存的是算法,每次调用
next(g)
就计算出 g 的下一个元素的值,直到计算到最后一个元素,没有更多的元素时,抛出StopIteration
的错误;在处理大的序列或文件时使用生成器非常解决内存空间,因为不必像列表一样需要将所有的数据读进内存;
调用
next()
方法取值太low, 一般最常用的方法是使用for
循环迭代出值, 而且不用考虑异常;生成器是一次性的,迭代完成会出现
StopIteration
错误;range()
与生成器的关系:range()
在底层就是用生成器实现的,在Python2里,range(10)
生产的就是列表,所以要用xrange(10)
,比较节约空间, python3优化了range()
,结果是类生成器,range(121212121212212)
在python shell下可以马上生成,但是list(range(121212121212121212))
可能会很慢甚至直接出现内存错误,因为列表需要将所有的值都读进内存,但是内存分配给列表的标准内存是有限的空间,所以会出现内存错误,此时生成器的有点就显现出来了-
可以将列表等可迭代对象转换成生成器
l = [1, 2, 3] print(type(l)) # <class 'list'> l1 = iter(l) print(type(l1)) # <class 'list_iterator'>
生成器表达式
通列表解析语法: g = (i for i in range(1000)
, g
就是生成器, next(g)
或者for
循环就可以迭代出内部的值,直到清空整个生成器;
g = (i for i in range(1, 3))
print(type(g)) # <class 'generator'>
print(g.__next__()) # 1
print(g.__next__()) # 2
try:
print(g.__next__())
except StopIteration as e:
print('StopIteration错误')
# StopIteration错误
生成器函数
常规函数定义,但是它是使用yield
语句返回值而不是return
语句返回
# fabonacci生成器
def fib(max):
n, a, b = 0, 0, 1
while n < max:
yield b
a, b = b, a + b
n = n + 1
return 'done'
f = fib(10) # 必须先生成生成器!
print(type(f)) # <class 'generator'>
for i in f:
print(i)
# 1, 1, 2, 3, 5, 8, 13, 21, 34, 55
# 生成器实现range( )函数
def range2(end):
count = 0
while count < end:
n = yield count
count += 1
g = range2(10)
print(next(g))
print(next(g))
print(next(g))
g.send('stop')
yield与协程
生成器调用send方法传输的参数可以被生成器函数内部的yield所接收.
- 通过列表解析或者定义生成器函数 制造生成器
g
; - 第一次执行可以先调用
g.next()
, 使得g
准备接受数据,num = yield
; - 当下一次执行
g.send(i)
,那么生成器内部就可以接收参数num = i
.
import time
def consumer(name):
print("%s 准备消费!" %name)
while True:
product = yield
print("[%s]来了,被[%s]吃了!" %(product,name))
def produce():
"""1. 生成 生成器; 2. 执行生成器,挂在yield处; 3. 生成器send值给yield接收, 逐个生产"""
a = consumer('A')
b = consumer('B')
a.__next__()
b.__next__()
print("开始生产!")
for i in range(1, 3):
time.sleep(1)
print("做了2个包子!")
a.send(i)
b.send(i)
produce()
迭代器Iterator
生成器都是迭代器;
迭代器不仅包含生成器, 还包含list
, tuple
, set
, dict
, str
等数据集合;
可迭代对象Iterable:
-
数据集合类型: 凡是可以用
for
循环的对象都是可迭代对象, 常见的数据集合list
,tuple
,set
,dict
,str
都是可迭代对象:>>> from collections import Iterable >>> isinstance([], Iterable) True
generator
: 包括生成器和带yield
的generator function