python學習筆記(三)---類與多態

依據廖雪峯官方網站的python教程整理

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

r'''
    類: 類和實例,訪問控制,繼承、多態
'''

__author__ = "Kingrumn"

import types

# 定義類
# 通過class關鍵字定義類
# class後面緊接着是類名,即Student,類名通常是大寫開頭的單詞
# (object),表示該類是從哪個類繼承下來的, 如果沒有合適的類繼承,用object總是沒錯的
class Student1(object):
    pass


# 創建實例
# 通過類名+()創建實例
stu = Student1()
print(stu)      # <__main__.Student object at 0x0145AEB0>


class Student2(object):
    # 通過定義一個特殊的__init__方法,在創建實例的時候,就把name,score等屬性綁上去
    def __init__(self, name, age):
        # 第一個參數永遠是self,表示創建的實例本身
        self.name = name
        self.age = age

    # 和普通的函數相比,在類中定義的函數只有一點不同,就是第一個參數永遠是實例變量self,
    # 調用時,不用傳遞該參數
    # 除此之外,類的方法和普通函數沒有什麼區別
    def print_age(self):
        print(self.age)

    # 給類增加新的方法
    def get_grade(self):
        if self.age > 18:
            return "大學"
        else:
            return "中學"


# 數據封裝
# 同樣的,可以在類外部通過封裝定義print_age函數
def print_age(s):
    print(s.age)


# 增加__init__方法後,在創建實例的時候,就不能傳入空的參數了,必須傳入與__init__方法匹配的參數,但self不需要傳
tom = Student2("Tom", 15)
print(tom.age, tom.name)        # 15 Tom
tom.print_age()         # 15
print_age(tom)          # 15

jack = Student2("Jack", 19)
print(tom.name, tom.get_grade())    # Tom 中學
print(jack.name, jack.get_grade())  # Jack 大學


# 訪問限制
# 通過添加__使屬性變爲私有屬性,外部不可訪問
class Student3(object):
    # 屬性的名稱前加上兩個下劃線__, 則變爲私有屬性
    def __init__(self, name, age):
        self.__name = name
        self.__age = age

    # 屬性私有後,需要提供訪問接口
    def get_age(self):
        return self.__age

    def set_age(self, age):
        self.__age = age

    def get_name(self):
        return self.__name

    def set_name(self, name):
        self.__name = name

    # 通過提供方法提供訪問私有屬性的能力
    def get_grade(self):
        if self.__age > 18:
            return "大學"
        else:
            return "中學"


# 內部可以訪問,外部不可以訪問
jack = Student3("Jack", 19)
print(jack.get_grade())  # 大學
# print(jack.__name)      # AttributeError: 'Student3' object has no attribute '__name'
jack.set_name("Jacky")
jack.set_age(15)
print(jack.get_name(), jack.get_age())  # Jacky 15

# 在Python中,變量名類似__xxx__的,也就是以雙下劃線開頭,並且以雙下劃線結尾的,是特殊變量,
# 特殊變量是可以直接訪問的,不是private變量,所以,不能用__name__、__score__這樣的變量名

# 有些時候,你會看到以一個下劃線開頭的實例變量名,比如_name,
# 這樣的實例變量外部是可以訪問的,
# 但是,按照約定俗成的規定,當你看到這樣的變量時,意思就是,“雖然我可以被訪問,但是,請把我視爲私有變量,不要隨意訪問”

# 一種錯誤的認知
jack.__name = "xxx"
print(jack.get_name(), jack.__name)     # Jacky xxx
# 你會發現__name並非類的屬性,雖然賦值是成功的,但是這是完全不同的2個東西


# 繼承和多態
class Animal(object):
    def run(self):
        print('Animal is running...')


class Dog(Animal):
    pass


class Cat(Animal):
    # 也可以對子類增加一些方法,比如Dog類:
    def run(self):
        # 子類的run方法覆蓋了父類的run方法
        print('Cat is running')

    def eat(self):
        print('Cat is Eating meat...')


# 上例中,Animal是Dog和Cat的父類、基類、超類;Dog和Cat是Animal的子類
# 子類獲得了父類的全部功能
dog = Dog()
dog.run()       # Animal is running...
cat = Cat()
cat.run()       # Cat is running
cat.eat()       # Cat is Eating meat...


# 多態定義
def who_run(animal):
    animal.run()


# dog和cat都是animal,所以都可以傳進來
dog.run()       # Animal is running...
cat.run()       # Cat is running
# 著名的“開閉”原則
# 對擴展開放:允許新增Animal子類;
# 對修改封閉:不需要修改依賴Animal類型的who_run()等函數


# 對於Python這樣的動態語言來說,則不一定需要傳入Animal類型。我們只需要保證傳入的對象有一個run()方法就可以了
# 對於靜態語言(例如Java)來說,如果需要傳入Animal類型,則傳入的對象必須是Animal類型或者它的子類,否則,將無法調用run()方法
class Wind(object):
    def run(self):
        print("wind is running...")


wind = Wind()
# 只要wind定義了run方法就可以,不需要一定是Animal類或者其子類
who_run(wind)   # wind is running...

# 獲取對象信息
# 使用type()
print(type(wind))   # <class '__main__.Wind'>

# 基本數據類型,int/str/bool/float
# 類型常量:import types:types.FunctionType、types.BuiltinFunctionType、types.LambdaType、types.GeneratorType
print(type((x * x for x in range(10))) == types.GeneratorType)  # True, 不推薦這樣使用,可使用isinstance()

# 使用isinstance(), 總是優先使用isinstance()判斷類型,可以將指定類型及其子類“一網打盡”。
print(isinstance(dog, Dog))     # True
print(isinstance(dog, Animal))  # True

# 使用dir()
# 獲得一個對象的所有屬性和方法, 它返回一個包含字符串的list
print(dir(tom))
# [
#   '_Student3__age', '_Student3__name',
#   '__class__', '__delattr__',
#   '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__',
#   '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__',
#   '__le__', '__lt__', '__module__', '__name', '__ne__', '__new__', '__reduce__',
#   '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__',
#   '__weakref__',
#   'get_age', 'get_grade', 'get_name', 'set_age', 'set_name'
# ]

# 配合getattr()、setattr()以及hasattr(),我們可以直接操作一個對象的狀態
print(hasattr(tom, '__name'))       # False
setattr(tom, 'score', 19)
print(getattr(tom, 'score'))        # 19

# 實例屬性和類屬性
# 給實例綁定屬性的方法是通過實例變量,或者通過self變量:
jack.instance_attr = 58     # 實例屬性,其他實例是無法訪問的
jack.get_name()         # 訪問的是類屬性,所有實例均可以訪問

更多更及時的博客更新請戳—> KingRumn

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