9.21 避免重復(fù)的屬性方法

2018-02-24 15:27 更新

問(wèn)題

你在類(lèi)中需要重復(fù)的定義一些執(zhí)行相同邏輯的屬性方法,比如進(jìn)行類(lèi)型檢查,怎樣去簡(jiǎn)化這些重復(fù)代碼呢?

解決方案

考慮下一個(gè)簡(jiǎn)單的類(lèi),它的屬性由屬性方法包裝:

class Person:
    def __init__(self, name ,age):
        self.name = name
        self.age = age

    @property
    def name(self):
        return self._name

    @name.setter
    def name(self, value):
        if not isinstance(value, str):
            raise TypeError('name must be a string')
        self._name = value

    @property
    def age(self):
        return self._age

    @age.setter
    def age(self, value):
        if not isinstance(value, int):
            raise TypeError('age must be an int')
        self._age = value

可以看到,為了實(shí)現(xiàn)屬性值的類(lèi)型檢查我們寫(xiě)了很多的重復(fù)代碼。 只要你以后看到類(lèi)似這樣的代碼,你都應(yīng)該想辦法去簡(jiǎn)化它。 一個(gè)可行的方法是創(chuàng)建一個(gè)函數(shù)用來(lái)定義屬性并返回它。例如:

def typed_property(name, expected_type):
    storage_name = '_' + name

    @property
    def prop(self):
        return getattr(self, storage_name)

    @prop.setter
    def prop(self, value):
        if not isinstance(value, expected_type):
            raise TypeError('{} must be a {}'.format(name, expected_type))
        setattr(self, storage_name, value)

    return prop

# Example use
class Person:
    name = typed_property('name', str)
    age = typed_property('age', int)

    def __init__(self, name, age):
        self.name = name
        self.age = age

討論

本節(jié)我們演示內(nèi)部函數(shù)或者閉包的一個(gè)重要特性,它們很像一個(gè)宏。例子中的函數(shù)<span class="pre" style="box-sizing: border-box;">typed_property()</span>?看上去有點(diǎn)難理解,其實(shí)它所做的僅僅就是為你生成屬性并返回這個(gè)屬性對(duì)象。 因此,當(dāng)在一個(gè)類(lèi)中使用它的時(shí)候,效果跟將它里面的代碼放到類(lèi)定義中去是一樣的。 盡管屬性的<span class="pre" style="box-sizing: border-box;">getter</span>?和?<span class="pre" style="box-sizing: border-box;">setter</span>?方法訪問(wèn)了本地變量如?<span class="pre" style="box-sizing: border-box;">name</span>?,?<span class="pre" style="box-sizing: border-box;">expected_type</span>?以及?<span class="pre" style="box-sizing: border-box;">storate_name</span>?,這個(gè)很正常,這些變量的值會(huì)保存在閉包當(dāng)中。

我們還可以使用?<span class="pre" style="box-sizing: border-box;">functools.partial()</span>?來(lái)稍稍改變下這個(gè)例子,很有趣。例如,你可以像下面這樣:

from functools import partial

String = partial(typed_property, expected_type=str)
Integer = partial(typed_property, expected_type=int)

# Example:
class Person:
    name = String('name')
    age = Integer('age')

    def __init__(self, name, age):
        self.name = name
        self.age = age

其實(shí)你可以發(fā)現(xiàn),這里的代碼跟8.13小節(jié)中的類(lèi)型系統(tǒng)描述器代碼有些相似。

以上內(nèi)容是否對(duì)您有幫助:
在線(xiàn)筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)