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高並發

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM