Python【第四篇】函數、內置函數、遞歸、裝飾器、生成器和迭代器

1、函數

函數是指將一組語句的集合經過一個名字(函數名)封裝起來,要想執行這個函數,只需調用其函數名便可python

特性:算法

  1. 減小重複代碼
  2. 使程序變的可擴展
  3. 使程序變得易維護

1.定義函數

def 函數名(參數):        
    ...
    函數體
    ...
    返回值

函數的定義主要有以下要點:spa

  • def:表示函數的關鍵字
  • 函數名:函數的名稱,可根據函數名調用函數
  • 函數體:函數中進行一系列的邏輯計算
  • 參數:爲函數體提供數據
  • 返回值:當函數執行完畢後,能夠給調用者返回數據。

2.參數設計

函數的有三種不一樣的參數:code

  • 普通參數(也稱爲位置參數)
  • 默認參數
  • 動態參數

普通參數htm

#定義函數
#n是函數name的形式參數,簡稱:形參

def name(n):
    print(n)  # jack

#執行函數 
#'jack'是函數name的實際參數,簡稱:實參
name('jack')

默認參數對象

def func(name, age = 18):
    print("%s:%s"%(name,age))

# 指定參數
func('jack', 19)  # 上面輸出jack:19
# 使用默認參數
func('jack')  # 上面輸出jack:18

注:默認參數須要放在參數列表最後

動態參數(*args) blog

def func(*args):
    print(args)

# 執行方式一
func(11,22,33,55,66)  # 上面輸出(11, 22, 33, 55, 66)

# 執行方式二
li = [11,22,33,55,66]  # 上面輸出(11, 22, 33, 55, 66)
func(*li)

動態參數(**kwargs) 遞歸

def func(**kwargs):
    print(kwargs)

# 執行方式一
func(name='jack',age=18)  # 上面輸出{'name': 'jack', 'age': 18}

# 執行方式二
li = {'name':'jack', 'age':18, 'job':'pythoner'}
func(**li)  # 上面輸出{'name': 'jack', 'age': 18, 'job': 'pythoner'}

參數順序:位置參數、默認參數(即關鍵字參數,形參中若是默認參數後面有可變位置參數,實參中,這個默認參數不能寫成關鍵字參數樣式,只能寫一個值,即位置參數的樣子)、可變位置參數、可變關鍵字參數。

def hi(a,*args,**kwargs):
    print(a,type(a))  # 11 <class 'int'>
    print(args,type(args))  # (22, 33) <class 'tuple'>
    print(kwargs,type(kwargs))  # {'k1': 'jack', 'k2': 'tom'} <class 'dict'>
hi(11,22,33,k1='jack',k2='tom')

3.返回值

函數外部的代碼要想獲取函數的執行結果,就能夠在函數裏用return語句把結果返回。

def stu_register(name, age, course='python' ,country='CN'):
    print("----註冊學生信息------")
    print("姓名:", name)
    print("age:", age)
    print("國籍:", country)
    print("課程:", course)
    if age > 22:
        return False
    else:
        return True

registriation_status = stu_register("老王",22,course="PY全棧開發",country='JP')

if registriation_status:
    print("註冊成功")
else:
    print("too old to be a student.")

注意:

  • 函數在執行過程當中只要遇到return語句,就會中止執行並返回結果,因此也能夠理解爲 return 語句表明着函數的結束
  • 若是未在函數中指定return,那這個函數的返回值爲None

4.全局、局部變量

在函數中定義的變量稱爲局部變量,在程序的一開始定義的變量稱爲全局變量。
全局變量做用域是整個程序,局部變量做用域是定義該變量的函數。
當全局變量與局部變量同名時,在定義局部變量的函數內,局部變量起做用;在其它地方全局變量起做用。

全局變量在函數裏能夠隨便調用,但要修改就必須用 global 聲明 

# 全局變量
P = 'jack'

def name():
    global P  # 聲明修改全局變量
    P = 'jenny'  # 局部變量
    print(P)  # jenny

def name2():
    print(P)  # jenny

name()
name2()  # jenny

2、內置函數

Python的內置函數有許多,以下圖:

# 匿名函數,冒號前面是形參,冒號後面是函數體,並將結果return到函數調用處
f = lambda a, b: a + b
print(f(2, 3))  # 5

# abs() 取絕對值
print(abs(-111))  # 111

# all() 循環可迭代對象的每一個元素,都爲真則返回True,不然返回假
# 0,None ,"",[],(),{} 是假的
print(all([11, 22]))  # True

# any 有一個爲真,所有都爲真
print(any([0, 0, None]))  # False

# bin 將十進制轉換成2進制
# oct() hex()
print(bin(11))  # 0b1011

# chr() 找到數字對應的ascii碼
# ord() ascii碼對應的數字
# chr ord 只適用於ascii碼
print(chr(65))  # A
print(ord('A'))  # 65

# divmod 返回除法的(值,餘數)
print(divmod(10, 3))  # (3,1)

# eval 計算器的功能 返回結果
print(eval('a+60', {'a': 90}))  # 150

# exec,執行python代碼,沒有返回值
exec("for i in range(5):print(i)")  # 直接循環輸出0,1,2,3,4


# filter(函數,可迭代的對象)
# 循環能夠迭代的對象,傳入函數中執行,若是不符合就過濾
def fun(s):  # 定義判斷一個數是不是偶數的函數
    if s % 2 == 0:
        return True
    else:
        return False

ret = filter(fun, [1, 2, 3, 4, 5, 6, 7, 8])
for i in ret:
    print(i)  # 打印出2,4,6,8

# 用匿名函數改寫一下
ret1 = filter(lambda x: x % 2 == 0, [1, 2, 3, 4, 5, 6, 7, 8])
for i in ret1:
    print(i)  # 2,4,6,8

# map將傳入的函數依次做用到序列的每一個元素,並把結果做爲新的Iterator返回
ret = map(lambda x: x + 100, [1, 2, 3])
for i in ret:
    print(i)  # 101,102,103

# globals() 獲取當前文件的全部全局變量
# locals()  獲取當前文件的全部局部變量
# hash()    獲取哈希值
# isinstance 看某個對象是否是某個類建立的

# iter() 建立一個能夠被迭代的對象 next()取下一個值
k = iter([1, 2, 3, 4])
print(next(k))  # 1

# pow() 求指數
print(pow(2, 10))  # 1024

# round() 四捨五入
# zip
l1 = [1, 2, 3, 4]
l2 = ['a', 'b', 'c', 'd']
k = zip(l1, l2)
for i in k:
    print(i)  # 打印出(1,a),(2,b)....
a = [1, 2, 3, 4, 5]
b = ['aaa', 'bbb', 'ccc', 'ddd']
c = [111, 222, 333, 444]
for i in zip(a, b, c):
    print(i)
# (1, 'aaa', 111)
# (2, 'bbb', 222)
# (3, 'ccc', 333)
# (4, 'ddd', 444)

for i, j, k in zip(a, b, c):
    print(i, j, k)
# 1 aaa 111
# 2 bbb 222
# 3 ccc 333
# 4 ddd 444

# zip應用
for m, n in zip(title_list, content_list):  # 把標題和圖片對個對應
    print('正在下載>>>>>:' + m, n)

3、遞歸

遞歸算法是一種直接或者間接地調用自身算法的過程。在計算機編寫程序中,遞歸算法對解決一大類問題是十分有效的,它每每使算法的描述簡潔並且易於理解。 
遞歸算法解決問題的特色:
  • 遞歸就是在過程或函數裏調用自身。
  • 在使用遞歸策略時,必須有一個明確的遞歸結束條件,稱爲遞歸出口。
  • 遞歸算法解題一般顯得很簡潔,但遞歸算法解題的運行效率較低。因此通常不提倡用遞歸算法設計程序。
  • 遞歸調用的過程中系統爲每一層的返回點、局部量等開闢了棧來存儲。遞歸次數過多容易形成棧溢出等。因此通常不提倡用遞歸算法設計程序。

用遞歸寫一個階乘函數 f(n)算出n的階乘

def f(n):
    if n==0:  # n=0的話直接返回空,對用戶輸入的零進行判斷
        return None
    elif 1==n: # n=1的話就再也不遞歸
        return n
    else:
        return n*f(n-1)  # 遞歸在執行f(n-1) 直到f(1)
print(f(5))  # 120
'''
    f(5)的執行過程以下
        ===> f(5)
        ===> 5 * f(4)
        ===> 5 * (4 * f(3))
        ===> 5 * (4 * (3 * f(2)))
        ===> 5 * (4 * (3 * (2 * f(1))))
        ===> 5 * (4 * (3 * (2 * 1)))
        ===> 5 * (4 * (3 * 2))
        ===> 5 * (4 * 6)
        ===> 5 * 24
        ===> 120
'''

利用函數編寫以下數列:

斐波那契數列指的是這樣一個數列 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233,377,610,987,1597,2584,4181,6765,10946,17711,28657,46368...

用遞歸獲取斐波那契數列中的第10個數

def fun(n): # fun(10)便可計算第十個斐波拉數
    if 1==n : # 直接定義前面兩個數爲 0 ,1,若是輸入的n爲1,2的話直接就返回了
        return 0
    elif 2==n:
        return 1
    else:
        return fun(n-1)+fun(n-2) #若是輸入的不是1,2則進行遞歸出來計算他前面兩個數的和
 
'''
    fun(5)的執行過程以下(fun(10)的結果爲34)
        ===> fun(5)
        ===> fun(4)+fun(3)
        ===> fun(3)+fun(2) + fun(2)+fun(1)
        ===> fun(2)+fun(1)+fun(2)+fun(2)+fun(1)
        ===> 1+0+1+1+1+0
        ===> 3
'''

4、裝飾器

本質是函數,裝飾其它函數,爲其它函數添加附加功能,原則:

  • 不能修改被裝飾函數的源代碼
  • 不能修改被裝飾的函數的調用方式

裝飾器對被裝飾的函數是透明的,即:被裝飾的函數感知不到裝飾器的存在,由於沒改函數的代碼,運行方式也沒變

裝飾器執行順序

def login(func): # 1,3
    def inner(arg): # 4,7
        print('yanzheng') # 8
        func(arg) # 9
    return inner # 5

@login # 2,10
def tv(name):
    print('welcom %s' %name) # 11

tv('wgy') # 6

統計函數執行時間

import time
def timmer(func):
    def warpper(*args, **kwargs):
        start_time = time.time()
        func()
        stop_time = time.time()
        print('the func run time is %s' %(stop_time-start_time))
    return warpper

@timmer
def test1():
    time.sleep(1)
    print('in the test1')
test1()

5、生成器和迭代器

經過列表生成式,咱們能夠直接建立一個列表。可是,受到內存限制,列表容量確定是有限的。並且,建立一個包含100萬個元素的列表,不只佔用很大的存儲空間,若是咱們僅僅須要訪問前面幾個元素,那後面絕大多數元素佔用的空間都白白浪費了。因此,若是列表元素能夠按照某種算法推算出來,那咱們是否能夠在循環的過程當中不斷推算出後續的元素呢?這樣就沒必要建立完整的list,從而節省大量的空間。在Python中,這種一邊循環一邊計算的機制,稱爲生成器:generator。要建立一個generator,有不少種方法。

第一種方法很簡單,只要把一個列表生成式的[]改爲(),就建立了一個generator:

b = (i*2 for i in range(3)) # 列表生成式的[]改成(),就變成了生成器
print(b)
for i in b:
    print(i)
''' <generator object <genexpr> at 0x00000000021DE8E0> 0 2 4 '''

經過函數寫生成器
函數中加yield(暫停),表示能夠將函數變成生成器,調用函數時獲得一個生成器,yield以後的代碼不執行,yield就返回生成器地址,next調用生成器,能夠像return同樣,返回值,可是不會像return返回一次函數就終止了,並且是在執行過程當中,能夠屢次將數據或者狀態返回到next調用處(能夠返回函數循環體中每次產生的值),如,讀取一個大文件,一邊讀一邊返回。此時腳本中的return信息只在拋出異常的時候打印。

def fib(max):
    n, a, b = 0, 0, 1
    while n < max:
        print('before yield')
        # print(b)
        yield b # 把函數的執行過程凍結在這一步,而且把b的值返回給外面的next()
        a, b = b, a + b
        n = n + 1
    return 'done'
f = fib(3) # 調用函數,將函數變成一個生成器,將地址返回給f
print(f)
print(f.__next__())
print(next(f))
print(f.__next__())
print(next(f))
''' <generator object fib at 0x0000000001E7E8E0> before yield 1 before yield 1 before yield 2 Traceback (most recent call last): File "F:/test.py", line 15, in <module> print(next(f)) StopIteration: done '''

迭代器

能夠直接做用於for循環的數據類型有如下幾種:

  • 一類是集合數據類型,如list、tuple、dict、set、str等;
  • 一類是generator,包括生成器和帶yield的generator function。

能夠被next()函數調用並不斷返回下一個值的對象稱爲迭代器:Iterator。

注意區別:
  可迭代對象:Iterable(能夠直接做用於for循環)
  迭代器:Iterator(能夠被next()函數調用並不斷返回下一個值)

生成器都是Iterator對象,但list、dict、str雖然是Iterable,卻不是Iterator。把list、dict、str等Iterable變成Iterator可使用iter()函數

from collections import Iterator
a = [1,2,3]
b = iter(a)
print(isinstance(b,Iterator))
print(b.__next__())
print(b.__next__())
print(b.__next__())
print(b.__next__())
'''
True
1
2
3
Traceback (most recent call last):
  File "F:/test.py", line 8, in <module>
    print(b.__next__())
StopIteration
'''

6、練習(加Q羣獲取參考答案)

1.寫一個range功能的生成器

相關文章
相關標籤/搜索