• 动态属性和特性


    使用动态属性访问

    一、使用动态属性访问JSON类数据

      feed['Schedule']['events'][40]['name'],这种句法冗长,在Javascript中,可以使用feed.Schedule.events[40].name,获取那个值。

      在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实例。

    处理无效属性名

    二、处理无效属性名

      对名称为Python关键字的属性做特殊处理

    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标识符。

      解决办法:抛出异常或者换成通用名称,attr_0,attr_1。

    三、使用__new__方法灵活的方式创建对象

      通常把__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

    四、调整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)

    特性全解析

    五、特性全解析

      内置的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 {})
    -- {}'
            print(text.format(self.members.pop(0), self.phrases.pop(0)))

    重要属性和函数

    六、处理属性的重要属性和函数:

    (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)

  • 相关阅读:
    【转载】分布式环境Raft一致性共识算法解读
    从码农到工程师:只要做到这6点
    产品思维的修炼–技术的必修课
    工具篇
    安全测试
    测试体会
    测试题目
    软件测试工具
    常见的性能测试方法
    性能测试在软件测试的周期位置
  • 原文地址:https://www.cnblogs.com/5poi/p/11456066.html
Copyright © 2020-2023  润新知