栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Python

Python 黑魔法【延迟计算属性】与【动态计算属性】

Python 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Python 黑魔法【延迟计算属性】与【动态计算属性】

那么众所周知,Pyhon延迟计算属性在很多技术博客上已经讲述过了,这种方法,最初是在Cookbook上讲的,并且根据该案例作一些小的修改,由此得到动态计算属性,此篇文章不仅重现该案例,下面的部分内容可能与其他博主或者官方相似,此篇文章主要是为了引出更多的方法。

首先先讲一下延迟计算属性吧,延迟计算属性所解决的问题是:你想将一个只读属性定义成一个property,并且只在访问的时候才会计算结果。 但是一旦被访问后,你希望结果值被缓存起来,不用每次都去计算。 这种方式主要也是为了提升性能,只有在初次调用的时候才会计算一次,后续调用也不会进行计算

1、延迟计算属性
class lazyproperty:
    def __init__(self, func):
        self.func = func

    def __get__(self, instance, cls):
        if instance is None:
            return self
        else:
            value = self.func(instance)
            setattr(instance, self.func.__name__, value)
            return value

通用下面这种方式使用它

import math

class Circle:
    def __init__(self, radius):
        self.radius = radius

    @lazyproperty
    def area(self):
        print('Computing area')
        return math.pi * self.radius ** 2

    @lazyproperty
    def perimeter(self):
        print('Computing perimeter')
        return 2 * math.pi * self.radius

在交互环境下的使用

>>> c = Circle(4.0)
>>> c.radius
4.0
>>> c.area
Computing area
50.26548245743669
>>> c.area
50.26548245743669
>>> c.perimeter
Computing perimeter
25.132741228718345
>>> c.perimeter
25.132741228718345
>>>

仔细观察你会发现消息 Computing area 和 Computing perimeter 仅仅出现一次。

2、延迟计算属性【私有属性】

那么这些属性的缺陷是它们是可以被修改的,如果你考虑安全方面的问题,可以 用如下方式实现。

def lazyproperty(func):
    name = '_lazy_' + func.__name__
    @property
    def lazy(self):
        if hasattr(self, name):
            return getattr(self, name)
        else:
            value = func(self)
            setattr(self, name, value)
            return value
    return lazy

import math

class Circle:
    def __init__(self, radius):
        self.radius = radius

    @lazyproperty
    def area(self):
        print('Computing area')
        return math.pi * self.radius ** 2

    @lazyproperty
    def perimeter(self):
        print('Computing perimeter')
        return 2 * math.pi * self.radius

通过设置私有属性的情况下,通过设置属性会报错

>>> c = Circle(4.0)
>>> c.area
Computing area
50.26548245743669
>>> c.area
50.26548245743669
>>> c.area = 25
Traceback (most recent call last):
    File "", line 1, in 
AttributeError: can't set attribute
>>>
3、动态计算属性【私有属性】

通过上述方法设置后,还是会存在一些缺陷,如果你即使想设置属性为私有属性,又想动态计算属性,以便于后续修改属性后,能改在调用对应的方法后,获得动态的私有属性值,那么博主在这里有一个简单的方法。

import math


class Circle:

    def __init__(self, radius):
        self.radius = radius

    def __getattr__(self, item):
        name = "__" + item
        if name in self.__dict__.keys():
            return self.__dict__.get(name)
        else:
            return self.__dict__.get(item)

    def __setattr__(self, item, value):
        name = "__" + item
        if name in self.__dict__.keys():
            raise AttributeError("can't set attribute")
        else:
            self.__dict__.update({item: value})

    def calc_prop(self, radius):
        area = math.pi * radius ** 2
        perimeter = 2 * math.pi * radius
        localss = locals()
        localss.pop("self")
        localss = {"__" + k: v for k, v in localss.items()}
        self.__dict__.update(localss)
        return self.__dict__

原理也很简单,首先在calc_prop方法里传入radius后,这里需要注意的是,获得局部变量后,需要在属性前添加__变为私有属性,把在局部变量里已经计算好的属性赋予__dict__即可,但是这里需要注意的是,即使赋值好后,类属性依然不是私有属性,所以需要通过手动重写 getattr 函数和 setattr 函数。

下面是交互结果

c = Circle(4.0)
print(c.radius)
print(c.__dict__)
print(c.calc_prop(3.0))
print("area", c.area)
c.area = 20
4.0
{'radius': 4.0}
{'radius': 4.0, '__radius': 3.0, '__area': 28.274333882308138, '__perimeter': 18.84955592153876}
area 28.274333882308138
Traceback (most recent call last):
  File "D:/Desktop/neimenggu/catch.py", line 38, in 
    c.area = 20
  File "D:/Desktop/neimenggu/catch.py", line 19, in __setattr__
    raise AttributeError("can't set attribute")
AttributeError: can't set attribute
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/934155.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号