国产chinesehdxxxx野外,国产av无码专区亚洲av琪琪,播放男人添女人下边视频,成人国产精品一区二区免费看,chinese丰满人妻videos

特殊方法(2)

2018-02-24 15:48 更新

書接上回,不管是實例還是類,都用__dict__來存儲屬性和方法,可以籠統(tǒng)地把屬性和方法稱為成員或者特性,用一句籠統(tǒng)的話說,就是__dict__存儲對象成員。但,有時候訪問的對象成員沒有存在其中,就是這樣:

>>> class A(object):
...     pass
... 
>>> a = A()
>>> a.x
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'A' object has no attribute 'x'

x不是實例的成員,用a.x訪問,就出錯了,并且錯誤提示中報告了原因:“'A' object has no attribute 'x'”

在很多情況下,這種報錯是足夠的了。但是,在某種我現(xiàn)在還說不出的情況下,你或許不希望這樣報錯,或許希望能夠有某種別的提示、操作等。也就是我們更希望能在成員不存在的時候有所作為,不是等著報錯。

要處理類似的問題,就要用到本節(jié)中的知識了。

__getattr__、__setattr__和其它類似方法

還是用上面的例子,如果訪問a.x,它不存在,那么就要轉向到某個操作。我們把這種情況稱之為“攔截”。就好像“尋隱者不遇”,卻被童子“遙指杏花村”,將你“攔截”了。在python中,有一些方法就具有這種“攔截”能力。

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

如果一時沒有理解,不要緊,是正常的。需要用例子說明。

>>> class A(object):
...     def __getattr__(self, name):
...         print "You use getattr"
...     def __setattr__(self, name, value):
...         print "You use setattr"
...         self.__dict__[name] = value
... 

類A是新式類,除了兩個方法,沒有別的屬性。

>>> a = A()
>>> a.x
You use getattr

a.x,按照本節(jié)開頭的例子,是要報錯的。但是,由于在這里使用了__getattr__(self, name)方法,當發(fā)現(xiàn)x不存在于對象的__dict__中的時候,就調用了__getattr__,即所謂“攔截成員”。

>>> a.x = 7
You use setattr

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

>>> a.x
7

它已經(jīng)存在于對象的__dict__之中。

在上面的類中,當然可以使用__getattribute__(self, name),因為它是新式類。并且,只要訪問屬性就會調用它。例如:

>>> class B(object):
...     def __getattribute__(self, name):
...         print "you are useing getattribute"
...         return object.__getattribute__(self, name)
...

為了與前面的類區(qū)分,新命名一個類名字。需要提醒注意,在這里返回的內容用的是return object.__getattribute__(self, name),而沒有使用return self.__dict__[name]像是。因為如果用這樣的方式,就是訪問self.__dict__,只要訪問這個屬性,就要調用`getattribute``,這樣就導致了無線遞歸下去(死循環(huán))。要避免之。

>>> b = B()
>>> b.y
you are useing getattribute
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 4, in __getattribute__
AttributeError: 'B' object has no attribute 'y'
>>> b.two
you are useing getattribute
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 4, in __getattribute__
AttributeError: 'B' object has no attribute 'two'

訪問不存在的成員,可以看到,已經(jīng)被__getattribute__攔截了,雖然最后還是要報錯的。

>>> b.y = 8
>>> b.y
you are useing getattribute
8

當給其賦值后,意味著已經(jīng)在__dict__里面了,再調用,依然被攔截,但是由于已經(jīng)在__dict__內,會把結果返回。

當你看到這里,是不是覺得上面的方法有點魔力呢?不錯。但是,它有什么具體應用呢?看下面的例子,能給你帶來啟發(fā)。

#!/usr/bin/env python
# coding=utf-8

"""
study __getattr__ and __setattr__
"""

class Rectangle(object):
    """
    the width and length of Rectangle
    """
    def __init__(self):
        self.width = 0
        self.length = 0

    def setSize(self, size):
        self.width, self.length = size
    def getSize(self):
        return self.width, self.length

if __name__ == "__main__":
    r = Rectangle()
    r.width = 3
    r.length = 4
    print r.getSize()
    r.setSize( (30, 40) )
    print r.width
    print r.length

上面代碼來自《Beginning Python:From Novice to Professional,Second Edittion》(by Magnus Lie Hetland),根據(jù)本教程的需要,稍作修改。

$ python 21301.py 
(3, 4)
30
40

這段代碼已經(jīng)可以正確運行了。但是,作為一個精益求精的程序員。總覺得那種調用方式還有可以改進的空間。比如,要給長寬賦值的時候,必須賦予一個元組,里面包含長和寬。這個能不能改進一下呢?

#!/usr/bin/env python
# coding=utf-8

"""
study __getattr__ and __setattr__
"""

class Rectangle(object):
    """
    the width and length of Rectangle
    """
    def __init__(self):
        self.width = 0
        self.length = 0

    def setSize(self, size):
        self.width, self.length = size
    def getSize(self):
        return self.width, self.length

    size = property(getSize, setSize)

if __name__ == "__main__":
    r = Rectangle()
    r.width = 3
    r.length = 4
    print r.size
    r.size = 30, 40
    print r.width
    print r.length

以上代碼的運行結果同上。但是,因為加了一句size = property(getSize, setSize),使得調用方法是不是更優(yōu)雅了呢?原來用r.getSize(),現(xiàn)在使用r.size,就好像調用一個屬性一樣。難道你不覺得眼熟嗎?在《多態(tài)和封裝》中已經(jīng)用到過property函數(shù)了,雖然寫法略有差別,但是作用一樣。

本來,這樣就已經(jīng)足夠了。但是,因為本節(jié)中出來了特殊方法,所以,一定要用這些特殊方法從新演繹一下這段程序。雖然重新演繹的不一定比原來的好,主要目的是演示本節(jié)的特殊方法應用。

#!/usr/bin/env python
# coding=utf-8

class NewRectangle(object):
    def __init__(self):
        self.width = 0
        self.length = 0

    def __setattr__(self, name, value):
        if name == "size":
            self.width, self.length = value
        else:
            self.__dict__[name] = value

    def __getattr__(self, name):
        if name == "size":
            return self.width, self.length
        else:
            raise AttributeError

if __name__ == "__main__":
    r = NewRectangle()
    r.width = 3
    r.length = 4
    print r.size
    r.size = 30, 40
    print r.width
    print r.length

除了類的樣式變化之外,調用樣式?jīng)]有變。結果是一樣的。

這就算了解了一些這些屬性了吧。但是,有一篇文章是要必須推薦給讀者閱讀的:Python Attributes and Methods,讀了這篇文章,對python的對象屬性和方法會有更深入的理解。

獲得屬性順序

通過實例獲取其屬性(也有說特性的,名詞變化了,但是本質都是屬性和方法),如果在__dict__中有相應的屬性,就直接返回其結果;如果沒有,會到類屬性中找。比如:

#!/usr/bin/env python
# coding=utf-8

class A(object):
    author = "qiwsir"
    def __getattr__(self, name):
        if name != "author":
            return "from starter to master."

if __name__ == "__main__":
    a = A()
    print a.author
    print a.lang

運行程序:

$ python 21302.py 
qiwsir
from starter to master.

a = A()后,并沒有為實例建立任何屬性,或者說實例的__dict__是空的,這在上節(jié)中已經(jīng)探討過了。但是如果要查看a.author,因為實例的屬性中沒有,所以就去類屬性中找,發(fā)現(xiàn)果然有,于是返回其值"qiwsir"。但是,在找a.lang的時候,不僅實例屬性中沒有,類屬性中也沒有,于是就調用了__getattr__()方法。在上面的類中,有這個方法,如果沒有__getattr__()方法呢?如果沒有定義這個方法,就會引發(fā)AttributeError,這在前面已經(jīng)看到了。

這就是通過實例查找特性的順序。

以上內容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號