日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > python >内容正文

python

python 动态属性和特性

發布時間:2024/7/5 python 26 豆豆
生活随笔 收集整理的這篇文章主要介紹了 python 动态属性和特性 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

文章目錄

    • 1. 使用動態屬性轉換數據
    • 2. @property
      • 2.1 help() 文檔
    • 3. 特性工廠函數
    • 4. 屬性刪除操作
    • 5. 處理屬性的重要屬性和函數
      • 5.1 處理屬性的內置函數
      • 5.2 處理屬性的特殊方法

learn from 《流暢的python》

1. 使用動態屬性轉換數據

  • 在 Python 中,數據的屬性和處理數據的方法統稱屬性(attribute)。其實,方法只是可調用的屬性
  • 我們還可以創建特性 (property)
from urllib.request import urlopen import warnings import os import jsonURL = 'http://www.oreilly.com/pub/sc/osconfeed' JSON = './osconfeed.json'def load():if not os.path.exists(JSON):msg = 'downloading {} to {}'.format(URL, JSON)warnings.warn(msg) # 發出提醒with urlopen(URL) as remote, open(JSON, 'wb') as local:# 使用兩個上下文管理器local.write(remote.read())# 讀取和保存遠程文件with open(JSON) as fp:return json.load(fp)feed = load() print(feed) print(sorted(feed['Schedule'].keys())) for key, value in sorted(feed['Schedule'].items()):print('{:3} {}'.format(len(value), key)) print(feed['Schedule']['speakers'][-1]['serial']) # 這種句法太長了。。。如何改進 from collections import abcclass FrozenJSON:# 一個只讀接口,使用屬性表示法訪問JSON類對象def __init__(self, mapping):self.__data = dict(mapping)def __getattr__(self, name):if hasattr(self.__data, name): # 有屬性,獲取return getattr(self.__data, name)# 調用 keys 等方法就是通過這種方式處理的else: # 沒有,構建 FrozenJSONreturn FrozenJSON.build(self.__data[name])@classmethod # 備選構造方法,@classmethod 裝飾器經常這么用def build(cls, obj):if isinstance(obj, abc.Mapping):return cls(obj) # 構建 FrozenJSONelif isinstance(obj, abc.MutableSequence):# 是序列,對每個元素都進行 buildreturn [cls.build(item) for item in obj]else:return objraw_feed = load() feed = FrozenJSON(raw_feed) print(len(feed.Schedule.speakers)) print(sorted(feed.Schedule.keys())) # ['conferences', 'events', 'speakers', 'venues'] print(feed.Schedule.events[-1].name) # Why Schools Don't Use Open Source to Teach Programming p = feed.Schedule.events[-1] print(type(p)) # <class '__main__.FrozenJSON'> print(p.name) # Why Schools Don't Use Open Source to Teach Programming print(p.speakers) # [157509] print(p.age) # KeyError: 'age'
  • 處理無效屬性名, 例如內置的關鍵字, keyword.iskeyword
grad = FrozenJSON({'name': 'Jim Bo', 'class': 1982}) # print(grad.class) # invalid syntax print(getattr(grad, 'class')) # 1982

修改類的構造函數:

def __init__(self, mapping):self.__data = {}for k,v in mapping.items():if keyword.iskeyword(k): # 如果是關鍵字,則增加下劃線后綴k += "_"self.__data[k] = v
  • 無效的命名,str.isidentifier()
grad = FrozenJSON({'2name': 'Jim Bo', 'class': 1982}) print(grad.2name) # SyntaxError: invalid syntax

改名

def __init__(self, mapping):self.__data = {}for k,v in mapping.items():if keyword.iskeyword(k):k += "_"if not k.isidentifier(): # 不是合法的命名,改之k = "_" + kself.__data[k] = v print(grad._2name) # Jim Bo
  • __init__ 方法其實是 “初始化方法”。真正的構造方法是 __new__(但是幾乎不需要自己編寫)
# 構建對象的偽代碼 def object_maker(the_class, some_arg): new_object = the_class.__new__(some_arg) # new 方法也可以返回其它類對象if isinstance(new_object, the_class): the_class.__init__(new_object, some_arg) return new_object class FrozenJSON:# 一個只讀接口,使用屬性表示法訪問JSON類對象def __new__(cls, arg): # 第一個參數是類本身if isinstance(arg, abc.Mapping):return super().__new__(cls)elif isinstance(arg, abc.MutableSequence):return [cls(item) for item in arg]else:return argdef __init__(self, mapping):self.__data = {}for k, v in mapping.items():if keyword.iskeyword(k):k += "_"if not k.isidentifier():k = "_" + kself.__data[k] = vdef __getattr__(self, name):if hasattr(self.__data, name): # 有屬性,獲取return getattr(self.__data, name)# 調用 keys 等方法就是通過這種方式處理的else: # 沒有,構建 FrozenJSONreturn FrozenJSON(self.__data[name])

2. @property

https://www.liaoxuefeng.com/wiki/1016959663602400/1017502538658208
請利用 @property 給一個Screen對象加上width和height屬性,以及一個只讀屬性resolution

class Screen(object):def __init__(self):self._w = 0self._h = 0self._r = 786432@property # 將方法變成屬性, 調用不要加()def width(self):return self._w@propertydef height(self):return self._h@width.setter # 可以設置屬性值,沒有該方法是只讀屬性def width(self, v):self._w = v@height.setterdef height(self, v):self._h = v@propertydef resolution(self):return self._r s = Screen() s.width = 1024 s.height = 768 print('resolution =', s.resolution) if s.resolution == 786432:print('測試通過!') else:print('測試失敗!')
  • 特性都是類屬性,但是特性管理的其實是實例屬性的存取
class Class:data = "class data attr"@propertydef prop(self):return "prop value"obj = Class() print(vars(obj)) # {}, vars 函數返回 obj 的 __dict__ 屬性 print(obj.data) # class data attr obj.data = "changed" print(vars(obj)) # {'data': 'changed'} print(Class.data) # class data attr # 實例修改了data,但是 類屬性沒有被修改print(Class.prop) # <property object at 0x0000021A91E4A680> print(obj.prop) # prop value # obj.prop = "changed prop" # 報錯 can't set attribute obj.__dict__["prop"] = "changed prop1" print(vars(obj)) # {'data': 'changed', 'prop': 'changed prop1'} print(obj.prop) # prop value # # 讀取 obj.prop 時仍會運行特性的讀值方法。特性沒被實例屬性遮蓋 Class.prop = "haha" # 覆蓋 Class.prop 特性,銷毀特性對象 print(obj.prop) # changed prop1 # 現在,obj.prop 獲取的是實例屬性。 # Class.prop 不是特性了,因此不會再覆蓋 obj.prop。 print(obj.data) # changed print(Class.data) # class data attr Class.data = property(lambda self : "data prop value") # 使用新特性覆蓋 Class.data print(obj.data) # data prop value # obj.data 被 Class.data 特性遮蓋了 del Class.data # 刪除特性 print(obj.data) # changed # 恢復原樣,obj.data 獲取的是實例屬性 data

obj.attr 這樣的表達式不會從 obj 開始尋找 attr,而是從 obj.__class__ 開始,而且,僅當類中沒有名為 attr 的特性時,Python 才會在 obj 實例中尋找。

這條規則不僅適用于特性, 還適用于一整類描述符——覆蓋型描述符(overriding descriptor)

2.1 help() 文檔

使用裝飾器創建 property 對象時,讀值方法(有 @property 裝飾器 的方法)的文檔字符串作為一個整體,變成特性的文檔

>>> class Foo:@propertydef bar(self):'''說明文檔'''return self.__dict__['bar']@bar.setterdef bar(self, val):self.__dict__['bar'] = val>>> help(Foo) Help on class Foo in module __main__:class Foo(builtins.object)| Data descriptors defined here:| | __dict__| dictionary for instance variables (if defined)| | __weakref__| list of weak references to the object (if defined)| | bar| 說明文檔>>> help(Foo.bar) Help on property:說明文檔
  • 經典寫法,傳入 doc 參數
weight = property(get_weight, set_weight, doc='weight in kilograms')

3. 特性工廠函數

為了減少編寫 getter,setter,可以使用特性工廠函數

def quantity(storage_name):def qty_getter(instance):return instance.__dict__[storage_name]def qty_setter(instance, value):if value > 0:instance.__dict__[storage_name] = valueelse:raise ValueError("value must be > 0")return property(qty_getter, qty_setter) class LineItem:weight = quantity('weight') # 使用特性工廠函數定義weight類屬性price = quantity('price') # price 屬性def __init__(self, description, weight, price):self.description = descriptionself.weight = weight # 激活屬性,確保不為負數和0self.price = pricedef subtotal(self):return self.weight * self.price # 使用特性中存儲的值line1 = LineItem("name1", 8, 13.5) print(line1.weight, line1.price) # 8 13.5 print(sorted(vars(line1).items())) # [('description', 'name1'), ('price', 13.5), ('weight', 8)]

weight 特性 覆蓋了 weight 實例屬性,因此對 self.weight 或 obj.weight 的 每個引用都由特性函數處理,只有直接存取 __dict__ 屬性才能跳過特性的處理邏輯

4. 屬性刪除操作

del 操作,刪除屬性很少見,但是 python 支持該操作

class BlackKnight:def __init__(self):self.members = ['an arm','another arm','a leg','another leg']self.phrases = ["'Tis but a scratch.","It's just a flesh wound.","I'm invincible!","All right, we'll call it a draw."]@propertydef member(self):print("next member is:")return self.members[0]@member.deleterdef member(self):text = 'BLACK KNIGHT (loses {})\n-- {}'print(text.format(self.members.pop(0), self.phrases.pop(0)))knight = BlackKnight() print(knight.member) # next member is: # an arm del knight.member # BLACK KNIGHT (loses an arm) # -- 'Tis but a scratch. del knight.member # BLACK KNIGHT (loses another arm) # -- It's just a flesh wound. del knight.member # BLACK KNIGHT (loses a leg) # -- I'm invincible! del knight.member # BLACK KNIGHT (loses another leg) # -- All right, we'll call it a draw. del knight.member # IndexError: pop from empty list
  • 經典寫法,fdel 參數設置刪除函數
member = property(member_getter, fdel=member_deleter)
  • 如果不使用特性,還可以實現低層特殊的 __delattr__ 方法處理 刪除屬性 的操作

5. 處理屬性的重要屬性和函數

  • __class__ 對象所屬類的引用(即 obj.__class__ 與 type(obj) 的作用相 同)
    Python 的某些特殊方法,例如 __getattr__,只在對象的類中尋找,而不在實例中尋找
  • __dict__ 一個映射,存儲對象或類的可寫屬性。
    有 __dict__ 屬性的對象, 任何時候都能隨意設置新屬性
    如果類有 __slots__ 屬性,它的實例可能沒有 __dict__ 屬性
  • __slots__
    類可以定義這個這屬性,限制實例能有哪些屬性
    __slots__ 屬性 的值是一個字符串組成的元組,指明允許有的屬性
    如果 __slots__ 中沒有 '__dict__',那么該類的實例沒有 __dict__ 屬性,實例只允許有指定名稱的屬性

5.1 處理屬性的內置函數

  • dir([object])
    列出對象的大多數屬性,dir 函數也不會列出類的幾個特殊屬性,例如 __mro__、__bases__ 和 __name__
>>> dir(Foo) ['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__','__init_subclass__', '__le__', '__lt__', '__module__','__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'bar']
  • getattr(object, name[, default])
    從 object 對象中獲取 name 字符串對應的屬性
    獲取的屬性可能來自 對象所屬的類或超類
    如果沒有指定的屬性,getattr 函數拋出 AttributeError 異常,或者返回 default 參數的值(如果設定了這個參數的話)
  • hasattr(object, name),調用上面的函數,看是否返回異樣
  • setattr(object, name, value),可能會創建一個新屬性,或者 覆蓋現有的屬性
  • vars([object]),返回 object 對象的 __dict__ 屬性
    如果實例所屬的類定義了 __slots__ 屬性,實例沒有 __dict__ 屬性,那么 vars 函數不能處理 那個實例

5.2 處理屬性的特殊方法

  • 使用點號或內置的 getattr、hasattr 和 setattr 函數存取屬性都會 觸發下述列表中相應的特殊方法

  • 但是直接通過實例的 __dict__ 屬性讀寫屬性不會觸發這些特殊方法,通常會使用這種方式 跳過特殊方法

  • 特殊方法不會被同名實例屬性 遮蓋

總結

以上是生活随笔為你收集整理的python 动态属性和特性的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。