深入理解python物件導向-類特殊成員
類成員的修飾符
類的所有成員在上一篇已經做了詳細的介紹,對於每一個類的成員都有兩種形式:公有成員、私有成員。成員定義是以雙下劃線開頭,就是私有成員。除了一些特殊成員除外,例如:__init__、__call__、__dict__、__del__等,剩下的都是公有成員。
class Base:
def __init__(self):
self.name = '公有欄位'
self.__foo = "私有欄位"
私有成員和公有成員的訪問級別不同:
普通欄位
- 公有普通欄位:物件可以訪問;類內部可以訪問;派生類中可以訪問
- 私有普通欄位:僅類內部可以訪問;
私有欄位其實不是不能訪問,只是Python直譯器對私有成員命名做了更改,物件._類名__私有欄位名,例如:obj._Base__foo
不建議強制訪問私有成員。
普通公有欄位例子
class Base:
def __init__(self):
self.foo = '公有欄位'
def func(self):
print(self.foo) # 類內部訪問
class Device(Base):
def show(self):
print(self.foo) # 派生類中訪問
obj = Base()
print(obj.foo) # 物件訪問
obj.func() # 類內部訪問
obj_son = Device()
obj_son.show() # 派生類訪問
普通私有欄位例子
class Base:
def __init__(self):
self.__foo = '私有欄位'
def func(self):
print(self.__foo) # 類內部訪問
class Device(Base):
def show(self):
print(self.__foo) # 派生類中訪問
obj = Base()
print(obj.__foo) # 物件訪問 報錯:AttributeError: 'Base' object has no attribute '__foo'
obj.func() # 類內部訪問
obj_son = Device()
obj_son.show() # 派生類訪問 報錯:AttributeError: 'Device' object has no attribute '_Device__foo'
方法、屬性的訪問都是相似的,即:私有成員只能在類內部使用
靜態欄位
- 公有靜態欄位:類可以訪問;類內部可以訪問;派生類中可以訪問
- 私有靜態欄位:僅類內部可以訪問;
靜態公有欄位例子
class Base:
name = "公有靜態欄位"
def func(self):
print(Base.name)
class Device(Base):
def show(self):
print(Base.name)
Base.name # 類訪問
obj = Base()
obj.func() # 類內部可以訪問
obj_son = Device()
obj_son.show() # 派生類中可以訪問
靜態私有欄位例子
class Base:
__name = "公有靜態欄位"
def func(self):
print(Base.__name)
class Device(Base):
def show(self):
print(Base.__name)
Base.__name # 類訪問 報錯:AttributeError: type object 'Base' has no attribute '__name'
obj = Base()
obj.func() # 類內部訪問
obj_son = Device()
obj_son.show() # 派生類訪問 報錯:AttributeError: type object 'Base' has no attribute '_Device__name'
透過上面的例子,你應該發現了一個問題,在普通公有欄位中,子類可以透過self.foo訪問父類定義的變數;在靜態公有欄位中,使用的是Base.name訪問。那為什麼不能使用self訪問呢?上一篇我們其實講過的,靜態欄位屬於類所有,在類中只儲存一份,所以它與繼承無關,不管經過多少重繼承,靜態欄位只有一份,只能透過類自身來訪問。而普通欄位是屬於物件的,所以繼承以後,每一個繼承類的物件都會儲存一份。
類的特殊成員
上面我們講了類成員以及成員修飾符,知道了類中有欄位、方法和屬性,並且有公有和私有兩種訪問限制。但是還是存在著一些具有特殊含義的成員,詳情如下:
- __doc__
表示類的描述資訊
庫函式:range
class range(object):
"""
range(stop) -> range object
range(start, stop[, step]) -> range object
Return an object that produces a sequence of integers from start (inclusive)
to stop (exclusive) by step. range(i, j) produces i, i+1, i+2, ..., j-1.
start defaults to 0, and stop is omitted! range(4) produces 0, 1, 2, 3.
These are exactly the valid indices for a list of 4 elements.
When step is given, it specifies the increment (or decrement).
"""
def count(self, value): # real signature unknown; restored from __doc__
""" rangeobject.count(value) -> integer -- return number of occurrences of value """
return 0
print(range.__doc__)
#輸出結果:
range(stop) -> range object
range(start, stop[, step]) -> range object
Return an object that produces a sequence of integers from start (inclusive)
to stop (exclusive) by step. range(i, j) produces i, i+1, i+2, ..., j-1.
start defaults to 0, and stop is omitted! range(4) produces 0, 1, 2, 3.
These are exactly the valid indices for a list of 4 elements.
When step is given, it specifies the increment (or decrement).
當我們在類上面加上一些說明,透過此方法可以進行檢視,同樣也可以檢視庫函式的說明
- __module__ 和 class
__module__ 表示當前操作的物件在那個模組
__class__ 表示當前操作的物件的類是什麼
class Foo:
def func(self):
pass
f = Foo()
print(f.__module__) #輸出:__main__
print(f.__class__) #輸出:<class '__main__.Foo'>
from multiprocessing import Process
p = Process()
print(p.__module__) #輸出:multiprocessing.context
print(p.__class__) #輸出:<class 'multiprocessing.context.Process'>
- __init__
構造方法,透過類建立物件時,自動觸發執行。
class Foo:
def __init__(self, name):
self.name = name
self.age = 18
obj = Foo('wupeiqi') # 自動執行類中的 __init__ 方法
注意:Python這裡的構造方法與C++不一樣
class Foo:
instance = None
def __init__(self):
print("__init__")
@classmethod
def __new__(cls, *args, **kwargs):
if not cls.instance:
cls.instance = object.__new__(cls)
print("__new__")
return cls.instance
f1 = Foo()
f2 = Foo()
print(id(f1), id(f2))
#輸出:
__new__
__init__
__init__
4343767560 4343767560
可以看到,f1和f2是同一個物件,佔據同一塊記憶體,也就是說在記憶體中只建立了一個物件,但是建構函式呼叫了兩次。所以這裡的建立物件時,自動觸發並不是特別準確,使用的時候要多注意。
- __del__
析構方法,當物件在記憶體中被釋放時,自動觸發執行。
此方法一般無須定義,因為Python是一門高階語言,程式設計師在使用時不需要關心記憶體的分配和釋放,因為都是交給Python直譯器來執行,所以解構函式的呼叫是由直譯器在進行垃圾回收時自動觸發執行的。
def __del__(self):
print("del")
還是上面那個例子,加上__del__函式的定義
輸出:
__new__
__init__:
__init__:
4424543312 4424543312
del
可以看到del也是呼叫了一次,再次證實了,物件只建立了一次
- __call__
物件後面加括號,觸發執行,相當於函式呼叫。
構造方法的執行是由建立物件觸發的,即:物件 = 類名() ;而對於 call 方法的執行是由物件後加括號觸發的,即:物件() 或者 類()()
class Foo:
def __init__(self):
pass
def __call__(self, *args, **kwargs):
print '__call__'
obj = Foo() # 執行 __init__
obj() # 執行 __call__
- __dict__
類或物件中的所有成員
上文中我們知道:類的普通欄位屬於物件;類中的靜態欄位和方法等屬於類,即:
class Foo:
instance = None
def __init__(self, name):
print("__init__: ", name)
self.name = name
def func(self):
pass
f1 = Foo("name1")
# 獲取物件的成員,即:普通欄位
print(f1.__dict__) # 輸出:{'name': 'name1', 'age': 123}
# 獲取類的成員,即:靜態欄位、方法、
print(Foo.__dict__) # 輸出:{'__module__': '__main__', 'instance': None, '__init__': <function Foo.__init__ at 0x10df509d8>, 'func': <function Foo.func at 0x1151b79d8>, '__dict__': <attribute '__dict__' of 'Foo' objects>, '__weakref__': <attribute '__weakref__' of 'Foo' objects>, '__doc__': None}
- __str__
如果一個類中定義了__str__方法,那麼在列印物件或者str轉換時,預設輸出該方法的返回值。
class Foo:
def __str__(self):
return 'foo_name'
obj = Foo()
print(obj)
# 輸出:foo_name
print(str(obj))
# 輸出:foo_name
- __getitem__、__setitem__、__delitem__
用於索引操作,如字典、列表。以上分別表示獲取、設定、刪除資料
字典
class Foo:
def __getitem__(self, item):
print("__getitem__: ", item)
def __setitem__(self, key, value):
print("__setitem__: ", key, value)
def __delitem__(self, key):
print("__delitem__: ", key)
obj = Foo()
result = obj["k1"] # 自動觸發__getitem__
obj["k2"] = "name" # 自動觸發__setitem__
del obj["k1"] # 自動觸發__delitem__
列表
class Foo:
def __getitem__(self, item):
print("__getitem__.start: ", item.start)
print("__getitem__.stop: ", item.stop)
print("__getitem__.step: ", item.step)
def __setitem__(self, key, value):
print("__setitem__.index: ", key)
print("__setitem__.value: ", value)
return value
def __delitem__(self, key):
print("__delitem__: ", key)
f = Foo()
f[1:5:2] #自動觸發__getitem__
f[0] = 1 #自動觸發__setitem__
del f[0] #自動觸發__delitem__
#輸出
__getitem__.start: 1
__getitem__.stop: 5
__getitem__.step: 2
__setitem__.index: 0
__setitem__.value: 1
__delitem__: 0
- __iter__
用於迭代器,之所以列表、字典、元組可以進行for迴圈,是因為型別內部定義了 iter
class Foo:
def __init__(self, sq):
self.sq = sq
def __iter__(self):
return iter(self.sq)
obj = Foo([11,22,33,44])
for i in obj:
print(i)
for迴圈迭代的其實是 iter([11,22,33,44]) ,所以相當於如下:
obj = iter([11,22,33,44])
for i in obj:
print(i)
For迴圈迭代器輸出
obj = iter([11,22,33,44])
while True:
try:
val = next(obj)
print(val)
except StopIteration as e:
break
還有__metaclass__也是非常重要的一個,這個牽扯到反射機制,我們放在下一篇進行詳細說明,今天的文章就到這裡了,你有沒有Get到新技能呢?歡迎留言一起探討
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/4301/viewspace-2822993/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- 物件導向類成員物件
- 【python】物件導向之類成員(屬性)Python物件
- python之成員(物件導向)Python物件
- Python基礎(二十):物件導向“類”第三課——類成員Python物件
- Python進階之物件導向(類的特殊方法)Python物件
- 【python】類的特殊成員Python
- Golang 物件導向深入理解Golang物件
- 物件導向 成員和巢狀物件巢狀
- python-程式導向、物件導向、類Python物件
- python物件導向思想(類與物件)Python物件
- 7.類特殊成員
- 深入理解Javascript物件導向程式設計JavaScript物件程式設計
- 深入理解javascript系列(十八):掌握物件導向(1)JavaScript物件
- 如何理解“物件導向”物件
- Python基礎教程09 - 物件導向深入Python物件
- 深入理解物件導向,物件導向3個特性7個原則6種關係物件
- Js物件導向(1): 理解物件JS物件
- 談談我對物件導向以及類與物件的理解物件
- JS的物件導向(理解物件,原型,原型鏈,繼承,類)JS物件原型繼承
- 物件導向-抽象類物件抽象
- Perl物件導向--類物件
- Java物件導向——類與物件Java物件
- c#物件導向- 靜態成員和非靜態成員的區別C#物件
- 如何理解物件導向(一)物件
- JavaScript 物件導向初步理解JavaScript物件
- 深入理解python中的類和物件Python物件
- Python - 物件導向程式設計 - 什麼是 Python 類、類物件、例項物件Python物件程式設計
- python中物件導向_類_物件的概念與定義Python物件
- [.net 物件導向程式設計基礎] (10) 類的成員(欄位、屬性、方法)物件程式設計
- Python——物件導向Python物件
- Python 物件導向Python物件
- python物件導向Python物件
- JAVA物件導向--抽象類Java物件抽象
- Python如何設計物件導向的類(上)Python物件
- Python如何設計物件導向的類(下)Python物件
- Python3 物件導向程式設計(類)Python物件程式設計
- Python關於反射和類的特殊成員方法詳解Python反射
- 《JavaScript物件導向精要》之三:理解物件JavaScript物件