原文地址: https://blog.csdn.net/fgf00/article/details/52479307 編輯:智能算法,歡迎關注! 上期我們一起學習了python中的類
今天我們繼續學習python中類。
目錄
1 類的高級方法
2 類的特殊成員方法
3 反射
4 異常處理
1 類的高級方法
1.1 靜態方法(@staticmethod)
通過@staticmethod裝飾器即可把其裝飾的方法變爲一個靜態方法。普通的方法,可以在實例化後直接調用,並且在方法裏可以通過self.調用實例變量或類變量,但靜態方法是不可以訪問實例變量或類變量的,一個不能訪問實例變量和類變量的方法,其實相當於跟類本身已經沒什麼關係了,它與類唯一的關聯就是需要通過類名來調用這個方法
1class Dog(object): 2 def __init__(self,name): 3 self.name = name 4 @staticmethod # 把eat方法變爲靜態方法 5 def eat(self): 6 print("%s is eating" %self.name) 7 8d = Dog("wangcai") 9d.eat()
上面的調用會出以下錯誤,
1TypeError: eat() missing 1 required positional argument: 'self'
說是eat需要一個self參數,但調用時卻沒有傳遞,沒錯,當eat變成靜態方法後,再通過實例調用時就不會自動把實例本身當作一個參數傳給self了。 想讓上面的代碼可以正常工作有兩種辦法
- 調用時主動傳遞實例本身給eat方法,即d.eat(d)
- 在eat方法中去掉self參數,但這也意味着,在eat中不能通過self. 調用實例中的其它變量了
1class Dog(object): 2 def __init__(self,name): 3 self.name = name 4 @staticmethod # 把eat方法變爲靜態方法 5 def eat(self): 6 print("%s is eating" %self.name) 7 8d = Dog("wangcai") 9d.eat(d)
作用:只是相當於一個單純函數,要傳參數,就要把實例傳進去。 如果說和類有關係,就是必須有類名去調用。調用不了類或實例中的任何屬性
1.2 類方法(@classmethod)
類方法通過@classmethod裝飾器實現,類方法和普通方法的區別是: 類方法只能訪問類變量,不能訪問實例變量
1class Dog(object): 2 def __init__(self,name): 3 self.name = name 4 @classmethod 5 def eat(self): 6 print("%s is eating" % self.name) 7 8d = Dog("wangcai") 9d.eat()
執行報錯如下,說Dog沒有name屬性,因爲name是個實例變量,類方法是不能訪問實例變量的
1AttributeError: type object 'Dog' has no attribute 'name'
此時可以定義一個類變量,也叫name,看下執行效果
1class Dog(object): 2 name = "哮天犬" 3 def __init__(self,name): 4 self.name = name 5 @classmethod 6 def eat(self): 7 print("%s is eating" % self.name) 8 9d = Dog("wangcai") 10d.eat()
1.3 屬性方法(@property)
屬性方法的作用就是通過@property把一個方法變成一個靜態屬性 (函數–>變量)
1class Dog(object): 2 def __init__(self,name): 3 self.name = name 4 @property 5 def eat(self): 6 print("%s is eating" % self.name) 7 8d = Dog("wangcai") 9d.eat()
調用會出以下錯誤, 說NoneType is not callable, 因爲eat此時已經變成一個靜態屬性了,不是方法了, 想調用已經不需要加()號了,直接d.eat就可以了
1TypeError: 'NoneType' object is not callable
正常調用如下
1d.eat 2# 輸出 3wangcai is eating
但是有個問題,如果eat有其他參數,沒法傳參數。而且即使變成了靜態屬性,也沒法像普通變量那樣“=”賦值 怎麼傳參數呢?
- 屬性方法賦值
1class Dog(object): 2 def __init__(self,name): 3 self.name = name 4 self.__food = None 5 @property 6 def eat(self): 7 print("%s is eating %s" %(self.name, self.__food)) 8 @eat.setter # 賦值調用屬性,調這個方法 9 def eat(self,food): 10 print("set to food:",food) 11 self.__food = food 12 13d = Dog("wangcai") 14d.eat 15d.eat = "baozi" 16d.eat
如果傳多個參數,‘d.eat = “baozi1”,”baozi2”’,接收爲元組形式。
- 刪除屬性方呢 執行del刪除
1del d.eat
報錯:
1AttributeError: can't delete attribute
默認不能刪除,要刪除也是在類裏再寫一個方法
1class Dog(object): 2 def __init__(self,name): 3 self.name = name 4 self.__food = None 5 @property 6 def eat(self): 7 print("%s is eating %s" %(self.name, self.__food)) 8 @eat.setter # 賦值調用屬性,調這個方法 9 def eat(self,food): 10 print("set to food:",food) 11 self.__food = food 12 @eat.deleter # 刪除屬性 13 def eat(self): 14 del self.__food 15 print("Delete the finished") 16 17d = Dog("wangcai") 18d.eat 19d.eat = "baozi" 20d.eat # 傳完參數後調用 21del d.eat 22d.eat # 刪完後調用
報錯如下,說明已經刪除了
1AttributeError: 'Dog' object has no attribute '_Dog__food'
好吧,把一個方法變成靜態屬性有什麼卵用呢?既然想要靜態變量,那直接定義成一個靜態變量不就得了麼?well, 以後你會需到很多場景是不能簡單通過 定義 靜態屬性來實現的, 比如 ,你想知道一個航班當前的狀態,是到達了、延遲了、取消了、還是已經飛走了, 想知道這種狀態你必須經歷以下幾步:
- 連接航空公司API查詢
- 對查詢結果進行解析
- 返回結果給你的用戶
因此這個status屬性的值是一系列動作後纔得到的結果,所以你每次調用時,其實它都要經過一系列的動作才返回你結果,但這些動作過程不需要用戶關心, 用戶只需要調用這個屬性就可以.
2 類的特殊成員方法
2.1 __doc__ 表示類的描述信息
1class Foo: 2 """ 描述類信息 """ 3 def func(self): 4 pass 5 6print Foo.__doc__
2.2 __module__和__class__
__module__ 表示當前操作的對象在那個模塊 __class__ 表示當前操作的對象的類是什麼 aa.py
1class C: 2 3 def __init__(self): 4 self.name = 'fgf'
index.py
1from aa import C 2 3obj = C() 4print obj.__module__ # 輸出 aa,即:輸出模塊 5print obj.__class__ # 輸出 aa.C,即:輸出類
2.3 __init__ 構造方法
通過類創建對象時,自動觸發執行。
2.4 __del__ 析構方法
當對象在內存中被釋放時,自動觸發執行。
2.5 __call__方法
對象後面加括號,觸發執行。 注:構造方法的執行是由創建對象觸發的,即:對象 = 類名() ;而對於 call 方法的執行是由對象後加括號觸發的,即:對象() 或者 類()()
1class Foo: 2 def __init__(self): 3 pass 4 5 def __call__(self, *args, **kwargs): 6 print ('__call__',args,kwargs) 7 8obj = Foo() # 執行 __init__ 9obj(1,2,3,a=4,b=5) # 執行 __call__
2.6 __dict__ 方法
查看類或對象中的所有成員
1class Province: 2 country = 'China' 3 def __init__(self, name, count): 4 self.name = name 5 self.count = count 6 def func(self, *args, **kwargs): 7 print ('func') 8 9# 獲取類的成員,即:靜態字段、方法、 10print (Province.__dict__) 11# 輸出:{'country': 'China', '__module__': '__main__', 'func': <function func at 0x10be30f50>, '__init__': <function __init__ at 0x10be30ed8>, '__doc__': None} 12 13obj1 = Province('HeBei',10000) 14# 獲取 對象obj1 的成員 15print (obj1.__dict__) 16# 輸出:{'count': 10000, 'name': 'HeBei'}
2.7 __str__ 方法
如果一個類中定義了str方法,那麼在打印對象 時,默認輸出該方法的返回值。
1class Foo: 2 def __str__(self): 3 return 'fgf' 4 5obj = Foo() 6print (obj)
定以後,輸入定義的值,不定義默認返回對象地址“<main.Foo object at 0x0000027BF421E9E8>”
2.8 __getitem__、__setitem__、__delitem__
用於索引操作,如字典。以上分別表示獲取、設置、刪除數據
1class Foo(object): 2 def __init__(self): 3 self.data = {} 4 def __getitem__(self, key): 5 print('__getitem__',key) 6 return self.data.get(key) 7 def __setitem__(self, key, value): 8 print('__setitem__',key,value) 9 self.data[key] = value 10 def __delitem__(self, key): 11 print('__delitem__',key) 12 13obj = Foo() 14 15obj['name'] = 'fgf' # 設置,自動觸發執行 __setitem__ 16print(obj.data) 17print(obj['name']) # 獲取值,自動觸發執行 __getitem__ 18del obj['name'] # 觸發__delitem__,只是調用那個方法,具體刪不刪看自己配置
2.9 類的起源 __new__和 __metaclass__
1class Foo(object): 2 def __init__(self,name): 3 self.name = name 4f = Foo("fgf")
上述代碼中,f 是通過 Foo類實例化的對象,其實,不僅f是一個對象,Foo類本身也是一個對象,因爲在Python中一切事物都是對象。 如果按照一切事物都是對象的理論:obj對象是通過執行Foo類的構造方法創建,那麼Foo類對象應該也是通過執行某個類的 構造方法 創建。
1print type(f) # 輸出:<class '__main__.Foo'> 表示,obj 對象由Foo類創建 2print type(Foo) # 輸出:<type 'type'> 表示,Foo類對象由 type 類創建
所以,f對象是Foo類的一個實例,Foo類對象是 type 類的一個實例,即:Foo類對象 是通過type類的構造方法創建。 那麼,創建類就可以有兩種方式:
- 普通方式(也是通過type創建的類,只是已經封裝好了)
1class Foo(object): 2 3 def func(self): 4 print 'hello fgf'
- 特殊方式
1def func(self): 2 print('hello fgf') 3 4Foo = type('Foo',(object,), {'talk': func}) 5# type第一個參數:類名 6# type第二個參數:當前類的基類 7# type第三個參數:類的成員 8f = Foo() 9f.talk()
f是Foo的對象,Foo又是type的對象,所以type又稱類的類 加上構造函數
1def func(self): 2 print("hello %s %s"%(self.name,self.age)) 3 4def __init__(self,name,age): 5 self.name = name 6 self.age = age 7Foo = type('Foo',(object,),{'func':func, 8 '__init__':__init__}) 9 10f = Foo("fgf",22) 11f.func()
So ,記住,類 是由 type類實例化產生,那麼問題來了,類默認是由type類實例化產生,type類中如何實現的創建類?類又是如何創建對象? 答:類中有一個屬性 __metaclass__,其用來表示該類由 誰來實例化創建,所以,我們可以爲 __metaclass__ 設置一個type類的派生類,從而查看類創建的過程。 以下代碼python2裏運行看效果
1class MyType(type): 2 def __init__(self, what, bases=None, dict=None): 3 print("--MyType init---") 4 super(MyType, self).__init__(what, bases, dict) 5 def __call__(self, *args, **kwargs): 6 print("--MyType call---") 7 obj = self.__new__(self, *args, **kwargs) 8 self.__init__(obj, *args, **kwargs) 9 10class Foo(object): 11 __metaclass__ = MyType 12 def __init__(self, name): 13 self.name = name 14 print("Foo ---init__") 15 def __new__(cls, *args, **kwargs): 16 # __new__是用來創建實例的,定製類,先運行new裏調用init,這裏寫,對默認的重構 17 print("Foo --new--") 18 # print(object.__new__(cls)) 19 return object.__new__(cls) # 返回給init,cls這代表Foo,相當於對象的self 20 # 調用父類的__new__方法 21 22# 第一階段:解釋器從上到下執行代碼創建Foo類 23# 第二階段:通過Foo類創建obj對象 24obj = Foo("Fgf")
(默認之前應該還有個myType.new)先執行myType.init,再執行myType.call,再執行Foo.new,最後Foo.init
3 反射
如果用戶輸入信息如”fgf”,通過輸入字符串”fgf”去調用實例的屬性,怎麼實現。 不可能”name=input()”,再用name去調用fgf屬性,那樣調用的是name而不是fgf。 要想把用戶輸入字符串轉爲一個變量名,而不是一個值就需要用到: 反射(實現用戶輸入字符串爲類的方法) 通過字符串映射或修改程序運行時的狀態、屬性、方法, 有以下4個方法 attr –> attribute [əˈtrɪbjut] 屬性; (人或物的) 特徵
- hasattr(obj,name_str)
判斷object中有沒有一個name字符串對應的方法或屬性
1class Foo(object): 2 # name = '' 3 def __int__(self): 4 self.name = 'fgf' 5 def func(self): 6 return "func" 7 8obj = Foo() 9 10print(hasattr(obj, 'name')) # 輸出False 11print(hasattr(obj, 'func')) # 輸出True
- getattr(obj,name_str)
根據字符串去獲取obj對應方法的內存地址
1def getattr(object, name, default=None): 2 """ 3 getattr(object, name[, default]) -> value 4 Get a named attribute from an object; getattr(x, 'y') is equivalent to x.y. 5 """
1class Foo(object): 2 def __init__(self): 3 self.name = 'fgf' 4 def func(self): 5 print(self.name,'say Hi') 6 return "func" 7 8obj = Foo() 9print(getattr(obj,'func')) 10getattr(obj, 'func')() # # same as: obj.func()
- setattr(obj,name_str,func)
動態把一個函數裝到類裏面
1def setattr(x, y, v): # real signature unknown; restored from __doc__ 2 """ 3 setattr(x, 'y', v) is equivalent to ``x.y = v'' 4 """
1def func1(self): 2 print(self.name,'say Hi') 3 return "func" 4 5class Foo(object): 6 def __init__(self): 7 self.name = 'fgf' 8 def func2(self): 9 print(self.name,'say fu') 10 return "func" 11obj = Foo() 12 13choice = input("請輸入調用方法名[func1|func2]:") 14if hasattr(obj,choice): # 實例中有這個方法,執行實例中的方法 15 print(getattr(obj,choice)) 16else: # 動態加載函數封裝到類中 17 setattr(obj,choice,func1) 18 func = getattr(obj,choice) 19 func(obj)
- delattr()
刪除set添加的屬性
1def delattr(x, y): # real signature unknown; restored from __doc__ 2 """ 3 delattr(x, 'y') is equivalent to ``del x.y'' 4 """
1class Foo(object): 2 def __init__(self): 3 self.name = 'fgf' 4 def func(self): 5 print(self.name,'say Hi') 6 7obj = Foo() 8 9setattr(obj, 'age', 18) 10print(obj.name,obj.age) 11# delattr(obj, 'func') 12delattr(obj,'age') # 只能刪除setattr動態添加的,默認的不可以刪除 13print(obj.name,obj.age)
動態導入模塊
1import importlib 2 3__import__('import_lib.metaclass') #這是解釋器自己內部用的 4#importlib.import_module('import_lib.metaclass') #與上面這句效果一樣,官方建議用這個
isinstance(obj, cls):檢查是否obj是否是類 cls 的對象 issubclass(sub, super):檢查sub類是否是 super 類的派生類
4 異常處理
在編程過程中爲了增加友好性,在程序出現bug時一般不會將錯誤信息顯示給用戶,而是現實一個提示的頁面
1try: 2 diction = {} 3 diction[1] 4 names = [] 5 names[2] 6except IndexError as e: # python3.x 裏不是逗號,都是as 7 print(e) 8except (KeyError,IndexError) as e: # 採用統一處理辦法 9 print(e) 10except Exception as e: # 抓住所有錯誤 11 print(e) 12else: # 沒出錯執行這個 13 pass 14finally: # 不管有沒有錯,都執行 15 pass 16 17try: 18 status = 1 19 if status != 0 : 20 raise Exception("自定義異常") 21except Exception as e: 22 print(e)
聲明:本文系網絡轉載,版權歸原作者所有。如涉及版權,請聯繫刪除!