Python語言學習(二)(函數、遞歸函數和字符串)

函數

  • 函數是完成特定功能的一個語句組,這組語句可以作爲一個單位使用,並且給它取一個名字,之後即可通過函數名執行。

  • 使用def定義函數,函數名後的圓括號用來放置傳遞給函數的參數。

  • 有返回值的函數,用return進行返回最終值。函數遇到return語句就會終止當前函數的執行,return之後的語句都會被忽略。

  • 在python中,函數的參數可以有默認值,也支持使用可變參數,因而不需要支持函數重載,而是在定義函數時讓它有多種不同的適用方式。

  • 函數重載是一種特殊情況,有些語言允許在同一作用域中聲明幾個類似的同名函數,這些同名函數的形參列表(參數個數,類型,順序)必須不同,常用來處理實現功能類似數據類型不同的問題,如不同數據類型的數值交換。

  • python可以給函數參數設定默認值(缺省參數),意味着在沒有對應位置參數傳入時,將使用默認值,即可以通過不同的方式調用函數,這與函數重載效果一致。

  • 在不確定參數個數時,可以使用可變參數,即在括號中參數名的前面添加*,表示它是一個可變參數。

  • 局部變量只能在特定部分使用,全局變量整個程序都能使用。如果局部變量與全局變量名稱相同,在局部變量作用的範圍內是局部變量在起作用。如果想在函數內部對全局變量進行修改,需要使用global關鍵字。

  • 函數優點:代碼可重用,提高開發效率;代碼更簡潔,可讀性好;編程更容易把握;封裝和信息隱藏。使用函數進行程序設計,被稱爲結構化程序設計方法。

  • 當兩個不同.py文件(即模塊,每個.py文件都是一個模塊)中含有同樣的函數名時,爲防止函數覆蓋,如module1.py和module.py兩個模塊都包含foo函數,有以下解決方式。除此之外,其他錯誤的導入調用方式,都會造成函數覆蓋,後導入的會覆蓋先導入的。

  • a.調用誰導入誰

from module1 import foo
foo()
from module2 import foo
foo()
  • b.將導入模塊重新命名
import module1 as m1
import module2 as m2
m1.foo()
m2.foo()
  • 如果導入的模塊除了定義函數還含有可執行代碼,那在導入模塊的過程中會執行這些代碼。因而,如果在模塊中編寫了執行代碼,應當將代碼放入if _name_ == '_main_':的條件中,_name_是python中一個隱含的變量,代表了模塊名,而只有被解釋器直接執行的模塊名纔是_main_
  • 在這種情況下,該模塊被別的模塊導入,由於導入名使用的是該模塊名而非_main_,在if條件下的代碼就不會被執行。
  • python中可以在函數內部再定義函數,可以使用global關鍵字在函數內定義或使用全局變量,nonlocal關鍵字可以在函數內使用外層變量。
  • 在實際開發中,應減少對全局變量的定義。全局變量作用於和影響過於廣泛,可能會發生意料之外的修改和使用,此外比局部變量擁有更長的生命週期,可能導致對象佔用的內存長時間無法被回收,同時也可以降低代碼耦合度。
  • 今後,就可以對代碼的書寫進行改進,書寫成一下形式:
def main():
    pass
if __name__ == '__main__':
    main()
  • 函數練習:給定年和月份,獲得當月第一天是周幾(從1800年1月1號開始計算,當天是週三),代碼如下:
def is_leap_year(year):
    if year % 4 == 0 and year % 100 != 0 or year % 400 == 0:
        return True
    else:
        return False

def get_num_of_days_in_month(year, month):
    if month in (1, 3, 5, 7, 8, 10, 12):
        return 31
    elif month in (4, 6, 9, 11):
        return 30
    elif is_leap_year(year):
        return 29
    else:
        return 28

def get_total_num_of_day(year, month):
    days = 0
    for y in range(1800, year):
        if is_leap_year(y):
            days += 366
        else:
            days += 365
    for m in range(1, month):
        days += get_num_of_days_in_month(year, m)

    return days

def get_start_day(year, month):
    return (3 + get_total_num_of_day(year, month)) % 7

print(get_start_day(2033, 12))

遞歸函數

  • 遞歸表現形式:在函數定義時有直接或者間接調用自身。
  • 遞歸解決問題的基本思想:if問題足夠簡單則直接求解,else將問題分解成與原問題同構的一個或多個更小的問題逐個解決,將結果組合成爲最終解返回。
  • 漢諾塔問題解決思想:1、將前n-1個盤子通過c,從a移動到b;2、從a到c移動第n個盤子;3、將前n-1個盤子通過a,從b移動到c。定義函數hanoi(n,a,b,c)表示把a上的n個盤子移動到c上,其中可以遇到b,代碼如下:
count = 0

def hanoi(n, a, b, c):
    global count
    if n == 1:
        print('Move', n, 'from', a, 'to', c)
        count += 1
    else:
        hanoi(n - 1, a, c, b)
        print('Move', n, 'from', a, 'to', c)
        count += 1
        hanoi(n - 1, b, a, c)
        print('')

hanoi(3, 'left', 'mid', 'right')
print('steps is', count)
  • 遞歸方法的時間開銷是比較大的,一般要比循環的方式慢得多。
  • 遞歸優勢:能使一個蘊含遞歸關係且結構複雜的程序簡潔精煉,增加可讀性;特別是在難於找到從邊界到解的全過程的情況下,如果把問題推進一步,其結果仍維持原問題的關係。
  • 遞歸劣勢:嵌套層次深,函數調用開銷大;重複計算。

字符串

  • 所謂字符串,就是由零個或多個字符組成的有限序列,一般記爲s=a1a2…an(0<=n<=無窮),下列代碼可以瞭解字符串的基本使用。
def main():
    str1 = 'hello, world!'
    # 通過len函數計算字符串的長度
    print(len(str1))  # 13
    # 獲得字符串首字母大寫的拷貝
    print(str1.capitalize())  # Hello, world!
    # 獲得字符串變大寫後的拷貝
    print(str1.upper())  # HELLO, WORLD!
    # 從字符串中查找子串所在位置
    print(str1.find('or'))  # 8
    print(str1.find('shit'))  # -1
    # 與find類似但找不到子串時會引發異常
    # print(str1.index('or'))
    # print(str1.index('shit'))
    # 檢查字符串是否以指定的字符串開頭
    print(str1.startswith('He'))  # False
    print(str1.startswith('hel'))  # True
    # 檢查字符串是否以指定的字符串結尾
    print(str1.endswith('!'))  # True
    # 將字符串以指定的寬度居中並在兩側填充指定的字符
    print(str1.center(50, '*'))
    # 將字符串以指定的寬度靠右放置左側填充指定的字符
    print(str1.rjust(50, ' '))
    str2 = 'abc123456'
    # 從字符串中取出指定位置的字符(下標運算)
    print(str2[2])  # c
    # 字符串切片(從指定的開始索引到指定的結束索引)
    print(str2[2:5])  # c12
    print(str2[2:])  # c123456
    print(str2[2::2])  # c246
    print(str2[::2])  # ac246
    print(str2[::-1])  # 654321cba
    print(str2[-3:-1])  # 45
    # 檢查字符串是否由數字構成
    print(str2.isdigit())  # False
    # 檢查字符串是否以字母構成
    print(str2.isalpha())  # False
    # 檢查字符串是否以數字和字母構成
    print(str2.isalnum())  # True
    str3 = '  [email protected] '
    print(str3)
    # 獲得字符串修剪左右兩側空格的拷貝
    print(str3.strip())


if __name__ == '__main__':
    main()
  • 字符串(String)是一個字符的序列,表示時需要使用成對的單引號或雙引號括起來。也可以使用成對的三引號,這種情況下會保留字符串中的所有格式信息。想要在字符串中輸入引號的話,需要在引號前加入轉義字符“\”。

  • 拼接:”+“,將兩個字符串拼接起來,但不能拼接整數,除非也用引號括起來。
    重複:“*”,字符串乘以整數(位置可以互換),表示重複整數次。

  • 成員運算符in:判斷一個字符串是否爲另一個字符串的子串,返回值爲Ture或False,示例如下,x的值即爲False。

name = 'hello world'
x = 'a' in name
print(x)
  • for語句:枚舉字符串的每個字符,示例如下,結果就會每個字符包括空格在內各佔一行,順序打印出來。
name = 'hello world'
for x in name:
    print(x)
  • for與in的練習:輸入一串字母,將其中爲元音的字母打印出來,實現代碼如下,輸入若爲abcde,則打印結果即爲ae。
def vowles_count(s):
    for c in s:
        if c in 'aeiouAEIOU':
            print(c, end="")
a = input()
vowles_count(a)
  • 字符串索引:字符串中每個字符都有一個索引值(下標),索引從0、1…(前向後)或-1、-2…(後向前)開始。
    索引運算符[]:[]中的數字代表獲得字符串中第幾個字符,要注意索引的開始值。

  • 切片:用來選擇字符串的子序列,語法爲[start:finish],其中start爲子序列開始位置的索引值,finish爲子序列結束位置的下一個字符的索引值。如果不提供start或finish,默認start爲第一個字符開始,finish爲最後一個字符。finish可以超出字符串的範圍,也表示到字符串的尾部。

  • []中還可以寫入第三個參數,爲計數參數,即[start:finish:countBy],默認countBy爲1,即獲得連續的子字符串,如果提高countBy的值到2,則會每次跳過一個字符,以此類推。countBy也可以爲負數,則獲得逆字符串,但此時要注意兩個參數的取值,可以兩個都不填,意味着從尾到頭獲得該字符串。

  • 字符串一旦生成是不可變的,如果想要改變字符串中的某一個內容,則需通過切片等操作獲得一個新的字符串,可以選擇將新字符串賦值給原字符串,也可以使用字符串方法。

  • 字符串方法:即對象提供的函數,如變量名.replace(old,new)方法,即生成一個新的字符串,使用new替換old子串,示例如下,這樣s中所有的e都變成了a。

s = 'hello world'
s = s.replace('e', 'a')
print(s)
  • 更多字符串方法:find(‘字符’)方法,即找到第一次出現該字符的下標;split()函數,括號中若無參數,則是在字符串空格的位置對原始字符串進行切分。關於字符串的方法,可以使用dir(str)函數來瀏覽。

  • 文件操作:
    打開文件:f = open(filename,mode),其中mode有r(讀,默認),w(寫)等。
    按行讀取文件內容:for line in f:pass。
    關閉文件:f.close()。
    寫文件:f.write(str)。

  • 練習:讀取人名列表文件names.txt,將每個人名轉換爲首字母大寫,其他字母小寫的格式,實現代碼如下,其中人名文件與代碼文件注意放在同一存儲空間。
    strip()函數可以用來去掉一個字符串開頭結尾的空格回車等。
    title()函數可以將字符串轉化爲首字母大寫,其他字母小寫的格式。

f = open('names.txt', 'r')
for line in f:
    line = line.strip()
    print(line.title())
f.close()
  • 字符串大小用字典序來表示:
    首先比較兩個字符串的第一個字符。
    如果相同則比較下一個,如果不同則由第一個字符決定大小關係。
    如果其中一個字符爲空(較短),則其更小。
  • 字符串格式化使用format()方法:
    format()中的參數都用來替換{}的內容,{}的格式爲{field name:align width.precision type},比如{:9.4f},即佔據9個字符,並以4位的小數精度輸出{}中的值,示例如下。
    如需調用math包中的pi,則需先聲明math包,即import math。
>>>print('PI is {:9.4f}'.format(3.1415926))
PI is    3.1416
  • 判斷一個人名是否滿足下列模式:
    判斷其是否爲Michael,則有Michale : name == ‘Michael’。
    是否以Mi開始:name[:2] == ‘Mi’。
    是否包含cha子串:‘cha’ in name。
    但如需判斷以下情況:是否包含c?a子串(即c與a中間隔字符不確定),是否包含c*e子串(即表示任意多個字符),則需要用到正則表達式。
  • 正則表達式用來描述字符串的模式:
    .表示任意字符。
    \d+表示一系列數字。
    [a-z]表示一個小寫字母。
    正則表達式還包含很多內容,不再贅述。
  • 練習:判斷一個人名是否含有C.A模式,即C與A之間相隔任意字符,代碼如下。
import re
f = open('names.txt')
for line in f:
    line = line.strip()
    pattern = 'C.A'
    result = re.search(pattern, line)
    if result:
        print('Name is {}'.format(line))
f.close()

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章