動態屬性和特性

 

使用動態屬性訪問

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)

相關文章
相關標籤/搜索