python之函数2

Python 函数

函数是组织好的,可重复使用的,用来实现单一,或相关联功能的代码段。

函数能提高应用的模块性,和代码的重复利用率。你已经知道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)

实参和形参

#定义函数括号里的一般叫形参
#调用时括号里传递的参数一般叫实参
#比如:
def students(age):
    print('my age is %s' % age)

students(18)

age就是形参,18就是实参
View Code

参数的具体应用

'''
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}
函数参数的传递

return 语句

return语句[表达式]退出函数,选择性地向调用方返回一个表达式。不带参数值的return语句返回None。之前的例子都没有示范如何返回数值,下例便告诉你怎么做:

# 可写函数说明
def add_sum(arg1, arg2):
    # 返回2个参数的和."
    total = arg1 + arg2
    print("函数内 : ", total)
    return total

# 调用sum函数
total = add_sum(10, 20)

 函数的特性

#函数可以被引用
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()
函数的特性

名称空间和作用域

#内置名称空间:(python启动时就有)python解释器内置的名字,print,max,min

#全局名称空间:(执行python文件时启动)定投定义的变量

#局部名称空间:(调用函数时启动,调用结束失效)函数内部定义的变量

总结:三者的加载顺序
        内置--->全局--->局部
    
        三者的访问顺序
        局部--->全局--->内置
View Code

 装饰器

什么是装饰器?
在不修改源代码和调用方式的基础上给其增加新的功能,多个装饰器可以装饰在同一个函数上
import time

def timer(func):
    def deco():
        start_time = time.time()
        res = func()
        end_time = time.time()
        print('cost', end_time-start_time)
        return res
    return deco
@timer
def bar():
    time.sleep(2)
    print('这是bar')
bar()
无参装饰器
import time

def timer(func):
    def deco(*args,**kwargs):
        start_time = time.time()
        res = func(*args,**kwargs)
        end_time = time.time()
        print('cost', end_time-start_time)
        return res
    return deco

@timer
def bar(a, b):
    time.sleep(2)
    print('这是bar')
    print(a)
    print(b)
bar(1,2)
有参装饰器
def default_engine(engine=None):
    def auth(func):
        def deco(*args, **kwargs):
            user = input('user:')
            password = input('password:')
            if engine == 'mysql':
                if user == 'root' and password == 'root':
                    res = func(*args, **kwargs)
                    return res
                else:
                    print('用户名或密码错误')
            else:
                print('没有这个引擎')
        return deco
    return auth

@default_engine(engine='mysql')
def index():
    print('welcome to home page')

# res = default_engine(engine='mysql')
# index = res(index)
index()
带参数的装饰器

 迭代器

 什么是迭代器?

 迭代是一个重复的过程,即每一次重复为一次迭代,并且每次迭代的结果都是下一次迭代的初始值

while True: #只是单纯的重复,因此不是迭代
    print('===>')


l = [1,2,3]
count=0
while count<len(l): #首先是重复动作,其次上一次的结果是下一次的初始值,因此,是迭代
    print(l[count])
    count+=1
View Code

为什么要有迭代器?什么是可迭代对象?什么是迭代器对象?

#1、为何要有迭代器?
对于序列类型:字符串、列表、元组,我们可以使用索引的方式迭代取出其包含的元素。但对于字典、集合、文件等类型是没有索引的,若还想取出其内部包含的元素,则必须找出一种不依赖于索引的迭代方式,这就是迭代器

#2、什么是可迭代对象?
可迭代对象指的是内置有__iter__方法的对象,即obj.__iter__,如下:
'world'.__iter__
(4,5,6).__iter__
[1,2,3].__iter__
{'a':1}.__iter__
{'a','b'}.__iter__
open('a.txt').__iter__
#3、什么是迭代器对象?
可迭代对象执行obj.__iter__()得到的结果就是迭代器对象
而迭代器对象指的是即内置有__iter__又内置有__next__方法的对象

#4、文件类型是迭代器对象
open('a.txt').__iter__()
open('a.txt').__next__()

#5、总结:
迭代器对象一定是可迭代对象,而可迭代对象不一定是迭代器对象
View Code

生成器

什么是生成器?

只要函数里有yield关键字,那么函数名()得到的结果就是生成器,并且不会执行函数内部代码

def bar():
    l = ['a','b','c']
    for i in l:
        yield i

res = bar()
for i in res:
    print(i)
yield用来返回多个值

 匿名函数

info = {
    'li':2000,
    'zhao':35000,
    'wu': 25000,
    'du': 30000
}

#max
print(max(info,key=lambda k: info[k]))

#sorted
print(sorted(info,key=lambda k: info[k],reverse=True))

#map
name = ['zhao', 'du', 'wu']
res = map(lambda name: '%s_NB' % name,name)
print(list(res))

#filter
name = ['zhao_NB', 'du_NB', 'wu']
res = filter(lambda name: name.endswith('NB'),name)
print(list(res))
max,sorted,map,filter

多任务

多线程特点:

#线程的并发是利用cpu上下文的切换(是并发,不是并行)
#多线程执行的顺序是无序的
#多线程共享全局变量
#线程是继承在进程里的,没有进程就没有线程
#GIL全局解释器锁
#只要在进行耗时的IO操作的时候,能释放GIL,所以只要在IO密集型的代码里,用多线程就很合适
# 无序的,并发的
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()
View Code
#计算并发所用的时间
import threading
import time

def test1(n):
    time.sleep(1)
    print('task', n)

def test2(n):
    time.sleep(1)
    print('task', n)

start = time.time()
l = []
t1 = threading.Thread(target=test1, args=(1,))
t2 = threading.Thread(target=test1, args=(2,))
t1.start()
t2.start()
l.append(t1)
l.append(t2)
for i in l:
    i.join()
end = time.time()
print(end - start)
View Code

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()
View Code

 多进程

#一个程序运行起来之后,代码+用到的资源称之为进程,它是操作系统分配资源的基本单位,不仅可以通过线程完成多任务,进程也是可以的
#进程之间是相互独立的
#cpu密集的时候适合用多进程

#进程之间不共享
import multiprocessing
from multiprocessing import Pool
import time
import threading
g_num = 0
def edit():
    global g_num
    for i in range(10):
        g_num += 1

def reader():
    print(g_num)


if __name__ == '__main__':
    p1 = multiprocessing.Process(target=edit)
    p2 = multiprocessing.Process(target=reader())
    p1.start()
    p2.start()
    p1.join()
    p2.join()
进程之间资源不共享
#多进程并发
import multiprocessing
from multiprocessing import Pool
import time
def test1():
    for i in range(10):
        time.sleep(1)
        print('test', i)

def test2():
    for i in range(10):
        time.sleep(1)
        print('test', i)

if __name__ == '__main__':
    p1 = multiprocessing.Process(target=test1)
    p2 = multiprocessing.Process(target=test2)
    p1.start()
    p2.start()
多进程并发
#进程池
import multiprocessing
from multiprocessing import Pool
import time
import threading
g_num = 0
def test1(n):
    for i in range(n):
        time.sleep(1)
        print('test1', i)

def test2(n):
    for i in range(n):
        time.sleep(1)
        print('test2', i)
def test3(n):
    for i in range(n):
        time.sleep(1)
        print('test3', i)

def test4(n):
    for i in range(n):
        time.sleep(1)
        print('test4', i)

if __name__ == '__main__':
    pool = Pool(3)#把进程声明出来括号里不写东西说明无限制,如果写数字,就是最大的进程数
    pool.apply_async(test1,(10,))#用pool去调用函数test1,参数为10格式为(10,)
    pool.apply_async(test2,(10,))#用pool去调用函数test2,参数为10格式为(10,)
    pool.apply_async(test3,(10,))#用pool去调用函数test3,参数为10格式为(10,)
    pool.apply_async(test4,(10,))#用pool去调用函数test4,参数为10格式为(10,)
    pool.close()#close必须在join的前面
    pool.join()
进程池并发 

协程并发(gevent)

# 进程是资源分配的单位
# 线程是操作系统调度的单位
# 进程切换需要的资源最大,效率低
# 线程切换需要的资源一般,效率一般
# 协程切换任务资源很小,效率高
# 多进程、多线程根据cpu核数不一样可能是并行的,但是协成在一个线程中
#协程,自动切换
import gevent,time
from gevent import monkey
monkey.patch_all()
def test1():
    for i in range(10):
        time.sleep(1)
        print('test1', 1)

def test2():
    for i in range(10):
        time.sleep(2)
        print('test2', 1)

g1 = gevent.spawn(test1)
g2 = gevent.spawn(test2)
g1.join()
g2.join()
协程
#server端
import gevent
from gevent import socket, monkey
monkey.patch_all()
def server_recv_conn(port):
    s = socket.socket()
    s.bind(('0.0.0.0', port))
    s.listen(200)
    while True:
        conn, addr = s.accept()
        g = gevent.spawn(recv_request, conn)
        g.join()
def recv_request(conn):
    while True:
        data = conn.recv(1024)
        data = data.decode('utf-8')
        print("recv:", data)
        conn.send(data.upper().encode('utf-8'))

if __name__ == '__main__':
    server_recv_conn(8888)







#client端
import socket

HOST = 'localhost'
PORT = 8888
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((HOST, PORT))
while True:
    msg = input("==>:")
    s.send(msg.encode('utf-8'))
    data = s.recv(1024)
    print('接收:', data.decode('utf-8'))
s.close()
协程实现socket server高并发
原文地址:https://www.cnblogs.com/fengzi7314/p/9955715.html