摘要:一利用動態屬性處理數據源屬性在中,數據的屬性和處理數據的方法統稱屬性。處理無效屬性名在中,由于關鍵字被保留,名稱為關鍵字的屬性是無效的。內置函數列出對象的大多數屬性。點號和內置函數會觸發這個方法。
導語:本文章記錄了本人在學習Python基礎之元編程篇的重點知識及個人心得,打算入門Python的朋友們可以來一起學習并交流。
本文重點:
1、了解如何利用動態屬性處理數據;一、利用動態屬性處理JSON數據源
2、掌握Python中的特性概念以及@property裝飾器;
3、了解Python中處理屬性的重要屬性和函數。
屬性:在Python中,數據的屬性和處理數據的方法統稱屬性。
元編程:用元類進行編程,元類→類→對象,元類比類更抽象,生成類的類。
第一版:利用json.load(fp)審查數據
from urllib.request import urlopen import warnings import os import json URL = "http://www.oreilly.com/pub/sc/osconfeed" JSON = "data/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: #在with語句中使用兩個上下文管理器分別用于讀取和保存遠程文件。 local.write(remote.read()) with open(JSON) as fp: return json.load(fp)#json.load函數解析JSON文件,返回Python原生對象。
第二版:使用動態屬性訪問JSON類數據
第一版查閱深層數據的格式比較冗長,例如feed"Schedule"40,我們希望在讀取屬性上采用feed.Schedule.events[40].name這類方式來改進。并且第二版的類能遞歸,自動處理嵌套的映射和列表。
from collections import abc class FronenJSON: def __init__(self,mapping): self.__data=dict(mapping)#創建副本,同時確保處理的是字典。 def __getattr__(self, name):#僅當沒有指定名稱的屬性才調用__getattr__方法。 if hasattr(self,name): return getattr(self.__data,name) else: return FronenJSON.build(self.__data[name]) @classmethod def __build__(cls,obj): if isinstance(obj,abc.Mapping):#判斷obj是否是映射。 return cls(obj)#創建FrozenJSON對象。 elif isinstance(obj,abc.MutableSequence): return [cls.build(item) for item in obj]#遞歸調用.build()方法,構建一個列表。 else:#既不是字典也不是列表,則返回元素本身。 return obj
分析: FronenJSON類的關鍵是__getattr__方法。僅當無法使用常規的方式獲取屬性(即在實例、類或超類中找不到指定的屬性),解釋器才會調用特殊的__getattr__方法。
2、處理無效屬性名在Python中,由于關鍵字被保留,名稱為關鍵字的屬性是無效的。因此需要對第二版中的__init__進行改進:
def __init__(self,mapping): self.__data={} for key,value in mapping.items(): if keyword.iskeyword(key): key+="_"#與Python關鍵字重復的key在尾部加上下劃線。 self.__data[key]=value3、使用特殊方法__new__
第三版:使用__new__構造方法把一個類轉換成一個靈活的對象工廠函數。
from collections import abc class FronenJSON: def __new__(cls, arg): # __new__是類方法,第一個參數是類本身cls。 if isinstance(arg, abc.Mapping): return super().__new__(cls) #委托給超類object基類的__new__方法處理。 elif isinstance(arg, abc.MutableSequence): # 余下方法與原先的build方法一致。 return [cls(item) for item in arg] else: return arg def __init__(self,mapping): self.__data={} for key,value in mapping.items(): if keyword.iskeyword(key): key+="_" self.__data[key]=value def __getattr__(self, name): if hasattr(self,name): return getattr(self.__data,name) else: return FronenJSON(self.__data[name])二、特性 1、類屬性、實例屬性、私有屬性與特性
類屬性:類屬性在__init__()外初始化,屬于類所有,所有實例共享一個屬性。
調用方法:類屬性在內部用classname.類屬性名調用,外部既可以用classname.類屬性名又可以用instancename.類屬性名來調用。
實例屬性:實例屬性屬于各個實例所有,互不干擾。
私有屬性:
單下劃線_開頭:只是告訴別人這是私有屬性,外部依然可以訪問更改。
雙下劃線__開頭:外部不可通過instancename.propertyname來訪問或者更改,實際將其轉化為了_classname__propertyname。
特性:是用于管理實例屬性的類屬性。
特性用途:經常用于把公開的屬性變成使用讀值方法和設值方法管理的屬性,且在不影響客戶端代碼的前提下實施業務規則。
注意:
不要對實例屬性和類屬性使用相同的名字。否則實例屬性會遮蓋類屬性,發生難以發現的錯誤。
實例屬性不會遮蓋類特性,但類特性會遮蓋實例屬性。
這是因為obj.attr不會從實例obj開始尋找attr,而是從obj.__class__開始;而且僅當類中沒有名為attr的特性時,Python才會在實例中尋找attr。
簡言之,就遮蓋層級而言,類特性>實例屬性>類屬性。
2、使用特性驗證屬性使用特性可以驗證實例屬性的有效性,同時能夠根據已知屬性和屬性之間的關系式調整其他屬性,避免硬編碼。
案例:假設某商店經營堅果、雜糧等多種有機食物,每位顧客的訂單會包含店中的一系列商品,我們需要根據客戶的訂單計算出總價。
分析:我們不希望顧客訂單的商品重量為非正數,需要借助@property裝飾器實現值的獲取與設置,從而驗證實例屬性的有效性。代碼如下:
class LineItem: def __init__(self,description,weight,price): self.description=description self.weight=weight self.price=price def subtotal(self): return self.weight*self.price @property#讀值。 def weight(self): return self.__weight#真正的值存儲在私有屬性中。 @weight.setter def weight(self,value): if value >0: self.__weight=value#有效值存入私有屬性中。 else: raise ValueError("Value must be > 0")#對于無效的值拋出ValueError。
Tips:當我們需要設置只讀屬性時,只使用@property,無需使用@func.setter。
原理解析:為了更好地理解@property裝飾器的原理,我們寫一版效果相同但沒使用裝飾器的代碼。
class LineItem: def __init__(self, description, weight, price): self.description = description self.weight = weight self.price = price def subtotal(self): return self.weight * self.price def get_weight(self): #普通讀值方法。 return self.__weight def set_weight(self, value): #普通設值方法。 if value > 0: self.__weight = value else: raise ValueError("value must be > 0") weight = property(get_weight, set_weight) #構建property對象,賦值給公開的類特性。
property 構造方法的完整簽名:
property(fget=None, fset=None, fdel=None, doc=None)3、特性工廠函數
抽象定義特性的方式有兩種,一是使用特性工廠函數,二是使用描述符類。
下面我們用特性工廠函數來完成上文中提到的訂單結算案例:
def quantity(storage_name): def qty_getter(instance): # instance指的是要把屬性存儲其中的LineItem實例。 return instance.__dict__[storage_name] # 引用閉包中的自由變量storage_name,值直接從instance.__dict__中獲取,以便跳過特性,防止無限遞歸。 def qty_setter(instance, value): if value > 0: instance.__dict__[storage_name] = value # 同理存儲,跳過特性。 else: raise ValueError("value must be > 0") return property(qty_getter, qty_setter) # 構建自定義特性對象并返回。 class LineItem: weight = quantity("weight") # 將自定義特性weight定義為類屬性。 price = quantity("price") # 同上。 def __init__(self, description, weight, price): self.description = description self.weight = weight # 此處特性已經激活,可驗證值的有效性。 self.price = price def subtotal(self): return self.weight * self.price # 此處利用特性獲取實例中存儲的值。4、使用特性刪除屬性
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."] @property def member(self): print("next member is:") return self.members[0] @member.deleter def member(self): text = "BLACK KNIGHT (loses {}) -- {}" print(text.format(self.members.pop(0), self.phrases.pop(0)))
刪除屬性只需在主程序中發出指令:del obj.attr
三、處理屬性的重要屬性和函數 1、特殊屬性__class__:對象所屬類的引用(即obj.__class__和type(obj)的作用相同)。Python中的某些特殊方法比如 __getattr__,只在對象的類中尋找,而不在實例中尋找。
__dict__:一個映射,存儲對象或類的可寫屬性。
__slots__:類可以定義這個屬性,限制實例有哪些屬性。
2、內置函數dir([object]):列出對象的大多數屬性。
getattr(object,name[,default]):從object對象中獲取name字符串對應的屬性。獲取的屬性可能來自對象所屬的類或超類。
hasattr(object,name):若object對象中存在指定的屬性,或者能以某種方式(如繼承)通過object對象獲取指定的屬性,返回True。
setattr(object,name,value):把object對象指定屬性的值設為value,前提是object對象能接受那個值。這個函數可能會創建一個新屬性,或者覆蓋現有的屬性。
var([object]):返回object對象的__dict__屬性。
3、特殊方法__delattr__(self,name):只要使用del語句刪除屬性,就會調用這個方法。
__dir__(self):把對象傳給dir函數時調用,列出屬性。
__getattr__(self,name):僅當獲取指定的屬性失敗,搜索過obj,Class和超類之后調用。
__getattribute__(self,name):嘗試獲取指定的屬性時總會調用這個方法。不過尋找的屬性是特殊屬性或特殊方法時除外。為了防止無限遞歸,__getattribute__方法的實現要使用super().__getattribute__(obj,name)。
__setattr__(self,name,value):嘗試設置指定的屬性時總會調用這個方法。點號和setattr內置函數會觸發這個方法。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/41455.html
摘要:下面我們用描述符來實現中的動態屬性和特性中提及的訂單結算代碼第四版使用描述符實現訂單結算功能描述符基于協議實現,無需創建子類。特性是覆蓋型描述符。非覆蓋型描述符沒有實現方法的描述符屬于非覆蓋型描述符。類中定義的方法是非覆蓋型描述符。 導語:本文章記錄了本人在學習Python基礎之元編程篇的重點知識及個人心得,打算入門Python的朋友們可以來一起學習并交流。 本文重點: 1、了解描述符...
摘要:有一些定制類的特殊方法,如,其中一些具有動態特性的方法可以用來很方便地處理某些動態狀況。動態化屬性和方法的調用,當調用不存在的屬性時,如果存在方法,就會調用方法來嘗試獲得屬性。這種完全動態的調用可以應對一些動態情況,例如實現。 Python有一些定制類的特殊方法,如__str__()、__iter__()、__getitem__(),其中一些具有動態特性的方法可以用來很方便地處理某些動...
摘要:本人很少寫代碼一般都是用的去年時用寫過一些收集系統信息的工具當時是邊看手冊邊寫的如今又要用來寫一個生成的工具就又需要查看手冊了至于為什么不用寫那是因為的庫不兼容永中在這里不得不說雖然很火但是一些庫還是不如多不如兼容性好為了避免以后再出這種事 Python3 Study Notes 本人很少寫 python 代碼, 一般都是用 go 的, 去年時用 python 寫過一些收集系統信息的工...
摘要:結果為對于迭代器和生成器你知道哪些,它們分別應用于什么場景先介紹什么是可迭代的任何可用于循環的都是可迭代的。示例結果為,迭代器任何可以使用函數的都是迭代器,也可使用函數將可迭代對象變為迭代器。未寫完,下次更新補上。 showImg(https://segmentfault.com/img/bVbuN3P); 閱讀本文大約需要 8 分鐘。 7.說一下 Python 中的裝飾器 原理:利用...
摘要:本篇內容將從鴨子類型的動態協議,逐漸過渡到使接口更明確能驗證實現是否符合規定的抽象基類。抽象基類介紹完動態實現接口后,現在開始討論抽象基類,它屬于靜態顯示地實現接口。標準庫中的抽象基類從開始,標準庫提供了抽象基類。 《流暢的Python》筆記。本篇是面向對象慣用方法的第四篇,主要討論接口。本篇內容將從鴨子類型的動態協議,逐漸過渡到使接口更明確、能驗證實現是否符合規定的抽象基類(Abst...
閱讀 2679·2023-04-25 20:28
閱讀 1849·2021-11-22 09:34
閱讀 3687·2021-09-26 10:20
閱讀 1834·2021-09-22 16:05
閱讀 3085·2021-09-09 09:32
閱讀 2502·2021-08-31 09:40
閱讀 2099·2019-08-30 13:56
閱讀 3320·2019-08-29 17:01