13.面向對象(多態/(性)/封裝)
封裝
特性
str
staticmethod解除綁定方法
classmethod
小結:
多態
同一種事物的多種形態
增加了程序的靈活性
增加了程序的可擴展性
封裝
封裝數據:保護隱私
封裝方法:隔離復雜度
第一種封裝:
什麽都不做
第二種封裝:
雙下劃線的隱藏屬性
語法糖:__xxx====>_類__xxx #這個過程就是變形
特性
@property #xxx = property(xxx)
def xxx():
pass
class Squera: #定義一個正方形的類 def __init__(self,lenth): #定義一個邊長參數 self.lenth = lenth @property #使用特性裝飾器,不用調用外部模塊,因為可以在內置模塊中看到property()函數,這個裝飾器的實現效果其實就是area = property(area) def area(self): return self.lenth*self.lenth @property def perimeter(self): return 4* self.lenth s = Squera(10) # print(s.area()) #如果不添加property,打印這個面積需要使用調用函數的方法,但是實際上,為了統一這些參數的調用,並且規範調用方式,所以才使用property # print(s.perimeter()) print(s.area) print(s.perimeter)
除了如上註釋中描述的優點,我們需要註意,這個area和perimeter看起來像作為一個參數屬性在引用,但是實際上他本質上仍然是一個計算函數的結果。
所以在修改了lenth的長度之後,再次調用print(s.area)
就會發現結果已經改變了。
被property裝飾的屬性會優於對象的屬性被引用。並且property的相關方法也會關聯一些語句,並且優先走有關聯語句的部分,例如下面代碼的xxx.name = ‘xxxxx‘就指向了name.setter函數,無論這個賦值是多少,優先運行name.setter的函數內容。
如果說這個name.setter中並沒有賦值相關的操作,那麽這個動作就按照name.setter來運行。不執行賦值。
class Name: def __init__(self,NAME): self.__name = NAME @property #將一個隱藏屬性定義成一個函數,並且把這個函數的計算結果返回出來 def name(self): print(self.__name) @name.setter #setter接口,用於設置之前property裝飾的函數,在函數內部修改被隱藏的值 def name(self,value): #定義一個可用值傳入 if type(value)==str: self.__name = value #將隱藏值在函數內部修改,如果在外部直接修改name,因為name是被property裝飾的一個函數,所以無法修改,起到了保護原隱藏值的作用。 else: raise TypeError(‘please enter str type‘) @name.deleter def name(self): #定義刪除原隱藏值函數 del self.__name peo1 = Name(‘scott‘) #定義對象peo1 print(peo1.name) #輸出對象名,由於__name 被隱藏,所以這裏輸出的其實是name()的運算結果 peo1.name = ‘jerry‘ #當出現針對name(其實是函數)的賦值動作,被setter裝飾的函數運行;擴展:可以進行添加age隱藏參數,同__name,也給age添加修改方法,但是添加name.setter,看修改的是哪個值。需要註意setter方法前需要註明修改的對象 print(peo1.name)#查看結果 peo1.name = 123 #輸入錯誤類 print(peo1.name) del peo1.name #出現刪除操作的時候,執行.deleter裝飾的方法 print(peo1.name)
str
class people:
def __init__(self,name,age):
self.name = name
self.age = age
def __str__(self):
return ‘name:%s,age:%s‘%(self.name,self.age)
p1 = people(‘scott‘,23)
print(p1)
>>>name:‘scott‘,age:23
定義在類的內部,必須返回一個字符串類型。
打印有這個類產生的對象時,會觸發執行。
其實str(p1)=========>p1.str()
staticmethod解除綁定方法
添加了@staticmethod之後,類中的方法就不再是綁定方法了,也就不存在self自動傳值的動作。
在類中,需要定義一個函數,就是給類使用的,而不和對象綁定,就要用到staticmethod。如果需要調用一個類的方法,並且不傳入self(也就是不綁定對象),也沒有使用staticmethod,那麽在實例調用的時候,就會報缺少參數的錯。
import time
class Date:
def __init__(self,year,month,day):# 定義一個基礎函數,傳入年月日
self.year = year
self.month = month
self.day = day
def test(): #定義一個test()方法,這個方法不與類綁定,在調用的時候與對象傳參無關(不接受對象的傳參)這時候其實test會有紅線提示,不規範的寫法
print(‘from test‘) #返回一個標識結果
@staticmethod #靜態方法(用於將裝飾的函數從類中解除綁定)
def now(): #定義的函數無需self傳值,但仍然可以作為類中的方法被對象調用
t = time.localtime() #.localtime()地方法傳給t,是一個類,包含著年月日時間參數
obj = Date(t.tm_year,t.tm_mon,t.tm_mday) #將t的年月日信息傳給Date類,生成obj對象
return obj #返回,將now()的結果變成obj對象
@staticmethod
def tomorrow():
t = time.localtime(time.time()+86400)
return Date(t.tm_year,t.tm_mon,t.tm_mday)
class EuroDate(Date):
def __str__(self):
return ‘%s year %s month %s day‘%((self.year,self.month,self.day))
d = Date(17,4,20) #設置對象
d.test() #報錯,對象不能調用test方法
#TypeError: test() takes 0 positional arguments but 1 was given
Date.test() #類可以調用方法,但是沒有意義,此時的test和類外部的函數一樣。
靜態方法的引用:
t = d.now()
print(t.year,t.month,t.day)
l = time.localtime()
print(type(l))
結果
2017 4 21
<class ‘time.struct_time‘>
實例也可以使用,但通常靜態方法都是給類用的,實例在使用時喪失了自動傳值的機制
在類中,如果沒被裝飾器裝飾過,就是綁定方法
import time
class Date:
def __init__(self,year,month,day):
self.year=year
self.month=month
self.day=day
@staticmethod
def now():
t=time.localtime()
return Date(t.tm_year,t.tm_mon,t.tm_mday)
class EuroDate(Date):
def __str__(self):
return ‘year:%s month:%s day:%s‘ %(self.year,self.month,self.day)
e=EuroDate.now()
print(e) #我們的意圖是想觸發EuroDate.__str__,但是結果為
‘‘‘
輸出結果:
<__main__.Date object at 0x1013f9d68>
‘‘‘
classmethod
@classmethod
http://blog.csdn.net/handsomekang/article/details/9615239#
把一個方法綁定給類
一般來說,要使用某個類的方法,需要先實例化一個對象再調用方法。
而使用@staticmethod或@classmethod,就可以不需要實例化,直接類名.方法名()來調用。
這有利於組織代碼,把某些應該屬於某個類的函數給放到那個類裏去,同時有利於命名空間的整潔。
既然@staticmethod和@classmethod都可以直接類名.方法名()來調用,那他們有什麽區別呢
從它們的使用上來看,
- @staticmethod不需要表示自身對象的self和自身類的cls參數,就跟使用函數一樣。
- @classmethod也不需要self參數,但第一個參數需要是表示自身類的cls參數。
如果在@staticmethod中要調用到這個類的一些屬性方法,只能直接類名.屬性名或類名.方法名。
而@classmethod因為持有cls參數,可以來調用類的屬性,類的方法,實例化對象等,避免硬編碼。
差別:綁定給對象的,第一個位置不用傳參staticmethod
綁定給類的(類.綁定給類的方法()),會把類本身當作第一個參數(原self
)傳給類
class A(object):
bar = 1
def foo(self):
print ‘foo‘
@staticmethod
def static_foo():
print ‘static_foo‘
print A.bar
@classmethod
def class_foo(cls):
print ‘class_foo‘
print cls.bar
cls().foo()
A.static_foo()
A.class_foo()
拿到一個類的內存地址後,就可以實例化或者引用類的屬性了。
小結:
在類內部定義的函數無非三種用途
一:綁定到對象的方法
只要是在類內部定義的,並且沒有被任何裝飾器修飾過的方法,都是綁定到對象的
class Foo:
def test(self): #綁定到對象的方法
pass
def test1(): #也是綁定到對象的方法,只是對象.test1(),會把對象本身自動傳給test1,因test1沒有參數所以會拋出異常
pass
綁定到對象,指的是:就給對象去用,
使用方式:對象.對象的綁定方法(),不用為self傳值
特性:調用時會把對象本身當做第一個參數傳給對象的綁定方法
二:綁定到類的方法:classmethod
在類內部定義的,並且被裝飾器@classmethod修飾過的方法,都是綁定到類的
#用來計算類被實例化的次數
# def get_no_(cls_obj):
# return cls_obj.times_inst
class Exm_cls:
#實例化次數的初始值為0
times_inst = 0
#類被實例化一次,就+1
def __init__(self):
Exm_cls.times_inst +=1
#在內部定義這個函數,並且把他綁定到類
@classmethod
def get_no_(cls):
return cls.times_inst
exm1 = Exm_cls()
exm2 = Exm_cls()
print(Exm_cls.get_no_())
# print(get_no_(Exm_cls))
綁定到對象,指的是:就給對象去用,
使用方式:對象.對象的綁定方法()
特性:調用時會把對象本身當做第一個參數傳給對象的綁定方法
三:解除綁定的方法:staticmethod
既不與類綁定,也不與對象綁定,不與任何事物綁定
綁定的特性:自動傳值(綁定到類的就是自動傳類,綁定到對象的就自動傳對象)
解除綁定的特性:不管是類還是對象來調用,都沒有自動傳值這麽一說了
所以說staticmethod就是相當於一個普通的工具包
class Foo:
def test1(self):
pass
def test2():
pass
@classmethod
def test3(cls):
pass
@classmethod
def test4():
pass
@staticmethod
def test5():
pass
test1與test2都是綁定到對象方法:調用時就是操作對象本身
<function Foo.test1 at 0x0000000000D8E488>
<function Foo.test2 at 0x0000000000D8E510>
test3與test4都是綁定到類的方法:調用時就是操作類本身
<bound method Foo.test3 of <class ‘__main__.Foo‘>>
<bound method Foo.test4 of <class ‘__main__.Foo‘>>
test5是不與任何事物綁定的:就是一個工具包,誰來都可以用,沒說專門操作誰這麽一說
<function Foo.test5 at 0x0000000000D8E6A8>
反射
getattr
setattr
delattr
hasattr
定制
13.面向對象(多態/(性)/封裝)