【Python 100天重新手到大師】字符串和經常使用數據結構

字符串和經常使用數據結構

使用字符串

第二次世界大戰促使了現代電子計算機的誕生,當初的想法很簡單,就是用計算機來計算導彈的彈道,所以在計算機剛剛誕生的那個年代,計算機處理的信息主要是數值,而世界上的第一臺電子計算機ENIAC每秒鐘可以完成約5000次浮點運算。隨着時間的推移,雖然對數值運算仍然是計算機平常工做中最爲重要的事情之一,可是今天的計算機處理得更多的數據都是以文本信息的方式存在的,而Python表示文本信息的方式咱們在很早之前就說過了,那就是字符串類型。所謂字符串,就是由零個或多個字符組成的有限序列,通常記爲html

$${\displaystyle s=a_{1}a_{2}\dots a_{n}(0\leq n \leq \infty)}$$

咱們能夠經過下面的代碼來了解字符串的使用。python

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 = ' jackfrued@126.com '
    print(str3)
    # 得到字符串修剪左右兩側空格的拷貝
    print(str3.strip())


if __name__ == '__main__':
    main()
複製代碼

除了字符串,Python還內置了多種類型的數據結構,若是要在程序中保存和操做數據,絕大多數時候能夠利用現有的數據結構來實現,最經常使用的包括列表、元組、集合和字典。git

使用列表

下面的代碼演示瞭如何定義列表、使用下標訪問列表元素以及添加和刪除元素的操做。編程

def main():
    list1 = [1, 3, 5, 7, 100]
    print(list1)
    list2 = ['hello'] * 5
    print(list2)
    # 計算列表長度(元素個數)
    print(len(list1))
    # 下標(索引)運算
    print(list1[0])
    print(list1[4])
    # print(list1[5]) # IndexError: list index out of range
    print(list1[-1])
    print(list1[-3])
    list1[2] = 300
    print(list1)
    # 添加元素
    list1.append(200)
    list1.insert(1, 400)
    list1 += [1000, 2000]
    print(list1)
    print(len(list1))
    # 刪除元素
    list1.remove(3)
    if 1234 in list1:
        list1.remove(1234)
    del list1[0]
    print(list1)
    # 清空列表元素
    list1.clear()
    print(list1)


if __name__ == '__main__':
    main()
複製代碼

和字符串同樣,列表也能夠作切片操做,經過切片操做咱們能夠實現對列表的複製或者將列表中的一部分取出來建立出新的列表,代碼以下所示。api

def main():
    fruits = ['grape', 'apple', 'strawberry', 'waxberry']
    fruits += ['pitaya', 'pear', 'mango']
    # 循環遍歷列表元素
    for fruit in fruits:
        print(fruit.title(), end=' ')
    print()
    # 列表切片
    fruits2 = fruits[1:4]
    print(fruits2)
    # fruit3 = fruits # 沒有複製列表只建立了新的引用
    # 能夠經過完整切片操做來複制列表
    fruits3 = fruits[:]
    print(fruits3)
    fruits4 = fruits[-3:-1]
    print(fruits4)
    # 能夠經過反向切片操做來得到倒轉後的列表的拷貝
    fruits5 = fruits[::-1]
    print(fruits5)


if __name__ == '__main__':
    main()
複製代碼

下面的代碼實現了對列表的排序操做。安全

def main():
    list1 = ['orange', 'apple', 'zoo', 'internationalization', 'blueberry']
    list2 = sorted(list1)
    # sorted函數返回列表排序後的拷貝不會修改傳入的列表
    # 函數的設計就應該像sorted函數同樣儘量不產生反作用
    list3 = sorted(list1, reverse=True)
    # 經過key關鍵字參數指定根據字符串長度進行排序而不是默認的字母表順序
    list4 = sorted(list1, key=len)
    print(list1)
    print(list2)
    print(list3)
    print(list4)
    # 給列表對象發出排序消息直接在列表對象上進行排序
    list1.sort(reverse=True)
    print(list1)


if __name__ == '__main__':
    main()
複製代碼

咱們還可使用列表的生成式語法來建立列表,代碼以下所示。數據結構

import sys


def main():
    f = [x for x in range(1, 10)]
    print(f)
    f = [x + y for x in 'ABCDE' for y in '1234567']
    print(f)
    # 用列表的生成表達式語法建立列表容器
    # 用這種語法建立列表以後元素已經準備就緒因此須要耗費較多的內存空間
    f = [x ** 2 for x in range(1, 1000)]
    print(sys.getsizeof(f))  # 查看對象佔用內存的字節數
    print(f)
    # 請注意下面的代碼建立的不是一個列表而是一個生成器對象
    # 經過生成器能夠獲取到數據但它不佔用額外的空間存儲數據
    # 每次須要數據的時候就經過內部的運算獲得數據(須要花費額外的時間)
    f = (x ** 2 for x in range(1, 1000))
    print(sys.getsizeof(f))  # 相比生成式生成器不佔用存儲數據的空間
    print(f)
    for val in f:
        print(val)


if __name__ == '__main__':
    main()
複製代碼

除了上面提到的生成器語法,Python中還有另一種定義生成器的方式,就是經過yield關鍵字將一個普通函數改形成生成器函數。下面的代碼演示瞭如何實現一個生成斐波拉切數列的生成器。所謂斐波拉切數列能夠經過下面遞歸的方法來進行定義:多線程

$${\displaystyle F_{0}=0}$$

$${\displaystyle F_{1}=1}$$

$${\displaystyle F_{n}=F_{n-1}+F_{n-2}}({n}\geq{2})$$

def fib(n):
    a, b = 0, 1
    for _ in range(n):
        a, b = b, a + b
        yield a


def main():
    for val in fib(20):
        print(val)


if __name__ == '__main__':
    main()
複製代碼

使用元組

Python 的元組與列表相似,不一樣之處在於元組的元素不能修改,在前面的代碼中咱們已經不止一次使用過元組了。顧名思義,咱們把多個元素組合到一塊兒就造成了一個元組,因此它和列表同樣能夠保存多條數據。下面的代碼演示瞭如何定義和使用元組。app

def main():
    # 定義元組
    t = ('駱昊', 38, True, '四川成都')
    print(t)
    # 獲取元組中的元素
    print(t[0])
    print(t[3])
    # 遍歷元組中的值
    for member in t:
        print(member)
    # 從新給元組賦值
    # t[0] = '王大錘' # TypeError
    # 變量t從新引用了新的元組原來的元組將被垃圾回收
    t = ('王大錘', 20, True, '雲南昆明')
    print(t)
    # 將元組轉換成列表
    person = list(t)
    print(person)
    # 列表是能夠修改它的元素的
    person[0] = '李小龍'
    person[1] = 25
    print(person)
    # 將列表轉換成元組
    fruits_list = ['apple', 'banana', 'orange']
    fruits_tuple = tuple(fruits_list)
    print(fruits_tuple)


if __name__ == '__main__':
    main()
複製代碼

這裏有一個很是值得探討的問題,咱們已經有了列表這種數據結構,爲何還須要元組這樣的類型呢?dom

  1. 元組中的元素是沒法修改的,事實上咱們在項目中尤爲是多線程環境(後面會講到)中可能更喜歡使用的是那些不變對象(一方面由於對象狀態不能修改,因此能夠避免由此引發的沒必要要的程序錯誤,簡單的說就是一個不變的對象要比可變的對象更加容易維護;另外一方面由於沒有任何一個線程可以修改不變對象的內部狀態,一個不變對象自動就是線程安全的,這樣就能夠省掉處理同步化的開銷。一個不變對象能夠方便的被共享訪問)。因此結論就是:若是不須要對元素進行添加、刪除、修改的時候,能夠考慮使用元組,固然若是一個方法要返回多個值,使用元組也是不錯的選擇。
  2. 元組在建立時間和佔用的空間上面都優於列表。咱們可使用sys模塊的getsizeof函數來檢查存儲一樣的元素的元組和列表各自佔用了多少內存空間,這個很容易作到。咱們也能夠在ipython中使用魔法指令%timeit來分析建立一樣內容的元組和列表所花費的時間,下圖是個人macOS系統上測試的結果。

使用集合

Python中的集合跟數學上的集合是一致的,不容許有重複元素,並且能夠進行交集、並集、差集等運算。

def main():
    set1 = {1, 2, 3, 3, 3, 2}
    print(set1)
    print('Length =', len(set1))
    set2 = set(range(1, 10))
    print(set2)
    set1.add(4)
    set1.add(5)
    set2.update([11, 12])
    print(set1)
    print(set2)
    set2.discard(5)
    # remove的元素若是不存在會引起KeyError
    if 4 in set2:
        set2.remove(4)
    print(set2)
    # 遍歷集合容器
    for elem in set2:
        print(elem ** 2, end=' ')
    print()
    # 將元組轉換成集合
    set3 = set((1, 2, 3, 3, 2, 1))
    print(set3.pop())
    print(set3)
    # 集合的交集、並集、差集、對稱差運算
    print(set1 & set2)
    # print(set1.intersection(set2))
    print(set1 | set2)
    # print(set1.union(set2))
    print(set1 - set2)
    # print(set1.difference(set2))
    print(set1 ^ set2)
    # print(set1.symmetric_difference(set2))
    # 判斷子集和超集
    print(set2 <= set1)
    # print(set2.issubset(set1))
    print(set3 <= set1)
    # print(set3.issubset(set1))
    print(set1 >= set2)
    # print(set1.issuperset(set2))
    print(set1 >= set3)
    # print(set1.issuperset(set3))


if __name__ == '__main__':
    main()
複製代碼

說明: Python中容許經過一些特殊的方法來爲某種類型或數據結構自定義運算符(後面的章節中會講到),上面的代碼中咱們對集合進行運算的時候能夠調用集合對象的方法,也能夠直接使用對應的運算符,例如&運算符跟intersection方法的做用就是同樣的,可是使用運算符讓代碼更加直觀。

使用字典

字典是另外一種可變容器模型,相似於咱們生活中使用的字典,它能夠存儲任意類型對象,與列表、集合不一樣的是,字典的每一個元素都是由一個鍵和一個值組成的「鍵值對」,鍵和值經過冒號分開。下面的代碼演示瞭如何定義和使用字典。

def main():
    scores = {'駱昊': 95, '白元芳': 78, '狄仁傑': 82}
    # 經過鍵能夠獲取字典中對應的值
    print(scores['駱昊'])
    print(scores['狄仁傑'])
    # 對字典進行遍歷(遍歷的實際上是鍵再經過鍵取對應的值)
    for elem in scores:
        print('%s\t--->\t%d' % (elem, scores[elem]))
    # 更新字典中的元素
    scores['白元芳'] = 65
    scores['諸葛王朗'] = 71
    scores.update(冷麪=67, 方啓鶴=85)
    print(scores)
    if '武則天' in scores:
        print(scores['武則天'])
    print(scores.get('武則天'))
    # get方法也是經過鍵獲取對應的值可是能夠設置默認值
    print(scores.get('武則天', 60))
    # 刪除字典中的元素
    print(scores.popitem())
    print(scores.popitem())
    print(scores.pop('駱昊', 100))
    # 清空字典
    scores.clear()
    print(scores)


if __name__ == '__main__':
    main()
複製代碼

練習

練習1:在屏幕上顯示跑馬燈文字

import os
import time


def main():
    content = '北京歡迎你爲你開天闢地…………'
    while True:
        # 清理屏幕上的輸出
        os.system('cls')  # os.system('clear')
        print(content)
        # 休眠200毫秒
        time.sleep(0.2)
        content = content[1:] + content[0]


if __name__ == '__main__':
    main()
複製代碼

練習2:設計一個函數產生指定長度的驗證碼,驗證碼由大小寫字母和數字構成。

import random


def generate_code(code_len=4):
    """ 生成指定長度的驗證碼 :param code_len: 驗證碼的長度(默認4個字符) :return: 由大小寫英文字母和數字構成的隨機驗證碼 """
    all_chars = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
    last_pos = len(all_chars) - 1
    code = ''
    for _ in range(code_len):
        index = random.randint(0, last_pos)
        code += all_chars[index]
    return code
複製代碼

練習3:設計一個函數返回給定文件名的後綴名。

def get_suffix(filename, has_dot=False):
    """ 獲取文件名的後綴名 :param filename: 文件名 :param has_dot: 返回的後綴名是否須要帶點 :return: 文件的後綴名 """
    pos = filename.rfind('.')
    if 0 < pos < len(filename) - 1:
        index = pos if has_dot else pos + 1
        return filename[index:]
    else:
        return ''
複製代碼

練習4:設計一個函數返回傳入的列表中最大和第二大的元素的值。

def max2(x):
    m1, m2 = (x[0], x[1]) if x[0] > x[1] else (x[1], x[0])
    for index in range(2, len(x)):
        if x[index] > m1:
            m2 = m1
            m1 = x[index]
        elif x[index] > m2:
            m2 = x[index]
    return m1, m2
複製代碼

練習5:計算指定的年月日是這一年的第幾天

def is_leap_year(year):
    """ 判斷指定的年份是否是閏年 :param year: 年份 :return: 閏年返回True平年返回False """
    return year % 4 == 0 and year % 100 != 0 or year % 400 == 0


def which_day(year, month, date):
    """ 計算傳入的日期是這一年的第幾天 :param year: 年 :param month: 月 :param date: 日 :return: 第幾天 """
    days_of_month = [
        [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31],
        [31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
    ][is_leap_year(year)]
    total = 0
    for index in range(month - 1):
        total += days_of_month[index]
    return total + date


def main():
    print(which_day(1980, 11, 28))
    print(which_day(1981, 12, 31))
    print(which_day(2018, 1, 1))
    print(which_day(2016, 3, 1))


if __name__ == '__main__':
    main()
複製代碼

練習6:打印楊輝三角

def main():
    num = int(input('Number of rows: '))
    yh = [[]] * num
    for row in range(len(yh)):
        yh[row] = [None] * (row + 1)
        for col in range(len(yh[row])):
            if col == 0 or col == row:
                yh[row][col] = 1
            else:
                yh[row][col] = yh[row - 1][col] + yh[row - 1][col - 1]
            print(yh[row][col], end='\t')
        print()


if __name__ == '__main__':
    main()
複製代碼

綜合案例

案例1:雙色球選號

from random import randrange, randint, sample


def display(balls):
    """ 輸出列表中的雙色球號碼 """
    for index, ball in enumerate(balls):
        if index == len(balls) - 1:
            print('|', end=' ')
        print('%02d' % ball, end=' ')
    print()


def random_select():
    """ 隨機選擇一組號碼 """
    red_balls = [x for x in range(1, 34)]
    selected_balls = []
    selected_balls = sample(red_balls, 6)
    selected_balls.sort()
    selected_balls.append(randint(1, 16))
    return selected_balls


def main():
    n = int(input('機選幾注: '))
    for _ in range(n):
        display(random_select())


if __name__ == '__main__':
    main()
複製代碼

說明: 上面使用random模塊的sample函數來實現從列表中選擇不重複的n個元素。

綜合案例2:約瑟夫環問題

""" 《幸運的基督徒》 有15個基督徒和15個非基督徒在海上遇險,爲了能讓一部分人活下來不得不將其中15我的扔到海里面去,有我的想了個辦法就是你們圍成一個圈,由某我的開始從1報數,報到9的人就扔到海里面,他後面的人接着從1開始報數,報到9的人繼續扔到海里面,直到扔掉15我的。因爲上帝的保佑,15個基督徒都倖免於難,問這些人最開始是怎麼站的,哪些位置是基督徒哪些位置是非基督徒。 """


def main():
    persons = [True] * 30
    counter, index, number = 0, 0, 0
    while counter < 15:
        if persons[index]:
            number += 1
            if number == 9:
                persons[index] = False
                counter += 1
                number = 0
        index += 1
        index %= 30
    for person in persons:
        print('基' if person else '非', end='')


if __name__ == '__main__':
    main()

複製代碼

綜合案例3:井字棋遊戲

import os


def print_board(board):
    print(board['TL'] + '|' + board['TM'] + '|' + board['TR'])
    print('-+-+-')
    print(board['ML'] + '|' + board['MM'] + '|' + board['MR'])
    print('-+-+-')
    print(board['BL'] + '|' + board['BM'] + '|' + board['BR'])


def main():
    init_board = {
        'TL': ' ', 'TM': ' ', 'TR': ' ',
        'ML': ' ', 'MM': ' ', 'MR': ' ',
        'BL': ' ', 'BM': ' ', 'BR': ' '
    }
    begin = True
    while begin:
        curr_board = init_board.copy()
        begin = False
        turn = 'x'
        counter = 0
        os.system('clear')
        print_board(curr_board)
        while counter < 9:
            move = input('輪到%s走棋, 請輸入位置: ' % turn)
            if curr_board[move] == ' ':
                counter += 1
                curr_board[move] = turn
                if turn == 'x':
                    turn = 'o'
                else:
                    turn = 'x'
            os.system('clear')
            print_board(curr_board)
        choice = input('再玩一局?(yes|no)')
        begin = choice == 'yes'


if __name__ == '__main__':
    main()
複製代碼

說明: 最後這個案例來自《Python編程快速上手:讓繁瑣工做自動化》一書(這本書對有編程基礎想迅速使用Python將平常工做自動化的人來講仍是不錯的選擇),對代碼作了一點點的調整。

好書推薦、視頻分享,公衆號讀書ReadBook與您一塊兒進步

Python大禮包請回復 「python0426」便可獲取

相關文章
相關標籤/搜索