1. 程式人生 > >13.面向對象(多態/(性)/封裝)

13.面向對象(多態/(性)/封裝)

一次 mda else 可用 truct raise 靜態 有關 執行

多態
封裝
特性
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.面向對象(多態/(性)/封裝)