Python--面向對象編程(2)
面向對象編程--進階
property屬性
property是一種特殊的屬性,訪問它時會執行一段功能(函數)然後返回值
import math class Circle: def __init__(self,radius): #圓的半徑radius self.radius=radius @property def area(self): return math.pi * self.radius**2 #計算面積 @property def perimeter(self): return 2*math.pi*self.radius #圓的周長和面積計算周長 c=Circle(10) print(c.radius) print(c.area) #可以向訪問數據屬性一樣去訪問area,會觸發一個函數的執行,動態計算出一個值 print(c.perimeter) #同上 ‘‘‘ 輸出結果: 314.1592653589793 62.83185307179586 ‘‘‘ #註意:此時的特性area和perimeter不能被賦值 c.area=3 #為特性area賦值 ‘‘‘ 拋出異常: AttributeError: can‘t set attribute ‘‘‘
為什麽要用property
將一個類的函數定義成特性以後,對象再去使用的時候obj.name,根本無法察覺自己的name是執行了一個函數然後計算出來的,這種特性的使用方式遵循了統一訪問的原則
ps:面向對象的封裝有三種方式:
【public】
這種其實就是不封裝,是對外公開的
【protected】
這種封裝方式對外不公開,但對朋友(friend)或者子類(形象的說法是“兒子”,但我不知道為什麽大家 不說“女兒”,就像“parent”本來是“父母”的意思,但中文都是叫“父類”)公開
【private】
這種封裝對誰都不公開
python並沒有在語法上把它們三個內建到自己的class機制中,在C++裏一般會將所有的所有的數據都設置為私有的,然後提供set和get方法(接口)去設置和獲取,在python中通過property方法可以實現
class Foo: def __init__(self,val): self.__NAME=val #將所有的數據屬性都隱藏起來 @property def name(self): return self.__NAME #obj.name訪問的是self.__NAME(這也是真實值的存放位置) @name.setter def name(self,value): if not isinstance(value,str): #在設定值之前進行類型檢查 raise TypeError(‘%s must be str‘ %value) self.__NAME=value #通過類型檢查後,將值value存放到真實的位置self.__NAME @name.deleter def name(self): raise TypeError(‘Can not delete‘) f=Foo(‘lee‘) print(f.name) # f.name=10 #拋出異常‘TypeError: 10 must be str‘ del f.name #拋出異常‘TypeError: Can not delete‘
一個靜態屬性property本質就是實現了get,set,delete三種方法
class Foo: @property def AAA(self): print(‘get的時候運行我啊‘) @AAA.setter def AAA(self,value): print(‘set的時候運行我啊‘) @AAA.deleter def AAA(self): print(‘delete的時候運行我啊‘) #只有在屬性AAA定義property後才能定義AAA.setter,AAA.deleter f1=Foo() f1.AAA f1.AAA=‘aaa‘ del f1.AAAView Code
class Foo: def get_AAA(self): print(‘get的時候運行我啊‘) def set_AAA(self,value): print(‘set的時候運行我啊‘) def delete_AAA(self): print(‘delete的時候運行我啊‘) AAA=property(get_AAA,set_AAA,delete_AAA) #內置property三個參數與get,set,delete一一對應 f1=Foo() f1.AAA f1.AAA=‘aaa‘ del f1.AAAView Code
class Goods: def __init__(self): # 原價 self.original_price = 100 # 折扣 self.discount = 0.8 @property def price(self): # 實際價格 = 原價 * 折扣 new_price = self.original_price * self.discount return new_price @price.setter def price(self, value): self.original_price = value @price.deleter def price(self): del self.original_price obj = Goods() obj.price # 獲取商品價格 obj.price = 200 # 修改商品原價 print(obj.price) del obj.price # 刪除商品原價示例
classmethod
類方法
class Classmethod_Demo(): role = ‘dog‘ @classmethod def func(cls): print(cls.role) Classmethod_Demo.func()
staticmethod
靜態方法
class Staticmethod_Demo(): role = ‘dog‘ @staticmethod def func(): print("當普通方法用") Staticmethod_Demo.func()
isinstance和issubclass
isinstance(obj,cls)檢查是否obj是否是類 cls 的對象
class Foo(object): pass obj = Foo() isinstance(obj, Foo)
issubclass(sub, super)檢查sub類是否是 super 類的派生類
class Foo(object): pass class Bar(Foo): pass issubclass(Bar, Foo)
反射
1 什麽是反射
反射的概念是由Smith在1982年首次提出的,主要是指程序可以訪問、檢測和修改它本身狀態或行為的一種能力(自省)。這一概念的提出很快引發了計算機科學領域關於應用反射性的研究。它首先被程序語言的設計領域所采用,並在Lisp和面向對象方面取得了成績。
2 python面向對象中的反射:通過字符串的形式操作對象相關的屬性。python中的一切事物都是對象(都可以使用反射)
四個可以實現自省的函數
下列方法適用於類和對象(一切皆對象,類本身也是一個對象)
def hasattr(*args, **kwargs): # real signature unknown """ Return whether the object has an attribute with the given name. This is done by calling getattr(obj, name) and catching AttributeError. """ passhasattr
def getattr(object, name, default=None): # known special case of getattr """ getattr(object, name[, default]) -> value Get a named attribute from an object; getattr(x, ‘y‘) is equivalent to x.y. When a default argument is given, it is returned when the attribute doesn‘t exist; without it, an exception is raised in that case. """ passgetattr
def setattr(x, y, v): # real signature unknown; restored from __doc__ """ Sets the named attribute on the given object to the specified value. setattr(x, ‘y‘, v) is equivalent to ``x.y = v‘‘ """ passsetattr
def delattr(x, y): # real signature unknown; restored from __doc__ """ Deletes the named attribute from the given object. delattr(x, ‘y‘) is equivalent to ``del x.y‘‘ """ passdelattr
class Foo: f = ‘類的靜態變量‘ def __init__(self,name,age): self.name=name self.age=age def say_hi(self): print(‘hi,%s‘%self.name) obj=Foo(‘egon‘,73) #檢測是否含有某屬性 print(hasattr(obj,‘name‘)) print(hasattr(obj,‘say_hi‘)) #獲取屬性 n=getattr(obj,‘name‘) print(n) func=getattr(obj,‘say_hi‘) func() print(getattr(obj,‘aaaaaaaa‘,‘不存在啊‘)) #報錯 #設置屬性 setattr(obj,‘sb‘,True) setattr(obj,‘show_name‘,lambda self:self.name+‘sb‘) print(obj.__dict__) print(obj.show_name(obj)) #刪除屬性 delattr(obj,‘age‘) delattr(obj,‘show_name‘) delattr(obj,‘show_name111‘)#不存在,則報錯 print(obj.__dict__)四個方法的使用演示
class Foo(object): staticField = "old boy" def __init__(self): self.name = ‘wupeiqi‘ def func(self): return ‘func‘ @staticmethod def bar(): return ‘bar‘ print getattr(Foo, ‘staticField‘) print getattr(Foo, ‘func‘) print getattr(Foo, ‘bar‘)類也是對象
import sys def s1(): print ‘s1‘ def s2(): print ‘s2‘ this_module = sys.modules[__name__] hasattr(this_module, ‘s1‘) getattr(this_module, ‘s2‘)反射當前模塊成員
導入其他模塊,利用反射查找該模塊是否存在某個方法
def test(): print(‘from the test‘)View Code
""" 程序目錄: module_test.py index.py 當前文件: index.py """ import module_test as obj #obj.test() print(hasattr(obj,‘test‘)) getattr(obj,‘test‘)()View Code
__str__和__repr__
改變對象的字符串顯示__str__,__repr__
自定制格式化字符串__format__
format_dict={ ‘nat‘:‘{obj.name}-{obj.addr}-{obj.type}‘,#學校名-學校地址-學校類型 ‘tna‘:‘{obj.type}:{obj.name}:{obj.addr}‘,#學校類型:學校名:學校地址 ‘tan‘:‘{obj.type}/{obj.addr}/{obj.name}‘,#學校類型/學校地址/學校名 } class School: def __init__(self,name,addr,type): self.name=name self.addr=addr self.type=type def __repr__(self): return ‘School(%s,%s)‘ %(self.name,self.addr) def __str__(self): return ‘(%s,%s)‘ %(self.name,self.addr) def __format__(self, format_spec): # if format_spec if not format_spec or format_spec not in format_dict: format_spec=‘nat‘ fmt=format_dict[format_spec] return fmt.format(obj=self) s1=School(‘oldboy1‘,‘北京‘,‘私立‘) print(‘from repr: ‘,repr(s1)) print(‘from str: ‘,str(s1)) print(s1) ‘‘‘ str函數或者print函數--->obj.__str__() repr或者交互式解釋器--->obj.__repr__() 如果__str__沒有被定義,那麽就會使用__repr__來代替輸出 註意:這倆方法的返回值必須是字符串,否則拋出異常 ‘‘‘ print(format(s1,‘nat‘)) print(format(s1,‘tna‘)) print(format(s1,‘tan‘)) print(format(s1,‘asfdasdffd‘))示例
class B: def __str__(self): return ‘str : class B‘ def __repr__(self): return ‘repr : class B‘ b=B() print(‘%s‘%b) print(‘%r‘%b)%s和%r
__del__
析構方法,當對象在內存中被釋放時,自動觸發執行。
註:此方法一般無須定義,因為Python是一門高級語言,程序員在使用時無需關心內存的分配和釋放,因為此工作都是交給Python解釋器來執行,所以,析構函數的調用是由解釋器在進行垃圾回收時自動觸發執行的。
class Foo: def __del__(self): print(‘執行我啦‘) f1=Foo() del f1 print(‘------->‘) #輸出結果 執行我啦 ------->示例
item系列
__getitem__/setitem__/__delitem__
class Foo: def __init__(self,name): self.name=name def __getitem__(self, item): print(self.__dict__[item]) def __setitem__(self, key, value): self.__dict__[key]=value def __delitem__(self, key): print(‘del obj[key]時,我執行‘) self.__dict__.pop(key) def __delattr__(self, item): print(‘del obj.key時,我執行‘) self.__dict__.pop(item) f1=Foo(‘lyq‘) f1[‘age‘]=18 f1[‘age1‘]=19 del f1.age1 del f1[‘age‘] f1[‘name‘]=‘lee‘ print(f1.__dict__)示例
__new__
class A: def __init__(self): self.x = 1 print(‘in init function‘) def __new__(cls, *args, **kwargs): print(‘in new function‘) return object.__new__(A, *args, **kwargs) a = A() print(a.x)示例
class Singleton: def __new__(cls, *args, **kw): if not hasattr(cls, ‘_instance‘): cls._instance = object.__new__(cls, *args, **kw) return cls._instance one = Singleton() two = Singleton() two.a = 3 print(one.a) # 3 # one和two完全相同,可以用id(), ==, is檢測 print(id(one)) # 29097904 print(id(two)) # 29097904 print(one == two) # True print(one is two)單例模式
__call__
對象後面加括號,觸發執行。
註:構造方法的執行是由創建對象觸發的,即:對象 = 類名() ;而對於 __call__ 方法的執行是由對象後加括號觸發的,即:對象() 或者 類()()
class Foo: def __init__(self): pass def __call__(self, *args, **kwargs): print(‘__call__‘) obj = Foo() # 執行 __init__ obj() # 執行 __call__示例
__len__
class A: def __init__(self): self.a = 1 self.b = 2 def __len__(self): return len(self.__dict__) a = A() print(len(a))示例
__hash__
class A: def __init__(self): self.a = 1 self.b = 2 def __hash__(self): return hash(str(self.a)+str(self.b)) a = A() print(hash(a))示例
__eq__
class A: def __init__(self): self.a = 1 self.b = 2 def __eq__(self,obj): if self.a == obj.a and self.b == obj.b: return True a = A() b = A() print(a == b)示例
class Person: def __init__(self,name,age,sex): self.name = name self.age = age self.sex = sex def __hash__(self): return hash(self.name+self.sex) def __eq__(self, other): if self.name == other.name and self.sex == other.sex:return True p_lst = [] for i in range(84): p_lst.append(Person(‘lee‘,i,‘male‘)) print(p_lst) print(set(p_lst))View Code
面向對象常用術語
抽象/實現
抽象指對現實世界問題和實體的本質表現,行為和特征建模,建立一個相關的子集,可以用於 繪程序結構,從而實現這種模型。抽象不僅包括這種模型的數據屬性,還定義了這些數據的接口。
對某種抽象的實現就是對此數據及與之相關接口的現實化(realization)。現實化這個過程對於客戶 程序應當是透明而且無關的。
封裝/接口
封裝描述了對數據/信息進行隱藏的觀念,它對數據屬性提供接口和訪問函數。通過任何客戶端直接對數據的訪問,無視接口,與封裝性都是背道而馳的,除非程序員允許這些操作。作為實現的 一部分,客戶端根本就不需要知道在封裝之後,數據屬性是如何組織的。在Python中,所有的類屬性都是公開的,但名字可能被“混淆”了,以阻止未經授權的訪問,但僅此而已,再沒有其他預防措施了。這就需要在設計時,對數據提供相應的接口,以免客戶程序通過不規範的操作來存取封裝的數據屬性。
註意:封裝絕不是等於“把不想讓別人看到、以後可能修改的東西用private隱藏起來”
真正的封裝是,經過深入的思考,做出良好的抽象,給出“完整且最小”的接口,並使得內部細節可以對外透明
(註意:對外透明的意思是,外部調用者可以順利的得到自己想要的任何功能,完全意識不到內部細節的存在)
合成
合成擴充了對類的 述,使得多個不同的類合成為一個大的類,來解決現實問題。合成 述了 一個異常復雜的系統,比如一個類由其它類組成,更小的組件也可能是其它的類,數據屬性及行為, 所有這些合在一起,彼此是“有一個”的關系。
派生/繼承/繼承結構
派生描述了子類衍生出新的特性,新類保留已存類類型中所有需要的數據和行為,但允許修改或者其它的自定義操作,都不會修改原類的定義。
繼承描述了子類屬性從祖先類繼承這樣一種方式
繼承結構表示多“代”派生,可以述成一個“族譜”,連續的子類,與祖先類都有關系。
泛化/特化
基於繼承
泛化表示所有子類與其父類及祖先類有一樣的特點。
特化描述所有子類的自定義,也就是,什麽屬性讓它與其祖先類不同。
多態與多態性
多態指的是同一種事物的多種狀態:水這種事物有多種不同的狀態:冰,水蒸氣
多態性的概念指出了對象如何通過他們共同的屬性和動作來操作及訪問,而不需考慮他們具體的類。
冰,水蒸氣,都繼承於水,它們都有一個同名的方法就是變成雲,但是冰.變雲(),與水蒸氣.變雲()是截然不同的過程,雖然調用的方法都一樣
自省/反射
自省也稱作反射,這個性質展示了某對象是如何在運行期取得自身信息的。如果傳一個對象給你,你可以查出它有什麽能力,這是一項強大的特性。如果Python不支持某種形式的自省功能,dir和type內建函數,將很難正常工作。還有那些特殊屬性,像__dict__,__name__及__doc__
Python--面向對象編程(2)