06 python開發之函數

06 python開發之函數

目錄

6 函數

6.1 基本使用

6.1.1 基本概念

  • 函數就是用來盛放代碼的容器python

    ​ 具有某一功能的工具-->工具mysql

    ​ 實現準備好工具的過程-->函數的定義sql

    ​ 遇到應用場景,拿來就用shell

  • 使用函數能夠解決代碼可讀性差以及拓展性差的問題express

  • 先定義後引用編程

6.1.2 定義函數

  • 定義函數的語法
def 函數名(參數1,參數2,...):
    """文檔描述"""
    函數體
    return 值

# def: 定義函數的關鍵字;
# 函數名:函數名指向函數內存地址,是對函數體代碼的引用。函數的命名應該反映出函數的功能;
# 括號:括號內定義參數,參數是無關緊要的,且無需指定參數的類型;
# 冒號:括號後要加冒號,而後在下一行開始縮進編寫函數體的代碼;
# """文檔描述""": 描述函數功能,參數介紹等信息的文檔,非必要,可是建議加上,從而加強函數的可讀性;
# 函數體:由語句和表達式組成;
# return 值:定義函數的返回值,return是無關緊要的。
def say_hello():  # say_hello=函數的內存地址
    print("======")
    print("hello world")
    print("======")

print(say_hello)  # <function say_hello at 0x00000241DA0273A0>
say_hello()

x = 10            # x=10的內存地址
print(x)
  • 函數定義階段與調用階段發生的事情
# 函數定義階段:只檢測語法,不執行代碼
def foo():
    xxx
    print('from foo')

# 函數調用階段:執行函數體代碼
foo()
  • 示例
示例1
def bar():
    print('from bar')

def foo():
    print('from foo')
    bar()

foo()		# from foo | from bar

示例二
def bar():
    print('from bar')
    foo()

def foo():
    print('from foo')

bar()		# from bar | from foo

示例三
def bar():
    print('from bar')
    foo()

bar()		# 報錯,要先定義後引用

def foo():
    print('from foo')
  • 定義函數的三種形式閉包

    • 無參函數
    # 無參函數
    def f1():
        print("hello")
        print("你好")
        print("Hi")
    f1()			# hello | 你好 | Hi
    
    
    def login():
        name = input('username>>>:').strip()
        pwd = input('password>>>:').strip()
        if name == "ccc" and pwd == '111':
            print('ok')
        else:
            print('error')
    
    login()			# username>>>:
    • 有參函數
    def login(name,pwd):
        if name == "ccc" and pwd == "123":
            print('ok')
        else:
            print('error')
    
    login("ccc","123")	# ok
    login("ccc","1234")	# error
    
    def f2(x, y):
        print(x)
        print(y)
    
    f2(111, 222)		# 111 | 222
    
    def add(x, y):
        print(x+y)
    
    add(10, 20)	        # 30
    add(30, 40)		# 70
    • 空函數
    def login():
        pass
    def transfer():
        pass
    def withdraw():
        pass
    def check_balance():
        pass

6.2 調用函數與函數返回值

6.2.1 調用函數三種形式

  • 語句形式
len("hello")
  • 表達式形式
res = len("hello") + 10
print(res)		      # 15
  • 函數調用做爲參數的形式
def add(x, y):
    z = x + y
    return z

print(add(5, 5))	      # 10
res = add(add(1, 2), 3)
print(res)		      # 6

6.2.2 函數的返回值return(函數的產品)

  • return做用1:控制返回值
Ⅰ 沒有return-->默認返回就是None
def func():
    print(111)

res = func()		      # 111
print(res)		      # None

Ⅱ return 值-->返回就是那一個值
def max2(x, y):
    if x > y:
        return x
    else:
        return y

res = max2(100, 200) * 5
print(res)		      # 1000

Ⅲ return 值1,值2,值3--->返回的是一個元組
def func():
    return [11, 22], 2, 3

res = func()
print(res, type(res))         # ([11, 22], 2, 3) <class 'tuple'>
  • return做用2:函數內能夠有多個return,但只要執行一個就會當即種植函數的運行,而且會將return後的值當作本次調用的產品返回
def func():
    print(111)
    return 2201202
    print(2222)
    return 11112222
    print(3333)

res = func()
print(res)          # 111 2201202
  • return就是函數的處理結果app

    函數內沒有return關鍵字,或者說return後面沒有值-->None編程語言

  • return 值
    return 值1,值2,值3--->(值1,值2,值3)函數式編程

  • 能夠有多個return,但只要執行一次整個函數就會當即結束,而且返回值

def f1():
    x = 10
    def f2():
        print('from f2')
    return f2

res = f1()  # 即res=f2的內存地址
print(res)  # <function f1.<locals>.f2 at 0x000001F5886D8700>
res()       # from f2

6.3 Type hinting

6.3.1 定義

  • python3新增類型提示功能,例如咱們能夠爲函數增長類型提示信息,而不影響函數自己的執行:

  • 註釋的通常規則是參數名後跟一個冒號(:),而後再跟一個expression,這個expression能夠是任何形式。

6.3.2 使用

# Type hinting
def add(x: int, y: int) -> int:
    res = x + y
    return res

print(add.__annotations__)
# {'x': <class 'int'>, 'y': <class 'int'>, 'return': <class 'int'>}

6.4 函數參數

6.4.1 函數分類

  • 形參

    在定義函數時,在括號內指定參數/變量名,稱之爲形式參數,簡稱形參

    形參的本質就是變量名

  • 實參

    在調用函數時,括號內傳入的值,稱之爲實際參數,簡稱實參

    實參的本質就是變量值

  • 實參與形參的關係

    在調用函數時,實參的值會綁定給形參,該綁定關係能夠在函數內使用

    在函數調用結束後,會解除綁定關係

6.4.2 參數詳解

6.4.2.1 位置形參

  • 在定義函數時,按照從左往右的順序依次定義的變量名,稱之爲位置形參

  • 特色:必須被傳值,多一個少一個都不行

def func(x, y):
    print(x, y)
func(1, 2)
func(1, 2, 3)       # 報錯
func(1)             # 報錯

6.4.2.2 位置實參

  • 在調用函數時,按照從左往右的順序依次傳入的值,稱之爲位置實參

  • 特色:按照位置與形參一一對應

def func(x, y):
    print(x, y)
func(1, 2)
func(2, 1)

6.4.2.3 默認形參

  • 在定義函數時就已經爲某個形參賦值了,該形參就稱爲默認參數

  • 特色:在調用階段能夠不用爲默認形參傳值

def func(x, y=111):
    print(x, y)
func(1, 2)          # 1 2
func(1)             # 1 111
def register(name, age, gender="male"):
    print(name, age, gender)
register("egon", 18)
register("lxx", 38)
register("hxx", 32, "female")
register("李建國", 30)
register("alex", 31)
register("xxx", 18)

# egon 18 male
# lxx 38 male
# hxx 32 female
# 李建國 30 male
# alex 31 male
# xxx 18 male

6.4.2.4 關鍵字實參

  • 在調用函數時,按照key=value的形式指定的實參,稱之爲關鍵字實參

  • 特色:能夠打亂順序,仍能指名道姓爲指定的形參賦值

def func(x, y=2222):
    print(x, y)
func(y=222, x=1212)       # 1212 222
func(x=111)               # 111 2222

6.4.3 實參的混用

  • 位置實參和關鍵字實參能夠混用,注意點:

    Ⅰ 位置實參必須放在關鍵字實參前面

    Ⅱ 不能爲同一個形參重複賦值

def func(x, y=2222):
    print(x, y)
func(1, y=2)              # 1 2
func(y=2, 1)              # 報錯
func(1, y=2, x=3)         # 報錯

6.4.4 形參的混用

  • 位置形參和默認形參能夠混用,注意點:

    位置形參必須放在默認形參前面

def func(x, y=111):
    print(x, y)

def func(y=111, x):     # 報錯
    print(x, y)

6.4.5 默認形參使用的注意點

  • 默認形參的值最好是不可變類型
m = 222
def func(x, y=m):
    print(x, y)
m = 666
func(111)               # 獲得111 222
def register(name,hobby,hobbies=[]):
    hobbies.append(hobby)
    print('%s 的愛好是 %s' %(name, hobbies))
def register(name, hobby, hobbies=None):
    if hobbies is None:
        hobbies=[]
    hobbies.append(hobby)
    print('%s 的愛好是 %s' %(name, hobbies))
register("egon", "smoke")
register("lxx", "dance")
register("hxx", "drink")

6.5 可變長函數(*與**的應用)

6.5.1 由來

  • 可變長函數是指在調用函數時,傳入參數個數不固定,而實參是爲形參賦值的

  • 所以必須有對應格式的形參來接受溢出的實參

6.5.2 在形參中使用

6.5.2.1 *args的使用

  • 在形參中帶*,*會將溢出的位置實參彙總成元組,而後賦值給其後變量名args
def func(x, y, *z):
    print(x, y, z)
func(1, 2, 3, 4, 5)      # 1 2 (3, 4, 5)
func(1, 2)               # 1 2 ()
func(1)                  # 報錯

def my_sum(*args):
    res = 0
    for i in args:
        res += i
    print(res)
my_sum(1)               # 1
my_sum(1, 2)            # 3
my_sum(1, 2, 3)         # 6

6.5.2.2 **kwargs的使用

  • 在形參中帶**,**一般會將溢出的關鍵字實參彙總成字典,而後賦值給其後變量名kwargs
def func(x, y, **kwargs):
    print(x, y, kwargs)
func(1, y=2, a=1, b=2, c=3)        # 1 2 {'a': 1, 'b': 2, 'c': 3}

6.5.3 在實參中使用

6.5.3.1 *的使用

  • 在實參中帶*:*會將緊跟其後的實參打散成位置實參
  • 注意*後跟的應該是一個能夠被for循環循環的類型
def func(a, b, c, d):
    print(a, b, c, d)
func(*"hello")                          # 報錯,不對應
func(*"hell")                           # h e l l
func(*[11, 22, 33, 44])                 # 11 22 33 44
func(11, 22, *[33, 44])                 # 11 22 33 44
func(11, 22, *{"k1": 111, "k2": 222})   # 11 22 k1 k2

6.5.3.2 **的使用

  • 在實參中帶**😗*會將緊跟其後的實參打散成關鍵字實參

  • 注意**後面跟的必須是一個字典

def func(a, b, c, d):
    print(a, b, c, d)
func(**{"k1": 333, "k2": 444})                              # 報錯
func(**{"d": 333, "b": 444, "a": 111, "c": 222})            # 111 444 222 333
func(**[("d", 333), ("b", 444), ("a", 111), ("c", 222)])    # 報錯

6.5.4 混用

  • 在形參中,*必須在**前

  • 在實參中,*必須在**前

def index(x, y, z):
    print('index------>', x, y, z)
def wrapper(*arges, **kwargs):
    index(*arges, **kwargs)

# wrapper(1, 2, 3, 4, a=1, b=2, c=3)      # 不能一一對應 報錯
wrapper(1, 2, 3)                          # index------> 1 2 3
wrapper(z=3, y=2, x=1)                    # index------> 1 2 3


def wrapper(*arges,**kwargs):
    print(arges)
    print(kwargs)

wrapper(1, 2, a=1,b=2,c=3)      # (1, 2) {'a': 1, 'b': 2, 'c': 3}

6.5.5 命名關鍵字形參

  • 在*與**之間定義的形參稱爲關鍵字形參

  • 特色:必須按照key=value的形式傳值

def func(x, y=1, *args, a=666, b, **kwargs):
    print(x)
    print(y)
    print(args)
    print(a)
    print(b)
    print(kwargs)
func(1, 2, 3, 4, 5, 6, 7, a=111, b=222, c=333)      # 1 2 (3, 4, 5, 6, 7) 111 222 {'c': 333}
func(1, 2, 3, 4, 5, 6, 7, b=222, c=333)             # 1 2 (3, 4, 5, 6, 7) 666 222 {'c': 333}
func(1, 2, 3, 4, 5, 6, 7, b=222, 333)               # 報錯

6.6 函數對象

  • 函數是第一等公民(充當變量)
def func():
    print('from func')

6.6.1能夠賦值

def func():
    print('from func')

f = func
f()                 # from func

6.6.2 能夠看成參數傳給另外一個函數

def func():
    print('from func')

def bar(x):
    print(x)
mmm = 11111
bar(mmm)           # 11111
bar(func)          # <function func at 0x000002D4F29B63A0>

6.6.3 能夠當一個函數的返回值

def add(x):        # x=函數func的內存地址
    return x       # return 函數func的內存地址
res = add(func)    # res=add(func的內存地址)
print(res)         # <function func at 0x000002D4F29B63A0>

6.6.4 能夠當容器類型的元素

x = 10
l = [x, func]
print(l)           # [10, <function func at 0x000002C48C4463A0>]
l[-1]()            # from func

6.6.5 示例

# 新的功能只須要在字典中加入便可,無需動循環
def login():
    print('login')
def register():
    print('register')
def transfer():
    print('transfer')
def withdraw():
    print('withdraw')

func_dic = {
    "1": [login, "登陸"],
    "2": [register, "註冊"],
    "3": [transfer, "轉帳"],
    "4": [withdraw, "提現"]
}

while True:
    print("0 退出")
    for k in func_dic:
        print(k, func_dic[k][-1])
    choice = input("請輸入操做編號:").strip()
    if choice == "0":
        break
    if choice in func_dic:
        func_dic[choice][0]()
    else:
        print("輸入的操做不存在")

6.7 函數嵌套

6.7.1 函數嵌套定義

def f1():
    print('from f1')
    def f2():
        print('from f2')
    print(f2)
    f2()
    x=1111
f1()

# from f1
# <function f1.<locals>.f2 at 0x00000274E0EB8700>
# from f2
from math import pi

def circle(radius, mode=0):
    def perimiter(radius):
        return 2 * pi *radius
    def area(radius):
        return pi * (radius ** 2)
    if mode == 0:
        return perimiter(radius)
    elif mode == 1:
        return area(radius)
res1 = circle(10, mode=0)
print(res1)                 # 62.83185307179586
res2 = circle(10, mode=1)
print(res2)                 # 314.1592653589793

6.7.2 函數嵌套調用

def max2(x, y):
    if x > y:
        return x
    else:
        return y

def max4(a, b, c, d):
    res1 = max2(a, b)
    res2 = max2(res1, c)
    res3 = max2(res2, d)
    return res3


res = max4(1, 2, 3, 4)
print(res)              # 4

6.8 名稱空間與做用域

6.8.1 名稱空間

  • 名稱空間namespace

    名稱空間就是存放名字的地方

  • 三類名稱空間(內置名稱空間、全局名稱空間、局部名稱空間)

    Ⅰ 內置名稱空間:存放內置的名字

    ​ 隨着解釋器啓動就產生,解釋器關閉就銷燬

    ​ print

    ​ input

    ​ len

    Ⅱ 全局名稱空間:存放的是頂級的名字

    ​ 運行頂級代碼前產生,文件運行完畢則銷燬

    Ⅲ 局部名稱空間:在函數內定義的名字

    ​ 調用函數則產生一個函數的局部名稱空間,該函數調用結束則當即銷燬

名稱空間示例
x = 10              # 全局名稱空間

def f1():           # f1自己是全局名稱空間
    y = 2           # 局部名稱空間

if 1 > 0:
    z = 3           # 全局名稱空間
    if 3 > 1:
        m = 333     # 全局名稱空間

6.8.2 名字的查找順序

  • 局部名稱空間-->全局名稱空間-->內置名稱空間

  • Tips:在全局沒法查看局部的,在局部能夠看全局的

優先級示例
x = 111
def f1():
    print(x)
    x = 222
f1()		# 報錯

x = 111
def f1():
    x = 222
    print(x)
f1()		# 222

6.8.3 global與nonlocal

  • 名稱空間的嵌套關係是在函數定義階段,即檢測語法時肯定的

    與調用函數的位置無關,與函數定義位置有關

  • global 局部對全局變量修改

x = 111
def f1():
    global x
    x = 222

f1()
print(x)        # 222
  • nonlocal 聲明的是來自外部嵌套函數定義的做用域(非全局)
x = 111
def f1():
    x = 222
    def f2():
        nonlocal x
        x = 333
    f2()
    print(x)
f1()		# 333

6.8.4 做用域

  • 做用域即範圍

    全局範圍(內置、全局名稱空間屬該範圍):全局存活,全局有效

    局部範圍(局部名稱空間屬該範圍):臨時存活,局部有效

  • 做用域關係是在函數定義階段固定的,與函數調用位置無關

  • 查看做用域

    LEGB表明名字查找順序:locals -> enclosing function -> globals -> _builtings_

    locals 是函數內的名字空間,包括局部變量和形參

    enclosing 外部嵌套函數的名字空間(閉包中常見)

    globals 全局變量,函數定義所在模塊的名字空間

    builtings 內置模塊的名字空間

6.9 閉包函數

6.9.1 簡介

  • 閉函數

    該函數就是定義在一個函數內部的函數

def outter():
    def wrapper():
        pass
  • 包函數

    閉函數引用了一個來源於外層函數的變量

def outter():
    x = 111
    def wrapper():
        print(x)

    wrapper()

x = 222         # 不妨礙
outter()        # 111

6.9.2 意義與應用

  • 意義:

    返回的函數對象外部還包了一層做用域

    該函數不管在何處調用都優先使用本身外層包裹的做用域

  • 應用:

    延遲計算

from urllib.request import urlopen

def index(url):
	def get():
		return urlopen(url).read()
	return get

baidu=index('http://www.baidu.com')
print(baidu().decode('utf-8'))

6.10 裝飾器

6.10.1 簡介

  • 什麼是裝飾器

    器---->工具

    裝飾-->添加新功能

    裝飾器就是定義個函數,用該函數去爲其餘函數添加新功能

  • 爲何用裝飾器

    開放封閉原則:針對上線的功能對拓展是開放的,可是對修改源代碼以及調用方式是封閉的

6.10.2 無參裝飾器

  • 需求:爲index加上新功能-->統計其運算時間

    import time

    def index():

    ​ time.sleep(1)

    ​ print('from index')

    index()

# 一、首先考慮直接在運行的代碼體上下方加入時間模塊
import time
def index():
    start = time.time()
    
    time.sleep(1)
    print('from index')
    
    stop = time.time()
    print('run time is %s' % (stop - start))
index()
# 可是這樣改修改了源代碼
# 二、不修改源代碼
import time
def index():
    time.sleep(1)
    print('from index')

start = time.time()
index()
stop = time.time()
print('run time is %s' % (stop - start))
# 這樣修改每次調用函數都要輸重複代碼,不方便
# 三、把調用放進函數
import time
def index():
    time.sleep(1)
    print('from index')
    
def wrapper():
    start = time.time()
    index()
    stop = time.time()
    print('run time is %s' % (stop - start))
wrapper()
# 調用方便,可是改變了調用方式
# 3.1改進一
import time
def index():
    time.sleep(1)
    print('from index')
    
def wrapper(f):
    start = time.time()
    f()
    stop = time.time()
    print('run time is %s' % (stop -start))
wrapper(index)
# 3.2改進二
import time
def index():
    time.sleep(1)
    print('from index')
    
def outter(f):
    def wrapper():
        start = time.time()
        f()
        stop = time.time()
        print('run time is %s' % (stop - start))
    return wrapper
index = outter(index)
index()
# 添加新功能home
import time
def index():
    time.sleep(1)
    print('from index')
    
def home(name):
    time.sleep(1)
    print('homepage welcome %s' % name)


def outter(f):
    def wrapper(*args, **kwargs):
        start = time.time()
        f(*args, **kwargs)
        stop = time.time()
        print('run time is %s' % (stop - start))
    return wrapper
index = outter(index)
index()
home = outter(home)
home('ccc')
# 最終版
import time
def index():
    time.sleep(1)
    print('from index')
    
def home(name):
    time.sleep(1)
    print('homepage welcome %s' % name)
    return 123


def outter(f):
    def wrapper(*args, **kwargs):
        start = time.time()
        res = f(*args, **kwargs)
        stop = time.time()
        print('run time is %s' % (stop - start))
        return res
    return wrapper
index = outter(index)
home = outter(home)

res = index()
print(res)		# None

res = home('ccc')
print(res)		# 123

6.10.3 裝飾器的語法糖@

  • 在被裝飾對象的正上方單獨一行添加@timer,當解釋器解釋到@timer時就會調用timer函數,且把它正下方的函數名當作實參傳入,而後將返回的結果從新賦值給原函數名

  • 原函數

import time
def index():
    time.sleep(1)
    print('from index')
    
def home(name):
    time.sleep(1)
    print('homepage welcome %s' % name)
    return 123


def outter(f):
    def wrapper(*args, **kwargs):
        start = time.time()
        res = f(*args, **kwargs)
        stop = time.time()
        print('run time is %s' % (stop - start))
        return res
    return wrapper
index = outter(index)
home = outter(home)

res = index()
print(res)		# None

res = home('ccc')
print(res)		# 123
  • 用語法糖@
import time

def timmer(f):
    def wrapper(*args, **kwargs):
        start = time.time()
        res = f(*args, **kwargs)
        stop = time.time()
        print('run time is %s' % (stop - start))
        return res
    return wrapper


@timmer  # index = timmer(index)
def index():
    time.sleep(1)
    print('from index')


@timmer  # home = time(home)
def home(name):
    time.sleep(2)
    print('welcome to %s' % name)
    return 123


index()
res = home("ccc")
print(res)

# from index
# run time is 1.0003390312194824

# welcome to ccc
# run time is 2.0008108615875244
# 123

6.10.4 有參裝飾器

  • 在函數func外部再包一層函數auth,專門用來接收額外的參數,保證在auth函數內不管多少層均可以引用到參數
def auth(driver):
    def func(x):
        ...
    return func

@auth(driver='file')
def index():
    pass
@auth(driver='mysql')
def home():
    pass
import time

def login(x, engine='file'):
    def auth(func):
        def wrapper(*args, **kwargs):
            print("========>", x)
            inp_user = input("username>>>>:").strip()
            inp_pwd = input("password>>>:").strip()

            if engine == "file":
                print("基於file認證")
                if inp_user == "egon" and inp_pwd == "123":
                    print('login successful')
                    res = func(*args, **kwargs)
                    return res
                else:
                    print('username or password error')
            elif engine == 'mysql':
                print("基於mysql認證")
            elif engine == "ldap":
                print("基於ldap認證")
            else:
                print("未知認證來源")
        return wrapper
    return auth


@login(11, engine='file')  # @auth  # index = auth(index)  # index=wrapper
def index():
    time.sleep(1)
    print('from index')


@login(22, engine='file')  # @auth  # home=auth(home)  # home=wrapper
def home(name):
    time.sleep(2)
    print('home page,welcome %s' % name)
    return 123


index()
home('egon')

# ========> 11
# username>>>>:egon
# password>>>:123
# 基於file認證
# login successful
# from index

# ========> 22
# username>>>>:egon3
# password>>>:123
# 基於file認證
# login successful
# home page,welcome egon

6.10.5 疊加多個裝飾器

  • 加載順序(outter函數的調用順序):自下而上

  • 執行順序(wrapper函數的執行順序):自上而下

    @deco1
    @deco2
    @deco3
    def foo():
    pass
    至關於

    foo=deco1(deco2(deco3(foo)))

def outter1(func1):  # func1 = wrapper2的內存地址
    print('============>outter1')
    def wrapper1(*args, **kwargs):
        print('=============wrapper1')
        res1 = func1(*args, **kwargs)
        return res1
    return wrapper1

def outter2(func2):  # func2 = wrapper3的內存地址
    print('============>outter2')
    def wrapper2(*args, **kwargs):
        print('=============wrapper2')
        res2 = func2(*args, **kwargs)
        return res2
    return wrapper2

def outter3(func3):  # func3 = index的內存地址
    print('============>outter3')
    def wrapper3(*args, **kwargs):
        print('=============wrapper3')
        res3 = func3(*args, **kwargs)
        return res3
    return wrapper3

          # index = wrapper1的內存地址
@outter1  # outter1(wrapper2的內存地址)-->wrapper1的內存地址
@outter2  # outter2(wrapper3的內存地址)-->wrapper2的內存地址
@outter3  # outter3(index的內存地址)-->wrapper3的內存地址
def index():
    print('from index')


print('*'*25)
# print(index)
index()

# ============>outter3
# ============>outter2
# ============>outter1
# *************************
# =============wrapper1
# =============wrapper2
# =============wrapper3
# from index
import time
def timmer(func):
    def wrapper1(*args, **kwargs):
        start = time.time()
        res = func(*args, **kwargs)
        stop = time.time()
        print('run time is:%s' % (stop - start))
        return res
    return wrapper1

def login(x, engine='file'):
    def auth(func):
        def wrapper2(*args, **kwargs):
            print("========>", x)
            inp_user = input("username>>>>:").strip()
            inp_pwd = input("password>>>:").strip()

            if engine == "file":
                print("基於file認證")
                if inp_user == "egon" and inp_pwd == "123":
                    print('login successful')
                    res = func(*args, **kwargs)
                    return res
                else:
                    print('username or password error')
            elif engine == 'mysql':
                print("基於mysql認證")
            elif engine == "ldap":
                print("基於ldap認證")
            else:
                print("未知認證來源")
        return wrapper2
    return auth


# 場景一
@timmer
@login(11, engine='file')
def index():
    time.sleep(1)
    print('from index')

index()
# ========> 11
# username>>>>:egon
# password>>>:123
# 基於file認證
# login successful
# from index
# run time is:9.147817134857178


# 場景二
@login(11, engine='file')
@timmer
def index():
    time.sleep(1)
    print('from index')

index()
# ========> 11
# username>>>>:egon
# password>>>:123
# 基於file認證
# login successful
# from index
# run time is:1.0001623630523682

6.10.6 wraps裝飾器

  • functools模塊下提供一個裝飾器wraps專門用來幫咱們保留原函數的文檔和函數名屬性,修正裝飾器
import time
from functools import wraps


def timmer(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        start = time.time()
        res = func(*args, **kwargs)
        stop = time.time()
        print('run time is:%s' % (stop - start))
        return res
    return wrapper


@ timmer
def index():
    """index函數"""
    time.sleep(1)
    print('from index')


help(index)

6.10.7 匿名函數lambda

def func(x, y):  # func=函數的內存地址
    return x+y

匿名函數:沒有名字的函數
應用場景:臨時用一次,一般用於與其餘函數配合使用
# 無用調用方式
f = lambda x, y: x+y
print(f)
res = f(1, 2)
print(res)

# 調用方式1
res = (lambda x, y: x+y)(1, 2)
print(res)

# 調用方式2
salaries = {
    "egon": 1000,
    "alex": 2000,
    "jack": 3000,
    "rose": 4000
}
# 求薪資最高的那我的的名字

def get_salary(name):
    return salaries[name]

print(max(salaries, key = get_salary))  # rose
print(max(salaries, key=lambda name: salaries[name]))  # rose
print(min(salaries, key=lambda name: salaries[name]))  # egon
print(sorted(salaries))  # ['alex', 'egon', 'jack', 'rose']
print(sorted(salaries, key=lambda name: salaries[name]))  # ['egon', 'alex', 'jack', 'rose']
print(sorted(salaries, key=lambda name: salaries[name], reverse=True))  # ['rose', 'jack', 'alex', 'egon']

6.11 內置函數

# abs 返回絕對值
print(abs(-11))  # 11
print(abs(0))    # 0
print(abs(11))   # 11

# all 若是是空或布爾值爲真則返回True
print(all(''))   # True
print(all([]))   # True
print(all([11, 22, 333, 0]))  # False

# any 只要有一個值的布爾值爲真就返回True
print(any(''))   # False
print(any([]))   # False
print(any([0, None, '', 1]))  # True
print(any([0, None, '']))     # False

# chr 對比ASCⅡ表:65-90 A-Z
print(chr(65))   # 數字->字母A
print(ord('A'))  # 字母->數字65
print(chr(90))   # Z

# bin/oct/hex 進制轉換
print(bin(11))   # 十進制->二進制 0b1011
print(oct(11))   # 十進制->八進制 0o13
print(hex(11))   # 十進制->十六進制 0xb

# 工廠函數
int
float
str
list
tuple
dict
set
bool
bytes

# callable查看對象是否可調用
print(callable(len))  # True

# 面向對象重點
classmethod
staticmethod
setattr
getattr
delattr
hasattr
dir
exec

# eval 讀取內容代碼時執行
l = eval("[1, 2, 3]")
print(l[0])  # 1
with open('user1.txt', mode='wt', encoding='utf-8') as f:
    dic = {"ccc": "123", "zzz": "456", "yyy": "789"}
    f.write(str(dic))

with open('user1.txt', mode='rt', encoding='utf-8') as f:
    data = f.read()
    print(data, type(data))  # {'ccc': '123', 'zzz': '456', 'yyy': '789'} <class 'str'>
    dic = eval(data)
    print(dic["ccc"])  # 123
# 一、map 映射
names = ["egon", "lxx", "hxx"]
res = (name if name == "egon" else name+"_sb" for name in names)
print(list(res))  # ['egon', 'lxx_sb', 'hxx_sb']

用map實現
names = ["egon", "lxx", "hxx"]
res = map(lambda name:name if name == "egon" else name+"_sb", names)
print(res)  # <map object at 0x0000020E897D5B50>
print(list(res))  # ['egon', 'lxx_sb', 'hxx_sb']


# 二、filter 過濾
names = ['egon', 'lxx_sb', 'hxx_sb']
res = (name for name in names if name.endswith("sb"))
print(list(res))  # ['lxx_sb', 'hxx_sb']

用filter實現
names = ['egon', 'lxx_sb', 'hxx_sb']
res = filter(lambda name:name.endswith("sb"), names)
print(list(res))  # ['lxx_sb', 'hxx_sb']


# 三、reduce
from functools import reduce
res = reduce(lambda x,y:x+y, "hello", "xxx")
print(res)  # xxxhello

from functools import reduce
res = reduce(lambda x,y:x+y, [1, 2, 3], 100)
print(res)  # 106


# 四、set可變集合/frozenset不可變集合
s = frozenset({1, 2, 3})   # 不可變
s1 = set({1, 2, 3})        # 可變
s1.add(4)
print(s1)   # {1, 2, 3, 4}


# 五、locals局部變量/globals全局變量
x = 3333
def func():
    x = 1
    y = 2
    print(locals())
    print(globals())
func()  # {'x': 1, 'y': 2}
	# {'__name__': '__main__', ...}

a = 1111
print(locals())  # {'__name__': '__main__', ...}
print(globals()) # {'__name__': '__main__', ...}
print(locals() is globals())  # True


# 六、hash返回散列值
print(hash("asaasdf"))  # -9196404338305892359


# 七、pow(base ** exp % mod)
res = pow(10, 3, 3)     # 10 ** 3 % 3
print(res)              # 1


# 八、reversed 反序
res = reversed([11, 333, 222, 77])
print(list(res))       # [77, 222, 333, 11]沒有排序,直接反序


# 九、round 四捨五入
print(round(3.5))      # 4


# 十、slice 切片
l = [11, 22, 33, 44, 55, 66, 77, 88, 99]
res = l[0: 7: 2]
print(res)  # [11, 33, 55, 77]

用slice實現
l = [11, 22, 33, 44, 55, 66, 77, 88, 99]
s = slice(0, 7, 2)
res = l[s]
print(res)  # [11, 33, 55, 77]


# 十一、zip 拉鍊
s1 = "hello"
l = [11, 22, 33, 44, 55, 66]
res1 = zip(s1, l)
print(list(res1))  # [('h', 11), ('e', 22), ('l', 33), ('l', 44), ('o', 55)]
res2 = zip(l, s1)
print(list(res2))  # [(11, 'h'), (22, 'e'), (33, 'l'), (44, 'l'), (55, 'o')]
zip多的元素會直接丟掉

6.12 迭代器

6.12.1 迭代器概述

  • 迭代器指的是迭代取值的工具

  • 迭代是一個重複的過程(不是單純重複),每次重複都是基於上一次的結果進行的

    count = 1

    while count < 5:

    ​ print(count)

    ​ count += 1

  • 使用迭代器的目的

    Ⅰ 爲了找到一種通用的迭代取值的方案

    Ⅱ 節省內存

6.12.2 使用迭代器

  • 可迭代對象

    內置有__iter__方法的類型都稱之爲可迭代對象

    但凡調用了__iter__方法,就會將該類型轉換成迭代對象

    ​ res = 值.__iter__() 或 res = iter(值)

  • 迭代器對象

    Ⅰ 內置有__next__方法

    Ⅱ 內置有__iter__方法(爲了方便for循環)

  • 迭代器對象必定是可迭代對象

  • 可迭代對象不必定是迭代器對象

6.12.3 迭代器優缺點

  • 優勢:

    Ⅰ 提供了一種不依賴索引的迭代取值方案

    Ⅱ 惰性計算,節省內存

  • 缺點:

    Ⅰ 取值麻煩

    Ⅱ 沒法預測值的長度

    Ⅲ 是一次性的

6.12.4 可迭代對象

"hello".__iter__()              # 字符串
[].__iter__()                   # 列表
(11,).__iter__()                # 元組
{"k1": 12, }.__iter__()         # 字典
{11, 22}.__iter__()             # 集合
f = open("a.txt", mode="wt")    # 文件
f.__iter__()

6.12.5 迭代器對象

l = [11, 22, 33]
iter_l = l.__iter__()
print(iter_l.__next__())    # 11
print(iter_l.__next__())    # 22
print(iter_l.__next__())    # 33

info = {'name': "ccc", "age": 18, "gender": 'male'}
iter_info = info.__iter__()
print(iter_info.__next__())     # name
print(iter_info.__next__())     # age
print(iter_info.__next__())     # gender
print(iter_info.__next__())     # 拋出異常StopIteration
迭代器的iter與自己
print(iter_info.__iter__().__iter__().__iter__() is iter_info)  # True
l = [11, 222, 333, 444, 555]
iter_l = iter(l)  # l.__iter__
while True:
    try:
        print(next(iter_l))
    except StopIteration:
        break
# 11
# 222
# 333
# 444
# 555

l = {'name': "ccc", "age": 18, "gender": 'male'}
iter_l = iter(l)
while True:
    try:
        print(next(iter_l))
    except StopIteration:
        break

print('--------------------')
while True:
    try:
        print(next(iter_l))
    except StopIteration:
        break
# name
# age
# gender
# --------------------

l = {'name': "ccc", "age": 18, "gender": 'male'}
iter_l = iter(l)
while True:
    try:
        print(next(iter_l))
    except StopIteration:
        break

print('--------------------')
iter_l = iter(l)
while True:
    try:
        print(next(iter_l))
    except StopIteration:
        break
# name
# age
# gender
# --------------------
# name
# age
# gender

l = [11, 222, 333, 444, 555]
iter_l = iter(l)
for item in iter_l:
    print(item)
print('====================')
for item in iter_l:
    print(item)
# 11
# 222
# 333
# 444
# 555
# ====================

l = [11, 222, 333, 444, 555]
iter_l = iter(l)
for item in iter_l:
    print(item)
print('====================')
iter_l = iter(l)
for item in iter_l:
    print(item)
# 11
# 222
# 333
# 444
# 555
# ====================
# 11
# 222
# 333
# 444
# 555
  • for循環原理
for循環原理:
一、調用可迭代對象.__iter__(),拿到一個迭代器對象
二、調用next(迭代對象),將返回值賦值變量item
三、循環往復,直到拋出異常Stopiteration,for會檢測異常而後結束循環

f = open('a.txt', mode='rt', encoding='utf-8')
for line in f:
    print(line)
print('=======')
for line in f:
    print(line)
f.close()
# 111
#
# 222
#
# 333
#
# 444
# =======

6.13 生成器

6.13.1 基本概念

  • 生成器:生成器就是一種自定義迭代器(生成器內置iter和next方法)

  • 使用生成器是爲了省內存

  • 使用生成器的方式:

    函數體內但凡出現yield關鍵字,調用函數不會觸發函數體代碼的運行,而是會返回一個生成器對象

  • yield與return

    相同點:在返回值角度的用法一致

    不一樣點:Ⅰ yield能夠返回屢次值,return只能返回一次

    ​ Ⅱ yield能夠暫停函數,而後能夠用next方法觸發函數體代碼的運行->協程(或用list)

6.13.2 基本使用

def func():
    print("111")
    yield 1
    print("222")
    yield 2
    print("333")
    yield 3
    print("444")
    
g = func()	      # 生成器本質就是一個迭代器
print(g)	      # <generator object func at 0x0000024D38B57900>

res = next(g)	      # 111
print(res)	      # 1
res = next(g)	      # 222
print(res)	      # 2
res = next(g)	      # 333
print(res)	      # 3
next(g)		      # 444 檢測異常StopIteration
g = func()
print(g)
for i in g:
    print(i)
# <generator object func at 0x0000020C4D217900>
# 111
# 1
# 222
# 2
# 333
# 3
# 444
def my_range(start, stop, step=1):
    while start < stop:
        yield start
        start += step

for i in my_range(0, 5, 2):
    print(i)
# 0
# 2
# 4

6.14 三元表達式

6.14.1 語法

res = "條件成立時返回的值" if "條件" else "條件不成立時返回的值"

6.14.2 使用場景

def max2(x, y):
    if x > y:
        return x
    else:
        return y
res = max2(1, 2)
# 可用三元表達式一行解決
x = 1
y = 2
res = x if x > y else y

6.14.3 生成式

[表達式 for i in 可迭代對象 if 條件]
{k:v for i in 可迭代對象 if 條件}
{k for i in 可迭代對象 if 條件}

6.14.3.1 列表生成式

l = []
for i in range(8):
    l.append(i)
print(l)  # [0, 1, 2, 3, 4, 5, 6, 7]

# 可用列表生成式一行解決
l = [i for i in range(10)]
print(l)  # [0, 1, 2, 3, 4, 5, 6, 7]
names = ["egon", "lxx_sb", "hxx_sb"]
name = []
for item in names:
    if item.endswith('sb'):
        name.append(item)
print(name)  # ['lxx_sb', 'hxx_sb']

# 可用列表生成式一行解決
names = ["egon", "lxx_sb", "hxx_sb"]
name = [name for name in names if name.endswith('sb')]
print(name)  # ['lxx_sb', 'hxx_sb']

6.14.3.2 字典生成式

res = {i: i for i in range(5) if i > 2}
print(res)  # {3: 3, 4: 4}

6.14.3.3 集合生成式

res = {i for i in range(5) if i > 2}
print(res, type(res))  # {3, 4} <class 'set'>

6.14.4 生成器表達式

6.14.4.1 建立生成器對象的兩種方式

  • 調用帶yield的函數

  • 生成器表達式,將列表生成式的[]換成()便可

(expression for item in iterable if condition)

6.14.4.2 較列表生成式的比較

  • 列表生成式返回的是一個列表,生成器表達式返回的是一個生成器對象

  • 生成器表達式更加節省內存(一次只產生一個值在內存中)

  • 若是要讀取一個大文件的字節數應該是基於生成器表達式的方式完成的

6.14.4.3 使用

res = (i for i in range(5))
print(res)		# <generator object <genexpr> at 0x00000274941E7900>
print(next(res))	# 0
print(next(res))	# 1
print(next(res))	# 2
print(next(res))	# 3
print(next(res))	# 4
print(next(res))	# 拋出異常StopIteration
with open(r'a.txt', mode='rt', encoding='utf-8') as f:
    res = 0
    for line in f:
        res += len(line)
    print(res)		# 15
    
# 生成式表達式
with open(r'a.txt', mode='rt',encoding='uyf-8') as f:
    res = sum(len(line) for line in f)
    print(res)		# 15	依次執行next(res),而後累加到一塊兒獲得結果

6.14.5 附錄a.txt

111
222
333
444

6.15 面向過程編程

6.15.1 面向過程簡介

  • 核心是過程二字

  • 過程指的是解決問題的步驟,即先幹什麼後幹什麼再幹什麼

  • 基於面向過程開發程序比如是在設計一條條流水線,是一種機械式的思惟方式,正好契合計算機的運行原理

  • 計算機運行原理:任何程序的執行最終都要轉換成cpu的指令流水按過程調度執行

    即不管採用什麼語言、何種編程範式設計出的程序,最終的執行都是過程式的

6.15.2 總結

  • 優勢:將複雜的問題流程化,進而簡單化

  • 缺點:程序的可擴展性差

6.15.3 應用場景

面向過程的程序設計通常用於那些功能一旦實現以後就不多須要改變的場景,
若是你只是寫一些簡單的腳本,去作一些一次性任務,用面向過程去實現是極好的,
但若是你要處理的任務是複雜的,且須要不斷迭代和維護,那仍是用面向對象最爲方便。

6.16 函數遞歸

6.16.1 基本概念

  • 函數遞歸調用:在調用一個函數的過程當中又調用了本身

  • 本質:循環的過程-->用函數來實現的循環

  • 遞歸調用必須在知足某種條件下結束,不能無限遞歸調用下去

  • python不是函數式編程語言,沒法對遞歸進行尾遞歸優化

  • 遞歸的兩個階段:

    Ⅰ 回溯-->一層層往下爬

    Ⅱ 遞推-->一層層往上爬

6.16.2 示例

def f1():
    print("from f1")
    f1()

f1()  # 先遞歸執行,超過最大限制後報錯RecursionError
# 可用getrecursionlimit修改限制
import sys
print(sys.getrecursionlimit())		# 默認限制是1000
sys.setrecursionlimit(2000)		# 可修改,但通常不修改(受主機操做系統棧大小限制)
print(sys.getrecursionlimit(2000))      # 此時修改成2000

6.16.3 回溯與遞推

# 問題
某公司四個員工坐在一塊兒,問第四我的薪水,他說比第三我的多1000,問第三我的薪水,第他說比第二我的多1000,問第二我的薪水,他說比第一我的多1000,最後第一人說本身每個月5000,請問第四我的的薪水是多少?

# 思路解析
要知道第四我的的月薪,就必須知道第三我的的,第三我的的又取決於第二我的的,第二我的的又取決於第一我的的,並且每個員工都比前一個多一千,數學表達式即
salary(4)=salary(3)+1000 
salary(3)=salary(2)+1000 
salary(2)=salary(1)+1000 
salary(1)=5000
總結爲: 
salary(n)=salary(n-1)+1000 (n>1) 
salary(1)=5000 (n=1)

# 回溯階段
要求第n個員工的薪水,須要回溯獲得(n-1)個員工的薪水,以此類推,直到獲得第一個員工的薪水,此時,salary(1)已知,於是沒必要再向前回溯了。而後進入遞推階段:從第一個員工的薪水能夠推算出第二個員工的薪水(6000),從第二個員工的薪水能夠推算出第三個員工的薪水(7000),以此類推,一直推算出第第四個員工的薪水(8000)爲止,遞歸結束。須要注意的一點是,遞歸必定要有一個結束條件,這裏n=1就是結束條件。

# 代碼實現
def salary(n):
    if n == 1:
        return 5000
    return salary(n-1)+1000

s=salary(4)
print(s)  # 8000

# 程序分析
在未知足n==1的條件時,一直進行遞歸調用,即一直回溯。而在知足n==1的條件時,終止遞歸調用,即結束回溯,從而進入遞推階段,依次推導直到獲得最終的結果。
items=[[1, 2], 3, [4, [5, [6, 7]]]]
def foo(items):
    for i in items:
        if isinstance(i, list):  #知足未遍歷完items以及if判斷成立的條件時,一直進行遞歸調用
            foo(i) 
        else:
            print(i, end=' ')

foo(items)                       #打印結果1 2 3 4 5 6 7

6.13.4 總結

使用遞歸,咱們只須要分析出要重複執行的代碼邏輯,而後提取進入下一次遞歸調用的條件或者說遞歸結束的條件便可,代碼實現起來簡潔清晰

相關文章
相關標籤/搜索