【Python】【整理】廖雪峯Python教程代碼整理——7、面向對象編程

7 面向對象編程

7.1 類和實例

定義類是通過class關鍵字:

class Student(object):
    pass

創建實例是通過類名+()實現的:

>>> bart = Student()
>>> bart
<__main__.Student object at 0x10a67a590>
>>> Student
<class '__main__.Student'>

給一個實例變量綁定屬性,比如,給實例bart綁定一個name屬性:

>>> bart.name = 'Bart Simpson'
>>> bart.name
'Bart Simpson'

通過定義一個特殊的__init__方法,在創建實例的時候,就把name,score等屬性綁上去:

class Student(object):

    def __init__(self, name, score):
        self.name = name
        self.score = score

7.1.1 數據封裝

通過函數來訪問數據,比如打印一個學生的成績:

>>> def print_score(std):
...     print('%s: %s' % (std.name, std.score))
...
>>> print_score(bart)
Bart Simpson: 59

直接在Student類的內部定義訪問數據的函數:

class Student(object):

    def __init__(self, name, score):
        self.name = name
        self.score = score

    def print_score(self):
        print('%s: %s' % (self.name, self.score))

調用:

>>> bart.print_score()
Bart Simpson: 59

給Student類增加新的方法,比如get_grade:

class Student(object):
    ...

    def get_grade(self):
        if self.score >= 90:
            return 'A'
        elif self.score >= 60:
            return 'B'
        else:
            return 'C'

7.2 訪問限制

把屬性的名稱前加上兩個下劃線__,就變成了一個私有變量(private):

class Student(object):

    def __init__(self, name, score):
        self.__name = name
        self.__score = score

    def print_score(self):
        print('%s: %s' % (self.__name, self.__score))

要獲取name和score,可以給Student類增加get_name和get_score這樣的方法:

class Student(object):
    ...

    def get_name(self):
        return self.__name

    def get_score(self):
        return self.__score

允許外部代碼修改score,可以再給Student類增加set_score方法:

class Student(object):
    ...

    def set_score(self, score):
        self.__score = score

在方法中,可以對參數做檢查,避免傳入無效的參數:

class Student(object):
    ...

    def set_score(self, score):
        if 0 <= score <= 100:
            self.__score = score
        else:
            raise ValueError('bad score')

變量名類似__xxx__的,是特殊變量,特殊變量是可以直接訪問的

以一個下劃線開頭的實例變量名,比如_name,視爲私有變量,不要隨意訪問

7.3 繼承和多態

編寫名爲Animal的class,有一個run()方法可以直接打印:

class Animal(object):
    def run(self):
        print('Animal is running...')

編寫Dog和Cat類時,就可以直接從Animal類繼承:

class Dog(Animal):
    pass

class Cat(Animal):
    pass

子類和父類都存在相同的run()方法時,子類的run()覆蓋了父類的run()——多態

判斷一個變量是否是某個類型可以用isinstance()判斷:

>>> isinstance(a, list)
True
>>> isinstance(b, Animal)
True
>>> isinstance(c, Dog)
True

7.3.1 靜態語言 vs 動態語言

7.4 獲取對象信息

7.4.1 使用 type()

基本類型都可以用type()判斷:

>>> type(123)
<class 'int'>
>>> type('str')
<class 'str'>
>>> type(None)
<type(None) 'NoneType'>

如果一個變量指向函數或者類,也可以用type()判斷:

>>> type(abs)
<class 'builtin_function_or_method'>
>>> type(a)
<class '__main__.Animal'>

判斷基本數據類型可以直接寫int,str等:

>>> type(123)==type(456)
True
>>> type(123)==int
True
>>> type('abc')==type('123')
True
>>> type('abc')==str
True
>>> type('abc')==type(123)
False

判斷一個對象是否是函數,可以使用types模塊中定義的常量:

>>> import types
>>> def fn():
...     pass
...
>>> type(fn)==types.FunctionType
True
>>> type(abs)==types.BuiltinFunctionType
True
>>> type(lambda x: x)==types.LambdaType
True
>>> type((x for x in range(10)))==types.GeneratorType
True

7.4.2 使用 isinstance()

對於class的繼承關係,可以使用isinstance()函數:

如果繼承關係是:
object -> Animal -> Dog -> Husky

>>> a = Animal()
>>> d = Dog()
>>> h = Husky()

>>> isinstance(h, Husky)
True

>>> isinstance(h, Dog)
True

>>> isinstance(d, Husky)
False

能用type()判斷的基本類型也可以用isinstance()判斷:

>>> isinstance('a', str)
True
>>> isinstance(123, int)
True
>>> isinstance(b'a', bytes)
True

判斷一個變量是否是某些類型中的一種:

>>> isinstance([1, 2, 3], (list, tuple))
True
>>> isinstance((1, 2, 3), (list, tuple))
True

7.4.3 使用dir()

獲得一個對象的所有屬性和方法:

>>> dir('ABC')
['__add__', '__class__',..., '__subclasshook__', 'capitalize', 'casefold',..., 'zfill']

筆者補充換行打印 dir(以Student爲例):

class Student(object):

    def __init__(self, name, score):
        self.__name = name
        self.__score = score

    def print_score(self):
        print('%s: %s' % (self.__name, self.__score))

k = Student('KK', 90)

L = dir(k)

for i in range(1,len(L)):
    print(L[i])

結果如下:

G:\PythonLearning\venv\Scripts\python.exe G:/PythonLearning/venv/Class.py
_Student__score
__class__
__delattr__
__dict__
__dir__
__doc__
__eq__
__format__
__ge__
__getattribute__
__gt__
__hash__
__init__
__init_subclass__
__le__
__lt__
__module__
__ne__
__new__
__reduce__
__reduce_ex__
__repr__
__setattr__
__sizeof__
__str__
__subclasshook__
__weakref__
print_score

測試對象的屬性:

>>> class MyObject(object):
...     def __init__(self):
...         self.x = 9
...     def power(self):
...         return self.x * self.x
...
>>> obj = MyObject()

>>> hasattr(obj, 'x') # 有屬性'x'嗎?
True
>>> obj.x
9
>>> hasattr(obj, 'y') # 有屬性'y'嗎?
False
>>> setattr(obj, 'y', 19) # 設置一個屬性'y'
>>> hasattr(obj, 'y') # 有屬性'y'嗎?
True
>>> getattr(obj, 'y') # 獲取屬性'y'
19
>>> obj.y # 獲取屬性'y'
19

傳入一個default參數,如果屬性不存在,就返回默認值:

getattr(obj, ‘z’, 404) # 獲取屬性’z’,如果不存在,返回默認值404
404

獲得對象的方法:

>>> hasattr(obj, 'power') # 有屬性'power'嗎?
True
>>> getattr(obj, 'power') # 獲取屬性'power'
<bound method MyObject.power of <__main__.MyObject object at 0x10077a6a0>>
>>> fn = getattr(obj, 'power') # 獲取屬性'power'並賦值到變量fn
>>> fn # fn指向obj.power
<bound method MyObject.power of <__main__.MyObject object at 0x10077a6a0>>
>>> fn() # 調用fn()與調用obj.power()是一樣的
81

7.4.4 小結

一個正確的用法的例子:

def readImage(fp):
    if hasattr(fp, 'read'):
        return readData(fp)
    return None

7.5 實例屬性和類屬性

通過實例變量給實例綁定屬性,或者通過self變量:

class Student(object):
    def __init__(self, name):
        self.name = name

s = Student('Bob')
s.score = 90

直接在class中定義屬性,是類屬性:

class Student(object):
    name = 'Student'
>>> class Student(object):
...     name = 'Student'
...
>>> s = Student() # 創建實例s
>>> print(s.name) # 打印name屬性,因爲實例並沒有name屬性,所以會繼續查找class的name屬性
Student
>>> print(Student.name) # 打印類的name屬性
Student
>>> s.name = 'Michael' # 給實例綁定name屬性
>>> print(s.name) # 由於實例屬性優先級比類屬性高,因此,它會屏蔽掉類的name屬性
Michael
>>> print(Student.name) # 但是類屬性並未消失,用Student.name仍然可以訪問
Student
>>> del s.name # 如果刪除實例的name屬性
>>> print(s.name) # 再次調用s.name,由於實例的name屬性沒有找到,類的name屬性就顯示出來了
Student
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章