1 面向對象
面向過程是一件事“該怎么做”,面向對象是一件事“該讓誰來做”,那個“誰”就是對象,他要怎么做是他自己的事,反正最后一群對象合力能把事做好。
類和對象
對象是面向對象的核心,在使用對象的過程中,為了將具有共同特征和行為的一組對象抽象定義,提出另外一個新的概念——類
1) 類就相當于制造飛機時的圖紙,用它來進行創(chuàng)建的飛機就相當于對象
2) 類就是創(chuàng)建對象的模板
3) 一切皆對象
面向對象的三個特性:繼承、封裝、多態(tài)。
類的構成,(分為3部分):
1) 類的名稱:類名
2) 類的屬性:一組數據,顏色、大小、高矮,年齡。。
3) 類的方法:允許對進行操作的方法(行為)
定義類
定義一個類,格式如下:
class 類名:
方法列表
創(chuàng)建對象
創(chuàng)建對象的格式為:
對象名=類名()
self的說明
在類的方法中,第一個參數一定是self,表示調用這個方法的對象本身
例,定義一個類和對象
#定義一個類
class Car:
def start(self): #方法的參數一定是self,否則報錯
print("汽車啟動")
def print_car_info(self):
print("車的名字:%s,顏色為:%s"%(self.name,self.color))
#當創(chuàng)建一個對象時,就是一個模子來制造一個實物
autoHome=Car() #構建一個Car的實例對象,一定在內存中有一塊空間存放對象的數據信息,此時可以通過實例對象autoHome來訪問屬性或者方法,
# autoHome是一個對象,它擁有屬性(數據)和方法(函數)
autoHome.name='邁騰' #給對象添加屬性,再次出現 autoHome.name='XXXX'表示對屬性進行修改
autoHome.color='金色'#'邁騰’,'金色'。。。等數據集中到對象autoHome里面就是對象autoHome的封裝
BWM=Car() #構建另外一個對象,與autoHome不同,但同屬于一個類
autoHome.print_car_info() #調用對象的方法print_car_info()
autoHome.start() #調用對象的方法start(),這里start()里面的self代表autoHome
BWM.start() # 這里start()里面的self代表對象BWM
魔法方法__init__
1. __init__()方法,在創(chuàng)建一個對象時默認被調用,不需要手動調用
2. __init__(self)中,默認有1個參數名稱為self,如果在創(chuàng)建對象時傳遞了2個實參,那么__init__(self)中除了self作為第一個形參外,還需要2個形參,例__init__(self,x,y)
3. __init__(self)中的self參數,不需要開發(fā)者傳遞,python解釋器會自動把當前 的對象引用傳遞進去。
例
'''
class Person:
def __init__(self): #初始化對象的方法,不是構建對象的方法,python解釋器自動調用
print("對象初始化")
self.name = 'zs'
self.age = '18'
def work(self):
pass
p1 = Person() #自動調用 __init__()方法
print(p1.name)
'''
class Person():
def __init__(self, name, age, height): #初始化對象的方法,不是構建對象的方法,python解釋器自動調用
self.name = name #self.name 指的是對象屬性為name,后面的name指的是參數name
self.age = age
self.height = height
def __str__(self): #把對象轉換成字符串,還不是地址,必須要有返回值
return "姓名:%s,年齡:%s,身高:%s"%(self.name, self.age, self.height)
def introduce(self):
print("姓名:%s,年齡:%s,身高:%s"%(self.name, self.age, self.height))
p1 = Person('zs', 18, 175) #自動調用 __init__()方法
#p1.introduce()
print(p1)
小結:
1. 在Pyth中方法名如果 是__XXXX__()的,那么就有特殊的功能,因此叫做“魔法”方法
2. 當使用print輸出對象的時候,只要自己定義了__str__(self)方法,那么就會打印從在這個方法中return的數據,必須要有返回值
保護對象的屬性
如果有一個對象,當需要對其進行修改屬性時,有2種方法
l 對象名.屬性名=數據 #直接修改
l 對象名.方法名() #間接修改
為了更好的保存屬性的安全,即不能隨意修改,一般的處理方式為:
l 將屬性定義為私有屬性(或稱為隱藏屬性)
l 添加一個可以調用的方法,供調用
【小結】:
python中沒有像C++中private這些關鍵字來區(qū)別公有屬性和私有屬性,
python以屬性命名方式來區(qū)分,如果在屬性名前面加了2個下劃線’__’,則表示該屬性為私有屬性,否則為公有屬性(方法也是一樣,方法名前面加2個下劃線的話,表示該方法是私有的,否則為公有)
xx | 公有變量 |
|
_x | 單前置下劃線 | 私有化屬性或方法,不能導入到其他py文件 |
__xx | 雙前置下劃線 | 避免與子類中的屬性命名沖突,無法在外部直接訪問(名字重整所以訪問不到) |
__xx__ | 雙前后下劃線 | 用戶名字空間的魔法對象或屬性。例如__init__,不要自己發(fā)明這樣的名字__ |
xx_ | 單后置下劃線 | 用于避免與python關鍵詞的沖突 |
class User:
def __init__(self,pw):
if len(pw)>=6:
self.__password = pw #隱藏(私有)屬性,不允許外部代碼調用和修改
else:
print("密碼%s不符合規(guī)則"%pw)
def __str__(self):
return "密碼%s"%self.__password
def get_password(self):
return self.__password
self.__say_hello() #內部可以調用隱藏方法
def __say_hello(self): #隱藏方法不允許外問題代碼調用
print(self.__password)
u1 = User('1231123')
print(u1)
#u1.__say_hello() # 隱藏方法,外部代碼無法進行修改
#u1.__password='1234123' # 隱藏屬性,外部代碼無法進行修改
print(u1.get_password()) # 需要通過對象的方法調用隱藏屬性
私有屬性:通過name mangling(名字重整)目的就是以防子類意外重寫基類的方法或者屬性。
_Class_object機制就可以訪問private
property用法
為私有屬性添加getter和setter方法
使用property升級getter和setter方法
例:money=property(getMoney,setMoney)
使用property取代getter和setter方法(注意:方法名與私有屬性名字保持一致)。
方法名前加修飾器@property,該方法變?yōu)?/span>getter
@property
@money.setter
__del__()方法
創(chuàng)建對象后,python解釋器默認用戶__init__()方法,
當刪除一個對象時,python解釋器也會默認調用一個方法,這個方法為__del__()方法,
當內存中構建 一個對象數據時,回調__init__()方法
當內存中銷毀(釋放)一個對象時,回調__del__()方法
class User:
def __init__(self):
print("******對象初始化*****")
def __del__(self):
print("******對象即將要被銷毀******")
u1 = User() # 新建一個對象
u2 = u1 # 將u1 賦值u2 ,共同指向一個對象
del u1 # 刪除 u1 與對象的引用關系
print("--"*30)
#del u2 # 刪除 u2 與對象的引用關系,對象未被引用(或程序結束時),被回收
print("=="*30)
Ø 當有1個變量保存了對象的引用時,此對象的引用計數就會加1
Ø 當使用DEL刪除變量指向的對象時,如果對象的引用計數不是1,比如3,那么此時只會讓這個引用計數減1,即變?yōu)?/span>2,當再次調用DEL時,變?yōu)?/span>1,如果 再調用1次DEL,此時會真的把對象進行刪除
繼承(單繼承)
繼承,在程序中,繼承描述的是事物之間的所屬關系,如貓狗都屬于動物,程序中便可以描述為貓和狗繼承自動物;同理,波斯貓和巴厘貓都繼承自貓,而沙皮狗和斑點狗都繼承狗。
只繼承父類的公有方法
class Animal:
def __init__(self):
print("***動物的初始化***")
self.name='動物'
def eat(self,food):
print("***吃飯***%s" %food)
def sleep(self):
print("***睡覺***")
class Dog(Animal): # 繼承父類(Animal)的方法
def __init__(self, name): # 初始化,屬性name
self.name = name
def shout(self):
print("***旺旺***")
class Cat(Animal): # 繼承父類(Animal)的方法
#def __init__(self): #未定義初始化方法時,調用父類的__init__()方法,
# print("貓初始化了")
def catch(self):
print("----捉老鼠----")
dog = Dog("小白") #傳遞數據給屬性
dog.eat("骨頭") # 調用父類的方法
dog.shout()
print(dog.name)
cat = Cat()
print(cat.name)
多繼承
class A:
def test(self):
print("A****test()")
class B:
def test(self):
print("B****test()")
class C(A,B):# C繼承了父類A和B
def test(self):
print("C****test()")
c = C()
print(C.__mro__) #查看方法優(yōu)先級
c.test()
輸出:
(<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class 'object'>) 'object'是所有類的父類
C****test()
重寫父類方法與調用父類方法
1. 重寫父類方法
所謂重寫,就是子類中,有一個和父類相同名字的方法,在子類中的方法會覆蓋掉父類中同名的方法
class Animal:
def __init__(self):
print("***動物的初始化***")
self.color = '黃色'
def eat(self):
print("***Animal類的eat方法" )
def sleep(self):
print("***睡覺***")
class Dog(Animal): # 繼承父類(Animal)的方法
def __init__(self, name): # init和父類的init名字一樣,所以叫方法的重寫
self.name = name
super().__init__() # super().父類方法名稱,主調用父類的init方法
def eat(self):
super().eat()
print("**DOG自己的eat方法")
def shout(self):
print("***旺旺***")
dog = Dog("小白")
print(dog.name, dog.color)
dog.eat()
輸出:
***動物的初始化***
小白 黃色
***Animal類的eat方法
**DOG自己的eat方法
多態(tài)
多態(tài)的概念是應用于java和C#這一類強類型語言中,而python崇尚鴨子類型。
所謂多態(tài),定義時的類型和運行時的類型不一樣,此時就稱為多態(tài)。
類屬性
類屬性就是類對象所擁有的屬性,它被所有類對象的實例對象所共有,在內存中只存在一個副本,這個和C++中類的靜態(tài)成員變量有點類似。對于公有的類屬性,在類外可以通過類對象和實例對象訪問。
類屬性:所屬類的,這個類下的所有對象都可以共享這個類屬性。相當于JAVA中靜態(tài)屬性,可以被子類繼承
class User(object):
name = 'ZhangSan' #類屬性,公有
__password = '123456' #類屬性,私有(隱藏的)
def __init__(self, sex, username):
self.sex = sex #對象屬性
self.username = username #對象屬性
u = User("男",'goldbin')
print("u名稱:%s"%u.name)#當對象屬性與類屬性名字一樣,如果通過對象來訪問,會選擇對象屬性,通過類訪問會選擇類屬性
u2 = User("女", "adsf")
print("u2名稱:%s"%u2.name)
u.name = 'ww' #本質沒有修改類的屬性,僅僅給該對象定義了一個對象屬性name,并賦值為'ww'
print(u.name)
User.name = 'ww' #真正的類屬性的修改,通過類的名稱來修改
print(User.name)
# 對象屬性,對象不一樣,屬性不一樣
# 類屬性,所有對象共有同一個類屬性
輸出:
u名稱:ZhangSan
u2名稱:ZhangSan
【小結】
如果需要在類外修改類屬性,必須通過類對象去引用然后進行修改。如果通過實例對象去引用,會產生一個同名的實例屬性,這種方式修改的是實例屬性,不會影響到類屬性,并且之后如果 通過實例對象去引用該名稱的屬性,實例屬性會強制屏蔽掉類屬性,即引用的是實例屬性,除非刪除了該實例屬性
屬性叫法 | 變量叫法 | 描述 |
類屬性(私有和公有) | 類變量 | 所有對象共享同一份類屬性 |
實例屬性(私有和公有) | 成員變量 | 每個不同對象,有不一樣值的實例屬性 |
類方法
類方法是類對象所擁有的方法,需要用修飾器@classmethod來標識 其為類方法,對于類方法,第一個參數必須 是類對象,一般以cls作為第一個參數(當然可以用其他名稱的變量作為其第一個參數,但是大部分人都習慣以‘cls’作為第一個參數的名字,就最好用cls),能夠通過實例對象和類對象去訪問
class A(object):
name = 'Zhangsan'
def test1(self):
print("A的test1方法")
# 類方法一定要在方法的上面加上一個修飾器(java注解)
# 類方法的參數一定是cls,代表當前的類
@classmethod
def test2(cls): #cls代表當前類
cls.name = 'WangWu'#cls類對象直接修改類屬性
print("A的test2方法")
a = A()
a.test1()
a.test2()# 通過實例對象訪問類方法
A.test2() # 可以直接用類名調用類方法
print(A.name)
【小結】
從類方法和實例方法以及靜態(tài)方法的定義形式就可以看出來,類方法的第一個參數是類對象cls,那么通過cls引用的必定是類對象的屬性和方法;而實例方法的第一個參數是實例對象self,那么通過self引用的可能是類屬性,也有可能 是實例屬性(這個需要具體分析),不過在存在相同名稱的類屬性和實例屬性的情況下,實例屬性優(yōu)先級更高。靜態(tài)方法中不需要額外定義參數,因此在靜態(tài)方法中引用類屬性的話,必須 通過類對象來引用。
方法類別 | 語法 | 描述 |
類方法 | @classmethod | 第一個形參cls,默認傳遞 |
靜態(tài)方法 | @staticmethod | 沒有默認傳遞的形參 |
對象方法(成員方法) | def 方法名 | 第一個形參是self,默認傳輸 |
靜態(tài)方法
需要通過修飾器@staticmethod來進行修飾,靜態(tài)方法不需要多定義參數
class A(object):
name = 'Zhangsan'
@staticmethod # 靜態(tài)方法,屬于類,沒有默認傳遞的參數,可以通過類的對象和類名調用
def test3():
A.name = 'LiSi' # 通過類名稱訪問類屬性
print("A的test3靜態(tài)方法")
a = A()
a.test3()
A.test3()
print(A.name)
__new__()方法
__new__,對象構造時由python調用的方法,必須有返回值,返回當前類的對象。
Ø __new__與__init__不同時,new方法用于構建對象,init用于初始化對象,默認調用父類object的方法。
Ø __new__至少要有一個參數cls,代表要實例化的類,此參數在實例化時由python解釋器自動提供
Ø __new__必須要有返回值,返回實例化出來的實例,這點在自己實現__new__時要特別注意,可以return父類__new__出來的實例,或者直接是object的__new__出來的實例
Ø __init__有一個參數self,就是這個__new__返回的實例,__init__在__new__的基礎上可以完成一些其它初始化的動作,__init__不需要返回值
Ø 先執(zhí)行__new__再執(zhí)行__init__
Ø 我們可以將類比例制造商,__new__方法就是前期的原材料購買環(huán)節(jié),__init__方法就是在有原材料的基礎上,加工,初始化商品環(huán)節(jié)
Ø class User(object):
def __init__(self, username, password):
self.username = username
self.password = password
print("對象已經構建好了,由解釋器自動以回調,對象初始化")
# new方法是當對象構建 的時候由解釋器自動回調的方法。該方法必須返回當前類的對象
def __new__(cls, username, password):
print("User類的對象開始構建")
return object.__new__(cls) # 沒有返回構建的對象時,后面代碼不執(zhí)行,調用object類,構建對象
def __str__(self):
return "用戶名%s;密碼%s"%(self.username,self.password)
u = User('ZhangSan','abc123')
print(u)
輸出:
User類的對象開始構建
對象已經構建好了,由解釋器自動以回調,對象初始化
用戶名ZhangSan;密碼abc123
特殊方法名 | 默認的參數 | 功能描述 |
__init__() | self | 初始化對象屬性 |
__str__() | self | print對象時,輸出 |
__del__() | self | 刪除對象 |
__new__() | cls | 創(chuàng)建對象 |
單例模式
單例模式例子:我們日常使用的電腦 上都有一個回收站,在整個操作系統(tǒng)中,回收站只能有一個實例,整個系統(tǒng)都使用這個唯一的實例,而且回收站自行提供自己的實例,因此回收是單例模式的應用。
確保某一個類只有一個實例,而且自行實例化并向整個系統(tǒng)提供這個實例,這上類稱為單例類,單例模式是一種對象創(chuàng)建型模式
使用單例模式的類的特點:
1. 在構造過程中非常復雜,消耗大量內存資源
2. 封裝的大量數據,可以當全局變量用
【單實例模式1】
class User(object):
__instance = None
def __init__(self, name):
print("運行__init__()方法")
self.name = name
@classmethod
def get_instance(cls, name):
print("運行get_instance()方法")
if not cls.__instance: # 一開始__instance = None 滿足條件,創(chuàng)建對象,
# 后面一直不滿足(只運行一次),
# 不創(chuàng)建新的對象實例,后面的name不變
cls.__instance = User(name)
return cls.__instance
u1 = User.get_instance('ZhangSan')
print("u1的name為%s"%u1.name)
u2 = User.get_instance("LiSi")
print("u2的name為%s"%u2.name)
print("最后:u1的name為%s;u2的name為%s" % (u1.name,u2.name))
print(u1 == u2) # 判斷表達式,如果返回True,這兩具對象是一個對象,并且內存地址相同
print("u1對象的內存地址%s u2對象的內存地址%s"%(id(u1), id(u2)))
輸出:
運行get_instance()方法
運行__init__()方法
u1的name為ZhangSan
運行get_instance()方法
u2的name為ZhangSan
最后:u1的name為ZhangSan;u2的name為ZhangSan
True
u1對象的內存地址2529384892176 u2對象的內存地址2529384892176
【單實例模式2】
class User(object):
__instance = None
def __init__(self, name):
print("運行__init__()方法")
self.name = name
def __new__(cls, name):
print("運行__new__()方法")
if not cls.__instance: # 保證 object.__new__(cls)方法只會調用一次
cls.__instance = object.__new__(cls)
return cls.__instance
u1 = User('ZhangSan')
print("u1的name為%s" % u1.name)
u2 = User("LiSi")
print("u2的name為%s" % u2.name)
print("最后:u1的name為%s;u2的name為%s" % (u1.name,u2.name))
print(u1 == u2) # 判斷表達式,如果返回True,這兩具對象是一個對象,并且內存地址相同
print("u1對象的內存地址%s u2對象的內存地址%s"%(id(u1), id(u2)))
輸出:
運行__new__()方法
運行__init__()方法
u1的name為ZhangSan
運行__new__()方法
運行__init__()方法
u2的name為LiSi
最后:u1的name為LiSi;u2的name為LiSi
True
u1對象的內存地址1584795191056 u2對象的內存地址1584795191056
簡單工廠模式
工廠模式是我們最常用的實例化對象模式,是用工廠方法代替new操作的一種模式,雖然這樣做可能多做一些工作,但會給你系統(tǒng)帶來更大的可擴展性和盡量少的修改量
解決兩個對象/兩個類之間存在依賴關系的時候的問題。提高系統(tǒng)的維護操作性。
簡單工廠模式Simple Factory模式不是獨立的設計模式,是Factory Method模式的一種簡單的、特殊的實現。也被稱為靜態(tài)工廠模式,通常創(chuàng)建都的創(chuàng)建方法被設計為static方便調用。包含:
1. 靜態(tài)的工廠類
2. 用全局函數改寫工廠類
【例1,靜態(tài)工廠類】
class Person(object):
def __init__(self, name):
self.name = name
def work(self, axe_type):
print(self.name+"開始工作了") # person完成work,需要一把斧頭
# 在原始社會,需要一把石斧
# axe = StoneAxe("花崗巖斧頭") # 通過StoneAxe()生產斧頭
# axe = SteelAxe("江南十三子斧頭") # SteelAxe()生產斧頭
axe = Factory.create_axe(axe_type) # 通過 第三方工廠來生產斧頭
axe.cut_stree()
class Axe(object):
def __init__(self,name):
self.name = name
def cut_tree(self):
print("%s斧頭,開始砍樹"%self.name)
class StoneAxe(Axe):
def cut_stree(self):
print("使用%s做的斧頭砍樹"%self.name)
class SteelAxe(Axe):
def cut_stree(self):
print("使用%s的鋼斧頭砍樹"%self.name)
class Factory(object):
# 生產斧頭,根據用戶指標的類型來生產
@staticmethod
def create_axe(type):
if type == "stone":
return StoneAxe("花崗巖斧頭")
elif type == "steel":
return SteelAxe("加爵斧頭")
else:
print("傳入的類型不對")
p = Person("ZhangSan")
p.work("steel")
例2,全局函數
class Person(object):
def __init__(self, name):
self.name = name
def work(self, axe_type):
print(self.name+"開始工作了") # person完成work,需要一把斧頭
# 在原始社會,需要一把石斧
# axe = StoneAxe("花崗巖斧頭") # 通過StoneAxe()生產斧頭
# axe = SteelAxe("江南十三子斧頭") # SteelAxe()生產斧頭
axe = create_axe(axe_type)# 通過全局函數來生產斧頭
axe.cut_stree()
class Axe(object):
def __init__(self,name):
self.name = name
def cut_tree(self):
print("%s斧頭,開始砍樹"%self.name)
class StoneAxe(Axe):
def cut_stree(self):
print("使用%s做的斧頭砍樹"%self.name)
class SteelAxe(Axe):
def cut_stree(self):
print("使用%s的鋼斧頭砍樹"%self.name)
# 全局函數-替代之前的工廠類
def create_axe(type):
if type == "stone":
return StoneAxe("花崗巖斧頭")
elif type == "steel":
return SteelAxe("加爵斧頭")
else:
print("傳入的類型不對")
p = Person("ZhangSan")
p.work("steel")
工廠方法模式
工廠方法模式去掉了簡單工廠模式中工廠方法的靜態(tài)方法,使得它可以被子類繼承,對于python來說,就是工廠類被具體工廠繼承。這樣在簡單工廠模式里集中在方法上的壓力可以由工廠方法模式里不同的工廠子類來分擔。
抽象的工廠類提供了一個創(chuàng)建對象的方法,也叫作工作方法。
1) 抽象工廠角色(Factory):這是工廠方法模式的核心,它與應用程序無關,是具體工廠色必須 實現的接口或者必須 繼承的父類。
2) 具體工廠角色(Stone_Axe_Factory,Steel_Axe_Factory):它含有和具體業(yè)務邏輯有關的代碼。由應用程序調用以創(chuàng)建不的具體產品時的對象
3) 抽象產品角色(Axe):它是具體產品繼承 的父類或者是實現的接口。在python中旬產品一般為父類。
4) 具體產品角色(Stone_Axe,Steel_Axe):具體工廠角色所創(chuàng)建的對象就是此角色 的實例,由一個具體在實現
class Person(object):
def __init__(self, name):
self.name = name
def work(self):
print(self.name+"開始工作了") # person完成work,需要一把斧頭
# 在原始社會,需要一把石斧
# axe = StoneAxe("花崗巖斧頭") # 通過StoneAxe()生產斧頭
# axe = SteelAxe("江南十三子斧頭") # SteelAxe()生產斧頭
# axe = Steel_Axe_Factory.create_axe() # 通過 第三方工廠來生產斧頭
axe = Stone_Axe_Factory.create_axe() # 通過 第三方工廠來生產斧頭
axe.cut_stree()
class Axe(object):
def __init__(self,name):
self.name = name
def cut_tree(self):
print("%s斧頭,開始砍樹"%self.name)
class StoneAxe(Axe):
def cut_stree(self):
print("使用%s做的斧頭砍樹"%self.name)
class SteelAxe(Axe):
def cut_stree(self):
print("使用%s的鋼斧頭砍樹"%self.name)
# 工廠類
class Factory(object):
# 生產斧頭,根據用戶指標的類型來生產
def create_axe(self):
pass
class Stone_Axe_Factory(Factory):
def create_axe():
return StoneAxe("花崗巖斧頭")
class Steel_Axe_Factory(Factory):
def create_axe():
return SteelAxe("鋼鐵斧頭")
p = Person("ZhangSan")
p.work()
內建屬性
內建屬性 |
常用專用有屬性 | 說明 | 觸發(fā)方式 |
__init__ | 構造初始化函數 | 創(chuàng)建實例后,賦值時使用,在__new__后 |
__new__ | 生成實例所需屬性 | 創(chuàng)建實例時 |
__class__ | 實例所在的類 | 實例__class__ |
__str__ | 實例字符串表示,可讀性 | print(類實例),如沒實現,使用repr結果 |
__repr__ | 實例字符串表示,準確性 | 類實例,回車 或都print(repr(類實例)) |
__del__ | 析構 | del 刪除實例 |
__dict__ | 實例自定義屬性 | vars(實例__dict__) |
__doc__ | 類文檔,子類不繼承 | help(類或實例) |
__getattribute__ | 屬性訪問攔截器 | 訪問實例屬性時 |
__bases__ | 類的所有父類 構成元素 | 類名__bases__ |
例__getattribute__屬性訪問攔截器
屬性訪問攔截器的坑:
限制任性對象的屬性__slots__
注意,__slots__定義的屬性公對當前類實例起作用,對繼承的子類是不起作用的
類裝飾器
裝飾器函數其實是這樣一個接口約束,它必須接受一個callable對象作為參數,然后返回一個callable對象。
一般callable對象都是函數,但也有例外。只要某個對象重寫了__call__()方法,那么這個對象就是callable的
例:類當函數用
例2類裝飾器