@property裝飾器的簡單理解

python使用@property與__gertattr__創建動態類屬性

最近在讀mmdetection的源碼,發現其中有很多的property與__gertattr__的使用,這裡研究記錄一下這兩個方法的使用。

問題示例

一個例子的問題描述:對於一張影像:

  • 屬性: 寬, 高

  • 方法:翻轉,旋轉

class Image:
    def __init__(self, width, height):
        self.width = width
        self.height = height
        
    def flip(self):
        pass
    def rotate(self):
        pass
    

如果需要添加一個屬性長寬比怎麼辦:

方法一、添加self.aspect_ratio屬性

最直接的方法,添加一個self.aspect_ratio屬性:

class Image:
    def __init__(self, width, height):
        self.width = width
        self.height = height
        self.aspect_ratio = self.height / self.width

    def flip(self):
        pass
    def rotate(self):
        pass

但是對於這個程式碼執行如下的操作就會出現問題,當修改影像的寬或者高以後,影像的寬高比就會出錯:

i = Image(1, 3)
print(i.width, i.height, i.aspect_ratio)  #  1   3    3.0
i.width = 4
print(i.width, i.height, i.aspect_ratio)  # 4    3     3.0

方法二、將寬高比定義為一個方法

另外一種正確的方法,將寬高比定義為一個方法:

class Image:
    def __init__(self, width, height):
        self.width = width
        self.height = height

    def flip(self):
        pass
    def rotate(self):
        pass
    def aspect_ratio(self):
        return self.height / self.width


i = Image(1, 3)
print(i.width, i.height, i.aspect_ratio())   #  1   3    3.0
i.width = 4
print(i.width, i.height, i.aspect_ratio())  # 4    3     0.75

這種方法雖然得到的結果正確,但是不是一個真正的屬性方法。

方法三、 property裝飾器

使用@property裝飾器,神奇的將一個方法看起來像是一個屬性:

class Image:
    def __init__(self, width, height):
        self.width = width
        self.height = height
        # self.aspect_ratio = self.height / self.width

    def flip(self):
        pass
    def rotate(self):
        pass
    @property
    def aspect_ratio(self):
        return self.height / self.width


i = Image(1, 3)
print(i.width, i.height, i.aspect_ratio)   #  1   3    3.0
i.width = 4
print(i.width, i.height, i.aspect_ratio ) # 4    3     0.75

## 方法四、使用__gertattr__方法獲取屬性

class Image:
    def __init__(self, width, height):
        self.width = width
        self.height = height
        # self.aspect_ratio = self.height / self.width

    def flip(self):
        pass
    def rotate(self):
        pass
    
    
    def __setattr__(self, name, value):
        if name == 'aspect_ratio':
            raise Exception('aspect_ratio can\'t be set. ')
        else:
            serlf.__dict__[name] = value

    def __getattr__(self, name):
        if name == 'aspect_ratio':
            return self.height / self.width
        else:
            raise AttributeError

其他,gertattr函數的使用方法

getattr獲取屬性的值

class Image:
    def __init__(self, width, height):
        self.width = width
        self.height = height
        # self.aspect_ratio = self.height / self.width

    def flip(self):
        pass
    def rotate(self):
        pass
    @property
    def height2(self):
        return getattr(self, 'height')

    
i = Image(1, 3)
print(i.width, i.height, i.height2)     #  1   3    3

如果getattr的name在勒種不存在這樣的屬性,那麼將會報錯,可以設置為一個默認值。

class Image:
    def __init__(self, width, height):
        self.width = width
        self.height = height
        # self.aspect_ratio = self.height / self.width

    def flip(self):
        pass
    def rotate(self):
        pass
    @property
    def height2(self):
        return getattr(self, 'height1', 0)

    
i = Image(1, 3)
print(i.width, i.height, i.height2)     #  1   3    0

更多編程筆記文章: //github.com/lxztju/notes

Tags: