一、__str__
改變對象的字符串顯示,可以理解為print打印對象的顯示
class Student:
def __init__(self, name, age):
self.name = name
self.age = age
# 定義對象的字符串表示
def __str__(self):
return self.name
s1 = Student('張三', 24)
print(s1) # 會調用s1的__str__方法
>>> 張三
二、__repr__
在python解釋器環境下,會默認顯示對象的repr表示。
>>> class Student:
... def __init__(self, name, age):
... self.name = name
... self.age = age
... def __repr__(self):
... return self.name
...
>>> s1 = Student('張三', 24)
>>> s1
張三
總結:
str函數或者print函數調用的是obj.str()
repr函數或者交互式解釋器調用的是obj.repr()
注意:
如果__str__沒有被定義,那么就會使用__repr__來代替輸出。
__str__和__repr__方法的返回值都必須是字符串。
三、__format__
class Student:
def __init__(self, name, age):
self.name = name
self.age = age
__format_dict = {
'n-a': '名字是:{obj.name}-年齡是:{obj.age}', # 名字是:lqz-年齡是:18
'n:a': '名字是:{obj.name}:年齡是:{obj.age}', # 名字是:lqz:年齡是:18
'n/a': '名字是:{obj.name}/年齡是:{obj.age}', # 名字是:/年齡是:18
}
def __format__(self, format_spec):
if not format_spec or format_spec not in self.__format_dict:
format_spec = 'n-a'
fmt = self.__format_dict[format_spec]
print(fmt) #{obj.name}:{obj.age}
return fmt.format(obj=self)
s1 = Student('lqz', 24)
ret = format(s1, 'n/a')
print(ret) # lqz/24
四、__del__
當對象的內存被施放的時候執行
注:此方法一般無須定義,因為Python是一門高級語言,程序員在使用時無需關心內存的分配和釋放,因為此工作都是交給Python解釋器來執行,所以析構函數的調用是由解釋器在進行垃圾回收時自動觸發執行的。
class A:
def __del__(self):
print('刪除了...')
a = A()
print(a) # <__main__.A object at 0x10164fb00>
del a # 刪除了...
print(a) # NameError: name 'a' is not defined
五、__dict__和__slots__
Python中的類,都會從object里繼承一個__dict__屬性,這個屬性中存放着類的屬性和方法對應的鍵值對。一個類實例化之后,這個類的實例也具有這么一個__dict__屬性。但是二者並不相同。
class A:
some = 1
def __init__(self, num):
self.num = num
a = A(10)
print(a.__dict__) # {'num': 10}
a.age = 10
print(a.__dict__) # {'num': 10, 'age': 10}
從上面的例子可以看出來,實例只保存實例的屬性和方法,類的屬性和方法它是不保存的。正是由於類和實例有__dict__屬性,所以類和實例可以在運行過程動態添加屬性和方法。
但是由於每實例化一個類都要分配一個__dict__變量,容易浪費內存。因此在Python中有一個內置的__slots__屬性。當一個類設置了__slots__屬性后,這個類的__dict__屬性就不存在了(同理,該類的實例也不存在__dict__屬性),如此一來,設置了__slots__屬性的類的屬性,只能是預先設定好的。
當你定義__slots__后,__slots__就會為實例使用一種更加緊湊的內部表示。實例通過一個很小的固定大小的小型數組來構建的,而不是為每個實例都定義一個__dict__字典,在__slots__中列出的屬性名在內部被映射到這個數組的特定索引上。使用__slots__帶來的副作用是我們沒有辦法給實例添加任何新的屬性了。
注意:盡管__slots__看起來是個非常有用的特性,但是除非你十分確切的知道要使用它,否則盡量不要使用它。比如定義了__slots__屬性的類就不支持多繼承。__slots__通常都是作為一種優化工具來使用。--摘自《Python Cookbook》8.4
注意事項:
slots__的很多特性都依賴於普通的基於字典的實現。
另外,定義了__slots__后的類不再 支持一些普通類特性了,比如多繼承。大多數情況下,你應該只在那些經常被使用到的用作數據結構的類上定義__slots,比如在程序中需要創建某個類的幾百萬個實例對象 。
關於__slots__的一個常見誤區是它可以作為一個封裝工具來防止用戶給實例增加新的屬性。盡管使用__slots__可以達到這樣的目的,但是這個並不是它的初衷。它更多的是用來作為一個內存優化工具。
六、__item__、__attr__系列
class Foo:
def __init__(self, name):
self.name = name
def __getitem__(self, item):
print(self.__dict__[item])
def __setitem__(self, key, value):
print('obj[key]=lqz賦值時,執行我')
self.__dict__[key] = value
def __delitem__(self, key):
print('del obj[key]時,執行我')
self.__dict__.pop(key)
def __delattr__(self, item):
print('del obj.key時,執行我')
self.__dict__.pop(item)
def __setattr__(self,key,value):
print('obj.key = value的時候執行我')
self.__dict__[key] = value
def __getattr__(self,item):
print('obj.get(key)的時候執行我')
return self[item]
f1 = Foo('sb')
print(f1.__dict__)
f1['age'] = 18
f1.hobby = '泡妞'
del f1.hobby
del f1['age']
f1['name'] = 'lqz'
print(f1.__dict__)
七、__init__
使用Python寫面向對象的代碼的時候我們都會習慣性寫一個 init 方法,init 方法通常用在初始化一個類實例的時候。例如:
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
def __str__(self):
return '<Person: {}({})>'.format(self.name, self.age)
p1 = Person('張三', 24)
print(p1)
上面是__init__最普通的用法了。但是__init__其實不是實例化一個類的時候第一個被調用的方法。當使用 Persion(name, age) 來實例化一個類時,最先被調用的方法其實是 new 方法。
八、__new__
其實__init__是在類實例被創建之后調用的,它完成的是類實例的初始化操作,而 __new__方法正是創建這個類實例的方法
class Person:
def __new__(cls, *args, **kwargs):
print('調用__new__,創建類實例')
return super().__new__(Person)
def __init__(self, name, age):
print('調用__init__,初始化實例')
self.name = name
self.age = age
def __str__(self):
return '<Person: {}({})>'.format(self.name, self.age)
p1 = Person('張三', 24)
print(p1)
輸出:
調用__new__,創建類實例
調用__init__,初始化實例
<Person: 張三(24)>
__new__方法在類定義中不是必須寫的,如果沒定義的話默認會調用object.__new__去創建一個對象(因為創建類的時候默認繼承的就是object)。
如果我們在類中定義了__new__方法,就是重寫了默認的__new__方法,我們可以借此自定義創建對象的行為。
舉個例子:
重寫類的__new__方法來實現單例模式。
class Singleton:
# 重寫__new__方法,實現每一次實例化的時候,返回同一個instance對象
def __new__(cls, *args, **kw):
if not hasattr(cls, '_instance'):
cls._instance = super().__new__(Singleton)
return cls._instance
def __init__(self, name, age):
self.name = name
self.age = age
s1 = Singleton('張三', 24)
s2 = Singleton('李四', 20)
print(s1, s2) # 這兩實例都一樣
print(s1.name, s2.name)
九、__call__
call 方法的執行是由對象后加括號觸發的,即:對象()。擁有此方法的對象可以像函數一樣被調用。
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
def __call__(self, *args, **kwargs):
print('調用對象的__call__方法')
a = Person('張三', 24) # 類Person可調用
a() # 對象a可以調用
注意:
new、init、__call__等方法都不是必須寫的。
十、__doc__
定義類的描述信息(注釋)
class A:
"""我是A類的描述信息"""
pass
print(A.__doc__)
十一、__iter__和__next__
如果一個對象擁有了__iter__和__next__方法,那這個對象就是迭代器
class A:
def __init__(self, start, stop=None):
if not stop:
start, stop = 0, start
self.start = start
self.stop = stop
def __iter__(self):
return self
def __next__(self):
if self.start >= self.stop:
raise StopIteration
n = self.start
self.start += 1
return n
a = A(1, 5)
from collections import Iterator
print(isinstance(a, Iterator))
for i in A(1, 5):
print(i)
for i in A(5):
print(i)
aaa=A(1)
print(next(aaa))
print(next(aaa)) #拋異常
十二、__enter__和__exit__
一個對象如果實現了__enter__和___exit__方法,那么這個對象就支持上下文管理協議,即with語句
class A:
def __enter__(self):
print('進入with語句塊時執行此方法,此方法如果有返回值會賦值給as聲明的變量')
return 'oo'
def __exit__(self, exc_type, exc_val, exc_tb):
print('退出with代碼塊時執行此方法')
print('1', exc_type)
print('2', exc_val)
print('3', exc_tb)
with A() as f:
print('進入with語句塊')
# with語句中代碼塊出現異常,則with后的代碼都無法執行。
# raise AttributeError('sb')
print(f) #f打印出oo
print('嘿嘿嘿')
十三、__len__
擁有__len__方法的對象支持len(obj)操作。
class A:
def __init__(self):
self.x = 1
self.y = 2
def __len__(self):
return len(self.__dict__)
a = A()
print(len(a))
十四、__hash__
擁有__hash__方法的對象支持hash(obj)操作。
class A:
def __init__(self):
self.x = 1
self.x = 2
def __hash__(self):
return hash(str(self.x) + str(self.x))
a = A()
print(hash(a))
十五、__eq__
擁有__eq__方法的對象支持相等的比較操作
class A:
def __init__(self,x,y):
self.x = x
self.y = y
def __eq__(self,obj):
# 打印出比較的第二個對象的x值
print(obj.x)
if self.x +self.y == obj.x+obj.y:
return True
else:
return False
a = A(1,2)
b = A(2,1)
print(a == b)