2018-6-6
序
面向對象編程——Object Oriented Programming,簡稱OOP。
程序的基本單元:對象
對象:包含了數據和操作數據的函數
在Python中,萬物皆爲對象。(程序員後宮佳麗三千喲)
所有的數據類型都可視爲對象,
對象也可以是自己創造的。
自己創造—自定義的對象數據類型就是類(class)。
Class是一種抽象概念,比如我們定義的Class——Student,是指學生這個概念,而實例(Instance)則是一個個具體的Student。
面向對象的設計思想是抽象出Class,根據Class創建Instance。
類和實例
class Student(object):
pass
class
後面跟的是類名——Student
,注意,類名通常是大寫開頭的單詞。
後面的(object)
,表示這個類是從哪個類繼承下來的。如果沒有合適的繼承類,就使用object
類,這是所有類最終都會繼承的類。【想想樹枝】
類定義以後就可以創建其實例:
bart = Student()
變量bart
指向的就是一個Student
的實例。
可以給實例變量bart
綁定一個屬性name
:
bart.name = 'Bart Simpson'
啊,類就像女媧捏泥人兒的模子,女媧捏人兒(創建實例)的時候,就得把一些個她認爲必須綁定的屬性強制填寫進去。那她就用了__init__
方法:
class Student(object):
def __init__(self, name, score):
self.name = name
self.score = score
應當注意,
注意到__init__
方法的第一個參數永遠是self
,表示創建的實例本身,因此,在__init__
方法內部,就可以把各種屬性綁定到self,因爲self就指向創建的實例本身。
數據封裝
一個實例本身是擁有一些屬性的數據的,可以通過函數來訪問。
但是可以通過Student
類的內部定義訪問數據的函數,這樣子,就相當於把數據給裝到黑匣子了嘛~並且這些封裝數據的函數是和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))
數據封裝的第二個好處是:
可以給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'
2018-6-7
訪問限制
在Python中,實例的變量名如果以__
開頭,就變成了一個私有變量(private),只有內部可以訪問,外部不能訪問。
def print_score(self):
print('%s: %s' % (self.__name, self.__score))
啊,這樣的話連訪問都不可以了,如果需要訪問的話需要給Student類增加get_name
和get_score
這樣的方法。
class Student(object):
...
def get_name(self):
return self.__name
def get_score(self):
return self.__score
這時候腦子抽了又想允許外部代碼修改score了(事兒多得很),這時候可以增加set_score
方法。
class Student(object):
...
def set_score(self, score):
self.__score = score
繼承和多態
在OOP中,我們可以通過一個現有的class繼承來定義一個class,這個新的class就稱爲子類——subclass,現有的被繼承的class稱爲基類、父類或者超類——Base class、Super class。
class Animal(object):
def run(self):
print('Animal is running...')
class Dog(Animal):
pass
class Cat(Animal):
pass
上面的例子中,Animal
是父類,Dog
和Cat
就是子類。
這樣做的目的是,子類擁有了父類全部的功能——在本例中實現了run()
方法。
2018-6-9
北京,雨
多態的好處:
對於一個變量,我們只需要知道它是什麼父類型,而無需知道它的子類型,就可以使用其方法——調用方只需要調用,而不需要知道細節。在增加一種子類時,只需要保證方法正確,不用管原來的代碼是如何調用的。也就是“開閉原則”:
對擴展開放:允許新增
Animal
子類;
對修改封閉:不需要修改依賴Animal
類型的run_twice()
等函數。
從具體理解上看,繼承就像是樹一樣:
靜態語言 vs 動態語言
對於靜態語言(例如Java)來說,如果需要傳入
Animal
類型,則傳入的對象必須是Animal
類型或者它的子類,否則,將無法調用run()
方法。
對於Python這樣的動態語言來說,則不一定需要傳入Animal
類型。我們只需要保證傳入的對象有一個run()
方法就可以了:
class Timer(object): def run(self): print('Start...')
獲取對象信息
type()
判斷對象是哪種基本數據類型,還是函數
>>> type(123)==int
True
>>> 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
isinstance()
原文稱:對於class的繼承關係來說,使用type()
不方便。
爲什麼呢?
Python type() 函數中談及isinstance()
與type()
區別:
- type() 不會認爲子類是一種父類類型,不考慮繼承關係。
- isinstance() 會認爲子類是一種父類類型,考慮繼承關係。
如果要判斷兩個類型是否相同推薦使用 isinstance()。
dir()
可以獲取一個對象的所有屬性和方法。
>>> dir('ABC')
['__add__', '__class__',..., '__subclasshook__', 'capitalize', 'casefold',..., 'zfill']
配合getattr()
、setattr()
以及hasattr()
,可以直接操作一個對象的狀態。
getattr()
:用於返回一個對象的屬性值——getattr(object, name[, default])
setattr()
:用於設置屬性值,該屬性必須存在——setattr(object, name, value)
hasattr()
:用於判斷對象是否包含對應的屬性——hasattr(object, name)
實例屬性和類屬性
實例屬性屬於各個實例所有,互不干擾;
類屬性屬於類所有,所有實例共享一個屬性;
不要對實例屬性和類屬性使用相同的名字,否則將產生難以發現的錯誤。
爲了統計學生人數,可以給Student類增加一個類屬性,每創建一個實例,該屬性自動增加:
class Student(object):
count = 0
def __init__(self, name):
self.name = name
Student.count += 1