1. 程式人生 > >第040講:類和物件:一些相關的BIF

第040講:類和物件:一些相關的BIF

目錄

0. 請寫下這一節課你學習到的內容:格式不限,回憶並複述是加強記憶的好方式!

測試題

0. 如何判斷一個類是否為另一個類的子類?

1. 如何判斷物件 a 是否為 類 A 的例項物件?

2. 如何優雅地避免訪問物件不存在的屬性(不產生異常)?

3. Python 的一些 BIF 很奇怪,但卻十分有用。請問 property() 函式的作用是什麼?

4. 請補充以下程式碼,使程式可以正常執行:

5. 通過自學【Python擴充套件閱讀】Python 函式修飾符(裝飾器)的使用,使用修飾符修改以下程式碼。

6. 你真的理解了修飾符的用法嗎?那請你寫出以下程式碼沒有用上修飾符的等同形式:

7. 通過自學【Python擴充套件閱讀】property 的詳細使用方法,將第 4 題的程式碼修改為“使用屬性修飾符建立描述符”的方式實現。


0. 請寫下這一節課你學習到的內容:格式不限,回憶並複述是加強記憶的好方式!

今天我們談談和類與物件相關的BIF,也就是內建函式。

(一)issubclass(class, classinfo)

如果第一個引數 class 是第二個引數 classinfo 的子類,就返回 True,關於這個函式有幾點需要注意的:

  1. 一個類被認為是其自身的子類
  2. classinfo 可以是類物件組成的元組,只要 class 是其中一個候選類的子類,就返回 True

(二)isinstance(object, classinfo)

檢查一個例項物件 object 是否屬於一個類 classinfo,關於這個函式有幾點需要注意的:

  1. 如果第一個引數不是物件,則永遠返回 False
  2. 如果第二個引數不是類或者由類物件組成的元組,則丟擲一個 TypeError 異常

另外,Python 提供了幾個BIF讓我們訪問物件的屬性:

(三)hasattr(object, name)            attr = attribute:屬性。

測試一個物件是否有指定的屬性。name 要用引號把屬性名引起來。

>>> class C:
	def __init__(self, x = 0):
		self.x = x

		
>>> c1 = C()
>>> hasattr(c1, "x")
True
>>> hasattr(c1, x)
Traceback (most recent call last):
  File "<pyshell#71>", line 1, in <module>
    hasattr(c1, x)
NameError: name 'x' is not defined

(四)getattr(object, name[ , default] )       

返回物件指定的屬性值。如果指定的屬性不存在,如果你有設定 default,它會把這個default 引數打印出來,否則會丟擲一個AttributeError異常。

>>> class C:
	def __init__(self, x = 0):
		self.x = x

		
>>> c1 = C()
>>> getattr(c1, 'x')
0
>>> getattr(c1, 'y')
Traceback (most recent call last):
  File "<pyshell#80>", line 1, in <module>
    getattr(c1, 'y')
AttributeError: 'C' object has no attribute 'y'
>>> getattr(c1, 'y', '你所訪問的屬性不存在')
'你所訪問的屬性不存在'

(五)setattr(object, name, value)

設定物件中指定屬性的值,如果指定的屬性不存在,會新建一個新的屬性,並給其賦值。

>>> setattr(c1, 'y', '來自江南的你')
>>> getattr(c1, 'y', '你所訪問的屬性不存在')
'來自江南的你'

(六)delattr(object, name)

刪除物件中指定的屬性,如果屬性不存在,就丟擲一個AttributeError異常。

俗話說,條條大路通羅馬。Python 其實提供了好幾個方式供你選擇,property 是一個BIF,作用是通過屬性設定屬性,

property(fget = None, fset = None, fdel = None, doc = None)

property 函式的作用就是設定一個屬性,這個屬性就是去設定定義好的屬性,它的第一個引數 fget 是獲取屬性的方法,第一個引數 fset 是設定屬性的方法,第一個引數 fdel 是刪除屬性的方法。舉例說明:

>>> class C:
	def __init__(self, size = 10):
		self.size = size
	def getSize(self):
		return self.size
	def setSize(self, value):
		self.size = value
	def delSize(self):
		del self.size
	x = property(getSize, setSize, delSize)

	
>>> c1 = C()
>>> c1.x
10
>>> c1.getSize()
10
>>> c1.x = 18
>>> c1.getSize()
18
>>> c1.setSize(20)
>>> c1.x
20
>>> del c1.x
>>> c1.getSize()
Traceback (most recent call last):
  File "<pyshell#104>", line 1, in <module>
    c1.getSize()
  File "<pyshell#94>", line 5, in getSize
    return self.size
AttributeError: 'C' object has no attribute 'size'

property 的優勢:舉個例子,在上面這個例子中,這個程式慢慢寫的很複雜了,有一天,你想把這個程式進行大改,把函式名進行改寫,如果沒有 property,那你提供給使用者的呼叫介面就西藥修改,就會降低使用者體驗,但是有了property,問題就不存在了,因為提供給使用者的介面都是 x,程式裡面無論如何修改,property裡面的引數跟著改進行了,使用者還是隻用呼叫 x 來設定或者獲取 size 屬性就可以了。


測試題

0. 如何判斷一個類是否為另一個類的子類?

答:使用 issubclass(class, classinfo) 函式,如果第一個引數(class)是第二個引數(classinfo)的一個子類,則返回 True,否則返回 False。
另外以下這些常識你應該知道:

  • 一個類被認為是其自身的子類
  • classinfo 可以是類物件組成的元祖,只要 class 與其中任何一個候選類的子類,則返回 True
  • 在其他情況下,會丟擲一個 TypeError 異常

1. 如何判斷物件 a 是否為 類 A 的例項物件?

答:使用 isinstance(object, classinfo) 函式,如果第一個引數(object)是第二個引數(classinfo)的例項物件,則返回 True,否則返回 False。
另外以下這些常識你應該知道:

  • 如果 objec t是 classinfo 的子類的一個例項,也符合條件
  • 如果第一個引數不是物件,則永遠返回False
  • classinfo 可以是類物件組成的元祖,只要class與其中任何一個候選類的子類,則返回 True
  • 如果第二個引數不是類或者由類物件組成的元祖,會丟擲一個 TypeError 異常

2. 如何優雅地避免訪問物件不存在的屬性(不產生異常)?

答:有兩種方法可以做到。
第一種先使用 hasattr(object, name) 函式判斷屬性是否存在,如果存在再訪問(第一個引數(object)是物件,第二個引數(name)是屬性名的字串形式);
第二種方法是直接使用 getattr(object, name[, default]) 函式並設定 default 引數(返回物件指定的屬性值,如果指定的屬性不存在,返回default(可選引數)的值)。

3. Python 的一些 BIF 很奇怪,但卻十分有用。請問 property() 函式的作用是什麼?

答:property() 函式允許程式設計人員輕鬆、有效地管理屬性訪問。

4. 請補充以下程式碼,使程式可以正常執行:

class C:
    def __init__(self, size=10):
        self.size = size

    def getXSize(self):
        return self.size

    def setXSize(self, value):
        self.size = value

    def delXSize(self):
        del self.size

        # 此處應該補充一句程式碼,程式才能正常執行

>>> c.x
10
>>> c.x = 12
>>> c.x
12

答:x = property(getXSize, setXSize, delXSize)

5. 通過自學【Python擴充套件閱讀】Python 函式修飾符(裝飾器)的使用,使用修飾符修改以下程式碼。

程式碼A:

class CodeA:
    def foo():
        print("呼叫靜態方法 foo()")

        # 將 foo() 方法設定為靜態方法
        foo = staticmethod(foo)

程式碼B:

class CodeB:
    def foo(cls):
        print("呼叫類方法 foo()")

        # 將 foo() 方法設定為類方法
        foo = classmethod(foo)

答:其實正是因為設定靜態方法和類方法過於討人吐槽,因此 Python 的作者才開發出了函式修飾符的形式替代。

程式碼A:

class CodeA:
        @staticmethod
    def foo():
        print("呼叫靜態方法 foo()")

程式碼B:

class CodeB:
        @classmethod
    def foo(cls):
        print("呼叫類方法 foo()")

6. 你真的理解了修飾符的用法嗎?那請你寫出以下程式碼沒有用上修飾符的等同形式:

@something
def f():
    print("I love FishC.com!")

答:其實 Python 的修飾符就是一種優雅的封裝,但要注意的是隻可以在模組或類定義內對函式進行修飾,不允許修飾一個類。

一個修飾符就是一個函式,它將被修飾的函式做為引數,並返回修飾後的同名函式或其它可呼叫的東西。

@something
def f():
    print("I love FishC.com!")

# 相當於

def f():
    print("I love FishC.com!")

f = something(f)

7. 通過自學【Python擴充套件閱讀】property 的詳細使用方法,將第 4 題的程式碼修改為“使用屬性修飾符建立描述符”的方式實現。

答:可能你還沒聽說過描述符(這個概念在你學完接下來的幾節課自然會了解),但這一點都影響聰明的你修改這個程式。

程式碼清單:

class C:
    def __init__(self, size=10):
        self.size = size
        
    @property
    def x(self):
        return self.size

    @x.setter
    def x(self, value):
        self.size = value

    @x.deleter
    def x(self):
        del self.size