1. Programs are composed of modules.
2. Modules contain statements.
3. Statements contain expressions.
4. Expressions create and process objects.html
Goto: http://www.runoob.com/python3/python3-class.htmlpython
Ref: [Advanced Python] 11 - Implement a Classc++
[面向對象]express
1、類的定義api
一個類,構造方法,self參數,「私有」屬性&方法,數組
2、類的字典屬性app
去查詢:屬性&方法 getattr()、setattr() 以及 hasattr()。函數
去模擬:字典的用法 __getattr__、__setattr__ 以及 __delattr__。post
3、類的實例this
實例的屬性,類的屬性
類的函數化:__call__
[類的繼承]
1、單繼承
調用父類的構造函數:<父類名>.__init__(self, <參數>)
2、多繼承
把「父類名」改成「子類的父親」:super(<子類名>, <類的實例>).__init__(self, *args, **kwargs)
3、類方法覆蓋
多態的運用,甚至支持「鴨子類型'。
[類的」變量化「]
1、類的屬性
也就是類的字典列表所得結果。
2、裝飾器
@property, @xxx.setter,當作變量同樣去使用。
3、重載運算符
類內部屬性示範。
數據封裝(Encapsulation )、繼承(inheritance )和多態(polymorphism)是面向對象的三大特色。
#!/usr/bin/python3 class MyClass: """一個簡單的類實例"""
i= 12345 def f(self): return 'hello world'
# 實例化類 x = MyClass() # 訪問類的屬性和方法 print("MyClass 類的屬性 i 爲:", x.i) print("MyClass 類的方法 f 輸出爲:", x.f())
賦值的的過程,就自動完成了類內的變量定義。
#!/usr/bin/python3 class Complex: def __init__(self, realpart, imagpart): self.r = realpart self.i = imagpart
x = Complex(3.0, -4.5) print(x.r, x.i) # 輸出結果:3.0 -4.5
類的方法與普通的函數只有一個特別的區別——它們必須有一個額外的第一個參數名稱。
記錄了 「類的地址」,「類的屬性」 等。
class Test: def prt(self): print(self) # address print(self.__class__) # name t = Test() t.prt()
執行結果:
<__main__.Test instance at 0x100771878> __main__.Test
注意:self 的名字並非規定死的,也可使用 this,可是最好仍是按照約定使用 self。
構造方法給私有屬性賦值。
加上雙下劃線定義屬性爲私有屬性。
#!/usr/bin/python3 #類定義 class people: #定義基本屬性 name = '' age = 0
#定義私有屬性,私有屬性在類外部沒法直接進行訪問 __weight = 0
#定義構造方法 def __init__(self,n,a,w): self.name = n self.age = a self.__weight = w def speak(self): print("%s 說: 我 %d 歲。" %(self.name, self.age)) # 實例化類 p = people('runoob',10,30) p.speak()
加上雙下劃線定義屬性爲私有方法。
#!/usr/bin/python3 class Site: def __init__(self, name, url): self.name = name # public self.__url = url #private def who(self): print('name : ', self.name) print('url : ', self.__url) def __foo(self): # 私有方法 print('這是私有方法') def foo(self): # 公共方法 print('這是公共方法') self.__foo() x = Site('菜鳥教程', 'www.runoob.com') x.who() # 正常輸出 x.foo() # 正常輸出 x.__foo() # 報錯
雙下劃線開頭的實例變量是否是必定不能從外部訪問呢?其實也不是。不能直接訪問__name
是由於Python解釋器對外把__name
變量改爲了_Student__name
,因此,仍然能夠經過_Student__name
來訪問__name
變量:
>>> bart._Student__name 'Bart Simpson'
可是強烈建議你不要這麼幹,由於不一樣版本的Python解釋器可能會把__name
改爲不一樣的變量名。
總的來講就是,Python自己沒有任何機制阻止你幹壞事,一切全靠自覺。
最後注意下面的這種錯誤寫法:__name與內部解釋後的_stduent__name可不是一個變量了。
>>> bart = Student('Bart Simpson', 59) >>> bart.get_name() 'Bart Simpson' >>> bart.__name = 'New Name' # 設置__name變量! >>> bart.__name 'New Name'
僅僅把屬性和方法列出來是不夠的,配合getattr()
、setattr()
以及hasattr()
,咱們能夠直接操做一個對象的狀態:
>>> 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
若是咱們在Class沒有定義這3個方法,那麼系統會用Python自帶的內部函數;
若是咱們在類裏面自定義了這3個函數,那麼python會先調用咱們本身定義的這3個函數。
Ref: c到底幹了什麼?
這裏要當心「無限循環」!須要經過__dict__操做。
class Cat: class_level = '貴族' def __init__(self,name,type,speed,age): self.name = name self.type = type self.speed = speed self.age = age def run(self): print('%s歲的%s%s正在以%s的速度奔跑' % (self.age, self.type, self.name, self.speed)) def __getattr__(self, item): print('你找的屬性不存在') def __setattr__(self, key, value): print('你在設置屬性') # self.key=value #這種方法不行,會產生無限遞歸了,由於他自己self.key=value也會觸發__setattr__ self.__dict__[key] = value #咱們在給對象屬性賦值的時候,內部原理就是操做對象的__dict__字典,因此咱們能夠直接操做對象的字典實現屬性賦值 #刪除屬性的時候會觸發 def __delattr__(self, item): print('你在刪除屬性') # del self.item #無限遞歸了,和上面的__setattr__原理同樣 self.__dict__.pop(item) #實例化,會到__init__函數裏去給實例數據屬性賦值 xiaohua = Cat('小花','波斯貓','10m/s',10)
能夠把一個類的全部屬性和方法調用所有動態化處理了,不須要任何特殊手段。這種徹底動態調用的特性有什麼實際做用呢?
做用就是,能夠針對徹底動態的狀況做調用:
class Chain(object): def __init__(self, path=''): self._path = path def __getattr__(self, path): return Chain('%s/%s' % (self._path, path)) # 有點遞歸的意思 def __str__(self): return self._path __repr__ = __str__ # 不返回地址,返回string; __repr__能夠把對象轉變爲字符串的形式
Output:
>>> Chain().status.user.timeline.list /status/user/timeline/list
>>> Chain().status.user /status/user
>>> Chain().status /status
動態語言的特色,二者不是一個東西。
>>> 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
class Student(object): def __init__(self, name): self.name = name def __call__(self): print('My name is %s.' % self.name)
>>> s = Student('Michael') >>> s() # self參數不要傳入 My name is Michael.
那麼,怎麼判斷一個變量是對象仍是函數呢?
>>> callable(Student()) True >>> callable(max) True >>> callable([1, 2, 3]) False >>> callable(None) False >>> callable('str') False
#!/usr/bin/python3 # 父類定義 class people: #定義基本屬性 name = '' age = 0 #定義私有屬性,私有屬性在類外部沒法直接進行訪問 __weight = 0
#定義構造方法 def __init__(self,n,a,w): self.name = n self.age = a self.__weight = w
def speak(self): print("%s 說: 我 %d 歲。" %(self.name,self.age))
#單繼承示例 class student(people): grade = ''
def __init__(self, n, a, w, g): #調用父類的構函 people.__init__(self,n,a,w) # <---- 這個方法可能不是很好 self.grade = g
#覆寫父類的方法 def speak(self): print("%s 說: 我 %d 歲了,我在讀 %d 年級"%(self.name,self.age,self.grade)) s = student('ken',10,60,3) s.speak()
(1) 如果父類中有相同的方法名,而在子類使用時未指定,python 從左至右 搜索 即方法在子類中未找到時,從左到右查找父類中是否包含方法。
(2) 子類不重寫 __init__,實例化子類時,會自動調用父類定義的 __init__。
#!/usr/bin/python3 #類定義 class people: #定義基本屬性 name = '' age = 0 #定義私有屬性,私有屬性在類外部沒法直接進行訪問 __weight = 0 #定義構造方法 def __init__(self,n,a,w): self.name = n self.age = a self.__weight = w def speak(self): print("%s 說: 我 %d 歲。" %(self.name,self.age))
#單繼承示例 class student(people): grade = '' def __init__(self,n,a,w,g): #調用父類的構函 people.__init__(self,n,a,w) self.grade = g
#覆寫父類的方法 def speak(self): print("%s 說: 我 %d 歲了,我在讀 %d 年級"%(self.name,self.age,self.grade)) #另外一個類,多重繼承以前的準備 class speaker(): topic = '' name = '' def __init__(self,n,t): self.name = n self.topic = t def speak(self): print("我叫 %s,我是一個演說家,我演講的主題是 %s"%(self.name,self.topic))
#多重繼承 class sample(speaker, student): # <---- 多繼承 a ='' def __init__(self,n,a,w,g,t): student.__init__(self, n, a, w, g) speaker.__init__(self, n, t) test = sample("Tim",25,80,4,"Python") test.speak() #方法名同,默認調用的是在括號中排前面的父類的方法
鑽石繼承時,c++中採用虛函數解決;python 怎麼辦?
參見高級部分:[Advanced Python] 11 - Implement a Class
class Father(object): def __init__(self, name, *args, **kwargs): self.name = name print("我是父類__init__")
class Son_1(Father): def __init__(self, name, age, *args, **kwargs): print("我是Son_1的__init__") super(Son_1, self).__init__(name, *args, **kwargs) self.age = age class Son_2(Father): def __init__(self, name, gender, *args, **kwargs): print("我是Son_2的__init__") self.gender = gender super(Son_2, self).__init__(name, *args, **kwargs)
class GrandSon(Son_1, Son_2): def __init__(self, name, age, gender): super(GrandSon, self).__init__(name, age, gender) def say_hello(self): print(self.name, self.age, self.gender) grand_son = GrandSon("老王", 24, "男")
經過 super(<子類名>, <子類對象>).<父類方法> 調用「被覆蓋的」 父方法。
#!/usr/bin/python3 class Parent: # 定義父類 def myMethod(self): print ('調用父類方法') class Child(Parent): # 定義子類 def myMethod(self): print ('調用子類方法') c = Child() # 子類實例 c.myMethod() # 子類調用重寫方法
super(Child, c).myMethod() #用子類對象調用父類已被覆蓋的方法
輸出:
調用子類方法
調用父類方法
類構造方法 被覆蓋
這裏提供了兩種方式。
(1)
super(子類,self).__init__(參數1,參數2,....)
(2) 父類名稱.__init__(self, 參數1,參數2,...)
animal做爲父類,支持子類做爲參數。
def run_twice(animal): # 參數用的是」父類「,也能夠直接帶入"其子類" animal.run() animal.run()
動態語言支持「鴨子類型」,具有「必要的」方法就能湊活的用了。
對於Python這樣的動態語言來講,則不必定須要傳入Animal
類型。咱們只須要保證傳入的對象有一個run()
方法就能夠了:
class Timer(object): def run(self): print('Start...')
__init__ : 構造函數,在生成對象時調用 __del__ : 析構函數,釋放對象時使用
__repr__ : 打印,轉換 __setitem__ : 按照索引賦值 __getitem__ : 按照索引獲取值 __len__ : 得到長度 __cmp__ : 比較運算 __call__: 函數調用
__add__ : 加運算 __sub__ : 減運算 __mul__ : 乘運算 __div__ : 除運算 __mod__ : 求餘運算 __pow__ : 乘方
過去的策略
分別定義set, get函數操做。
裝飾器策略
將函數當作屬性來使用。
對外的api是 score 和 name。對內的變量實則 _score 和 _name。
class student(object): #新式類 def __init__(self,id): self.__id=id @property #只讀 def score(self): return self._score @score.setter #只寫 def score(self, value): if not isinstance(value,int): raise ValueError('score must be an integer!') if value<0 or value>100: raise ValueError('score must between 0 and 100') self._score=value @property #只讀 def get_id(self): return self.__id
s=student('123456') s.score=100 # 寫 print(s.score) # 讀 print(s.__dict__) print (s.get_id) # 只讀 #s.get_id=456 #只能讀,不可寫: AttributeError: can't set attribute
================================================================
class A(object): # 新式類(繼承自object類) def __init__(self): self.__name=None def getName(self): return self.__name def setName(self,value): self.__name=value def delName(self): del self.__name name=property(getName, setName, delName) a=A() print(a.name) # 讀 a.name='python' # 寫 print(a.name) # 讀 del a.name # 刪除 #print a.name #a.name已經被刪除 AttributeError: 'A' object has no attribute '_A__name'
再一個例子:展現deleter的使用。
class test1: #經典類:沒有繼承object def __init__(self): self.__private='alex 1' #私有屬性以2個下劃線開頭 #讀私有屬性 @property def private(self): return self.__private #嘗試去寫私有屬性(對於經典類而言,「寫」是作不到的) @private.setter def private(self,value): self.__private=value #嘗試去刪除私有屬性(對於經典類而言,「刪除」也是作不到的) @private.deleter
def private(self): del self.__private ==================================================== class test2(object):# 新式類:繼承了object def __init__(self): self.__private='alex 2' #私有屬性以2個下劃線開頭 #讀私有屬性 @property def private(self): return self.__private #寫私有屬性 @private.setter def private(self,value): self.__private=value #刪除私有屬性 @private.deleter def private(self): del self.__private
t1=test1() #print t1.__private # 外界不可直接訪問私有屬性 print (t1.private) # 讀私有屬性 print (t1.__dict__) t1.private='change 1' #對於經典類來講,該語句其實是改變了實例t1的實例變量private print (t1.__dict__) print (t1.private) # 輸出剛剛添加的實例變量private t1.private='change 2' print (t1.__dict__) del t1.private # 刪除剛剛添加的實例變量private print (t1.__dict__) #print (t1.private) #讀私有屬性,由於已經刪除,因此這裏會報錯
print ('-------------------------------------------------------') t2=test2() print (t2.__dict__) print (t2.private) # 繼承了object,添加@private.setter後,才能夠寫 t2.private='change 2' # 修改私有屬性 print (t2.__dict__) print (t2.private) del t2.private #刪除私有變量 #print t2.private #私有變量已經被刪除,執行「讀」操做會報錯:AttributeError: 'test2' object has no attribute '_test2__private' print (t2.__dict__)
關鍵字:__add__
#!/usr/bin/python3 class Vector: def __init__(self, a, b): self.a = a self.b = b def __str__(self): return 'Vector (%d, %d)' % (self.a, self.b) def __add__(self, other): return Vector(self.a + other.a, self.b + other.b)
v1 = Vector(2,10) v2 = Vector(5,-2) print (v1 + v2)
執行結果:
Vector(7,8)
打印類(的字符串)
知識點:直接顯示變量調用的不是__str__()
,而是__repr__()
,二者的區別是__str__()
返回用戶看到的字符串,而__repr__()
返回程序開發者看到的字符串,也就是說,__repr__()
是爲調試服務的。
class people: def __init__(self,name,age): self.name=name self.age=age def __str__(self): return '這我的的名字是%s,已經有%d歲了!'%(self.name,self.age) a=people('孫悟空',999) print(a)
執行結果:
這我的的名字是孫悟空,已經有999歲了!
# 若是沒有重載函數的話輸出的就是一串看不懂的字符串: # <__main__.people object at 0x00000272A730D278>
以前是經過函數實現;這裏則是「類的形式」,看上去比較好一些。
class Fib(object): def __init__(self): self.a, self.b = 0, 1 # 初始化兩個計數器a,b def __iter__(self): return self # 實例自己就是迭代對象,故返回本身 def __next__(self): self.a, self.b = self.b, self.a + self.b # 計算下一個值 if self.a > 100000: # 退出循環的條件 raise StopIteration() return self.a # 返回下一個值
# Fib()返回的是本身自己;迭代的則是__next__提供的結果。 >>> for n in Fib(): ... print(n) ... 1 1 2 3 5 ... 46368 75025
本質上仍是經過從新計算得出結果。
class Fib(object): def __getitem__(self, n): a, b = 1, 1 for x in range(n): a, b = b, a + b return a >>> f = Fib() >>> f[0] 1 >>> f[1] 1 >>> f[2] 2 >>> f[3] 3 >>> f[10] 89 >>> f[100] 573147844013817084101
class Fib(object): def __getitem__(self, n):
if isinstance(n, int): # n是索引 a, b = 1, 1 for x in range(n): a, b = b, a + b return a
if isinstance(n, slice): # n是切片 start = n.start stop = n.stop if start is None: start = 0 a, b = 1, 1 L = [] for x in range(stop): if x >= start: L.append(a) a, b = b, a + b return L >>> f = Fib() >>> f[0:5] [1, 1, 2, 3, 5] >>> f[:10] [1, 1, 2, 3, 5, 8, 13, 21, 34, 55]
與之對應的是__setitem__()
方法,把對象視做list或dict來對集合賦值。最後,還有一個__delitem__()
方法,用於刪除某個元素。
End.