python講稿4_2 類2 單下劃線,雙下劃線

  1. 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,.

參考文獻

  1. https://www.cnblogs.com/nkwy2012/p/6264031.html
  2. http://python.jobbole.com/88582/
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章