- Python 用下劃線作爲變量前綴和後綴指定特殊變量
分三種:
- 單下劃線開始的變量,如_xxx,表示不能用’from module import *'導入
- 雙下劃線開始的變量,如__xxx,表示類中的私有變量名
- 前後各有雙下劃線的變量,如__xxx__,表示系統定義名字,也叫magic variable(魔術變量)
class Student:
def __init__(self,name='空',age=None,addr=None):
self.__name=name
self._age=age
self.addr=addr
def get_name(self):
return self.__name
def set_name(self,name):
self.__name=name
s1=Student('xf1',21,'harbin')
print(s1._age)
print(s1.addr)
可以看到_age和addr可以直接訪問,但__name不能直接訪問.
>>>print(s1.__name)
AttributeError: 'Student' object has no attribute '__name'
>>>print(s1.get_name())
xf1
上面提示__name找不到,其實Student類將__name重命名爲_Student__name,看下面:
>>>s1.__dict__
{'_Student__name': 'xf1', '_age': 21, 'addr': 'harbin'}
>>>print(s1._Student__name)
xf1
2 魔術方法(__xxx__)
可以分爲以下幾類:
- 2.1 類的基礎方法
- 2.2 計算屬性
- 2.3 可比較的類
- 2.4 行爲方式與迭代器類似的類
- 2.5 可序列化的類(未講)
- 2.6 可在 with 語塊中使用的類(未講)
2.1 類的基礎方法
這裏介紹__str__()和__repr__,先看一個例子:
class Student:
def __init__(self,name='空',age=None,addr=None):
self.__name=name
self._age=age
self.addr=addr
s1=Student('xf1',21,'harbin')
print(s1)
語句print(s1)打印對象s1,輸出結果爲:
<__main__.Student object at 0x000001AFBA0D34A8>
現在我們在Student中添加__str__方法如下:
class Student:
def __init__(self,name='空',age=None,addr=None):
self.__name=name
self._age=age
self.addr=addr
def __str__(self):
return '(Student:%s,%s,%s)'%(self.__name,self._age,self.addr)
s1=Student('xf1',21,'harbin')
print(s1)
結果如下:
(Student:xf1,21,harbin)
但是:直接輸入s1,結果仍不友好.
>>>s1
<__main__.Student at 0x1afba0d3710>
下面介紹__repr__方法:
class Student:
def __init__(self,name='空',age=None,addr=None):
self.__name=name
self._age=age
self.addr=addr
def __str__(self):
return '(Student:%s,%s,%s)'%(self.__name,self._age,self.addr)
def __repr__(self):
return '(Student info:%s,%s,%s)'%(self.__name,self._age,self.addr)
s1=Student('xf1',21,'harbin')
s1
結果爲:
(Student info:xf1,21,harbin)
注: 大家自己總結__str__()和__repr__()的區別.
爲什麼每個類都最好有一個 repr 方法
如果你沒有添加 str 方法,Python 在需要該方法但找不到的時候,它會去調用 repr 方法。因此,我推薦在寫自己的類的時候至少添加一個 repr 方法,這能保證類到字符串始終有一個有效的自定義轉換方式。
2.2 計算屬性
2.2.1 __setattr__ 攔截 屬性的的賦值語句 (obj.xx = xx),可以替代set函數
class Student:
def __init__(self,name='空',age=None,addr=None):
self.__name=name
self._age=age
self.addr=addr
def __setattr__(self,key,value):
print('__setattr__ called')
self.__dict__[key]=value
s1=Student('xf1',21,'harbin')
s1._Student__name='xf2'
結果爲:
__setattr__ called
__setattr__ called
__setattr__ called
__setattr__ called
2.2.2 __getattribute__和__getattr__
__getattribute__攔截運算(obj.xx),當每次調用屬性時,python會無條件進入__getattribute__中,不論屬性存在與否.
__getattr__是在查找不到屬性時調用,當obj.xx找不到屬性xx時,__getattr__被調用.
class Student:
def __init__(self,name='空',age=None,addr=None):
self.__name=name
self._age=age
self.addr=addr
def __setattr__(self,key,value):
print('__setattr__ called')
self.__dict__[key]=value
def __getattribute__(self,*args,**kwargs):
print('__getattribute__ called')
return object.__getattribute__(self,*args,**kwargs)
def __getattr__(self,item):
return 'not found '+item
s1=Student('xf1',21,'harbin')
s1._Student__name='xf2'
s1.gg
注意: 1 在__getattribute__和__getattr__中不用使用類似 self.*** 這種調用方式因爲 每次調用類的屬性都會強制調用 __getattribute__ ,所以極有可能造成遞歸調用.
2 訪問屬性的時候首先會調用__getattribute__ 方法,這個方法會檢測__dict__ 裏面有沒有這個屬性,沒有的話最後再調用一下 __getattr__.
2.2.3 其他的一些magic method
目的 | 所編寫代碼 | python實際調用 |
---|---|---|
序列的長度 | len(s) | s.__len__() |
是否包含特定值 | x in s | s.__contains__(x) |
通過鍵來獲取值 | x[key] | x.__getitem__(key) |
通過鍵來設置值 | x[key]=value | x.__setitem__(key,value) |
class Dog:
def __init__(self,name1,age1,desc):
self.name = name1
self.age = age1
self.desc = desc
def __len__(self):
return len(self.name)
def __getitem__(self,key):
if key in self.__dict__:
return self.__dict__[key]
def __setitem__(self,key,value):
self.__dict__[key] = value
dog1 = Dog('join_merry',2,'my friend')
len(dog1)
dog1['age'] = 3
dog1['age']
dog1['speces'] = '2ha'
dog1.__dict__
再看一個例子。
例1 Stu類
class Stu:
def __init__(self,name='',age=None,addr=None):
self.__name=name
self._age=age
self.addr=addr
def get_name(self):
return self.__name
def set_name(self,name):
self.__name=name
def __repr__(self):
return '(Student info:%s,%s,%s)'%(self.__name,self._age,self.addr)
def __len__(self):
return len(self.__name)
def __getitem__(self,key):
if key=='__name':
return self.__name
elif key=='_age':
return self._age
elif key=='addr':
return self.addr
s1=Stu('xf21',21,'harbin')
s1['__name'],s1['_age'],s1['addr'],len(s1)
結果爲(‘xf21’, 21, ‘harbin’, 4).
2.3 可比較的類
目的 | 所編寫代碼 | python實際調用 |
---|---|---|
相等 | x==y | x.__eq__(y) |
不相等 | x!=y | x.__ne__(y) |
小於 | x<y | x.__lt__(y) |
小於或等於 | x<=y | x.__le__(y) |
大於 | x>y | x.__gt__(y) |
大於或等於 | x>=y | x.__ge__(y) |
上下文環境中的真值 | if x: | x.__bool__() |
2.4 迭代器和可迭代對象
例2 自定義字符串類MyStr(本身也是迭代器),以及可迭代對象MyStr_iter.
class MyStr: #這是一個迭代器,含有__iter__,__next__
import copy
def __init__(self,data):
self.__data=data
self.__index=0
def __repr__(self):
return self.__data
def __len__(self):
return len(self.__data)
def __contains__(self,x): # x in str1
if x in self.__data:
return True
else:
return False
def __getitem__(self,key): # str1[0],str1[1],...
for i in range(len(self)):
if key==i:
return self.__data[i]
def __setitem__(self,key,value): # str1[0]=value1
if key=='__data':
self.__data=value
for i in range(len(self)):
if key==i:
t=list(self.__data)
t[i]=value
self.__data=''.join(t)
#運算符重載
def __setattr__(self,key,value): # 賦值運算符=
#print('__setattr__ called')
self.__dict__[key]=value
def __add__(self,s): # 加法+
self.__data=self.__data+s.__data
return self
#添加迭代器,需要__iter__,__next__
def __iter__(self):
return copy.copy(self)
def __next__(self):
if self.__index < len(self):
res=self.__data[self.__index]
self.__index+=1
return res
else:
raise StopIteration
class MyStr_iter: #創建一個類,用來實例化可迭代對象,可迭代對象只有__iter__方法
def __init__(self,data):
self.__data=data
def __iter__(self):
return MyStr(self.__data)
下面是使用舉例.
ms1_iter=MyStr_iter("hello")
for i in ms1_iter:
print(i,end=',')
結果爲h,e,l,l,o,.
參考文獻
- https://www.cnblogs.com/nkwy2012/p/6264031.html
- http://python.jobbole.com/88582/