1、使用動態屬性訪問JSON類數據app
feed['Schedule']['events'][40]['name'],這種句法冗長,在Javascript中,可使用feed.Schedule.events[40].name,獲取那個值。ide
在Python中,能夠實現一個近似字典的類,達到一樣的效果。函數
from collections import abc class FrozenJSON: def __init__(self,mapping): self.__data = dict(mapping) def __getattr__(self, name): if hasattr(self.__data, name): return getattr(self.__data, name) else: return FrozenJSON.build(self.__data[name]) @classmethod def build(cls,obj): if isinstance(obj, abc.Mapping): return cls(obj) elif isinstance(obj, abc.MutableSequence): return [cls.build(item) for item in obj] else: return obj
FrozenJSON類的關鍵是__getattr__方法。(僅當沒法使用常規的方式獲取屬性,即在實例、類或超類中找不到指定的屬性,解釋器纔會調用特殊的__getattr__方法)工具
FrozenJSON.build方法。能深刻JSON數據的嵌套結構,使用類方法build把每一層嵌套轉換成一個FrozenJSON實例。ui
2、處理無效屬性名spa
對名稱爲Python關鍵字的屬性作特殊處理code
import keyword class FrozenJSON: def __init__(self,mapping): self.__data = {} for key,value in mapping.items(): if keyword.iskeyword(): key += '_' self.__data[key] = value
對於數字開頭的key,str類提供的s.isidentifier()方法,能根據語言的語法判斷s是否爲有效的Python標識符。orm
解決辦法:拋出異常或者換成通用名稱,attr_0,attr_1。對象
3、使用__new__方法靈活的方式建立對象blog
一般把__init__稱爲構造方法,這是從其餘語言借鑑過來的術語。其實,用於構建實例的是特殊方法__new__;
這是一個類方法,使用特殊方式處理,所以沒必要使用@classmethod裝飾器,必須返回一個實例。
返回的實例會做爲第一個參數(即self)傳給__init__方法。
由於調用__init__方法時要傳入實例,並且禁止返回任何值,因此__init__方法實際上是「初始化方法」。真正的構造方法是__new__。
咱們不要本身編寫__new__方法,由於從object類集成的實現已經足夠了。
class FrozenJSON: def __new__(cls, arg): if isinstance(arg,abc.Mapping): return super().__new__(cls) elif isinstance(arg, abc.MutableSequence): return [cls.build(item) for item in arg] else: return arg
4、調整OSCON數據源的結構
對象的__dict__屬性中存儲着對象的屬性,前提是類中沒有聲明__slots__屬性。
更新實例的__dict__屬性,把值設爲一個映射,能快速地在那個實例中建立一堆屬性。
class Record: def __init__(self,**kwargs): self.__dict__.update(**kwargs) def load_db(db): raw_data = data.load() for collection,rec_list in raw_data['Schedule'].items(): record_type = collection[:-1] for record in rec_list: key = '{}.{}'.format(record_type,record['serial']) record['serial'] = key db[key] = Record(**kwargs)
5、特性全解析
內置的property常常用做裝飾器,但它實際上是一個類。在Python中,函數和類一般能夠互換,由於兩者都是可調用的對象。
property構造方法的完整簽名以下:
property(fget=None, fset=None, fdel=None, doc=None)
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)
特性會覆蓋實例屬性
特性都是類屬性,可是特性管理的實際上是實例屬性的存取。
(1)當實例和類有同名數據屬性時,那麼實例屬性會覆蓋類屬性;
class Foo: data = 'the Foo data attr' @property def prop(self): return 'the prop value' >>> f = Foo() >>> vars(f) {} >>> f.data 'the Foo data attr' >>> f.data = 'Bar' >>> vars(f) {'data':'Bar'} >>> Foo.data 'the Foo data attr'
vars函數返回obj的__dict__屬性,代表沒有實例屬性。
(2)實例屬性不會覆蓋類特性
>>> f = Foo() >>> Foo.prop <property object at 0x00000234D2F473B8> >>> f.prop 'the prop value' >>> f.prop = 'fooooo' Traceback (most recent call last): File "<stdin>", line 1, in <module> AttributeError: can't set attribute >>> f.__dict__['prop'] = 'fooooo' >>> vars(f) {'prop': 'fooooo'} >>> f.prop 'the prop value' >>> Foo.prop <property object at 0x00000234D2F473B8> >>> Foo.prop = 'barrrrr' >>> f.prop 'fooooo'
直接從Foo中讀取prop特性,獲取的是特性對象自己,不會運行特性的讀值方法。
(3)新添加的類特性覆蓋現有實例屬性
>>> f.data 'bar' >>> Foo.data 'the Foo data attr' >>> Foo.data = property(lambda self:'xxxxxxxxxxx') >>> f.data 'xxxxxxxxxxx' >>> del Foo.data >>> f.data 'bar'
▲ obj.attr這樣的表達式不會從obj開始尋找attr,而是從obj.__class__開始,並且,僅當類中沒有名爲attr的特性時,Python纔會在obj實例中尋找。
特性的文檔:
控制檯中的help()函數或IDE等工具須要顯示特性的文檔時,會從特性的__doc__屬性中提取信息。
爲property對象設置文檔字符串的方法時傳入doc參數:
weight = property(get_weight,set_weight, doc='weight in kilograms')
使用裝飾器建立property對象時,讀值方法的文檔字符串做爲一個總體,變成特性文檔。
class Foo: @property def bar(self): '''The bar attrbute''' return self.__dict__['bar'] @bar.setter def bar(self,value): self.__dict__['bar'] = value print(help(Foo.bar))
特性工廠:
def quantity(storage_name): def qty_setter(instance,value): if value > 0: instance.__dict__[storage_name] = value else: raise ValueError('Value must be > 0')
def qty_getter(instance): return instance.__dict__[storage_name] return property(qty_getter,qty_setter) class LineItem: weight = quantity('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
屬性刪除操做:
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 {})\n-- {}' print(text.format(self.members.pop(0), self.phrases.pop(0)))
6、處理屬性的重要屬性和函數:
(1)__class__:
對象所屬類的引用(即obj.__class__與type(obj)的做業相同)。
(2)__dict__:
一個映射,存儲對象或類的可寫屬性。有__dict__屬性的對象,任什麼時候候都能隨意設置新屬性。
若是類有__slots__屬性,它的實例可能沒有__dict__屬性。
(3)__slots__:
類能夠定義這個屬性,限制實例能有哪些屬性。__slots__屬性的值是一個字符串組成的元組,指明容許有的屬性。
若是__slots__中沒有__dict__,那麼該類的實例沒有__dict__屬性,實例只容許指定民稱的屬性。
內置函數:
(1)dir([object]):
列出對象的大多數屬性。
(2)getattr(object, name[, default]):
從object對象中獲取name字符串對應的屬性。獲取的屬性可能來自對象所屬的類或超類。
若是沒有指定的屬性,getattr函數拋出AttributeError異常,或者返回default參數的值。
(3)hasattr(object, name):
若是object對象中存在指定的屬性,或者能以某種方式(繼承)經過object對象獲取指定的屬性,返回True。
(4)setattr(object, name, value):
把object對象指定的屬性值設爲value,前提是object對象能接受那個值。這個函數可能會建立一個新屬性,或者覆蓋現有的屬性。
(5)vars([object])
返回object對象的__dict__屬性,若是實例所屬的類定義了__slots__屬性,實例沒有__dict__屬性,那麼vars函數不能處理那個實例!
相反dir函數能處理這樣的實例。
若是沒有指定參數,那麼vars()函數和locals()函數同樣,返回表示本地做用域的字典。
處理屬性的特殊方法:
(1)__delattr__(self, name):
只要使用del語句刪除屬性,就會調用這個方法。
(2)__dir__(self):
把對象傳給dir函數時調用,列出屬性。
(3)__getattr__(self, name):
僅當獲取指定的屬性失敗,搜索過obj、class和超類以後調用。
(4)__getattribute__(self, name):
嘗試獲取指定的屬性時總會調用這個方法,不過,尋找的屬性時特殊屬性或特殊方法時除外。
點號與getattr和hasattr內置函數會觸發這個方法。如:obj.attr和getattr(obj, 'attr', xx)
調用__getattribute__方法且拋出AttributeError異常時,纔會調用__getattr__方法。
爲了在獲取obj實例的屬性不致使無限遞歸,__getattribute__方法的實現要使用super().__getattribute__(obj. name)
(5)__setter__(self, name, value):
嘗試設置指定的屬性時總會調用這個方法。點號和setattr內置函數會觸發這個方法。
obj.attr = xx 和 setter(obj,'attr',xx)