日韩久久久精品,亚洲精品久久久久久久久久久,亚洲欧美一区二区三区国产精品 ,一区二区福利

Python語言學(xué)習(xí)(五)(面向?qū)ο蠡A(chǔ)&進(jìn)階、整數(shù)比較和嵌套列表)

系統(tǒng) 1823 0

面向?qū)ο蠡A(chǔ)

  • 面向?qū)ο缶幊蹋喊岩唤M數(shù)據(jù)結(jié)構(gòu)和處理它們的方法組成對象(object),把相同行為的對象歸納為類(class),通過類的封裝(encapsulation)隱藏內(nèi)部細(xì)節(jié),通過繼承(inheritance)實現(xiàn)類的特化(specialization)和泛化(generalization),通過多態(tài)(polymorphism)實現(xiàn)基于對象類型的動態(tài)分派。
  • 簡單地說,類是對象的藍(lán)圖和模板,對象是類的實例。
  • python中可以使用class關(guān)鍵字定義類,在類中通過函數(shù)定義方法,代碼如下。
            
              class Student(object):

    # __init__是一個特殊方法用于在創(chuàng)建對象時進(jìn)行初始化操作
    # 通過這個方法我們可以為學(xué)生對象綁定name和age兩個屬性
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def study(self, course_name):
        print('%s正在學(xué)習(xí)%s.' % (self.name, course_name))

    # PEP 8要求標(biāo)識符的名字用全小寫多個單詞用下劃線連接
    # 但是部分程序員和公司更傾向于使用駝峰命名法(駝峰標(biāo)識)
    def watch_movie(self):
        if self.age < 18:
            print('%s只能觀看《熊出沒》.' % self.name)
        else:
            print('%s正在觀看島國愛情大電影.' % self.name)

            
          
  • 當(dāng)定義好類后,可以創(chuàng)建對象并給對象發(fā)消息。
            
              def main():
    # 創(chuàng)建學(xué)生對象并指定姓名和年齡
    stu1 = Student('駱昊', 38)
    # 給對象發(fā)study消息
    stu1.study('Python程序設(shè)計')
    # 給對象發(fā)watch_av消息
    stu1.watch_movie()
    stu2 = Student('王大錘', 15)
    stu2.study('思想品德')
    stu2.watch_movie()


if __name__ == '__main__':
    main()

            
          
  • 在python中,屬性和方法的訪問權(quán)限只有兩種:公開和私有。如果希望屬性是私有的,在給屬性命名時可以用兩個下劃線作為開頭,代碼如下。
            
              class Test:

    def __init__(self, foo):
        self.__foo = foo

    def __bar(self):
        print(self.__foo)
        print('__bar')


def main():
    test = Test('hello')
    # AttributeError: 'Test' object has no attribute '__bar'
    test.__bar()
    # AttributeError: 'Test' object has no attribute '__foo'
    print(test.__foo)


if __name__ == "__main__":
    main()

            
          
  • 但事實上,python并未從語法上嚴(yán)格保證其私有性,而只是給私有的屬性和方法換了個名字妨礙訪問,如果了解更換名字的規(guī)則就可以訪問到,代碼如下。
            
              class Test:

    def __init__(self, foo):
        self.__foo = foo

    def __bar(self):
        print(self.__foo)
        print('__bar')


def main():
    test = Test('hello')
    test._Test__bar()
    print(test._Test__foo)


if __name__ == "__main__":
    main()

            
          

面向?qū)ο筮M(jìn)階

  • 在實際開發(fā)中,并不建議將屬性設(shè)為私有,這會導(dǎo)致子類無法訪問。但如果直接將屬性暴露給外界也是有問題的,建議是將屬性名以單下劃線開頭,這樣既不會設(shè)為私有,又能夠表示其是受保護(hù)的,在訪問該屬性時就要保持慎重。
    如果想訪問該類屬性,可以通過屬性的getter(訪問器)和setter(修改器)方法進(jìn)行相應(yīng)的操作。
    可以使用@property包裝器來包裝getter和setter方法,使得對屬性的訪問既安全又方便。
            
              class Person(object):

    def __init__(self, name, age):
        self._name = name
        self._age = age

    # 訪問器 - getter方法
    @property
    def name(self):
        return self._name

    # 訪問器 - getter方法
    @property
    def age(self):
        return self._age

    # 修改器 - setter方法
    @age.setter
    def age(self, age):
        self._age = age

    def play(self):
        if self._age <= 16:
            print('%s正在玩飛行棋.' % self._name)
        else:
            print('%s正在玩斗地主.' % self._name)


def main():
    person = Person('王大錘', 12)
    person.play()
    person.age = 22
    person.play()
    # person.name = '白元芳'  # AttributeError: can't set attribute


if __name__ == '__main__':
    main()

            
          
  • python是一門動態(tài)語言,允許在程序運行時給對象綁定新的屬性或方法,也可對已綁定的進(jìn)行解綁。但如果需要限定自定義類型的對象只能綁定某些屬性,可以通過在類中定義_slots_變量來完成,_slots_的限定只對當(dāng)前類的對象生效,對子類不起作用。
            
              class Person(object):

    # 限定Person對象只能綁定_name, _age和_gender屬性
    __slots__ = ('_name', '_age', '_gender')

def main():
    person = Person('王大錘', 22)
    person._gender = '男'
    # AttributeError: 'Person' object has no attribute '_is_gay'
    # person._is_gay = True

            
          
  • 在類中定義的方法包括對象方法、靜態(tài)方法和類方法等。
  • 對象方法是發(fā)送給對象的消息,而靜態(tài)方法只要定義了類,不必建立類的實例就可使用,屬于類本身而非類的某個對象。
            
              from math import sqrt


class Triangle(object):

    def __init__(self, a, b, c):
        self._a = a
        self._b = b
        self._c = c

    @staticmethod # 靜態(tài)方法
    def is_valid(a, b, c):
        return a + b > c and b + c > a and a + c > b

    def perimeter(self):
        return self._a + self._b + self._c

    def area(self):
        half = self.perimeter() / 2
        return sqrt(half * (half - self._a) *
                    (half - self._b) * (half - self._c))


def main():
    a, b, c = 3, 4, 5
    # 靜態(tài)方法和類方法都是通過給類發(fā)消息來調(diào)用的
    if Triangle.is_valid(a, b, c):
        t = Triangle(a, b, c)
        print(t.perimeter())
        # 也可以通過給類發(fā)消息來調(diào)用對象方法但是要傳入接收消息的對象作為參數(shù)
        # print(Triangle.perimeter(t))
        print(t.area())
        # print(Triangle.area(t))
    else:
        print('無法構(gòu)成三角形.')


if __name__ == '__main__':
    main()

            
          
  • 類方法第一個參數(shù)約定名為cls,它代表當(dāng)前類相關(guān)信息的對象(類本身也是一個對象,也稱為類的元數(shù)據(jù)對象),通過該參數(shù)可以獲取和類相關(guān)的信息且創(chuàng)建類的對象。
            
              from time import time, localtime, sleep


class Clock(object):
    """數(shù)字時鐘"""

    def __init__(self, hour=0, minute=0, second=0):
        self._hour = hour
        self._minute = minute
        self._second = second

    @classmethod # 類方法
    def now(cls):
        ctime = localtime(time())
        return cls(ctime.tm_hour, ctime.tm_min, ctime.tm_sec)

    def run(self):
        """走字"""
        self._second += 1
        if self._second == 60:
            self._second = 0
            self._minute += 1
            if self._minute == 60:
                self._minute = 0
                self._hour += 1
                if self._hour == 24:
                    self._hour = 0

    def show(self):
        """顯示時間"""
        return '%02d:%02d:%02d' % \
               (self._hour, self._minute, self._second)


def main():
    # 通過類方法創(chuàng)建對象并獲取系統(tǒng)時間
    clock = Clock.now()
    while True:
        print(clock.show())
        sleep(1)
        clock.run()


if __name__ == '__main__':
    main()

            
          
  • 類與類之間有三種關(guān)系:is-a、has-a和use-a。
    is-a關(guān)系即繼承或泛化,如學(xué)生-人。
    has-a關(guān)系即關(guān)聯(lián),如部門-員工。如果是整體和部分的關(guān)聯(lián),即聚合關(guān)系;如果整體進(jìn)一步負(fù)責(zé)了部分的生命周期(整體和部分是不可分割的,同時同在也同時消亡),即為合成關(guān)系,屬于最強的關(guān)聯(lián)關(guān)系。
    use-a關(guān)系即依賴,如司機的駕駛方法,其中參數(shù)用到了汽車。
  • 面向?qū)ο笕筇匦裕悍庋b、繼承和多態(tài)。
    封裝:隱藏一切可以隱藏的實現(xiàn)細(xì)節(jié),只向外界提供簡單接口。在創(chuàng)建對象后,只需要調(diào)用方法就可以執(zhí)行,只需要知道方法的名字和傳入的參數(shù),而不需要知道方法內(nèi)部的實現(xiàn)細(xì)節(jié)。
    繼承:讓一個類從另一個類那里將屬性和方法直接繼承下來,減少重復(fù)代碼的編寫。提供信息的為父類,又稱超類或基類;繼承信息的為子類,又稱派生或衍生類。子類不僅可以繼承父類的屬性和方法,還可以定義自己的,在實際開發(fā)中,通常會用子類對象替換父類對象,即里氏替換原則。
    多態(tài):子類對父類的方法給出新的實現(xiàn)版本,稱為方法重寫(override),從而讓父類的同一行為在子類中擁有不同版本。當(dāng)調(diào)用這個經(jīng)過子類重寫的方法時,不同子類對象會表現(xiàn)出不同的行為,即多態(tài)(poly-morphism)。
            
              from abc import ABCMeta, abstractmethod


class Pet(object, metaclass=ABCMeta):
    """寵物"""

    def __init__(self, nickname):
        self._nickname = nickname

    @abstractmethod
    def make_voice(self):
        """發(fā)出聲音"""
        pass


class Dog(Pet):
    """狗"""

    def make_voice(self):
        print('%s: 汪汪汪...' % self._nickname)


class Cat(Pet):
    """貓"""

    def make_voice(self):
        print('%s: 喵...喵...' % self._nickname)


def main():
    pets = [Dog('旺財'), Cat('凱蒂'), Dog('大黃')]
    for pet in pets:
        pet.make_voice()


if __name__ == '__main__':
    main()

            
          
  • 上面代碼里,將Pet類處理為抽象類,即不能夠創(chuàng)建對象,只為讓其他類來繼承。python從語法層面沒有提供對抽象類的支持,但可以通過abc模塊的ABCMeta元類和abstractmethod包裝器來達(dá)到抽象類的效果,如果一個類中存在抽象方法它就不能實例化。Dog和Cat子類分別對Pet類中的make_voice抽象方法進(jìn)行了不同地重寫,在main函數(shù)中調(diào)用該方法時,就表現(xiàn)出了多態(tài)行為。

整數(shù)比較

  • 在python中比較兩個整數(shù)時有兩種運算符:==和is:is比較的是兩者id值是否相等,即是否指向同一個地址;==比較兩者內(nèi)容是否相等,實際上調(diào)用了_eq_()方法。
  • 矛盾1 代碼如下。
            
              def main():
    x = y = -1
    while True:
        x += 1
        y += 1
        if x is y:
            print('%d is %d' % (x, y))
        else:
            print('Attention! %d is not %d' % (x, y))
            break

    x = y = 0
    while True:
        x -= 1
        y -= 1
        if x is y:
            print('%d is %d' % (x, y))
        else:
            print('Attention! %d is not %d' % (x, y))
            break


if __name__ == '__main__':
    main()

            
          
  • 矛盾1 解釋:python出于對性能的考慮,會將一些頻繁使用的整數(shù)對象緩存到一個叫small_ints的鏈表中,任何需要使用這些整數(shù)對象的時候,都不需要再重新創(chuàng)建,而是直接引用緩存。緩存的區(qū)間為[-5,256],當(dāng)使用is進(jìn)行比較時,在該范圍內(nèi)的指向同一地址,而超出該范圍則為重新創(chuàng)建的,就會得到257 is 257結(jié)果為false的情況。
  • 矛盾2 代碼如下:
            
              import dis
a = 257


def main():
    b = 257  # 第6行
    c = 257  # 第7行
    print(b is c)  # True
    print(a is b)  # False
    print(a is c)  # False


if __name__ == "__main__":
    main()

            
          
  • 矛盾2 解釋:代碼塊是程序的最小執(zhí)行單位,在上述代碼中,a=257和main屬于兩個代碼塊。python為進(jìn)一步提高性能,規(guī)定在同一個代碼塊中創(chuàng)建的整數(shù)對象,即便超出[-5,256]的范圍,只要存在有值相同的整數(shù)對象,后續(xù)創(chuàng)建就直接引用。該規(guī)則對不在small_ints范圍內(nèi)的負(fù)數(shù)和負(fù)數(shù)值浮點數(shù)并不適用,但對非負(fù)浮點數(shù)和字符串都適用。因而c引用了b的257,而a與b不在同一個代碼塊內(nèi),才會得出注釋中的執(zhí)行結(jié)果。
  • 導(dǎo)入dis模塊并在main()方法下添加“dis.dis(main)”一句,可進(jìn)行反匯編,從字節(jié)碼的角度來看該代碼。運行結(jié)果中,代碼第6、7行的257,是從同一位置加載的,而第9行的a則是從不同地方加載的,因此引用的是不同的對象。

嵌套列表

  • 把列表作為列表中的元素,即為嵌套列表,可以模擬現(xiàn)實中的表格、矩陣和棋盤等,但需謹(jǐn)慎使用,否則會出現(xiàn)問題,代碼如下。
            
              def main():
    names = ['關(guān)羽', '張飛', '趙云', '馬超', '黃忠']
    subjs = ['語文', '數(shù)學(xué)', '英語']
    scores = [[0] * 3] * 5
    for row, name in enumerate(names):
        print('請輸入%s的成績' % name)
        for col, subj in enumerate(subjs):
            scores[row][col] = float(input(subj + ': '))
    print(scores)


if __name__ == '__main__':
    main()

            
          
  • 上述代碼原本打算錄入五位同學(xué)的三門成績,但最終輸出結(jié)果卻發(fā)現(xiàn),每個學(xué)生三門課程的成績是一樣的,而且就是最后錄入的那個學(xué)生的成績。解決此問題,需要區(qū)分開對象和對象的引用兩個概念,這就涉及到內(nèi)存中的棧和堆。
  • 程序中可以使用的內(nèi)存從邏輯上可以分為五個部分,按照地址從高到低依次是:棧、堆、數(shù)據(jù)段、只讀數(shù)據(jù)段和代碼段。
    其中,棧用來存儲局部、臨時變量,以及函數(shù)調(diào)用時保存和恢復(fù)現(xiàn)場需要用到的數(shù)據(jù),這部分內(nèi)存在代碼塊執(zhí)行開始時自動分配,結(jié)束時自動釋放,通常由編譯器自動管理。
    堆的大小不固定,可以動態(tài)地分配和回收,如果程序中有大量數(shù)據(jù)需要處理,通常都放在堆上。如果堆空間沒有被正確釋放,會引發(fā)內(nèi)存泄露的問題,像Python、Java等都使用了垃圾回收機制(自動回收不再使用的堆空間),來實現(xiàn)自動化的內(nèi)存管理。
  • 因此以如下代碼為例,變量a并不是真正的對象,而是對象的引用,相當(dāng)于記錄了對象在堆空間中的地址;同理,變量b是列表容器的引用,它引用了堆空間上的列表容器,而列表容器中并沒有保存真正的對象。
            
              a = object()
b = ['apple', 'pitaya', 'grape']

            
          
  • 再看最初的程序,對列表進(jìn)行[[0]*3]*5操作時,僅僅是將[0,0,0]這個列表的地址進(jìn)行了復(fù)制,并沒有創(chuàng)建新的列表對象。所以容器中雖然有五個元素,卻都引用的是同一個列表對象,每次輸入都相當(dāng)于對該列表對象進(jìn)行了修改,因此最終顯示的即為最后一個學(xué)生的成績,正確的代碼如下。
            
              def main():
    names = ['關(guān)羽', '張飛', '趙云', '馬超', '黃忠']
    subjs = ['語文', '數(shù)學(xué)', '英語']
    scores = [[]] * 5 # 或scores = [[0] * 3 for _ in range(5)]
    for row, name in enumerate(names):
        print('請輸入%s的成績' % name)
        scores[row] = [0] * 3
        for col, subj in enumerate(subjs):
            scores[row][col] = float(input(subj + ': '))
    print(scores)


if __name__ == '__main__':
    main()

            
          

更多文章、技術(shù)交流、商務(wù)合作、聯(lián)系博主

微信掃碼或搜索:z360901061

微信掃一掃加我為好友

QQ號聯(lián)系: 360901061

您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。

【本文對您有幫助就好】

您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描上面二維碼支持博主2元、5元、10元、自定義金額等您想捐的金額吧,站長會非常 感謝您的哦!!!

發(fā)表我的評論
最新評論 總共0條評論
主站蜘蛛池模板: 博罗县| 禹城市| 龙井市| 舞阳县| 蓬安县| 卫辉市| 搜索| 金平| 炎陵县| 安徽省| 屏东市| 白朗县| 茌平县| 图木舒克市| 南召县| 棋牌| 麻江县| 大安市| 九寨沟县| 冕宁县| 盐源县| 绥阳县| 大英县| 固原市| 桦甸市| 光泽县| 武定县| 柏乡县| 安塞县| 巩留县| 抚宁县| 商洛市| 金溪县| 上饶县| 南投县| 屯昌县| 邵东县| 海淀区| 资源县| 抚松县| 大关县|