python惰性_如何创建惰性属性以提高Python的性能

python惰性

Lazy evaluation is a programming implementation paradigm that defers evaluating necessary operations until it’s requested to do so.

惰性评估是一种编程实现范例,用于延迟评估必要的操作,直到被要求这样做为止。

Usually, lazy evaluation is the preferred implementation when the operation is expensive, requiring either extensive processing time or memory. For example, in Python, one of the best-known techniques involving lazy evaluation is generators. Instead of creating whole sequences for the iteration, which can consume lots of memory, generators lazily evaluate the current need and yield one element at a time when requested.

通常,当操作昂贵,需要大量处理时间或内存时,首选惰性评估。 例如,在Python中,涉及延迟评估的最著名技术之一是生成器。 生成器懒惰地评估当前需求并在请求时一次生成一个元素,而不是为可能会消耗大量内存的迭代创建整个序列。

Besides the generators feature, outside the Python world, many other object-oriented programming languages, such as Swift and Kotlin, have lazy evaluations pertaining to objects. Specifically, you can specify that particular attributes of your custom instance objects are lazy in these languages, which means these attributes aren’t created until they’re explicitly accessed.

除了生成器功能外,在Python世界之外,许多其他面向对象的编程语言(如Swift和Kotlin)对对象的评估也比较懒惰。 具体来说,您可以指定自定义实例对象的特定属性在这些语言中是惰性的,这意味着这些属性只有在明确访问它们之后才能创建。

However, lazy attributes are less discussed in Python tutorials, as far as I know. Thus, to provide a proof of concept to Python learners, I’d like to introduce you to lazy attributes in Python.

但是,据我所知,懒惰属性在Python教程中很少讨论。 因此,为了向Python学习者提供概念证明,我想向您介绍Python中的惰性属性。

问题 (The Question)

Before we start discussing lazy attributes, some people may wonder why it matters or why we bother using lazy attributes.

在我们开始讨论惰性属性之前,有些人可能想知道为什么这很重要,或者为什么我们要麻烦使用惰性属性。

Here’s a possible scenario: Suppose we’re building a website that allows the user to interact with other users. One functionality is to view one’s followers, presented in a list. When we tap one user, we can view the user’s profile in a pop-up window. Let’s write some code to show a possible implementation.

这是一种可能的情况:假设我们正在建立一个允许用户与其他用户进行交互的网站。 一种功能是查看列表中显示的关注者。 当我们点击一​​个用户时,我们可以在弹出窗口中查看该用户的个人资料。 让我们写一些代码来展示可能的实现。

The problem
问题

As you can see, the current implementation involves fetching the profile data for all the users in the list. The operation of fetching the profile data can be expensive. It not only requires a trip to the remote server but also needs to store the data in the memory.

如您所见,当前的实现涉及为列表中的所有用户获取配置文件数据。 提取配置文件数据的操作可能很昂贵。 它不仅需要访问远程服务器,还需要将数据存储在内存中。

Importantly, these profile data aren’t required for displaying the needed user interface because the profile data are only used when a particular username is tapped. This use case sets the perfect foundation for us to implement lazy attributes.

重要的是,这些配置文件数据对于显示所需的用户界面不是必需的,因为仅当点击特定的用户名时才使用配置文件数据。 这个用例为我们实现惰性属性奠定了完美的基础。

解决方案1:使用“ @property” (Solution 1: Use ‘@property’)

To begin with, properties aren’t exactly the same concept as attributes in Python. Essentially, properties are decorated functions. And through the decoration, regular functions are converted into properties, which can be used as other attributes, such as supporting the dot-notation access.

首先,属性与Python中的属性并不完全相同。 本质上, 属性是装饰功能。 并通过修饰将常规函数转换为属性,这些属性可用作其他属性,例如支持点符号访问。

Thus, strictly speaking, creating a property isn’t really creating a lazy attribute itself. Instead, it’s just a matter of providing an interface to ease data handling. Behind the scenes, some other processes are involved. Let’s look at the following code before I explain how it works.

因此,严格来说,创建属性本身并不是在创建惰性属性。 取而代之的是,提供接口以简化数据处理即可。 在幕后,还涉及其他一些过程。 在解释其工作方式之前,让我们看一下下面的代码。

Lazy attribute: ‘@property’
惰性属性:“ @ property”

The above code has the following changes that are relevant to the implementation of the lazy attribute using the @property decorator.

上面的代码具有以下更改,这些更改与使用@property装饰器实现lazy属性有关。

  • We now have a protected attribute, _profile_data, which is created as None during the instantiation of the object, which is used as a placeholder for later data processing.

    现在,我们有了一个受保护的属性_profile_data ,该属性在对象实例化时被创建为None ,用作以后的数据处理的占位符。

  • We use the @property decorator to decorate the profile_data function, such that we can use the dot notation to access the attribute of profile_data.

    我们使用@property装饰器来装饰profile_data函数,以便可以使用点表示法来访问profile_data的属性。

  • In the profile_data function, we first check if the _profile_data attribute has data or not, and we only perform the heavy operation when it has no data (i.e., None). In our use case, if a particular user is never tapped for displaying their profile, we’ll never need to fetch the profile data, which is exactly what lazy evaluation is meant to be — it’ll only run the expensive operation only when it’s absolutely needed.

    profile_data函数中,我们首先检查_profile_data属性是否具有数据,并且仅当它没有数据时才执行重操作(即None )。 在我们的用例中,如果从不窃听特定用户的个人资料,我们将永远不需要获取个人资料数据,这正是惰性评估的含义–仅当它执行时,才运行昂贵的操作绝对需要。

Evaluate lazy attributes implemented with ‘@property’
评估使用'@property'实现的惰性属性

The above code was used to check if the implementation of lazy attributes worked as expected. We tried to access the attributes twice, and as you can see, the expensive operation ran only for the first time when the attribute was None. When we accessed the attribute for the second time, we got the fetched value immediately.

上面的代码用于检查惰性属性的实现是否按预期工作。 我们尝试过两次访问属性,如您所见,昂贵的操作仅在属性为None时才第一次运行。 当我们第二次访问该属性时,我们立即获得了获取的值。

解决方案2:使用“ __getattr__”特殊方法 (Solution 2: Use the ‘__getattr__’ Special Method)

In Python, functions that have double underscores before and after their names are called special or magic methods. Some people also call them dunder (i.e., double underscores) methods. One particular special method — __getattr__— can help us implement lazy attributes.

在Python中,名称前后带有双下划线的函数称为特殊方法或魔术方法。 有些人也称它们为dunder (即双下划线)方法。 一种特殊的特殊方法__getattr__可以帮助我们实现惰性属性。

With custom classes, instance objects have their attributes saved in a dictionary, which can be accessed using the special method __dict__. Specifically, this dictionary stores the attribute names as its keys and the corresponding attribute values as its values. Notably, if the dictionary doesn’t contain the specified attribute, the special method __getattr__ will get called as a fallback mechanism.

对于自定义类,实例对象的属性保存在字典中,可以使用特殊方法__dict__进行访问。 具体而言,该词典将属性名称存储为键,并将相应的属性值存储为值。 值得注意的是,如果字典不包含指定的属性,则特殊方法__getattr__将作为后备机制被调用。

It may sound confusing, but it’s not that hard with a real code example, as shown below.

听起来可能令人困惑,但是使用实际的代码示例并不难,如下所示。

Lazy attribute: ‘__getattr__’
惰性属性:“ __ getattr__”

The above code has the following changes that are relevant to the implementation of lazy attributes using the __getattr__ special method.

上面的代码具有以下更改,这些更改与使用__getattr__特殊方法实现惰性属性有关。

  • The updated __init__ method removes the attribute setting with the profile data. It’s a necessary change because if it’s set, even with a value of None, the attribute and its value will be stored in the object’s __dict__ attribute. In that case, the special method __getattr__ won’t get called.

    更新的__init__方法将删除配置文件数据的属性设置。 这是必要的更改,因为即使设置为None ,该属性及其值也将存储在对象的__dict__属性中。 在这种情况下,将不会调用特殊方法__getattr__

  • We also implement the __str__ method, which will be used to display the object for informational purposes (i.e., in the raised exception in the __getattr__ method).

    我们还实现了__str__方法,该方法将用于显示对象以供参考(即,在__getattr__方法中引发的异常中)。

  • In the __getattr__ method, we specified that when the profile_data attribute is accessed, we’ll fetch the data remotely and set the attribute using the setattr method.

    __getattr__方法中,我们指定访问profile_data属性时,将远程获取数据并使用setattr方法设置属性。

Testing of the lazy attribute
测试惰性属性

The above code was run to show whether the special method __getattr__ was able to help us implement lazy attributes.

运行上面的代码以显示特殊方法__getattr__是否能够帮助我们实现惰性属性。

When we tried to get the attribute named profile_data for the first time, the __getattr__ special method got called and created the attribute profile_data for us.

当我们第一次尝试获取名为profile_data的属性时, __getattr__特殊方法被调用并为我们创建了属性profile_data

Notably, the special __getattr__ method wasn’t called the second time, when we tried to retrieve the profile_data attribute, because this particular attribute has been available in the __dict__ dictionary. Let’s check that with the following code.

值得注意的是,当我们尝试检索profile_data属性时,没有再次调用特殊的__getattr__方法,因为该特殊属性在__dict__词典中可用。 让我们用以下代码检查一下。

‘__dict__’ and ‘__getattr__’
'__dict__'和'__getattr__'

As shown above, the profile_data attribute wasn’t in the dictionary. After we tried to retrieve it, the __getattr__ method got called, and the profile_data attribute and its value got saved in the dictionary.

如上所示, profile_data属性不在字典中。 在尝试检索它之后,调用了__getattr__方法,并将profile_data属性及其值保存在字典中。

The next time we accessed the attribute, it was directly returned from the dictionary without triggering the __getattr__ method. It’s exactly the behavior we wanted the lazy evaluation to have.

下次访问该属性时,该属性直接从字典中返回,而不会触发__getattr__方法。 这正是我们希望懒惰评估所具有的行为。

荣誉奖:'__getattribute__' (An Honorable Mention: ‘__getattribute__’)

Besides the __getattr__ method, there’s another special method called __getattribute__ in Python. Although they have similar names and work similarly to some extent, please don’t confuse these two different methods.

除了__getattr__方法之外, __getattr__还有另一个特殊的方法称为__getattribute__ 。 尽管它们具有相似的名称,并且在某种程度上相似地工作,但是请不要混淆这两种不同的方法。

Unlike the __getattr__ method, which doesn’t get called when a particular attribute is in the instance dictionary, the __getattribute__ method gets called every time an attribute is retrieved.

__getattr__方法不同,当实例字典中有特定属性时不会调用__getattr__方法,而每次检索属性时都会调用__getattribute__方法。

This feature is only useful when you expect the attribute to change very frequently and only the latest data are relevant. In these cases, we can achieve the effect by defining pertinent functions instead.

仅当您希望属性更改非常频繁且仅最新数据相关时,此功能才有用。 在这些情况下,我们可以通过定义相关函数来达到效果。

In other words, I don’t recommend you try implementing it because it’s tricky to make it function properly (see here for a brief discussion). For example, one particular problem you may run into is infinite recursive loops — the __getattribute__ method gets called an infinite number of times and will crash your program.

换句话说,我不建议您尝试实现它,因为要使其正常运行是很棘手的(请参见此处进行简要讨论)。 例如,您可能会遇到的一个特定问题是无限递归循环__getattribute__方法被调用了无数次,这将使您的程序崩溃。

结论 (Conclusions)

In this article, we were focused on discussing two practical ways (plus a tricky way not discussed in detail and not recommended) of implementing lazy attributes in Python: one using the @property decorator and the other using the __getattr__ special method.

在本文中,我们集中讨论了两种在Python中实现懒惰属性的实用方法(不建议详细讨论的一种棘手方法):一种使用@property装饰器,另一种使用__getattr__特殊方法。

Personally, I prefer using the property decorator, which is more straightforward and easier to understand. However, when you need to define multiple lazy attributes, the __getattr__ method is better because it provides a centralized place to manage these lazy attributes.

就我个人而言,我更喜欢使用属性装饰器,该装饰器更加简单明了。 但是,当您需要定义多个惰性属性时, __getattr__方法更好,因为它提供了一个集中的位置来管理这些惰性属性。

Thanks for reading.

谢谢阅读。

翻译自: https://medium.com/better-programming/how-to-create-lazy-attributes-to-improve-performance-in-python-b369fd72e1b6

python惰性

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值