目錄
- new
- str & repr
- iter
- getitem、setitem、delitem
- getattr、setattr、delattr
- call
- slots
定制類和魔法方法
- new
- str , repr
- iter
- getitem , setitem , delitem
- getattr , setattr , delattr
- call
new
在 Python 中,當我們創建一個類的實體時,類會先呼叫 new(cls[, ...]) 來創建實體,然后 init 方法再對該實體(self)進行初始化,
關于 new 和 init 有幾點需要注意:
new 是在 init 之前被呼叫的;
new 是類方法,init 是實體方法;
多載 new 方法,需要回傳類的實體;
一般情況下,我們不需要多載 new 方法,但在某些情況下,我們想控制實體的創建程序,這時可以通過多載 _new 方法來實作,
class A(object):
_dict = dict()
def __new__(cls):
if 'key' in A._dict:
print "EXISTS"
return A._dict['key']
else:
print "NEW"
return object.__new__(cls)
def __init__(self):
print "INIT"
A._dict['key'] = self
str & repr
class Foo(object):
def __init__(self, name):
self.name = name
def __str__(self):
return 'Foo object (name: %s)' % self.name
def __repr__(self):
return 'Foo object (name: %s)' % self.name
print Foo('ethan') # 使用 print
Foo object (name: ethan)str(Foo('ethan')) # 使用 str
'Foo object (name: ethan)'Foo('ethan') # 直接顯示
<main.Foo at 0x10c37a490>Foo('ethan') # 使用repr(類中實作)
'Foo object (name: ethan)'
iter
在某些情況下,我們希望實體物件可被用于 for...in 回圈,這時我們需要在類中定義 iter 和 next(在 Python3 中是 next)方法,其中,iter 回傳一個迭代物件,next 回傳容器的下一個元素,在沒有后續元素時拋出 StopIteration 例外.
看一個斐波那契數列的例子:
class Fib(object):
def __init__(self):
self.a, self.b = 0, 1
def __iter__(self): # 回傳迭代器物件本身
return self
def next(self): # 回傳容器下一個元素
self.a, self.b = self.b, self.a + self.b
return self.a
fib = Fib()
for i in fib:
... if i > 10:
... break
... print i
getitem、setitem、delitem
geitem 用于獲取值,類似地,setitem 用于設定值,delitem 用于洗掉值,讓我們看下面一個例子:
class Point(object):
def __init__(self):
self.coordinate = {}
def __str__(self):
return "point(%s)" % self.coordinate
def __getitem__(self, key):
return self.coordinate.get(key)
def __setitem__(self, key, value):
self.coordinate[key] = value
def __delitem__(self, key):
del self.coordinate[key]
print 'delete %s' % key
def __len__(self):
return len(self.coordinate)
__repr__ = __str__
在上面,我們定義了一個 Point 類,它有一個屬性 coordinate(坐標),是一個字典,讓我們看看使用:
>>> p = Point()
>>> p['x'] = 2 # 對應于 p.__setitem__('x', 2)
>>> p['y'] = 5 # 對應于 p.__setitem__('y', 5)
>>> p # 對應于 __repr__
point({'y': 5, 'x': 2})
>>> len(p) # 對應于 p.__len__
2
>>> p['x'] # 對應于 p.__getitem__('x')
2
>>> p['y'] # 對應于 p.__getitem__('y')
5
>>> del p['x'] # 對應于 p.__delitem__('x')
delete x
>>> p
point({'y': 5})
>>> len(p)
1
getattr、setattr、delattr
當我們獲取物件的某個屬性,如果該屬性不存在,會拋出 AttributeError 例外,比如:
class Point(object):
def __init__(self, x=0, y=0):
self.x = x
self.y = y
>>> p = Point(3, 4)
>>> p.x, p.y
(3, 4)
>>> p.z
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-547-6dce4e43e15c> in <module>()
----> 1 p.z
AttributeError: 'Point' object has no attribute 'z'
那有沒有辦法不讓它拋出例外呢?當然有,只需在類的定義中加入 getattr 方法,比如:
class Point(object):
def __init__(self, x=0, y=0):
self.x = x
self.y = y
def __getattr__(self, attr):
if attr == 'z':
return 0
>>> p = Point(3, 4)
>>> p.z
0
現在,當我們呼叫不存在的屬性(比如 z)時,解釋器就會試圖呼叫 getattr(self, 'z') 來獲取值,但是,上面的實作還有一個問題,當我們呼叫其他屬性,比如 w ,會回傳 None,因為 getattr 默認回傳就是 None,只有當 attr 等于 'z' 時才回傳 0,如果我們想讓 getattr 只回應幾個特定的屬性,可以加入例外處理,修改 getattr 方法,如下:
def __getattr__(self, attr):
if attr == 'z':
return 0
raise AttributeError("Point object has no attribute %s" % attr)
setattr, delattr
class Point(object):
def __init__(self, x=0, y=0):
self.x = x
self.y = y
def __getattr__(self, attr):
if attr == 'z':
return 0
raise AttributeError("Point object has no attribute %s" % attr)
def __setattr__(self, *args, **kwargs):
print 'call func set attr (%s, %s)' % (args, kwargs)
return object.__setattr__(self, *args, **kwargs)
def __delattr__(self, *args, **kwargs):
print 'call func del attr (%s, %s)' % (args, kwargs)
return object.__delattr__(self, *args, **kwargs)
>>> p = Point(3, 4)
call func set attr (('x', 3), {})
call func set attr (('y', 4), {})
>>> p.z
0
>>> p.z = 7
call func set attr (('z', 7), {})
>>> p.z
7
>>> p.w
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 8, in __getattr__
AttributeError: Point object has no attribute w
>>> p.w = 8
call func set attr (('w', 8), {})
>>> p.w
8
>>> del p.w
call func del attr (('w',), {})
>>> p.__dict__
{'y': 4, 'x': 3, 'z': 7}
call
我們一般使用 obj.method() 來呼叫物件的方法,那能不能直接在實體本身上呼叫呢?在 Python 中,只要我們在類中定義 call 方法,就可以對實體進行呼叫,比如下面的例子:
class Point(object):
def __init__(self, x, y):
self.x, self.y = x, y
def __call__(self, z):
return self.x + self.y + z
使用如下:
>>> p = Point(3, 4)
>>> callable(p) # 使用 callable 判斷物件是否能被呼叫
True
>>> p(6) # 傳入引數,對實體進行呼叫,對應 p.__call__(6)
13 # 3+4+6
slots
在 Python 中,我們在定義類的時候可以定義屬性和方法,當我們創建了一個類的實體后,我們還可以給該實體系結任意新的屬性和方法,
看下面一個簡單的例子:
class Point(object):
def __init__(self, x=0, y=0):
self.x = x
self.y = y
>>> p = Point(3, 4)
>>> p.z = 5 # 系結了一個新的屬性
>>> p.z
5
>>> p.__dict__
{'x': 3, 'y': 4, 'z': 5}
在上面,我們創建了實體 p 之后,給它系結了一個新的屬性 z,這種動態系結的功能雖然很有用,但它的代價是消耗了更多的記憶體,
因此,為了不浪費記憶體,可以使用 slots 來告訴 Python 只給一個固定集合的屬性分配空間,對上面的代碼做一點改進,如下:
class Point(object):
__slots__ = ('x', 'y') # 只允許使用 x 和 y
def __init__(self, x=0, y=0):
self.x = x
self.y = y
上面,我們給 slots 設定了一個元組,來限制類能添加的屬性,現在,如果我們想系結一個新的屬性,比如 z,就會出錯了,如下:
>>> p = Point(3, 4)
>>> p.z = 5
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-648-625ed954d865> in <module>()
----> 1 p.z = 5
AttributeError: 'Point' object has no attribute 'z'
注意:
使用 slots 有一點需要注意的是,slots 設定的屬性僅對當前類有效,對繼承的子類不起效,除非子類也定義了 slots,這樣,子類允許定義的屬性就是自身的 slots 加上父類的 slots,
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/99226.html
標籤:其他
