动态属性和特性

动态属性和特性

在Python中,数据的属性和处理数据的方法统称属性(attribute)。其实,方法只是可调用的属性。除了这二者外,我们还可以创建特性(property),在不改变类接口的前提下,使用存取方法(即读值方法和设值方法)修改数据属性。

除了特性,Python还提供丰富的API,用于控制属性的访问权限,以及实现动态属性。使用点号访问属性(obj.attr),Python解释器会调用特殊的方法(如__getattr____setattr__)计算属性。

使用动态属性转换数据

数据为:

{
  "Schedule": {
    "conferences": [
      {
        "serial": 115
      }
    ],
    "events":[
        {
        "serial": 33451,
        "name": "Migrating to the Web Using Dart and Polymer - A Guide for Legacy OOP Developers",
        "event_type": "40-minute conference session",
        "time_start": "2014-07-23 17:00:00",
        "time_stop": "2014-07-23 17:40:00",
        "venue_serial": 1458,
        "description": "The web development platform is massive. With tons of libraries...",
        "website_url": 				       "https://conferences.oreilly.com/oscon/oscon2014/public/schedule/detail/33451",
        "speakers": [
          149868
        ],
        "categories": [
          "Emerging Languages"
        ]
      },
    ],
    "speakers":[
        {
        "serial": 149868,
        "name": "Faisal Abid",
        "photo": "https://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_%40user_149868.jpg",
        "url": "https://medium.com/@faisalabid",
        "position": "Google Developer Expert. Senior Engineer at Zoom.ai. ",
        "affiliation": "Zoom.ai",
        "twitter": "FaisalAbid",
        "bio": "\u003cp\u003eI am a software engineer, author, teacher and..."
      },
    ],
    "venues":[
        {
        "serial": 1448,
        "name": "Portland Ballroom",
        "category": "Conference Venues"
      },
    ]
  }

实现一个可以通过feed.Schedule.events[0].name(feed为反序列后的dict对象)来获取feed['Schedule']['events'][0]['name']的类。

import json
import keyword
from collections import abc


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 arg

    def __init__(self, mapping):
        self.__data = {}
        for key, value in mapping.items():
            # 判断是否为python中的关键字
            if keyword.iskeyword(key):
                key += '_'
            self.__data[key] = value

    def __getattr__(self, name):
        if hasattr(self.__data, name):
            return getattr(self.__data, name)
        else:
            return FrozenJSON(self.__data[name])


def load():
    with open('osconfeed.json', encoding="utf-8") as fp:
        return json.load(fp)


if __name__ == '__main__':
    raw_feed = load()
    feed = FrozenJSON(raw_feed)
    print(vars(feed))
    print(feed.Schedule.events[0].name)

分析:

​ 递归的将obj.attr转换成obj.__data[attr]

使用特性验证属性

创建一个可以读写的特性

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] = value
        else:
            raise ValueError('value must be > 0')

    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

分析:

weight和price都需要是正数,所以用特性工厂函数quantity来创建特性,其值存储在instance.__dict__[storage_name]中,内置的property虽然用作装饰器,但它是个类,如下所示:

class property(object):
    """
    Property attribute.
    
      fget
        function to be used for getting an attribute value
      fset
        function to be used for setting an attribute value
      fdel
        function to be used for del'ing an attribute
      doc
        docstring
    
    Typical use is to define a managed attribute x:
    
    class C(object):
        def getx(self): return self._x
        def setx(self, value): self._x = value
        def delx(self): del self._x
        x = property(getx, setx, delx, "I'm the 'x' property.")
    
    Decorators make defining new properties or modifying existing ones easy:
    
    class C(object):
        @property
        def x(self):
            "I am the 'x' property."
            return self._x
        @x.setter
        def x(self, value):
            self._x = value
        @x.deleter
        def x(self):
            del self._x
    """

特性会覆盖实例属性

特性都是类变量,但是特性管理的其实是实例属性的存取。 如果实例和所属的类有同名数据属性, 那么实例属性会覆盖(或称遮盖) 类属性——至少通过那个实例读取属性时是这样

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值