Python學(xué)習(xí)筆記(五)

第五章 函數(shù)和代碼復(fù)用

函數(shù)的基本使用

函數(shù)的定義

  • 函數(shù)是一段具有特定功能的、可重用的語(yǔ)句組,用函數(shù)名來(lái)表示并通過(guò)函數(shù)名進(jìn)行完成功能調(diào)用。
  • 函數(shù)也可以看作是一段具有名字的子程序,可以在需要的地方調(diào)用執(zhí)行,不需要在每個(gè)執(zhí)行地方重復(fù)編寫(xiě)這些語(yǔ)句。每次使用函數(shù)可以提供不同的參數(shù)作為輸入,以實(shí)現(xiàn)對(duì)不同數(shù)據(jù)的處理;函數(shù)執(zhí)行后,還可以反饋相應(yīng)的處理結(jié)果。

Python定義一個(gè)函數(shù)使用def保留字,語(yǔ)法形式如下:

def <函數(shù)名>(<參數(shù)列表>):
   <函數(shù)體>
   return <返回值列表>

微實(shí)例5.1:生日歌。

過(guò)生日時(shí)要為朋友唱生日歌,歌詞為:
Happy birthday to you!
Happy birthday to you!
Happy birthday, dear <名字>
Happy birthday to you!
編寫(xiě)程序?yàn)镸ike和Lily輸出生日歌。最簡(jiǎn)單的實(shí)現(xiàn)方法是重復(fù)使用print()語(yǔ)句

#需要打印所有變量(而不只是最后一個(gè))
from IPython.core.interactiveshell import InteractiveShell
InteractiveShell.ast_node_interactivity = "all"
#重復(fù)使用print()語(yǔ)句
print("Happy birthday to you!")
print("Happy birthday to you!")
print("Happy birthday, dear Mike!")
print("Happy birthday to you!")

Happy birthday to you!
Happy birthday to you!
Happy birthday, dear Mike!
Happy birthday to you!
#使用函數(shù)
def happy():
    print("Happy birthday to you!")
def happyB(name):
    happy()
    happy()
    print("Happy birthday, dear {}!".format(name))
    happy()
happyB("Mike")
print()
happyB("Lily")

Happy birthday to you!
Happy birthday to you!
Happy birthday, dear Mike!
Happy birthday to you!

Happy birthday to you!
Happy birthday to you!
Happy birthday, dear Lily!
Happy birthday to you!

函數(shù)調(diào)用的過(guò)程

程序調(diào)用一個(gè)函數(shù)需要執(zhí)行以下四個(gè)步驟:
(1)調(diào)用程序在調(diào)用處暫停執(zhí)行;
(2)在調(diào)用時(shí)將實(shí)參復(fù)制給函數(shù)的形參;
(3)執(zhí)行函數(shù)體語(yǔ)句;
(4)函數(shù)調(diào)用結(jié)束給出返回值,程序回到調(diào)用前的暫停處繼續(xù)執(zhí)行。

lambda函數(shù)

Python的有33個(gè)保留字,其中一個(gè)是lambda,該保留字用于定義一種特殊的函數(shù)——匿名函數(shù),又稱lambda函數(shù)。
匿名函數(shù)并非沒(méi)有名字,而是將函數(shù)名作為函數(shù)結(jié)果返回,如下:
<函數(shù)名> = lambda <參數(shù)列表>: <表達(dá)式>
lambda函數(shù)與正常函數(shù)一樣,等價(jià)于下面形式:

def <函數(shù)名>(<參數(shù)列表>):
     return <表達(dá)式>

簡(jiǎn)單說(shuō),lambda函數(shù)用于定義簡(jiǎn)單的、能夠在一行內(nèi)表示的函數(shù),返回一個(gè)函數(shù)類型,實(shí)例如下。

f = lambda x, y : x + y
type(f)
f(10,12)
function






22

函數(shù)的參數(shù)傳遞

可選參數(shù)和可變數(shù)量參數(shù)

在定義函數(shù)時(shí),有些參數(shù)可以存在默認(rèn)值

def dup(str, times = 2):
        print(str*times)
dup("knock~")
dup("knock~",4)

knock~knock~
knock~knock~knock~knock~

在函數(shù)定義時(shí),可以設(shè)計(jì)可變數(shù)量參數(shù),通過(guò)參數(shù)前增加星號(hào)(*)實(shí)現(xiàn)

def vfunc(a, *b):
        print(type(b))
        print(b)
        for n in b:
             a += n
        return a
vfunc(1,2,3,4,5)

<class 'tuple'>
(2, 3, 4, 5)





15

參數(shù)的位置和名稱傳遞

Python提供了按照形參名稱輸入實(shí)參的方式,調(diào)用如下:

result = func(x2=4, y2=5, z2=6, x1=1, y1=2, z1=3)

由于調(diào)用函數(shù)時(shí)指定了參數(shù)名稱,所以參數(shù)之間的順序可以任意調(diào)整。

變量的返回值

  • return語(yǔ)句用來(lái)退出函數(shù)并將程序返回到函數(shù)被調(diào)用的位置繼續(xù)執(zhí)行。
  • return語(yǔ)句同時(shí)可以將0個(gè)、1個(gè)或多個(gè)函數(shù)運(yùn)算完的結(jié)果返回給函數(shù)被調(diào)用處的變量,例如。
def func(a, b):
    return a*b
s = func("knock~", 2)
print(s)

knock~knock~

函數(shù)可以沒(méi)有return,此時(shí)函數(shù)并不返回值,如微實(shí)例5.1的happy()函數(shù)。函數(shù)也可以用return返回多個(gè)值,多個(gè)值以元組類型保存,例如。

def func(a, b):
        return b,a
s = func("knock~", 2)
print(s, type(s))

(2, 'knock~') <class 'tuple'>

函數(shù)對(duì)變量的作用

一個(gè)程序中的變量包括兩類:全局變量和局部變量。

  • 全局變量指在函數(shù)之外定義的變量,一般沒(méi)有縮進(jìn),在程序執(zhí)行全過(guò)程有效。
  • 局部變量指在函數(shù)內(nèi)部使用的變量,僅在函數(shù)內(nèi)部有效,當(dāng)函數(shù)退出時(shí)變量將不存在。
n = 1    #n是全局變量
def func(a, b):
    c = a * b     #c是局部變量,a和b作為函數(shù)參數(shù)也是局部變量
    return c
s = func("knock~", 2)
print(c)

---------------------------------------------------------------------------

NameError                                 Traceback (most recent call last)

<ipython-input-12-b329afe441ac> in <module>()
      4     return c
      5 s = func("knock~", 2)
----> 6 print(c)


NameError: name 'c' is not defined

這個(gè)例子說(shuō)明,當(dāng)函數(shù)執(zhí)行完退出后,其內(nèi)部變量將被釋放。如果函數(shù)內(nèi)部使用全局變量呢?

n = 1    #n是全局變量
def func(a, b):
    n = b     #這個(gè)n是在函數(shù)內(nèi)存中新生成的局部變量,不是全局變量 
    return a*b
s = func("knock~", 2)
print(s, n)  #測(cè)試一下n值是否改變

knock~knock~ 1

函數(shù)func()內(nèi)部使用了變量n,并且將變量參數(shù)b賦值給變量n,為何全局變量n值沒(méi)有改變?
如果希望讓func()函數(shù)將n當(dāng)作全局變量,需要在變量n使用前顯式聲明該變量為全局變量,代碼如下。

n = 1    #n是全局變量
def func(a, b):
    global n
    n = b     #將局部變量b賦值給全局變量n 
    return a*b
s = func("knock~", 2)
print(s, n)  #測(cè)試一下n值是否改變

knock~knock~ 2

如果此時(shí)的全局變量不是整數(shù)n,而是列表類型ls,會(huì)怎么樣呢?理解如下代碼。

ls = []    #ls是全局列表變量
def func(a, b):
    ls.append(b)   #將局部變量b增加到全局列表變量ls中 
    return a*b
s = func("knock~", 2)
print(s, ls)  #測(cè)試一下ls值是否改變

knock~knock~ [2]

如果func()函數(shù)內(nèi)部存在一個(gè)真實(shí)創(chuàng)建過(guò)且名稱為ls的列表,則func()將操作該列表而不會(huì)修改全局變量,例子如下。

ls = []    #ls是全局列表變量
def func(a, b):
    ls = []     #創(chuàng)建了名稱為ls的局部列表變量列
    ls.append(b)   #將局部變量b增加到全局列表變量ls中 
    return a*b
s = func("knock~", 3)
print(s, ls)  #測(cè)試一下ls值是否改變

knock~knock~knock~ []

Python函數(shù)對(duì)變量的作用遵守如下原則:

  • 簡(jiǎn)單數(shù)據(jù)類型變量無(wú)論是否與全局變量重名,僅在函數(shù)內(nèi)部創(chuàng)建和使用,函數(shù)退出后變量被釋放;
  • 簡(jiǎn)單數(shù)據(jù)類型變量在用global保留字聲明后,作為全局變量;
  • 對(duì)于組合數(shù)據(jù)類型的全局變量,如果在函數(shù)內(nèi)部沒(méi)有被真實(shí)創(chuàng)建的同名變量,則函數(shù)內(nèi)部可直接使用并修改全局變量的值;
  • 如果函數(shù)內(nèi)部真實(shí)創(chuàng)建了組合數(shù)據(jù)類型變量,無(wú)論是否有同名全局變量,函數(shù)僅對(duì)局部變量進(jìn)行操作。

datetime庫(kù)的使用

datetime庫(kù)概述

以不同格式顯示日期和時(shí)間是程序中最常用到的功能。Python提供了一個(gè)處理時(shí)間的標(biāo)準(zhǔn)函數(shù)庫(kù)datetime,它提供了一系列由簡(jiǎn)單到復(fù)雜的時(shí)間處理方法。datetime庫(kù)可以從系統(tǒng)中獲得時(shí)間,并以用戶選擇的格式輸出。
datetime庫(kù)以類的方式提供多種日期和時(shí)間表達(dá)方式:

  • datetime.date:日期表示類,可以表示年、月、日等
  • datetime.time:時(shí)間表示類,可以表示小時(shí)、分鐘、秒、毫秒等
  • datetime.datetime:日期和時(shí)間表示的類,功能覆蓋date和time類
  • datetime.timedelta:時(shí)間間隔有關(guān)的類
  • datetime.tzinfo:與時(shí)區(qū)有關(guān)的信息表示類

datetime庫(kù)解析

使用datetime.now()獲得當(dāng)前日期和時(shí)間對(duì)象,使用方法如下:

 datetime.now()

作用:返回一個(gè)datetime類型,表示當(dāng)前的日期和時(shí)間,精確到微秒。

from datetime import datetime
today = datetime.now()
today

datetime.datetime(2017, 7, 16, 15, 29, 51, 458207)

也可以直接使用datetime()構(gòu)造一個(gè)日期和時(shí)間對(duì)象,使用方法如下:

datetime(year, month, day, hour=0, minute=0,second=0, microsecond=0)

作用:返回一個(gè)datetime類型,表示指定的日期和時(shí)間,可以精確到微秒。

#調(diào)用datetime()函數(shù)直接創(chuàng)建一個(gè)datetime對(duì)象,表示2016年9月16日22:33,32秒7微秒
someday = datetime(2016,9,16,22,33,32,7)
someday
#datetime對(duì)象有3個(gè)常用的時(shí)間格式化方法
someday.isoformat()
someday.isoweekday()
someday.strftime("%Y-%m-%d %H:%M:%S")

datetime.datetime(2016, 9, 16, 22, 33, 32, 7)






'2016-09-16T22:33:32.000007'






5






'2016-09-16 22:33:32'

strftime()格式化字符串的數(shù)字左側(cè)會(huì)自動(dòng)補(bǔ)零,上述格式也可以與print()的格式化函數(shù)一起使用

from datetime import datetime
now = datetime.now()
now.strftime("%Y-%m-%d")
now.strftime("%A, %d. %B %Y %I:%M%p")
print("今天是{0:%Y}年{0:%m}月{0:%d}日".format(now))

'2017-07-16'






'Sunday, 16. July 2017 03:34PM'



今天是2017年07月16日

七段數(shù)碼管繪制

七段數(shù)碼管(seven-segment indicator)由7段數(shù)碼管拼接而成,每段有亮或不亮兩種情況,改進(jìn)型的七段數(shù)碼管還包括一個(gè)小數(shù)點(diǎn)位置。
七段數(shù)碼管能形成27=128種不同狀態(tài),其中部分狀態(tài)能夠顯示易于人們理解的數(shù)字或字母含義,因此被廣泛使用
每個(gè)0到9的數(shù)字都有相同的七段數(shù)碼管樣式,因此,可以通過(guò)設(shè)計(jì)函數(shù)復(fù)用數(shù)字的繪制過(guò)程。進(jìn)一步,每個(gè)七段數(shù)碼管包括7個(gè)數(shù)碼管樣式,除了數(shù)碼管位置不同外,繪制風(fēng)格一致,也可以通過(guò)函數(shù)復(fù)用單個(gè)數(shù)碼段的繪制過(guò)程。

import turtle, datetime
def drawLine(draw):   #繪制單段數(shù)碼管
    turtle.pendown() if draw else turtle.penup()
    turtle.fd(40)
    turtle.right(90)
def drawDigit(d): #根據(jù)數(shù)字繪制七段數(shù)碼管
    drawLine(True) if d in [2,3,4,5,6,8,9] else drawLine(False)
    drawLine(True)  if d in [0,1,3,4,5,6,7,8,9] else drawLine(False)
    drawLine(True) if d in [0,2,3,5,6,8,9] else drawLine(False)
    drawLine(True) if d in [0,2,6,8] else drawLine(False)
    turtle.left(90)
    drawLine(True) if d in [0,4,5,6,8,9] else drawLine(False)
    drawLine(True) if d in [0,2,3,5,6,7,8,9] else drawLine(False)
    drawLine(True) if d in [0,1,2,3,4,7,8,9] else drawLine(False)
    turtle.left(180)
    turtle.penup()
    turtle.fd(20) 
def drawDate(date):  #獲得要輸出的數(shù)字
    for i in date:
        drawDigit(eval(i))  #注意: 通過(guò)eval()函數(shù)將數(shù)字變?yōu)檎麛?shù)
def main():
    turtle.setup(800, 350, 200, 200)
    turtle.penup()
    turtle.fd(-300)
    turtle.pensize(5)
    drawDate(datetime.datetime.now().strftime('%Y%m%d'))
    turtle.hideturtle()
main()

改進(jìn)的代碼:

import turtle, datetime
def drawGap(): #繪制數(shù)碼管間隔
    turtle.penup()
    turtle.fd(5)
def drawLine(draw):   #繪制單段數(shù)碼管
    drawGap()
    turtle.pendown() if draw else turtle.penup()
    turtle.fd(40)
    drawGap()    
    turtle.right(90)
def drawDigit(d): #根據(jù)數(shù)字繪制七段數(shù)碼管
    drawLine(True) if d in [2,3,4,5,6,8,9] else drawLine(False)
    drawLine(True) if d in [0,1,3,4,5,6,7,8,9] else drawLine(False)
    drawLine(True) if d in [0,2,3,5,6,8,9] else drawLine(False)
    drawLine(True) if d in [0,2,6,8] else drawLine(False)
    turtle.left(90)
    drawLine(True) if d in [0,4,5,6,8,9] else drawLine(False)
    drawLine(True) if d in [0,2,3,5,6,7,8,9] else drawLine(False)
    drawLine(True) if d in [0,1,2,3,4,7,8,9] else drawLine(False)
    turtle.left(180)
    turtle.penup()
    turtle.fd(20) 
def drawDate(date):
    turtle.pencolor("red")
    for i in date:
        if i == '-':
            turtle.write('年',font=("Arial", 18, "normal"))
            turtle.pencolor("green")
            turtle.fd(40) 
        elif i == '=':
            turtle.write('月',font=("Arial", 18, "normal"))
            turtle.pencolor("blue")
            turtle.fd(40)
        elif i == '+':
            turtle.write('日',font=("Arial", 18, "normal"))
        else:
            drawDigit(eval(i))
def main():
    turtle.setup(800, 350, 200, 200)
    turtle.penup()
    turtle.fd(-350)
    turtle.pensize(5)
    drawDate(datetime.datetime.now().strftime('%Y-%m=%d+'))
    turtle.hideturtle()
main()

代碼的復(fù)用和模塊化設(shè)計(jì)

函數(shù)是程序的一種基本抽象方式,它將一系列代碼組織起來(lái)通過(guò)命名供其他程序使用。函數(shù)封裝的直接好處是代碼復(fù)用,任何其他代碼只要輸入?yún)?shù)即可調(diào)用函數(shù),從而避免相同功能代碼在被調(diào)用處重復(fù)編寫(xiě)。代碼復(fù)用產(chǎn)生了另一個(gè)好處,當(dāng)更新函數(shù)功能時(shí),所有被調(diào)用處的功能都被更新。
當(dāng)程序的長(zhǎng)度在百行以上,如果不劃分模塊就算是最好的程序員也很難理解程序含義程序的可讀性就已經(jīng)很糟糕了。解決這一問(wèn)題的最好方法是將一個(gè)程序分割成短小的程序段,每一段程序完成一個(gè)小的功能。無(wú)論面向過(guò)程和面向?qū)ο缶幊?,?duì)程序合理劃分功能模塊并基于模塊設(shè)計(jì)程序是一種常用方法,被稱為“模塊化設(shè)計(jì)”。
模塊化設(shè)計(jì)一般有兩個(gè)基本要求:

  • 緊耦合:盡可能合理劃分功能塊,功能塊內(nèi)部耦合緊密;
  • 松耦合:模塊間關(guān)系盡可能簡(jiǎn)單,功能塊之間耦合度低。

使用函數(shù)只是模塊化設(shè)計(jì)的必要非充分條件,根據(jù)計(jì)算需求合理劃分函數(shù)十分重要。一般來(lái)說(shuō),完成特定功能或被經(jīng)常復(fù)用的一組語(yǔ)句應(yīng)該采用函數(shù)來(lái)封裝,并盡可能減少函數(shù)間參數(shù)和返回值的數(shù)量。

函數(shù)的遞歸

遞歸的定義

函數(shù)作為一種代碼封裝,可以被其他程序調(diào)用,當(dāng)然,也可以被函數(shù)內(nèi)部代碼調(diào)用。這種函數(shù)定義中調(diào)用函數(shù)自身的方式稱為遞歸。就像一個(gè)人站在裝滿鏡子的房間中,看到的影像就是遞歸的結(jié)果。遞歸在數(shù)學(xué)和計(jì)算機(jī)應(yīng)用上非常強(qiáng)大,能夠非常簡(jiǎn)潔的解決重要問(wèn)題。
數(shù)學(xué)上有個(gè)經(jīng)典的遞歸例子叫階乘。
階乘的例子揭示了遞歸的2個(gè)關(guān)鍵特征:
(1)存在一個(gè)或多個(gè)基例,基例不需要再次遞歸,它是確定的表達(dá)式;
(2)所有遞歸鏈要以一個(gè)或多個(gè)基例結(jié)尾。

遞歸的使用方法

微實(shí)例5.21:階乘的計(jì)算。

根據(jù)用戶輸入的整數(shù)n,計(jì)算并輸出n的階乘值。

def fact(n):
    if n == 0:
        return 1
    else:
        return n * fact(n-1)
num = eval(input("請(qǐng)輸入一個(gè)整數(shù): "))
print(fact(abs(int(num))))

請(qǐng)輸入一個(gè)整數(shù): 6
720

微實(shí)例5.32:字符串反轉(zhuǎn)。

對(duì)于用戶輸入的字符串s,輸出反轉(zhuǎn)后的字符串。
解決這個(gè)問(wèn)題的基本思想是把字符串看作一個(gè)遞歸對(duì)象。

def reverse(s):
    return reverse(s[1:]) + s[0]
reverse("ABC")

---------------------------------------------------------------------------

RecursionError                            Traceback (most recent call last)

<ipython-input-33-74f5c92adb66> in <module>()
      1 def reverse(s):
      2     return reverse(s[1:]) + s[0]
----> 3 reverse("ABC")


<ipython-input-33-74f5c92adb66> in reverse(s)
      1 def reverse(s):
----> 2     return reverse(s[1:]) + s[0]
      3 reverse("ABC")


... last 1 frames repeated, from the frame below ...


<ipython-input-33-74f5c92adb66> in reverse(s)
      1 def reverse(s):
----> 2     return reverse(s[1:]) + s[0]
      3 reverse("ABC")


RecursionError: maximum recursion depth exceeded

錯(cuò)誤的原因是沒(méi)有基例,修改如下:

def reverse(s):
    if s == "":
        return s
    else:
        return reverse(s[1:]) + s[0]
reverse("ABC")
'CBA'

觀察這個(gè)函數(shù)的工作過(guò)程。s[0]是首字符,s[1:]是剩余字符串,將它們反向連接,可以得到反轉(zhuǎn)字符串。

科赫曲線繪制

自然界有很多圖形很規(guī)則,符合一定的數(shù)學(xué)規(guī)律,例如,蜜蜂蜂窩是天然的等邊六角形等。科赫(Koch)曲線在眾多經(jīng)典數(shù)學(xué)曲線中非常著名,由瑞典數(shù)學(xué)家馮·科赫(H·V·Koch)于1904年提出,由于其形狀類似雪花,也被稱為雪花曲線。
科赫曲線的基本概念和繪制方法如下:
正整數(shù)n代表科赫曲線的階數(shù),表示生成科赫曲線過(guò)程的操作次數(shù)??坪涨€初始化階數(shù)為0,表示一個(gè)長(zhǎng)度為L(zhǎng)的直線。對(duì)于直線L,將其等分為三段,中間一段用邊長(zhǎng)為L(zhǎng)/3的等邊三角形的兩個(gè)邊替代,得到1階科赫曲線,它包含四條線段。進(jìn)一步對(duì)每條線段重復(fù)同樣的操作后得到2階科赫曲線。繼續(xù)重復(fù)同樣的操作n次可以得到n階科赫曲線。
科赫曲線屬于分形幾何分支,它的繪制過(guò)程體現(xiàn)了遞歸思想,繪制過(guò)程代碼。

import turtle
def koch(size, n):
    if n == 0:
        turtle.fd(size)
    else:
        for angle in [0, 60, -120, 60]:
           turtle.left(angle)
           koch(size/3, n-1) 
def main():
    turtle.setup(800,400)
    turtle.speed(0)  #控制繪制速度
    turtle.penup()
    turtle.goto(-300, -50)
    turtle.pendown()
    turtle.pensize(2)
    koch(600,3)     # 0階科赫曲線長(zhǎng)度,階數(shù)
    turtle.hideturtle()
main()

科赫曲線的雪花效果

import turtle
def koch(size, n):
    if n == 0:
        turtle.fd(size)
    else:
        for angle in [0, 60, -120, 60]:
           turtle.left(angle)
           koch(size/3, n-1) 
def main():
    turtle.setup(600,600)
    turtle.speed(0)
    turtle.penup()
    turtle.goto(-200, 100)
    turtle.pendown()
    turtle.pensize(2)
    level = 5
    koch(400,level)
    turtle.right(120)
    koch(400,level)
    turtle.right(120)
    koch(400,level)
    turtle.hideturtle()
main()

Python內(nèi)置函數(shù)

Python解釋器提供了68個(gè)內(nèi)置函數(shù),其中,前36個(gè)已經(jīng)或?qū)⒃谡n程中出現(xiàn),需要掌握。

ls = [1,2,5,0]
all(ls)
any(ls)
hash("中國(guó),你好")
id(ls)
id("中國(guó),你好")
False






True






-1553274804152303886






1965726597064






1965750150104
list(reversed(ls))
sorted(ls)
ls
sorted(ls,reverse=True)
[0, 5, 2, 1]






[0, 1, 2, 5]






[1, 2, 5, 0]






[5, 2, 1, 0]

練習(xí)題


最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容