面向對象進階 面向對象高級語法部分 靜態方法、類方法、屬性方法 類的特殊方法 反射 異常處理 1、靜態方法(@staticmethod) 通過@staticmethod裝飾器即可把其裝飾的方法變為一個靜態方法。 普通的方法,可以在實例化後直接調用,並且在方法里可以通過self.調用實例變數或類變數, ...
面向對象進階
總結、補充(http://blog.csdn.net/fgf00/article/details/52479307)
面向對象高級語法部分
靜態方法、類方法、屬性方法
類的特殊方法
反射
動態綁定屬性,限制綁定( __slots__
)
異常處理
一、 面向對象高級語法部分
1、靜態方法(@staticmethod)
通過@staticmethod裝飾器即可把其裝飾的方法變為一個靜態方法。
普通的方法,可以在實例化後直接調用,並且在方法里可以通過self.調用實例變數或類變數,但靜態方法是不可以訪問實例變數或類變數的,
一個不能訪問實例變數和類變數的方法,其實相當於跟類本身已經沒什麼關係了,它與類唯一的關聯就是需要通過類名來調用這個方法.
class Person(object):
def __init__(self,name,age):
self.name = name
self.age = age
@staticmethod # 把eat方法變為靜態方法
def eat(self):
print("%s is eating" %self.name)
d = Person("xiaoming", 18)
d.eat()
上面的調用會出以下 錯誤
TypeError: eat() missing 1 required positional argument: 'self'
解決方法(2種):
1. 調用時主動傳遞實例本身給eat方法,即 `d.eat(d)`
2. 在eat方法中去掉self參數,但這也意味著,在eat中不能通過self.調用實例中的其它變數了
作用:只是相當於一個單純函數,要傳參數,就要把實例傳進去。
如果說和類有關係,就是必須有類名去調用。調用不了類或實例中的任何屬性
2、類方法(@classmethod)
類方法通過@classmethod裝飾器實現,類方法和普通方法的區別是:
類方法只能訪問類變數,不能訪問實例變數
class Person(object):
def __init__(self,name,age):
self.name = name
self.age = age
@classmethod
def eat(self):
print("%s is eating" %self.name)
d = Person("xiaoming", 18)
d.eat()
執行報錯如下, ` AttributeError: type object 'Dog' has no attribute 'name' ` ,Dog沒有name屬性,因為name是個實例變數,類方法是不能訪問實例變數的
此時可以定義一個類變數,也叫name,看下執行效果:
class Person(object):
name = "xiaohong" # 類變數
def __init__(self,name,age):
self.name = name
self.age = age
@classmethod
def eat(self):
print("%s is eating" %self.name)
d = Person("xiaoming", 18)
d.eat()
3、屬性方法(@property)
屬性方法的作用就是通過@property把一個方法變成一個靜態屬性 (函數–>變數)
class Person(object):
name = "xiaohong" # 類變數
def __init__(self,name,age):
self.name = name
self.age = age
@property
def eat(self):
print("%s is eating" %self.name)
d = Person("xiaoming", 18)
d.eat()
可能報錯 ` TypeError: 'NoneType' object is not callable `
正常調用如下:
d.eat
# 輸出
xiaoming is eating
傳參:
屬性方法賦值:
d.eat = "baozi"
多個參數時:d.eat = "baozi", "baozi2"
接收為元組形式
刪除屬性方法:
del d.eat
報錯:AttributeError: can't delete attribute
類中定義以下方法即可:
@eat.deleter # 刪除屬性
def eat(self):
del self.__food
print("Delete the finished")
此時代碼:
class Person(object):
name = "xiaohong" # 類變數
def __init__(self,name,age):
self.name = name
self.age = age
@property
def eat(self):
print("%s is eating" %self.name)
@eat.setter # 賦值調用屬性,調這個方法
def eat(self,food):
print("set to food:",food)
self.__food = food
@eat.deleter # 刪除屬性
def eat(self):
del self.__food
print("Delete the finished")
d = Person("xiaoming", 18)
d.eat()
d.eat = "baozi"
d.eat # 傳完參數後調用
del d.eat
d.eat # 刪完後調用
此時報錯: AttributeError: 'Dog' object has no attribute '_Dog__food'
,說明已刪除。
好吧,把一個方法變成靜態屬性有什麼卵用呢?既然想要靜態變數,那直接定義成一個靜態變數不就得了麽?well, 以後你會需到很多場景是不能簡單通過 定義 靜態屬性來實現的, 比如 ,你想知道一個航班當前的狀態,是到達了、延遲了、取消了、還是已經飛走了, 想知道這種狀態你必須經歷以下幾步:
- 連接航空公司API查詢
- 對查詢結果進行解析
- 返回結果給你的用戶
因此這個status屬性的值是一系列動作後才得到的結果,所以你每次調用時,其實它都要經過一系列的動作才返回你結果,但這些動作過程不需要用戶關心, 用戶只需要調用這個屬性就可以
二、類的特殊成員方法
1. __doc__
表示類的描述信息
2. __module__
和 __class__
__module__ 返回當前操作的對象對應的模塊名
__class__ 表示當前操作的對象的類名
3. __init__
構造方法,通過類 ** 創建 ** 對象時,自動觸發執行。
4. __call__
對象後面加括弧 ** 執行 **,觸發執行。
5. __del__
析構方法,當對象在記憶體中被釋放時,自動觸發執行。
註:構造方法的執行是由創建對象觸發的,即:對象 = 類名() ;而對於 call 方法的執行是由對象後加括弧觸發的,即:對象() 或者 類()()
6. __dict__
查看類或對象中的所有成員
7. __str__
如果一個類中定義了_str_方法,那麼在列印對象時,預設輸出該方法的返回值。(不定義此方法,預設返回對象地址)
8. __iter__
如果一個類想被用於for ... in迴圈,類似list或tuple那樣,就必須實現一個__iter__()方法,該方法返回一個迭代對象,
然後,Python的for迴圈就會不斷調用該迭代對象的 `__next__()` 方法拿到迴圈的下一個值,
直到遇到StopIteration錯誤時退出迴圈。
我們以斐波那契數列為例,寫一個Fib類,可以作用於for迴圈:
class Fib(object):
def __init__(self):
self.a, self.b = 0, 1 # 初始化兩個計數器a,b
def __iter__(self):
return self # 實例本身就是迭代對象,故返回自己
def __next__(self):
self.a, self.b = self.b, self.a + self.b # 計算下一個值
if self.a > 100000: # 退出迴圈的條件
raise StopIteration()
return self.a # 返回下一個值
結果:
>>> for n in Fib():
... print(n)
...
1
1
2
3
5
...
46368
75025
9. __getitem__
、__setitem__
、__delitem__
用於索引操作,如字典。以上分別表示獲取、設置、刪除數據 (可以直接用 * 對象[] * 索引)
class Foo(object):
def __init__(self):
self.data = {}
def __getitem__(self, key):
print('__getitem__',key)
return self.data.get(key)
def __setitem__(self, key, value):
print('__setitem__',key,value)
self.data[key] = value
def __delitem__(self, key):
print('__delitem__',key)
obj = Foo()
obj['name'] = 'fgf' # 設置,自動觸發執行 __setitem__
print(obj.data)
print(obj['name']) # 獲取值,自動觸發執行 __getitem__
del obj['name'] # 觸發__delitem__,只是調用那個方法,具體刪不刪看自己配置
10. 類的起源 __new__
和 __metaclass__
( type 元類 )
class Foo(object):
def __init__(self,name):
self.name = name
f = Foo("fgf")
上述代碼中,可知 f 是通過 Foo類 實例化的對象,萬物皆對象,其實 Foo類 也是對象
f 通過執行 Foo類 的構造方法創建, Foo類 通過執行 ** type ** 類的構造方法創建。
所以type又稱類的類。
print type(f) # 輸出:<class '__main__.Foo'> 表示,f 對象由Foo類創建
print type(Foo) # 輸出:<type 'type'> 表示,Foo類對象由 type 類創建
由此,創建類的方法就有 * 兩種 * :
普通方法(通過type創建的類,只是已經封裝好了):
class Cat(object): pass
特殊方法:
# 定義方法
def func(self):
print('hello fgf')
# 創建構造函數
def __init__(self,name,age):
self.name = name
self.age = age
# 創建類
Foo = type('Foo',(object,), {'talk': func, '__init__':__init__})
# type第一個參數:類名
# type第二個參數:當前類的基類(父類)
# type第三個參數:類的成員(字典形式)
f = Foo("fgf",18)
f.talk()
類是由type實例化產生,那麼type類是如何創建的?類又是如何創建對象的?
類中屬性: ** __mateclass__** ,即用於表示該類時由誰來實例化創建。
下麵可為__metaclass__設置一個type類的派生類,從而瞭解類創建的過程。
class MyType(type):
def __init__(self, what, bases=None, dict=None):
print("--MyType init---")
super(MyType, self).__init__(what, bases, dict)
def __call__(self, *args, **kwargs):
print("--MyType call---")
obj = self.__new__(self, *args, **kwargs)
self.__init__(obj, *args, **kwargs)
class Foo(object):
__metaclass__ = MyType
def __init__(self, name):
self.name = name
print("Foo ---init__")
def __new__(cls, *args, **kwargs):
# __new__是用來創建實例的,定製類,先運行new里調用init,這裡寫,對預設的重構
print("Foo --new--")
# print(object.__new__(cls))
return object.__new__(cls) # 返回給init,cls這代表Foo,相當於對象的self
# 調用父類的__new__方法
# 第一階段:解釋器從上到下執行代碼創建Foo類
# 第二階段:通過Foo類創建obj對象
obj = Foo("Fgf")
(預設之前應該還有個myType.new)先執行myType.init,再執行myType.call,再執行Foo.new,最後Foo.init
三、 反射
反射(實現用戶輸入字元串為類的方法)
通過字元串映射或修改程式運行時的狀態、屬性、方法, 有以下4個方法
attr –> attribute [əˈtrɪbjut] 屬性; (人或物的) 特征
(直接使用的函數)
1. hasattr(obj,name_str) :判斷object中有沒有一個name_str對應的方法或屬性,返回True或False
2. getattr(obj,name_str):根據字元串去獲取obj對應方法的記憶體地址
def getattr(object, name, default=None):
"""
getattr(object, name[, default]) -> value
Get a named attribute from an object; getattr(x, 'y') is equivalent to x.y.
"""
class Foo(object):
def __init__(self):
self.name = 'fgf'
def func(self):
print(self.name,'say Hi')
return "func"
obj = Foo()
print(getattr(obj,'func'))
getattr(obj, 'func')() # # same as: obj.func()
3. setattr(obj,name_str,value)動態把一個屬性或函數裝到類裡面
例:對象ojb,函數 `def func1: pass` 添加: ` setattr(obj, 'func1', func1) `
4. delattr(obj, name_str) (只能)刪除setattr添加的屬性
5. isinstance(obj, cls):檢查是否obj是否是類 cls 的對象
6. issubclass(sub, super):檢查sub類是否是 super 類的派生類
7. 動態導入模塊
import importlib
__import__('import_lib.metaclass') #這是解釋器自己內部用的
#importlib.import_module('import_lib.metaclass') #與上面這句效果一樣,官方建議用這個
四、 動態綁定類成員,限制綁定(slots)
class Student(object):
pass
s = Student()
- 動態綁定類成員:
- 動態添加實例的屬性
s.name = 'xiaoming'
動態添加實例的函數from types import MethodType
from types import MethodType
class Student(object):
pass
s = Student()
def set_age(self, age):
self.age = age
s.set_age = MethodType(set_age, s) # 給實例綁定一個方法
s.set_age(25) # 調用實例方法
s.age # 測試結果
< ** 此綁定只適用於單個實例,對其他實例無用。**
為了給所有實例都綁定方法,可以給 * class * 綁定方法:
- 動態綁定類成員
Student.set_score = set_score
def set_score(self, score):
self.score = score
Student.set_score = set_score
s, s2 = Student()
# s, s2都可調用set_score()
s.set_score(100)
s.score
s2.set_score(99)
s2.score
- 限制類成員 slots
class Student(object):
slots = ('name', 'age') # 用tuple定義允許綁定的屬性名稱
註意: __slots__
定義的屬性僅對當前類實例起作用,對繼承的 子類 是不起作用的:
如果在子類中也定義__slots__,這樣,子類實例允許定義的屬性就是 *自身的__slots__加上父類的__slots__*
五、異常處理
在編程過程中為了增加友好性,在程式出現bug時一般不會將錯誤信息顯示給用戶,而是現實一個提示的頁面
try:
diction = {}
diction[1]
names = []
names[2]
except IndexError as e: # python3.x 里不是逗號,都是as
print(e)
except (KeyError,IndexError) as e: # 採用統一處理辦法
print(e)
except Exception as e: # 抓住所有錯誤
print(e)
else: # 沒出錯執行這個
pass
finally: # 不管有沒有錯,都執行
pass
try:
status = 1
if status != 0 :
raise Exception("自定義異常")
except Exception as e:
print(e)