函数是组织好的,可重复使用的,用来实现单一,或相关联功能的代码段。
函数能提高应用的模块性,和代码的重复利用率。你已经知道Python提供了许多内建函数,比如print()。但你也可以自己创建函数,这被叫做用户自定义函数。
定义一个函数
你可以定义一个由自己想要功能的函数,以下是简单的规则:
- 函数代码块以 def 关键词开头,后接函数标识符名称和圆括号()。
- 任何传入参数和自变量必须放在圆括号中间。圆括号之间可以用于定义参数。
- 函数的第一行语句可以选择性地使用文档字符串—用于存放函数说明。
- 函数内容以冒号起始,并且缩进。
- return [表达式] 结束函数,选择性地返回一个值给调用方。不带表达式的return相当于返回 None。
def ChangeInt(a):
a = 10 b = 2
ChangeInt(b)
print(b)
def changeme(mylist):
"修改传入的列表" mylist.append([1, 2, 3, 4])
print("函数内取值: ", mylist)
return
# 调用changeme函数
mylist = [10, 20, 30]
changeme(mylist)
print("函数外取值: ", mylist)
def printme(str): "打印任何传入的字符串"
print(str)
return
# 调用printme函数
printme(str="My string")
#可写函数说明
def printinfo(name, age): "打印任何传入的字符串"
print("Name: ", name)
print("Age ", age)
return
#调用printinfo函数
printinfo(age=50, name="miki");
def printinfo(args,*vartuple): "打印任何传入的参数"
print(args) for var in vartuple:
print(var)
return
# 调用printinfo 函数
printinfo(10)
printinfo(70, 60, 50)</pre>
实参和形参
定义函数括号里的一般叫形参
调用时括号里传递的参数一般叫实参
#比如:
def students(age):
print('my age is %s' % age)
students(18)
age就是形参,18就是实参
参数的具体应用
1、位置参数:按照从左到右的顺序定义的参数
位置形参:必选参数
位置实参:按照位置给形参传值 '''
def foo(x,y):
print(x,y)
foo(1,2) #结果:1,2
2、关键字参数:按照key=value的形式定义的实参
无需按照位置为形参传值
注意的问题:
1. 关键字实参必须在位置实参右面
2. 对同一个形参不能重复传值 '''
def foo(x,y):
print(x,y)
foo(y=2,x=1)
结果:1,2
3、默认参数:形参在定义时就已经为其赋值
可以传值也可以不传值,经常需要变得参数定义成位置形参,变化较小的参数定义成默认参数(形参)
注意的问题:
1. 只在定义时赋值一次
2. 默认参数的定义应该在位置形参右面
3. 默认参数通常应该定义成不可变类型 '''
def foo(x,y=2):
print(x,y)
foo(1) #结果:1,2
foo(1,3) #结果:1,3
4、可变长参数:
可变长指的是实参值的个数不固定
而实参有按位置和按关键字两种形式定义,针对这两种形式的可变长,形参对应有两种解决方案来完整地存放它们,分别是*argsh和**kwargs '''
#*args:传递的参数会变成元组
def foo(x,y,*args): print(x,y,args)
foo(1,2,3) #结果:1 2 (3,) #**kwargs:传递的参数会变成字典
def foo(x,y,**kwargs): print(x,y,kwargs)
foo(1,2,a=3,b=4) #结果:1 2 {'a': 3, 'b': 4}
函数的特性
函数可以被引用
def f1():
print('this is f1')
return 'ok'
res = f1()
print(res) #函数可以当参数传递
def foo(a):
print(a)
def bar(b):
print(b)
bar(foo('你好')) #嵌套函数
def f1():
def f2():
def f3():
print('this is f3')
f3()
f2()
f1()
名称空间和作用域
1、内置名称空间:(python启动时就有)python解释器内置的名字,print,max,min
2、全局名称空间:(执行python文件时启动)定投定义的变量
3、局部名称空间:(调用函数时启动,调用结束失效)函数内部定义的变量
总结:三者的加载顺序
内置--->全局--->局部
三者的访问顺序
局部--->全局--->内置
装饰器
什么是装饰器?
在不修改源代码和调用方式的基础上给其增加新的功能,多个装饰器可以装饰在同一个函数上
def deco(func):
def wrapper(name,age):
func(name,age)#func = func1
print('add new function')
return wrapper
@deco # func1 = deco(func1)
def func1(name,age):
print('my name is %s,my age is %s' % (name, age))
func1 = deco(func1)#func1 = wrapper
func1()#wrapper()
func1('蒋介石',99)
@deco
def func2(a,b):
print('this is func2')
func2(1,2)
def login(func):
def wrapper():
username = input('username:')
password = input('password:')
if username == 'root' and password == 'root':
func()
else:
print('用户名密码错误')
return wrapper
def index():
print('welcome to home')
index = login(index)
index()
迭代器
什么是迭代器?
迭代是一个重复的过程,即每一次重复为一次迭代,并且每次迭代的结果都是下一次迭代的初始值
一般调用方法:
l1 = ['a','b','c','d']
num = 0
while num < len(l1):
print(l1[num])
num += 1
为什么要有迭代器?什么是可迭代对象?什么是迭代器对象?
像集合字典这种是没有办法用下标取值,因此,python解释器必须给我们
提供一种不依赖于索引的取值方式
l1 = ['a','b','c','d']
t1 = ('a','b','c','d')
str1 = 'hello,world'
info = {
'name': '蒋介石',
'age':88
}
s1 = {'a','b','c','d'}
num = 100
f = open('a.txt', 'r',encoding='utf-8')
有.__iter__()方法的叫做可迭代对象
l1.__iter__()
t1.__iter__()
str1.__iter__()
info.__iter__()
s1.__iter__()
f.__iter__()
迭代器对象:必须要有.__iter__和.__next__方法
f.__next__()
从可迭代对象变成迭代器对象
s2 = iter(s1)
res1 = next(s1)
res2 = next(s1)
res3 = next(s1)
res4 = next(s1)
res5 = next(s1)
print(res1)
print(res2)
print(res3)
print(res4)
print(res5)
info1 = iter(info)
a = 0
while a < len(info):
print(next(info1))
a += 1
把info可迭代对象变成迭代器对象, 解决异常问题
for data in iter(info):
print(data)
总结:迭代器对象一定是可迭代对象,可迭代对象不一定是迭代器对象
生成器
什么是生成器?
生成器就是迭代器,在函数当中出现yield关键字,那么它就是一个生成器
1、优点:生成器跟列表很像,生成器同一时间只存储一个值,这样比列表节省空间
2、缺点:只能往后取值,不能往回取值
return和yield区别:return只能返回一个值,yield可以返回多个值
return往后的代码将不会被执行,
遇见yield关键字,暂停
def test():
yield 1
yield 2
yield 3
yield 4
yield 5
res = test()
res.__iter__()
res.__next__()
print(next(res))
print(next(res))
print(next(res))
def test():
for i in range(10):
yield i
res = test()
for i in res:
print(i)
匿名函数
随着程序代码的不断增加,起名字其实也是非常困难的一件事一些简单的功能完全没必要用def函数,匿名函数足以.
简单定义的函数:
def test(x,y):
return x+y
res = test(1,2)
print(res)
匿名函数的定义方式:
res = (lambda x,y: x+y)(1,2)
print(res)
匿名函数定义的相关应用场景
max, sorted, map, filter
info = {
'li': 2000,
'zhao':30000,
'wu': 25000,
'du': 40000
}
1、max
def func(k):
return info[k]
res = max(info,key=lambda k: info[k])
print(res)
2、sorted
res = sorted(info,key=lambda k:info[k])
print(res)
3、map:映射
l1 = [1,2,3]
l2 = ['a','b','c','d']
res = zip(l1,l2)
print(list(res))
names = ['zhao', 'du', 'wu']
info = []
for name in names:
data = name.replace(name,'%s_SB' % name)
info.append(data)
print(info)
res = map(lambda name: '%s_SB' % name,names)
print(list(res))
4、filter:过滤
names = ['zhao_SB', 'du_SB', 'wu_SB','li']
for name in names:
if name.endswith('SB'):
print(name)
res = filter(lambda name: name.endswith('SB'),names)
print(list(res))
多线程
多线程特点:
线程的并发是利用cpu上下文的切换(是并发,不是并行)
多线程执行的顺序是无序的
多线程共享全局变量
线程是继承在进程里的,没有进程就没有线程
GIL全局解释器锁
只要在进行耗时的IO操作的时候,能释放GIL,所以只要在IO密集型的代码里,用多线程就很合适
import time,threading
1、 并发
def test1(n):
for i in range(n):
time.sleep(1)
print('task1', i)
def test2(n):
for i in range(n):
time.sleep(1)
print('task2', i)
t1 = threading.Thread(target=test1,args=(10,))
t2 = threading.Thread(target=test2,args=(10,))
t1.start()
t2.start()
2、 无序的
def test1(n):
time.sleep(1)
print('task', n)
for i in range(10):
t = threading.Thread(target=test1,args=('t-%s' % i,))
t.start()
3、多线程共享全局变量
num = 0
def test1():
global num
for i in range(10):
num += 1
def test2():
global num
print(num)
t1 = threading.Thread(target=test1)
t2 = threading.Thread(target=test2)
t1.start()
t2.start()
GIL的全称是:Global Interpreter Lock,意思就是全局解释器锁,这个GIL并不是python的特性,他是只在Cpython解释器里引入的一个概念,而在其他的语言编写的解释器里就没有这个GIL例如:Jython,Pypy
为什么会有gil?:
随着电脑多核cpu的出现核cpu频率的提升,为了充分利用多核处理器,进行多线程的编程方式更为普及,随之而来的困难是线程之间数据的一致性和状态同步,而python也利用了多核,所以也逃不开这个困难,为了解决这个数据不能同步的问题,设计了gil全局解释器锁。
说到gil解释器锁,我们容易想到在多线程中共享全局变量的时候会有线程对全局变量进行的资源竞争,会对全局变量的修改产生不是我们想要的结果,而那个时候我们用到的是python中线程模块里面的互斥锁,哪样的话每次对全局变量进行操作的时候,只有一个线程能够拿到这个全局变量;看下面的代码:
import threading
global_num = 0
def test1():
global global_num
for i in range(1000000):
global_num += 1
print("test1", global_num)
def test2():
global global_num
for i in range(1000000):
global_num += 1
print("test2", global_num)
t1 = threading.Thread(target=test1)
t2 = threading.Thread(target=test2)
t1.start()
t2.start()
在上面的例子里,我们创建了两个线程来争夺对global_num的加一操作,
但是结果并非我们想要的,所以我们在这里加入了互斥锁
import threading
import time
global_num = 0
lock = threading.Lock()
def test1():
global global_num
lock.acquire()
for i in range(1000000):
global_num += 1
lock.release()
print("test1", global_num)
def test2():
global global_num
lock.acquire()
for i in range(1000000):
global_num += 1
lock.release()
print("test2", global_num)
t1 = threading.Thread(target=test1)
t2 = threading.Thread(target=test2)
start_time = time.time()
t1.start()
t2.start()
多线程工作原理图:
多进程
一个程序运行起来之后,代码+用到的资源称之为进程,它是操作系统分配资源的基本单位,不仅可以通过线程完成多任务,进程也是可以的
进程之间是相互独立的
进程启动耗费的资源是比较大的,但是效率比较高
cpu密集的时候适合用多进程
import multiprocessing
import time
1、多进程实现多任务
num = 0
def test1():
for i in range(10):
time.sleep(1)
print('task1',i)
def test2():
for i in range(10):
time.sleep(1)
print('task2',i)
if __name__ == '__main__':
p1 = multiprocessing.Process(target=test1)
p2 = multiprocessing.Process(target=test2)
p1.start()
p2.start()
2、进程之间是相互独立的
num = 0
def test1():
global num
for i in range(10):
num += 1
print('test1',num)
def test2():
global num
print('test2',num)
if __name__ == '__main__':
p1 = multiprocessing.Process(target=test1)
p2 = multiprocessing.Process(target=test2)
p1.start()
p2.start()
3、进程池
from multiprocessing import Pool
def test1(n):
for i in range(n):
time.sleep(1)
print('task1',i)
def test2(n):
for i in range(n):
time.sleep(1)
print('task2',i)
if __name__ == '__main__':
pool = Pool(1)#允许的最大进程数
pool.apply_async(test1,(100,))
pool.apply_async(test2,(100,))
pool.close()
pool.join()
协程并发(gevent)
协程:也叫微线程,协程是在一个线程里面的
异步IO:遇到io请求就切换
现有进程,再有线程,才能有协程
进程是资源分配的单位
线程是操作系统调度的单位
进程切换需要的资源最大,效率低
线程切换需要的资源一般,效率一般
协程切换任务资源很小,效率高
多进程、多线程根据cpu核数不一样可能是并行的,但是协成在一个线程中
import time
import gevent
from gevent import monkey #导入模块使程序识别time模块
monkey.patch_all()
def test1(n):
for i in range(n):
# gevent.sleep(1)
time.sleep(1)
print('task1',i)
def test2(n):
for i in range(n):
# gevent.sleep(1)
time.sleep(1)
print('task2',i)
g1 = gevent.spawn(test1,10)
g2 = gevent.spawn(test2,10)
g1.join()
g2.join()