python學習筆記05-python面向物件程式設計
1.面向物件程式設計
1. 類和例項
注意,類的所有方法,的第一個引數一定是self。
在呼叫類的方法時,不需要傳入第一個引數self。#!/usr/bin/env python # -*- coding: utf-8 -*- #定義一個類Student,繼承自object class Student(object): #構造器 def __init__(self,name,score): #將name和score儲存的私有成員變數中 self.__name = name self.__score = score #定義一個公有的show方法 def show(self): print 'name =%s,score =%.2f' %(self.__name,self.__score) s = Student(18,99.5) s.show() #用這種方式去訪問類中私有的變數,是非常不建議的。因為,在不同的python版本中,可能會不同。 #正確的做法應該是新增setter和getter方法。 #print s._Student__name
2.繼承和多型
#!/usr/bin/env python # -*- coding: utf-8 -*- class Person(object): def show(self): print 'Person show' #繼承Person類 class Student(Person): def show(self): print 'Student show' #多型 def run(p): p.show() run(Person()) run(Student())
3. 獲取物件資訊
判斷物件型別,使用type()函式
Python把每種type型別都定義好了常量,放在types模組裡,使用之前,需要先匯入:types模組
所有型別本身的型別就是TypeType#!/usr/bin/env python # -*- coding: utf-8 -*- class Person(object): def show(self): print 'Person show' class Student(Person): def show(self): print 'Student show' p = Person() s = Student() print type(123) print type('abc') print type(int) print type(p) print type(s) import types print type('abc') == types.StringType
執行結果
<type 'int'> <type 'str'> <type 'type'> <class '__main__.Person'> <class '__main__.Student'> True
4.isinstance詳解
#!/usr/bin/env python # -*- coding: utf-8 -*- class Person(object): def show(self): print 'Person show' class Student(Person): def show(self): print 'Student show' p = Person() s = Student() #判斷是否是某個類的例項 print isinstance(p,Person) print isinstance(s,Person) print isinstance(p,Student) #判斷是否是某兩個類中任意一個類的例項 print isinstance(p,(Student,Person)) #判斷是str或者unicode型別中的任意一個 print isinstance('a',(str,unicode)) print isinstance(u'a',(str,unicode)) #由於str和unicode都是從basestring繼承下來的,所以,還可以把上面的程式碼簡化為: print isinstance(u'a',basestring)
5.使用dir()
如果要獲得一個物件的所有屬性和方法,可以使用dir()函式,它返回一個包含字串的list
類似xxx的屬性和方法在Python中都是有特殊用途的,比如len方法返回長度。在Python中,如果你呼叫len()函式試圖獲取一個物件的長度,實際上,在len()函式內部,它自動去呼叫該物件的len()方法
getattr()、setattr()以及hasattr()
如果試圖獲取不存在的屬性,會丟擲AttributeError的錯誤。可以傳入一個default引數,如果屬性不存在,就返回預設值#!/usr/bin/env python # -*- coding: utf-8 -*- class Person(object): def show(self): print 'Person show' p = Person() print dir(Person) == dir(p) #獲取物件的所有屬性和方法 dir(p) #呼叫len('ABC')等同於呼叫'ABC'.__len__() print len('ABC') print 'ABC'.__len__() #判斷物件是否包含某個屬性,如果不包含,則新增則屬性 if hasattr(p,'score'): print p.score else: setattr(p,'score',65.5) print p.score def err(): print 'no method' #獲取一個物件的某個屬性,並傳入預設值。 fn1 = getattr(p,'show',err) fn2 = getattr(p,'show2',err) fn1() fn2()
2.面向物件高階程式設計
1.為物件或者類動態繫結屬性或者方法
#!/usr/bin/env python # -*- coding: utf-8 -*- class Person(object): def show(self): print 'Person show' p = Person() #為物件動態繫結屬性 p.name = 'zhangsan' print p.name #匯入MethodType模組,以便為物件或類動態繫結方法 from types import MethodType def test(self): print 'test succ' self.show() #為物件動態繫結方法(此方法只會作用在當前物件上) p.test = MethodType(test,p,Person) p.test() #為類動態繫結方法(此方法會作用在所有物件上) Person.test = MethodType(test,None,Person) p2 = Person() p2.test(
2.使用slots限定class可以繫結的屬性
Python允許在定義class的時候,定義一個特殊的slots變數,來限制該class能新增的屬性
注意,子類不受父類的slots的限制
如果子類也定義了slots,那麼他可以新增的屬性為父類中slots定義的和子類自己的slotsclass Student(object): __slots__ = ('name', 'age') # 用tuple定義允許繫結的屬性名稱 ...省略...
[email protected]
作用:把一個方法變為屬性。(其實是簡化getter和setter的呼叫,讓getter和setter方法可以像直接操作屬性一樣簡單)
把一個getter方法變成屬性,只需要加上@property就可以了,此時,@property本身又建立了另一個裝飾器@score.setter,負責把一個setter方法變成屬性賦值#!/usr/bin/env python # -*- coding: utf-8 -*- class Student(object): @property def birth(self): return self.__birth @birth.setter def birth(self,value): self.__birth = value @property def age(self): return 2016 - self.__birth s = Student() s.birth = 1991 print 'birth =',s.birth print 'age =',s.age
4.多重繼承
在設計類的繼承關係時,通常,主線都是單一繼承下來的,例如,Ostrich繼承自Bird。但是,如果需要“混入”額外的功能,通過多重繼承就可以實現,比如,讓Ostrich除了繼承自Bird外,再同時繼承Runnable。這種設計通常稱之為Mixin。
#!/usr/bin/env python # -*- coding: utf-8 -*- class Animal(object): pass class Mammal(Animal): pass class Bird(Animal): pass class RunnableMixin(object): def run(self): print('Running...') class FlyableMixin(object): def fly(self): print('Flying...') #多重繼承(同時繼承自Mammal和RunnableMixin) class Dog(Mammal,RunnableMixin): pass #多重繼承(同時繼承自Mammal和FlyableMixin) class Bat(Mammal,FlyableMixin): pass d = Dog() b = Bat() d.run() b.fly()
5.定製類
1.str和repr-定製顯示資訊
#!/usr/bin/env python # -*- coding: utf-8 -*- class Student(object): #構造器 def __init__(self,name): self.__name = name #定製給使用者看的類資訊 #當呼叫print xxx時,會呼叫__str__方法 def __str__(self): return 'Student object (name:%s)' % self.__name #定製給程式設計師看的類資訊 #當在互動終端直接輸入xxx時,會呼叫__repr__方法 __repr__ = __str__ s = Student('zhangsan') print s
2.iter、next、getitem-讓一個物件成為可迭代物件
讓一個物件成為一個可以被迭代,可以使用下標索引元素,可以切片的物件。
#!/usr/bin/env python # -*- coding: utf-8 -*- 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 > 10000: raise StopIteration() return self.a #返回下一個值 def __getitem__(self,n): #對使用下標索引元素的支援 if isinstance(n,int): a,b = 1,1 for x in range(n): a,b = b,a+b return a #對切片方法的支援 if isinstance(n,slice): start = n.start stop = n.stop 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() for i in f: print i print '\n' print f[2] print '\n' print f[2:4]
3.getattr-訪問不存在的屬性或方法
#!/usr/bin/env python # -*- coding: utf-8 -*- class Student(object): #當且僅當物件的屬性或方法不存在的時候,才會呼叫getattr方法 def __getattr__(self,attr_name): if attr_name == 'age': return lambda:25 s = Student() print s.age()
4.call-把例項變數當函式呼叫
#!/usr/bin/env python # -*- coding: utf-8 -*- class Student(object): def __init__(self,age): self.__age = age def __call__(self,age): return age def getAge(self): return self.__age s = Student(18) #此時呼叫的為getAge方法 print s.getAge() #判斷例項變數是否可以直接當函式呼叫 if(callable(s)): #直接把物件當方法呼叫,則呼叫的為__call__方法 print s(29) else: print 'can not call instance'
6.使用type()函式建立一個新的型別
要建立一個class物件,type()函式依次傳入3個引數:
- class的名稱
- 繼承的父類集合,注意Python支援多重繼承,如果只有一個父類,別忘了tuple的單元素寫法;
- class的方法名稱與函式繫結
#!/usr/bin/env python # -*- coding: utf-8 -*- def fn(self,name='world'): #先定義函式 print('Hello,%s' % name) Hello = type('Hello',(object,),dict(hello=fn)) #建立Hello class h = Hello() h.hello() print(type(Hello)) print(type(h))
7.使用metaclass建立一個list類的子類
new方法接收到的引數依次是:
1. 當前準備建立的類的物件
2. 類的名字
3. 類繼承的父類集合
4. 類的方法集合#!/usr/bin/env python # -*- coding: utf-8 -*- # metaclass是建立類,所以必須從'type'型別派生 class ListMetaclass(type): def __new__(cls,name,bases,attrs): attrs['add'] = lambda self,value: self.append(value) return type.__new__(cls,name,bases,attrs) class MyList(list): __metaclass__ = ListMetaclass #指示使用ListMetaclass來定製類 L = MyList() L.add(1) L.add('aa') print L
8.ORM——metaclass使用場景
當用戶定義一個class User(Model)時,Python直譯器首先在當前類User的定義中查詢metaclass,如果沒有找到,就繼續在父類Model中查詢metaclass,找到了,就使用Model中定義的metaclass的ModelMetaclass來建立User類,也就是說,metaclass可以隱式的繼承到子類,但子類自己卻感覺不到。
在ModelMetaclass中,一共做了幾件事情:
1. 排除掉對Model類的修改
2. 在當前類(比如User)中查詢定義的類的所有屬性,如果找到一個Field屬性,就把它儲存到一個mappings的dict中,同時從類屬性中刪除該Field屬性,否則,容易執行時錯誤
3. 把表名儲存到table中,這裡簡化為表名預設為類名#!/usr/bin/env python # -*- coding: utf-8 -*- class Field(object): def __init__(self,name,column_type): self.name = name self.column_type = column_type def __str__(self): return '<%s:%s>' % (self.__class__.__name__,self.name) class StringField(Field): def __init__(self,name): super(StringField,self).__init__(name,'varchar(100)') class IntegerField(Field): def __init__(self,name): super(IntegerField,self).__init__(name,'bigint') class ModelMetaclass(type): def __new__(cls,name,bases,attrs): #當Model類建立時,會進入if條件中,然後使用type建立類後,直接返回Model類 if name=='Model': print '--->attrs:%s' % attrs return type.__new__(cls,name,bases,attrs) print '<--->attrs:%s' % attrs #當User類建立時,不會進入if條件,而會執行下面的邏輯 mappings = dict() #對User類中的所有屬性或者方法進行遍歷,發現有值為Field型別的屬性,則將屬性名以及此屬性的值(注意,屬性的值其實是一個Field的子類型別)加入到mappings中 #此Mappings用於記錄User類中每個屬性都是什麼型別的 for k,v in attrs.iteritems(): if isinstance(v,Field): print('Found mapping:%s==>%s' % (k,v)) mappings[k] = v #將User類中所有Field型別的屬性全部刪除 for k in mappings.iterkeys(): attrs.pop(k) #將__table__屬性的值設定為類名 attrs['__table__'] = name #假設表名和類名一致 #將__mappings__屬性的值設定為儲存有"列名<--->類型別"對映關係的mappings attrs['__mappings__'] = mappings #儲存屬性和列的對映關係 #修改過attrs,使用type建立一個類,並返回 return type.__new__(cls,name,bases,attrs)