1. 程式人生 > 實用技巧 >Python基礎學習筆記(21)二分查詢 os.walk() 面向物件基礎

Python基礎學習筆記(21)二分查詢 os.walk() 面向物件基礎

Python基礎學習(21)二分查詢 os.walk() 面向物件基礎

一、今日大綱

  • 遞迴演算法的擴充套件:二分查詢
  • os 模組的擴充套件:walk 方法
  • 面向物件基礎

二、遞迴演算法的擴充套件

之前在介紹遞迴函式的時候已經介紹過,遞迴演算法本質上是分治法的應用,其核心意義就在於把一個複雜的問題不斷分解成一個個小問題,從而達到解決問題的方法。利用遞迴函式實現二分法必然是遞迴演算法的一項重要應用:

# 由於遞迴次數很多,所以效率其實挺低的
def b_search(lis, element, left=None, right=None):
    if left == None and right == None:
        left = 0
        right = len(lis) - 1
        # print(right)
    mid = (right + left) // 2
    if lis[mid] == element:
        return mid
    elif left >= right:
        return 0
    return b_search(lis, element, left, mid - 1) + b_search(lis, element, mid + 1, right)


# l = [1,2,3,4,5,321312,421,2133,16,7]
# print(b_search(l,2133))

利用遞迴二分法查詢是瞭解遞迴的一個重要過程,但其效率實在是不高,所以更多的是將其作為一個瞭解學習的過程。

三、os 模組的擴充套件:walk 方法

之前學習遞迴函式的時候我們嘗試過利用遞迴方法檢視一個目錄下面的所有內容以及計算目錄空間佔用情況,但在實際應用中,用 os 模組中的 walk 方法遍歷目錄顯然是更好的方法,下面進行基本的介紹:

import os
g = os.walk('D:\Python\Python_Project')

for i in g:
    print(i)
    
# 迭代器每次迭代返回一個元組,中間包含三個內容:字串形式的目錄路徑、目錄下的子目錄組成的列表、目錄下的檔案組成的列表
('D:\\Python\\Python_Project\\week02 作業\\blog\\lib\\__pycache__', [], ['common.cpython-36.pyc'])
('D:\\Python\\Python_Project\\week02 作業\\blog\\log', [], ['access.log'])
('D:\\Python\\Python_Project\\week02 作業\\blog\\user', [], ['marui.txt', '母豬的產後護理', '要求.md', '要求.txt'])
('D:\\Python\\Python_Project\\week03 作業', ['calculator'], [])
('D:\\Python\\Python_Project\\week03 作業\\calculator', [], [])
...

四、面向物件楔子

假如我們要利用 Python 編寫一個遊戲程式,程式的名字叫做:人狗大戰(Legend of Dogs & Humans)。人類具有名字、血條、性別、職業、等級、武器、攻擊力等基本屬性;狗具有名字、品種、血條、技能等基本屬性。我們可以通過字典定義分別定義一個人類角色和一個狗角色:

alex = {
    'name': 'alex',
    'gender': 'unknown',
    'job': '搓澡工',
    'level': 0,
    'hp': 250,
    'weapon': '搓澡巾',
    'ad': 1,
}

xiaobai = {
    'name': '小白',
    'kind': '泰迪',
    'hp': 500,
    'ad': 249
}

可是如果這個遊戲有好多人玩,我們只能一個一個定義角色嗎;又如何保證所有玩家初始化的時候都有相同的屬性;人和狗的技能又要怎麼實現?可以嘗試寫一個人類和狗的還有技能的函式,然後每次新建一個角色,都通過函式呼叫套這個人和狗的模子:

def Person(name, gender, job, hp, weapon, ad, level=0):  # 人
    dic = {
        'name': name,
        'gender': gender,
        'job': job,
        'level': level,
        'hp': hp,
        'weapon': weapon,
        'ad': ad,
    }
    return dic

def Dog(name, kind, hp, ad):  # 狗
    dic = {
        'name': name,
        'kind': kind,
        'hp': hp,
        'ad': ad
    }
    return dic

# 人類技能:搓澡
def Rub(person, dog):
    dog['hp'] -= person['ad']
    print("%s rubbed %s, %s lose %sHP."%(person['name'], dog['name'], dog['name'], person['ad']))

# 狗技能:舔
def Lick(dog, person):
    person['hp'] -= dog['ad']
    print("%s licked %s, %s lose %sHP."%(dog['name'], person['name'], person['name'], dog['ad']))
    
    
alex = Person('alex', 'unknown', '搓澡工', 250, '搓澡巾', 1)
wusir = Person('wusir', 'male', '法師', 500, '打狗棒', 1000)
xiaojin = Dog('xiaojin', 'keji', 10000, 499)
xiaobai = Dog('xiaobai', 'taidi', 5000, 249)

Rub(alex, xiaobai)  # alex rubbed xiaobai, xiaobai lose 1HP.
Lick(xiaobai, alex)  # xiaobai licked alex, alex lose 249HP.

這樣好像整個功能都已經大概實現了,但是這時除了一個意外:

Rub(xiaobai, alex)  # xiaobai rubbed alex, alex lose 249HP.

狗使用了人的已經,這一定不是這個遊戲允許發生的,那麼我們該如何解決這個問題呢?可以嘗試將人和狗的技能封裝在各自的 Person 和 Dog 函式中,這樣就只有人類能使用rub()且只有狗能使用lick()了。

# Lick函式應該不是一個公用的函式,是一個有歸屬的技能,這時我們可以再次對Dog和Person下手
def Dog(name, kind, hp, ad):
    def Lick(dog, person):
        person['hp'] -= dog['ad']
        print("%s licked %s, %s lose %sHP." % (dog['name'], person['name'], person['name'], dog['ad']))

    dic = {
        'name': name,
        'kind': kind,
        'hp': hp,
        'ad': ad,
        'ability': Lick
    }
    return dic

def Person(name, gender, job, hp, weapon, ad, level=0):  # 人模子
    def Rub(person, dog):
        dog['hp'] -= person['ad']
        print("%s rubbed %s, %s lose %sHP." % (person['name'], dog['name'], dog['name'], person['ad']))

    dic = {
        'name': name,
        'gender': gender,
        'job': job,
        'level': level,
        'hp': hp,
        'weapon': weapon,
        'ad': ad,
        'ability': Rub
    }
    return dic


alex = Person('alex', 'unknown', '搓澡工', 250, '搓澡巾', 1)
wusir = Person('wusir', 'male', '法師', 500, '打狗棒', 1000)
xiaojin = Dog('xiaojin', 'keji', 10000, 499)
xiaobai = Dog('xiaobai', 'taidi', 5000, 249)


# 現在我們列印以下xiaobai和alex,發現這樣是可以的
print(xiaobai)
print(alex)
# {'name': 'xiaobai', 'kind': 'taidi', 'hp': 4999, 'ad': 249, 'ability': <function Dog.<locals>.Lick at 0x0000023DC6560730>}
# {'name': 'alex', 'gender': 'unknown', 'job': '搓澡工', 'level': 0, 'hp': 1, 'weapon': '搓澡巾', 'ad': 1, 'ability': <function Person.<locals>.Rub at 0x0000023DC61C1F28>}

而根據閉包知識,我們可以知道 Person 和 Dog 中的字典是一個閉包,這樣我們可以減少一個技能方法引數:

# 我們根據閉包知識,還可以對Person和Dog進行下面的修改,減少一個技能的引數
def Dog(name, kind, hp, ad):
    def Lick(person):
        person['hp'] -= dic['ad']
        print("%s licked %s, %s lose %sHP." % (dic['name'], person['name'], person['name'], dic['ad']))

    dic = {
        'name': name,
        'kind': kind,
        'hp': hp,
        'ad': ad,
        'ability': Lick
    }
    return dic

def Person(name, gender, job, hp, weapon, ad, level=0):  # 人模子
    def Rub(dog):
        dog['hp'] -= dic['ad']
        print("%s rubbed %s, %s lose %sHP." % (dic['name'], dog['name'], dog['name'], dic['ad']))

    dic = {
        'name': name,
        'gender': gender,
        'job': job,
        'level': level,
        'hp': hp,
        'weapon': weapon,
        'ad': ad,
        'ability': Rub
    }
    return dic


alex = Person('alex', 'unknown', '搓澡工', 250, '搓澡巾', 1)
wusir = Person('wusir', 'male', '法師', 500, '打狗棒', 1000)
xiaojin = Dog('xiaojin', 'keji', 10000, 499)
xiaobai = Dog('xiaobai', 'taidi', 5000, 249)

但是這時出了個比較要命的問題,呼叫技能的方式發生了改變:

xiaobai['ability'](alex)  # xiaobai licked alex, alex lose 249HP.

我們發現利用函式的知識無論怎麼樣優化這個程式的編寫,好像也無法達到完美的效果,這時我們就要引入一種全新的概念:面向物件開發。

其實上面所寫的函式已經是面向物件開發的基本思路了,即兩個物件/類之間的不斷互動影響。在一些複雜的、擁有開放式結局的程式,就可以使用面向物件開發,如購物、遊戲程式等。

五、面向物件基礎

  1. 定義類

    在Python等高階程式語言中,類就是我們剛才所討論的“模子”(C語言不含面向物件開發),每個類包括一系列屬性以及一系列方法等,我們就這剛才的“人狗大戰”問題,定義人的類。

    class Person:  # 類名
        def __init__(self):
            # 這個方法必須叫這個名字,不能改變
            # 所有的基本屬性都可以寫在這裡
            # print('-' * 20)
            self.name = 'alex'  # 物件的屬性/例項變數
            self.gender = 'unknow'
            self.job = 'rubber'
            self.level = 0
            self.hp = 250
            self.ad = 1
            self.weapon = 'cuozaojin'
            # print(self, self.__dict__)
            # print('*' * 20)
        # print('in person')
    
    # 無需呼叫,class內部會從上而下在自動執行(不含方法內部)
    # in person
    
  2. 通過類獲取物件(例項化)

    在我們呼叫類的時候會自動呼叫__init__方法,__init__方法內部的self會作為返回值從呼叫類處返回,這樣我們就實現了通過類獲取物件的過程,這個過程也被稱為例項化。

    alex = Person()  # 物件名 alex=Person()是通過類獲取一個物件的過程,也稱為例項化
    
    # 因為呼叫類的過程會自動呼叫__init__方法,所以在上面Person類的定義下,會自動返回這些“值。
    # --------------------
    # <__main__.Person object at 0x0000016A0E973588> {'name': 'alex', 'gender': 'unknow', 'job': 'rubber', 'level': 0, 'hp': 250, 'ad': 1, 'weapon': 'cuozaojin'}
    # ********************
    
  3. 讀取物件的屬性和方法

    object_name.__dict__可以返回物件的所有屬性和方法:

    print(alex, alex.__dict__)
    
    # <__main__.Person object at 0x0000016A0E973588> {'name': 'alex', 'gender': 'unknow', 'job': 'rubber', 'level': 0, 'hp': 250, 'ad': 1, 'weapon': 'cuozaojin'}
    
  4. 類和物件的關係

    • 類 class :是一個大範圍、是一個模子,它約束了一種事物有哪些屬性,但是它不能約束具體的值。
    • 物件(例項) object :是一個具體的內容,它是類的產物,它遵循類的約束,同時又給屬性賦予上了具體的值。
  5. 屬性的讀取和修改

    假如我們現在要讀取物件alex的血量資料,我們利用alex.__dict__['hp']讀取,但是這樣的方法很明顯比較笨拙,所以 Python 給我們提供這樣的方法:

    # 稱為屬性的檢視
    print(alex.gender)  # print(alex.__dict__['name'])
    
    # 也可以通過這種方法實現屬性的修改
    alex.gender = 'male'
    print(alex.gender)  # male
    
    # 也可以通過這種方法新增屬性
    alex.money = 100000
    print(alex.money)  # 100000
    
    # 也可以通過這種方法刪除屬性
    del alex.money
    
  6. 例項化所經歷的步驟

    • 呼叫之後首先開闢一塊記憶體空間
    • 呼叫__init__把空間的記憶體地址作為self函式傳遞到函式內部
    • 所有的一個個物件所需要的屬性都要和self關聯起來
    • 執行完__init__中的邏輯之後,self變數會自動返回到呼叫處
  7. 在類中定義和呼叫方法

    我們剛剛解決了“人狗大戰”兩類角色的屬性問題,但是還沒有解決技能問題,我們可以取之前定義的類,對它進行這樣的修改:

    class Person:
        def __init__(self, name, gender, job, hp, weapon, ad):
            self.name = name
            self.gender = gender
            self.job = job
            self.level = 0
            self.hp = hp
            self.ad = ad
            self.weapon = weapon
    
        def rub(self, dog):
            dog.hp -= self.ad
            print(f'{self.name} rubbed {dog.name}, {dog.name} lose {self.ad}HP.')
            
    class Dog:
        def __init__(self, name, kind, hp, ad):
            self.name = name
            self.kind = kind
            self.hp = hp
            self.ad = ad
        def lick(self, person):
            person.hp -= self.ad
            print(f'{self.name} licked {person.name}, {person.name} lose {self.ad}.')
            
    
    xiaobai = Dog('xiaobai', 'taidi', 5000, 249)
    alex = Person('alex', 'unknown', 'rubber', 250, 'cuozaojin', 1)
    alex.rub(xiaobai)  # alex rubbed xiaobai, xiaobai lose 1HP.
    xiaobai.lick(alex)  # xiaobai.lick(alex)
    

    這樣就基本解決了“人狗大戰”角色屬性和技能的問題了。

  8. 練習

    # 定義一個圓形類,半徑是這個圓形的屬性,例項化一個半徑為5的圓形,一個半徑為10的圓形
    # 實現方法:計算圓的面積和周長
    # 定義一個使用者類,使用者名稱和密碼是這個類的屬性,例項化兩個使用者,分別由不同的使用者名稱和程式碼
    from math import pi
    class Circle:
        def __init__(self, radius):
            self.radius = radius
    
        def perimeter(self):
            return 2 * pi * self.radius
        def area(self):
            return self.radius ** 3 * pi
    circle1 = Circle(5)
    circle2 = Circle(10)
    print(circle1.perimeter())
    print(circle1.area())
    
    
    
    class User:
        def __init__(self, username, password):
            self.username = username
            self.password = password
    
    user1 = User('taibai', 'taibai123')
    user2 = User('alex', 'alex123')