Python - 特殊屬性和方法淺析

__dict__

  • Python 中,一切皆對象
  • 不管是類還是實例的屬性和方法,都符合 object.attribute 格式。并且屬性類型
class A(object):
    pass
a = A()
dir(a)

輸出:

    ['__class__', '__delattr__', '__dict__', '__dir__', '__doc__','__eq__','__format__','__ge__','__getattribute__','__gt__','__hash__','__init__','__init_subclass__','__le__','__lt__', '__module__','__ne__','__new__', '__reduce__', '__reduce_ex__','__repr__','__setattr__', '__sizeof__', '__str__', '__subclasshook__','__weakref__']

class Spring(object):
    season = 'the spring of class'
Spring.__dict__

輸出:

    mappingproxy({'__dict__': <attribute '__dict__' of 'Spring' objects>,
                  '__doc__': None,
                  '__module__': '__main__',
                  '__weakref__': <attribute '__weakref__' of 'Spring' objects>,
                  'season': 'the spring of class'})


#訪問的是類屬性
print(Spring.__dict__['season'])
print(Spring.season)

輸出:

 'the spring of class'

#訪問的是實例屬性,因為class中沒有定義實例屬性所以打印為空
s = Spring()
s.__dict__

輸出:

 {}

s.season    #訪問的類屬性

輸出:

 'the spring of class'

#為class 的實例 中添加一個 season 屬性
s.season = "instance";
s.__dict__

輸出:

{'season': 'instance'}

#當類屬性和實例屬性重名時,實例屬性會覆蓋掉類屬性
s.__dict__['season']

輸出:

'instance'

#但是類屬性不會變
Spring.__dict__['season']

輸出:

'the spring of class'

Spring.season

輸出:

 'the spring of class'

#刪除實例屬性后,就回到了實例化類的時候的狀態,沒有實例屬性,只有類屬性
del s.season
s.__dict__

輸出:

    {}

s.season  #類屬性

輸出:

    'the spring of class'

#屬性和方法是同樣的
class Spring_def(object):
    def tree(self,x):
        self.x= x
        return self.x

Spring_def.__dict__

輸出:

 mappingproxy({'__dict__': <attribute '__dict__' of 'Spring_def' objects>,
                  '__doc__': None,
                  '__module__': '__main__',
                  '__weakref__': <attribute '__weakref__' of 'Spring_def' objects>,
                  'tree': <function __main__.Spring_def.tree>})

Spring_def.__dict__['tree']

輸出:

    <function __main__.Spring_def.tree>


t = Spring_def()
t.__dict__

輸出:

    {}

t.tree('value')
t.__dict__

輸出:

    {}

class Spring_no(object):
    def tree(self,x):
        return x

ts = Spring_no()
ts.tree('No arg')
ts.__dict__

輸出:

    {}

Spring_no.__dict__

輸出:

    mappingproxy({'__dict__': <attribute '__dict__' of 'Spring_no' objects>,
                  '__doc__': None,
                  '__module__': '__main__',
                  '__weakref__': <attribute '__weakref__' of 'Spring_no' objects>,
                  'tree': <function __main__.Spring_no.tree>})

__slots__

  • 能夠限制屬性的定義
  • 優化內存使用
  • __dict__替換成__slots__
  • 用類給一個屬性賦值后,實例就不能給這個屬性賦值了。
  • 也不能新增實例屬性,這樣就把屬性管控了起來。內存得到優化
  • 大量實例的話會顯現出效果
class Spring_s(object):
    __slots__ = ("tree","flows")
dir(Spring_s)

輸出:

    ['__class__','__delattr__','__dir__','__doc__','__eq__', '__format__','__ge__','__getattribute__', '__gt__','__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__','__reduce_ex__', '__repr__', '__setattr__', '__sizeof__',  '__slots__', '__str__',   '__subclasshook__', 'flows',  'tree']

Spring_s.__dict__
輸出:
mappingproxy({'__doc__': None,
                  '__module__': '__main__',
                  '__slots__': ('tree', 'flows'),
                  'flows': <member 'flows' of 'Spring_s' objects>,
                  'tree': <member 'tree' of 'Spring_s' objects>})

Spring_s.__slots__

輸出:

    ('tree', 'flows')

ts = Spring_s()
ts.__slots__

輸出:

    ('tree', 'flows')

#使用__slots__后就沒有__dict__ 屬性了
ts.__dict__

輸出:

#錯誤
    ---------------------------------------------------------------------------

    AttributeError                            Traceback (most recent call last)

    <ipython-input-40-f3db88226dd5> in <module>()
          1 #使用__slots__后就沒有__dict__ 屬性了
    ----> 2 ts.__dict__
    

    AttributeError: 'Spring_s' object has no attribute '__dict__'

Spring_s.tree = "liushu"
Spring_s.tree

輸出:

    'liushu'

# 報錯中顯示 實例屬性 tree 只是制度的,不能修改
ts.tree = "yangshu"

輸出:

#錯誤
    ---------------------------------------------------------------------------

    AttributeError                            Traceback (most recent call last)

    <ipython-input-42-d6b9cb191c20> in <module>()
          1 # 報錯中顯示 實例屬性 tree 只是制度的,不能修改
    ----> 2 ts.tree = "yangshu"
    

    AttributeError: 'Spring_s' object attribute 'tree' is read-only


ts.tree

輸出:

    'liushu'

  • 前面已經通過類給屬性賦值了,就不能用實例屬性來修改。
  • 當使用實例給屬性賦值后,還是可以再用實例來給屬性賦值,但是當這個屬性被類賦值后,就不能再用實例給屬性賦值了,只能用類來給屬性賦值.
#第一次實例賦值
ts.flows = "meigui"
ts.flows

輸出:

    'meigui'

#第二次實例賦值
ts.flows = "yueji"
ts.flows

輸出:

    'yueji'

#使用類賦值
Spring_s.flows = "shuixianhua"
Spring_s.flows

輸出:

 'shuixianhua'

#實例的屬性的值會變成類屬性的值
ts.flows

輸出:

    'shuixianhua'

#再用實例給屬性賦值就會報只讀錯誤
ts.flows = "qianniuhua"

輸出:

#錯誤
    ---------------------------------------------------------------------------

    AttributeError                            Traceback (most recent call last)

    <ipython-input-48-726c06affe50> in <module>()
          1 #再用實例給屬性賦值就會報只讀錯誤
    ----> 2 ts.flows = "qianniuhua"
    

    AttributeError: 'Spring_s' object attribute 'flows' is read-only


Spring_s.flows = "baihe"
Spring_s.flows

輸出:

    'baihe'

ts.flows

輸出:

    'baihe'

  • 實例中添加屬性
# 新增實例失敗 
ts.water = "green"

輸出:

#錯誤
    ---------------------------------------------------------------------------

    AttributeError                            Traceback (most recent call last)

    <ipython-input-52-cde7efd2f5b8> in <module>()
    ----> 1 ts.water = "green"
    

    AttributeError: 'Spring_s' object has no attribute 'water'

__getattr____setattr__ 和其他類似方法

  • __setattr__(self,name,value): 如果要給name賦值,就調用這個方法
  • __getattr__(self,name): 如果name被訪問,同事它不存在,此方法被調用
  • __getattribute__(self,name): 當name被訪問時自動被調用(注意:這個僅能用于新式類),無論name是否存在,都要被調用
  • __delattr__(self,name): 如果要刪除name,這個方法就被調用。
class B(object):
    pass
b = B()
b.x 

輸出:

    ---------------------------------------------------------------------------

    AttributeError                            Traceback (most recent call last)

    <ipython-input-42-f8f5c58b62c7> in <module>()
          2     pass
          3 a = A()
    ----> 4 a.x
    

    AttributeError: 'A' object has no attribute 'x'

x 不是勢力的成員(屬性和方法)所以報錯。也就是說,如果訪問 b.x ,它不存在,那么就要轉向到某個操作 ,我們把這種情況叫做“攔截”。

class C(object):
    def __getattr__(self,name):
        print('you use getattr')
    def __setattr__(self,name,value):
        print('you use setattr')
        self.__dict__[name] = value

類C是新式類,出來2個方法,沒有別的屬性

c = C()
c.x

輸出:

    you use getattr

本來按照開頭class B 是要報錯的。但是由于這里使用了 __getattr__(self,name) 當發現x不存在與對象__dict__ 時,就會調用__getattr“攔截成員” 。

c.x = 7

輸出:

    you use setattr

給對象的屬性賦值是,調用了__setattr__(self,name,value)方法,這個方法有一句 self.__dict__[name] = value通過這個語句,就將屬性和數據保存到了對象的 __dict__中。如果再調用這個屬性:

c.x  # x 已經存在于對象的 __dict__中

輸出:

    7

使用__getattribute__(self,name):,只要訪問屬性就會調用它

class getAt(object):
    def __getattribute__(self,name):
        print('使用了getattribute')
        return object.__getattribute__(self,name)
at = getAt()
at.y

輸出:

    使用了getattribute



    ---------------------------------------------------------------------------

    AttributeError                            Traceback (most recent call last)

    <ipython-input-54-6e81b53b32de> in <module>()
          1 at = getAt()
    ----> 2 at.y
    

    <ipython-input-53-a6779a4b213a> in __getattribute__(self, name)
          2     def __getattribute__(self,name):
          3         print('使用了getattribute')
    ----> 4         return object.__getattribute__(self,name)
    

    AttributeError: 'getAt' object has no attribute 'y'

  • 訪問一個不存在的屬性,雖然攔截了但還是報錯了。
  • 但是只要賦值后就可以訪問了,因為這樣就意味著做個屬性已經存在于__dict__中了。雖然依然被攔截,但是會把結果返回。
at.y = 9
at.y

輸出:

    使用了getattribute
    9

注意:這里沒有 return self.__dict__[name],因為如果用這樣的方法訪問self.__dict__只要訪問這個方法,就會去調用__getattribute__,這樣就會無限遞歸下去,造成死循環。

示例:
''' study __getattr__  and  __setattr__'''
class Rectangle(object):
    ''' the width  and length of Rectangle'''
    def __init__(self):
        self.width = 0
        self.height = 0
    def setSize(self,size):
        self.width,self.height = size
    def getSize(self):
        return self.width,self.height
if __name__ == "__main__":
    r = Rectangle()
    r.width = 3
    r.height = 4
    print (r.getSize())
    r.setSize((30,40))
    print(r.width)
    print(r.height)

輸出:

    (3, 4)
    30
    40

長寬賦值的時候必須是一個元組,里面包含長寬。改進。。。

class Rectangle_2(object):
    ''' 使用property改進調用方法傳參'''
    def __init__(self):
        self.width = 0
        self.height = 0
    def setSize(self,size):
        self.width,self.height = size
    def getSize(self):
        return self.width,self.height
    #---------新加---------#
    size = property(getSize,setSize)
    #---------結束---------#
if __name__ == "__main__":
    r = Rectangle_2()
    r.width = 3
    r.length = 4
    print(r.size)
    r.size = 30, 40
    print (r.width)
    print (r.length)

輸出:

    (3, 0)
    30
    4

雖然方法調用就像屬性一樣,但是沒有用上特殊方法。繼續改進。。。

class Rectangle_3(object):
    '''使用特殊方法'''
    def __init__(self):
        self.width = 0
        self.height = 0
    def __setattr__(self, name, value):
        if name == "size":
            self.width, self.height = value
        else:
            self.__dict__[name] = value
    def __getattr__(self, name):
        if name == "size":
            return self.width,self.height
        else:
            return AttributeError
    
if __name__ == "__main__":
    nr = Rectangle_3()
    nr.width = 3
    nr.height = 4
    print(nr.size)
    nr.size = 30,40
    print(nr.width)
    print(nr.height)

輸出:

    (3, 4)
    30
    40

獲得屬性順序

  • 通過實例獲取其屬性,如果在__dict__中有,就直接返回其結果,如果沒有,就會到類屬性中找。
class search(object):
    author = 'Python'
    def __getattr__(self , name):
        if name != 'author':
            return '查詢的不是author屬性'
if __name__ == '__main__':
    a = search()
    print(a.author)
    print(a.none)
    print(a.__dict__)

輸出:

    Python
    查詢的不是author屬性
    {}

  • 初始化后 a 沒有建立任何實例屬性。實例屬性__dict__是空,沒有屬性值,a.author 則有值,因為是類屬性,而實例中又沒有,所以就去類屬性中去找,所以就返回了'Python'
  • 當a.none 的時候不僅實例中沒有,類中也沒有,所以就調用了__getattr__方法。如果不寫__getattr__方法就會報錯了。
  • 這就是 通過實例查找特性的順序

參考:《跟著老齊學Python:從入門到精通》 作者:齊偉 電子工業出版社

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,431評論 6 544
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,637評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,555評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,900評論 1 318
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,629評論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,976評論 1 328
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,976評論 3 448
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,139評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,686評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,411評論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,641評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,129評論 5 364
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,820評論 3 350
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,233評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,567評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,362評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,604評論 2 380

推薦閱讀更多精彩內容

  • importUIKit classViewController:UITabBarController{ enumD...
    明哥_Young閱讀 3,861評論 1 10
  • 20- 枚舉,枚舉原始值,枚舉相關值,switch提取枚舉關聯值 Swift枚舉: Swift中的枚舉比OC中的枚...
    iOS_恒仔閱讀 2,310評論 1 6
  • 1、什么叫魔法方法? 魔法方法:Python解釋器自動給出默認的,是可以給你的類增加魔力的特殊方法。如果你的對象實...
    Bling_ll閱讀 1,060評論 0 2
  • 要點: 函數式編程:注意不是“函數編程”,多了一個“式” 模塊:如何使用模塊 面向對象編程:面向對象的概念、屬性、...
    victorsungo閱讀 1,552評論 0 6
  • 曾經聽到這樣一個故事。一個漁夫在沙灘上曬太陽。 一個富人走過,問他:“你為什么躺在這里曬太陽,不去補網...
    磊磊popo閱讀 701評論 13 3