9.21 避免重復的屬性方法

2018-02-24 15:27 更新

問題

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

解決方案

考慮下一個簡單的類,它的屬性由屬性方法包裝:

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

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

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ù)或者閉包的一個重要特性,它們很像一個宏。例子中的函數(shù)<span class="pre" style="box-sizing: border-box;">typed_property()</span>?看上去有點難理解,其實它所做的僅僅就是為你生成屬性并返回這個屬性對象。 因此,當在一個類中使用它的時候,效果跟將它里面的代碼放到類定義中去是一樣的。 盡管屬性的<span class="pre" style="box-sizing: border-box;">getter</span>?和?<span class="pre" style="box-sizing: border-box;">setter</span>?方法訪問了本地變量如?<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>?,這個很正常,這些變量的值會保存在閉包當中。

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

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

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

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

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號