Python3中的特性-----Property介紹

      網(wǎng)友投稿 882 2025-04-04

      Python的Property詳細檔案

      今天我們就來好好聊聊Python3里面的Property

      特性的引入

      特性和屬性的區(qū)別是什么?

      在python 中 屬性 這個 實例方法, 類變量 都是屬性.

      在python 中 數(shù)據(jù)的屬性 和處理數(shù)據(jù)的方法 都可以叫做 屬性.

      class Animal:

      name = 'animal'

      def bark(self):

      print('bark')

      pass

      @classmethod

      def sleep(cls):

      print('sleep')

      pass

      @staticmethod

      def add():

      print('add')

      在命令行里面執(zhí)行

      >>> animal = Animal()

      >>> animal.add()

      add

      >>> animal.sleep()

      sleep

      >>> animal.bark()

      bark

      >>> hasattr(animal,'add') #1

      True

      >>> hasattr(animal,'sleep')

      True

      >>> hasattr(animal,'bark')

      True

      可以看出#1 animal 中 是可以拿到 add ,sleep bark 這些屬性的.

      特性: property 這個是指什么? 在不改變類接口的前提下使用

      存取方法 (即讀值和取值) 來修改數(shù)據(jù)的屬性.

      什么意思呢?

      就是通過 obj.property 來讀取一個值,

      obj.property = xxx ,來賦值

      還以上面 animal 為例:

      class Animal:

      @property

      def name(self):

      print('property name ')

      return self._name

      @name.setter

      def name(self, val):

      print('property set name ')

      self._name = val

      @name.deleter

      def name(self):

      del self._name

      這個時候 name 就是了特性了.

      >>> animal = Animal()

      >>> animal.name='dog'

      property set name

      >>> animal.name

      property name

      'dog'

      >>>

      >>> animal.name='cat'

      property set name

      >>> animal.name

      property name

      'cat'

      肯定有人會疑惑,寫了那么多的代碼, 還不如直接寫成屬性呢,多方便.

      比如這段代碼:

      >>> class Animal:

      ...? ? ?name=None

      ...

      >>> animal = Animal()

      >>> animal.name

      >>> animal.name='frank'

      >>> animal.name

      'frank'

      >>> animal.name='chang'

      >>> animal.name

      'chang'

      >>> animal.name=250

      >>> animal

      >>> animal.name

      250

      >>> type(animal.name)

      這里給?animal.name?賦值成 250, 程序從邏輯上來說 沒有問題. 但其實這樣賦值是毫無意義的.

      我們一般希望 不允許這樣的賦值,就希望 給出?報錯或者警告?之類的.

      animal= Animal()

      animal.name=100

      property set name

      Traceback (most recent call last):

      File "", line 1, in

      File "", line 13, in name

      ValueError: expected val is str

      其實當name 變成了property 之后,我們就可以對name 賦值 進行控制. 防止一些非法值變成對象的屬性.

      比如說name 應(yīng)該是這個字符串, 不應(yīng)該是數(shù)字 這個時候 就可以在 setter 的時候 進行判斷,來控制 能否賦值.

      要實現(xiàn)上述的效果, 其實也很簡單 setter 對value進行判斷就好了.

      class Animal:

      @property

      def name(self):

      print('property name ')

      return self._name

      @name.setter

      def name(self, val):

      print('property set name ')

      # 這里 對 value 進行判斷

      if not isinstance(val,str):

      raise? ValueError("expected val is str")

      self._name = val

      感受到 特性的魅力了吧,可以通過 賦值的時候 ,對 值進行校驗,方式不合法的值,進入到對象的屬性中. 下面 看下 如何設(shè)置只讀屬性, 和如何設(shè)置讀寫 特性.

      假設(shè) 有這樣的一個需求 , 某個類的屬性一個初始化之后 就不允許 被更改,這個 就可以用特性這個問題 , 比如一個人身高是固定, 一旦 初始化后,就不允許改掉.

      設(shè)置只讀特性

      class Frank:

      def __init__(self, height):

      self._height = height

      @property

      def height(self):

      return self._height

      >>> frank = Frank(height=100)

      >>> frank.height

      100

      >>> frank.height =150

      Traceback (most recent call last):

      File "", line 1, in

      AttributeError: can't set attribute

      這里初始化 frank后 就不允許 就修改 這個 height 這個值了. (實際上也是可以修改的)

      設(shè)置讀寫特性

      class Frank:

      def __init__(self, height):

      self._height = height

      @property

      def height(self):

      return self._height

      @height.setter

      def height(self, value):

      """

      給特性賦值

      """

      self._height = value

      比如對人的身高 在1米 到 2米之間 這樣的限制

      >>> frank = Frank(height=100)

      >>> frank.height

      100

      >>> frank.height=165

      >>> frank.height

      165

      對特性的合法性進行校驗

      class Frank:

      def __init__(self, height):

      self.height = height? # 注意這里寫法

      @property

      def height(self):

      return self._height

      @height.setter

      def height(self, value):

      """

      判斷邏輯 屬性的處理邏輯

      定義 了 setter 方法之后就? 修改 屬性 了.

      判斷 屬性 是否合理 ,不合理直接報錯. 阻止賦值,直接拋異常

      :param value:

      :return:

      """

      if not isinstance(value, (float,int)):

      raise ValueError("高度應(yīng)該是 數(shù)值類型")

      if value < 100 or value > 200:

      raise ValueError("高度范圍是100cm 到 200cm")

      self._height = value

      >>> frank = Frank(100)

      >>> frank.height

      100

      >>> frank.height='aaa'

      Traceback (most recent call last):

      File "", line 1, in

      File "", line 21, in height

      ValueError: 高度應(yīng)該是 數(shù)值類型

      >>> frank.height=250

      Traceback (most recent call last):

      File "", line 1, in

      File "", line 23, in height

      ValueError: 高度范圍是100cm 到 200cm

      這樣就可以進行嚴格的控制, 一些特性的方法性 ,通過寫setter 方法 來保證數(shù)據(jù) 準確性,防止一些非法的數(shù)據(jù)進入到實例中.

      Property是什么?

      實際上是一個類 , 然后就是一個裝飾器. 讓一個方法變成 一個特性.

      其實特性模糊了方法和數(shù)據(jù)的界限.

      方法是可調(diào)用的屬性 , 而property 是 可定制化的'屬性'?. 一般方法的名稱是一個動詞(行為). 而特性property 應(yīng)該是名詞.

      如果我們一旦確定了屬性不是動作, 我們需要在標準屬性 和 property 之間做出選擇 .

      一般來說你如果要控制 property 的 訪問過程,就要用property. 否則用標準的屬性即可 .

      attribute屬性和property特性的區(qū)別在于當property被讀取, 賦值, 刪除時候, 自動會執(zhí)行某些特定的動作.

      peroperty 詳解

      特性都是類屬性,但是特性管理的其實是實例屬性的存取。

      ----- 摘自 fluent python

      下面的例子來自 fluent python

      看一下幾個例子來說明幾個特性和屬性區(qū)別

      >>> class Class:

      """

      data 數(shù)據(jù)屬性和 prop 特性。

      """

      ...? ? ?data = 'the class data attr'

      ...

      ...? ? ?@property

      ...? ? ?def prop(self):

      ...? ? ? ? ?return 'the prop value'

      ...

      >>>

      >>> obj= Class()

      >>> vars(obj)

      {}

      >>> obj.data

      'the class data attr'

      >>> Class.data

      'the class data attr'

      >>> obj.data ='bar'

      >>> Class.data

      'the class data attr'

      實例屬性遮蓋類的數(shù)據(jù)屬性 , 就是說如果obj.data重新修改了 , 類的屬性不會被修改 .

      下面嘗試obj 實例的prop特性

      >>> Class.prop

      >>> obj.prop

      'the prop value'

      >>> obj.prop ='foo'

      Traceback (most recent call last):

      File "", line 1, in

      AttributeError: can't set attribute

      >>> obj.__dict__['prop'] ='foo'

      >>> vars(obj)

      {'data': 'bar', 'prop': 'foo'}

      >>> obj.prop? #1

      'the prop value'

      >>> Class.prop ='frank'

      >>> obj.prop

      'foo'

      我嘗試修改 obj.prop 會直接報錯 ,這個容易理解, 因為property沒有實現(xiàn) setter 方法 . 我直接修改obj.dict

      然后 在#1的地方, 發(fā)現(xiàn) 還是正常調(diào)用了特性 ,而沒有屬性的值.

      當我改變Class.prop變成一個屬性的時候 .

      再次調(diào)用obj.prop才調(diào)用到了 實例屬性.

      再看一個例子 添加 特性

      class Class:

      data = 'the class data attr'

      @property

      def prop(self):

      return 'the prop value'

      >>> obj.data

      'bar'

      >>> Class.data

      Python3中的特性-----Property介紹

      'the class data attr'

      # 把類的data 變成 特性

      >>> Class.data = property(lambda self:'the "data" prop value')

      >>> obj.data

      'the "data" prop value'

      >>> del Class.data

      >>> obj.data

      'bar'

      >>> vars(obj)

      {'data': 'bar', 'prop': 'foo'}

      改變 data 變成特性后, obj.data也改變了. 刪除這個特性的時候 , obj.data 又恢復(fù)了.

      本節(jié)的主要觀點是, obj.attr 這樣的表達式不會從 obj 開始尋找 attr,而是從

      obj.__class__ 開始,而且,僅當類中沒有名為 attr 的特性時, Python 才會在 obj 實

      例中尋找。這條規(guī)則適用于特性 .

      property 實際上 是一個類

      def __init__(self, fget=None, fset=None, fdel=None, doc=None):

      pass

      # known special case of property.__init__

      完成 的要實現(xiàn)一個特性 需要 這 4個參數(shù), get , set ,del , doc 這些參數(shù).但實際上大部分情況下,只要實現(xiàn) get ,set 即可.

      Property的兩種寫法

      第一種寫法

      使用 裝飾器 property 來修飾一個方法

      # 方法1

      class Animal:

      def __init__(self, name):

      self._name = name

      @property

      def name(self):

      print('property name ')

      return self._name

      @name.setter

      def name(self, val):

      print('property set name ')

      if not isinstance(val, str):

      raise ValueError("expected val is str")

      self._name = val

      @name.deleter

      def name(self):

      del self._name

      第二種寫法

      直接 實現(xiàn) set get delete 方法 即可, 通過property 傳入 這個參數(shù)

      # 方法二

      class Animal2:

      def __init__(self, name):

      self._name = name

      def _set_name(self, val):

      if not isinstance(val, str):

      raise ValueError("expected val is str")

      self._name = val

      def _get_name(self):

      return self._name

      def _delete_name(self):

      del self._name

      name = property(fset=_set_name, fget=_get_name,fdel= _delete_name,doc= "name 這是特性描述")

      if __name__ == '__main__':

      animal = Animal2('dog')

      >>> animal = Animal2('dog')

      >>>

      >>> animal.name

      'dog'

      >>> animal.name

      'dog'

      >>> help(Animal2.name)

      Help on property:

      name 這是特性描述

      >>> animal.name='cat'

      >>> animal.name

      'cat'

      替換背景的新方法:選擇背景圖設(shè)置后,左邊模板、、剪貼板和圖庫都可以點擊,根據(jù)選擇的內(nèi)容,設(shè)置背景到當前的編輯布局上。如果選擇了的是圖片,都把圖片設(shè)置被背景圖。如果選擇了一個帶背景的模板,就把這個模板的背景給復(fù)制過來。

      常見的一些例子

      A、對一些值進行合法性校驗.

      在舉一個小例子 比如 有一個貨物, 有重量 和 價格 ,需要保證 這兩個屬性是正數(shù) 不能是 0 , 即>0 的值

      基礎(chǔ)版本的代碼:

      class Goods:

      def __init__(self, name, weight, price):

      """

      :param name: 商品名稱

      :param weight:? 重量

      :param price: 價格

      """

      self.name = name

      self.weight = weight

      self.price = price

      def __repr__(self):

      return f"{self.__class__.__name__}(name={self.name},weight={self.weight},price={self.price})"

      @property

      def weight(self):

      return self._weight

      @weight.setter

      def weight(self, value):

      if value < 0:

      raise ValueError(f"expected value > 0, but now value:{value}")

      self._weight = value

      @property

      def price(self):

      return self._price

      @price.setter

      def price(self, value):

      if value < 0:

      raise ValueError(f"expected value > 0, but now value:{value}")

      self._price = value

      >>> goods = Goods('apple', 10, 30)

      ...

      >>> goods

      Goods(name=apple,weight=10,price=30)

      >>> goods.weight

      10

      >>> goods.weight=-10

      Traceback (most recent call last):

      File "", line 1, in

      File "", line 26, in weight

      ValueError: expected value > 0, but now value:-10

      >>> goods.price

      30

      >>> goods.price=-3

      Traceback (most recent call last):

      File "", line 1, in

      File "", line 37, in price

      ValueError: expected value > 0, but now value:-3

      >>> goods

      Goods(name=apple,weight=10,price=30)

      >>> goods.price=20

      >>> goods

      Goods(name=apple,weight=10,price=20)

      代碼 可以正常的判斷出來 ,這些非法值了. 這樣寫 有點問題是什么呢? 就是 發(fā)現(xiàn) weight ,price 判斷值的邏輯 幾乎是一樣的代碼… 都是判斷是 大于 0 嗎? 然而我卻寫了 兩遍相同的代碼 .

      優(yōu)化后的代碼

      有沒有更好的解決方案呢?

      是有的, 我們可以寫一個 工廠函數(shù) 來返回一個property , 這實際上是兩個 property 而已.

      下面 就是工廠函數(shù) ,用來生成一個 property 的.

      def validate(storage_name):

      """

      用來驗證 storage_name 是否合法性 , weight? , price

      :param storage_name:

      :return:

      """

      pass

      def _getter(instance):

      return instance.__dict__[storage_name]

      def _setter(instance, value):

      if value < 0:

      raise ValueError(f"expected value > 0, but now value:{value}")

      instance.__dict__[storage_name] = value

      return property(fget=_getter, fset=_setter)

      class Goods:

      weight = validate('weight')

      price = validate('price')

      def __init__(self, name, weight, price):

      """

      :param name: 商品名稱

      :param weight:? 重量

      :param price: 價格

      """

      self.name = name

      self.weight = weight

      self.price = price

      def __repr__(self):

      return f"{self.__class__.__name__}(name={self.name},weight={self.weight},price={self.price})"

      >>> goods = Goods('apple', 10, 30)

      >>> goods.weight

      10

      >>> goods.weight=-10

      Traceback (most recent call last):

      File "", line 1, in

      File "", line 16, in _setter

      ValueError: expected value > 0, but now value:-10

      >>> goods

      Goods(name=apple,weight=10,price=30)

      >>> goods.price=-2

      Traceback (most recent call last):

      File "", line 1, in

      File "", line 16, in _setter

      ValueError: expected value > 0, but now value:-2

      >>> goods

      Goods(name=apple,weight=10,price=30)

      B、緩存某些值

      ...?from urllib.request import urlopen

      ... class WebPage:

      ...

      ...? ? ?def __init__(self, url):

      ...? ? ? ? ?self.url = url

      ...

      ...? ? ? ? ?self._content = None

      ...

      ...? ? ?@property

      ...? ? ?def content(self):

      ...? ? ? ? ?if not self._content:

      ...? ? ? ? ? ? ?print("Retrieving new page")

      ...? ? ? ? ? ? ?self._content = urlopen(self.url).read()[0:10]

      ...

      ...? ? ? ? ?return self._content

      ...

      >>>

      >>>

      >>> url = 'http://www.baidu.com'

      >>> page = WebPage(url)

      >>>

      >>> page.content

      Retrieving new page

      b'

      >>> page.content

      b'

      >>> page.content

      b'

      可以看出 第一次調(diào)用了 urlopen 從網(wǎng)頁中讀取值, 第二次就沒有調(diào)用urlopen 而是直接返回content 的內(nèi)容.

      總結(jié)

      python的特性算是python的高級語法,不要因為到處都要用這個特性的語法.實際上大部分情況是用不到這個語法的. 如果代碼中,需要對屬性進行檢查就要考慮用這樣的語法了. 希望你看完之后不要認為這種語法非常常見, 事實上不是的. 其實更好的做法對屬性檢查可以使用描述符來完成. 描述符是一個比較大的話題,本文章暫未提及,后續(xù)的話,可能 會寫一下 關(guān)于描述的一些用法 ,這樣就能更好的理解python,更加深入的理解python.

      參考文檔

      fluent python(流暢的Python)

      Python3面向?qū)ο缶幊?/p>

      Python為什么要使用描述符?

      https://juejin.im/post/5cc4fbc0f265da0380437706

      https://tech-summary.readthedocs.io/en/latest/python_property.html

      "歡迎關(guān)注,了解更多python內(nèi)幕"

      Python

      版權(quán)聲明:本文內(nèi)容由網(wǎng)絡(luò)用戶投稿,版權(quán)歸原作者所有,本站不擁有其著作權(quán),亦不承擔相應(yīng)法律責(zé)任。如果您發(fā)現(xiàn)本站中有涉嫌抄襲或描述失實的內(nèi)容,請聯(lián)系我們jiasou666@gmail.com 處理,核實后本網(wǎng)站將在24小時內(nèi)刪除侵權(quán)內(nèi)容。

      版權(quán)聲明:本文內(nèi)容由網(wǎng)絡(luò)用戶投稿,版權(quán)歸原作者所有,本站不擁有其著作權(quán),亦不承擔相應(yīng)法律責(zé)任。如果您發(fā)現(xiàn)本站中有涉嫌抄襲或描述失實的內(nèi)容,請聯(lián)系我們jiasou666@gmail.com 處理,核實后本網(wǎng)站將在24小時內(nèi)刪除侵權(quán)內(nèi)容。

      上一篇:excel表格數(shù)據(jù)怎么返回指定百分點的值?
      下一篇:電腦好用辦公表格軟件(電腦好用辦公表格軟件推薦)
      相關(guān)文章
      亚洲国产综合91精品麻豆| 久久精品国产亚洲5555| 亚洲精品一品区二品区三品区| 国产亚洲漂亮白嫩美女在线| 亚洲精品无码日韩国产不卡av| 亚洲av无码不卡久久| 亚洲va在线va天堂va手机| 亚洲另类图片另类电影| 亚洲中文字幕久在线| 亚洲人成在线中文字幕| 亚洲中文字幕人成乱码| 亚洲人成网网址在线看| 亚洲五月丁香综合视频| 亚洲一级在线观看| 亚洲日韩国产精品乱-久| 亚洲人成www在线播放| 亚洲砖码砖专无区2023 | 亚洲精品制服丝袜四区| 国产亚洲精品va在线| 亚洲AV无码国产精品色午友在线 | 激情无码亚洲一区二区三区| 精品久久亚洲一级α| 亚洲AV中文无码乱人伦| 亚洲精品无码成人片在线观看 | 亚洲综合精品网站在线观看| 综合亚洲伊人午夜网 | 亚洲AV一宅男色影视| 18gay台湾男同亚洲男同| 亚洲国产av一区二区三区丶| 亚洲Av无码一区二区二三区| 亚洲色偷偷综合亚洲av78| 欧美激情综合亚洲一二区| 亚洲精品成a人在线观看| 一本色道久久综合亚洲精品| 亚洲AV综合色一区二区三区| 精品亚洲成AV人在线观看| 亚洲五月综合缴情婷婷| 国产精品亚洲一区二区在线观看| 亚洲精品成人a在线观看| 亚洲精品美女久久久久99| 日本久久久久亚洲中字幕|