python实例化对象_python实例对象的生命周期

python实例化对象

When our project grows in its scope, it becomes too tedious to manage related data using built-in types, such as lists and dictionaries. In this case, we define our own custom classes. When you learn to implement custom classes, you may have come across various terms related to the creation of custom class instances. Some of these terms sound confusing to beginners. Thus, the first part of the present article is to articulate these terms, while the second part reviews some key life events for Python objects by going through some specific code examples.

当我们的项目范围扩大时,使用内置类型(如列表和字典)来管理相关数据就变得很繁琐。 在这种情况下,我们定义自己的自定义类。 当您学习实现定制类时,您可能会遇到与创建定制类实例相关的各种术语。 这些术语中的一些听起来让初学者感到困惑。 因此,本文的第一部分将阐明这些术语,而第二部分将通过一些特定的代码示例来回顾一些Python对象的关键生命事件。

第一部分关键术语 (Part I. Key Terminology)

实例和实例化 (Instance and Instantiation)

An instance is also known as an instance object, which is the actual object of the class that holds the data. For instance, suppose that we have a class called Person. You can think of an instance of this class as an actual person in your life, which can have attributes, such as name and height, and have functions, such as walk and speak.

实例也称为实例对象,它是保存数据的类的实际对象。 例如,假设我们有一个名为Person的类。 您可以将此类的实例视为您生活中的真实人物,可以具有名称和身高等属性,并具有走路和说话等功能。

The process of creating an instance is known as instantiation. Basically, you instantiate an instance object of a particular class. Under the hood, instantiation involves the allocation of memory to the object that you’re creating. Sometimes, the instantiation is also referred to as the construction of an instance.

创建实例的过程称为实例化。 基本上,您实例化特定类的实例对象。 在幕后,实例化涉及将内存分配给正在创建的对象。 有时,实例化也称为实例的构造。

初始化和构造函数 (Initialization and Constructor)

As part of the instantiation, in most cases, your class instances have some initial attributes that you need to set. The process of setting the initial state of your instance is known as initialization. As you may know, the name of the __init__ method actually stands for initialization.

作为实例化的一部分,在大多数情况下,您的类实例具有一些需要设置的初始属性。 设置实例初始状态的过程称为初始化。 您可能知道, __init__方法的名称实际上代表初始化。

The constructor is the method that you use to create your custom class. Suppose that the custom class Person defines the __init__ method as the following signature: __init__(self, name), which is essentially equivalent to the constructor method Person(name). In other words, you can think of constructors as a special method called by the class, the execution of which is by default through calling the __init__ method.

构造函数是用于创建自定义类的方法。 假设自定义类Person__init__方法定义为以下签名: __init__(self, name) ,其本质上等效于构造函数方法Person(name) 。 换句话说,可以将构造函数视为由类调用的特殊方法,默认情况下通过调用__init__方法来执行该构造函数。

变量与对象 (Variable and Object)

You may be surprised that we even need to discuss what variable and object are. Some people may think that they mean the same thing, but they don’t. In Python, objects are actual data items stored in the memory, while variables are labels that refer to the underlying objects. In other words, we—programmers — play with variables, while Python — the interpreter — handles the objects under the hood for us based on our interaction with the variables.

您可能会惊讶于我们甚至需要讨论什么是变量和对象。 有些人可能认为他们是同一件事,但事实并非如此。 在Python中,对象是存储在内存中的实际数据项,而变量是引用基础对象的标签。 换句话说,我们(程序员)使用变量,而Python(解释器)则根据与变量的交互为我们处理内部对象。

命名空间和范围 (Namespaces and Scopes)

When Python tries to interpret your code, one key task is to understand what variables and functions that you refer to. To get the job done, Python uses namespaces to track the variables and functions that it can search. Specifically, you can think of namespaces as dictionaries that store all the variables that are available to you — they are tools in your tool boxes.

当Python尝试解释您的代码时,一项关键任务是了解您所引用的变量和函数。 为了完成工作,Python使用名称空间来跟踪它可以搜索的变量和函数。 具体来说,您可以将名称空间视为存储所有可用变量的字典,它们是工具箱中的工具。

Scopes are a concept closely related to namespaces. Scopes define boundaries where Python can resolve the variables (resolve means Python understands what variables it should use). As mentioned above, namespaces are tracking the tools in your boxes, while scopes are different tool boxes — some are larger and some are smaller (local scope for a function) and embedded in larger scopes (e.g., global scope for the module).

范围是与名称空间密切相关的概念。 范围定义了Python可以解析变量的边界(resolve表示Python理解其应使用的变量)。 如上所述,名称空间正在跟踪您框中的工具,而范围是不同的工具箱-有些较大,有些较小(功能的本地范围),并嵌入较大的范围(例如,模块的全局范围)。

引用计数垃圾收集 (Reference Counting and Garbage Collection)

Related to the previous concept, each variable holds a reference to the underlying object. Python tracks how many references that an underlying object has to decide if it needs to keep the objects alive or not in the memory. It’s a very important mechanism to prevent your computer from running out of memory.

与先前的概念相关,每个变量都包含对基础对象的引用。 Python跟踪一个基础对象必须决定多少引用,以决定是否需要使这些对象保持活动状态或不在内存中。 这是防止计算机内存不足的一种非常重要的机制。

In normal use cases, we don’t need to manage memory allocation and deallocation for Python objects because Python uses reference counting to free up the memory space. However, there are occasions when reference counting won’t work (e.g., reference cycles). In other words, there is garbage (e.g., objects actually not needed but have non-zero references) in the memory. In this case, Python will try freeing up the memory used by these objects using the garbage collection mechanisms.

在正常使用情况下,我们不需要管理Python对象的内存分配和释放,因为Python使用引用计数来释放内存空间。 但是,在某些情况下参考计数不起作用(例如,参考周期)。 换句话说,内存中存在垃圾(例如,实际上不需要的对象,但具有非零引用)。 在这种情况下,Python将尝试使用垃圾回收机制释放这些对象使用的内存。

取消初始化 (De-initialization)

The process of removing a particular object from the memory is called de-initialization. It can happen when the reference counting for an object reaches zero or execution of the entire program is completed. Some people also refer to this process as destruction.

从内存中删除特定对象的过程称为取消初始化。 当对象的引用计数达到零或整个程序执行完成时,可能会发生这种情况。 有些人也将此过程称为破坏。

第二部分 典型的生命周期 (Part II. The Typical Life Cycle)

实例化 (Instantiation)

The life of an instance starts with its birth — instantiation. In most cases, we instantiate an object using its constructor. For instance, the following code snippet shows you how we can create instance objects of built-in data types. For some built-in data types, we can use literals to instantiate objects, such as [1, 2, 3] for a list object and {“a”: 1, “b”: 2} for a dict object.

实例的生命始于其诞生-实例化。 在大多数情况下,我们使用其构造函数实例化一个对象。 例如,以下代码片段向您展示了如何创建内置数据类型的实例对象。 对于某些内置数据类型,我们可以使用文字来实例化对象,例如[1, 2, 3]表示列表对象, {“a”: 1, “b”: 2}表示字典对象。

# Create an empty list
a_list = list()
# Create an empty dictionary
a_dict = dict()
# Create an empty set
a_set = set()
# Create an empty tuple
a_tuple = tuple()

Custom classes allow us to write some logging information, and thus we can understand more about the instantiation process with a custom class. Let’s see a trivial example, as shown below.

自定义类允许我们编写一些日志记录信息,因此我们可以通过自定义类来了解有关实例化过程的更多信息。 让我们来看一个简单的示例,如下所示。

>>> class Person:
...     def __new__(cls, *args):
...         new_person = object.__new__(cls)
...         print("Person __new__ gets called")
...         print(f"Person Instance Memory Address: {id(new_person)}")
...         return new_person
... 
...     def __init__(self, name):
...         print(self.__dict__)
...         self.name = name
...         print("Person __init__ gets called")
...         print(f"Set Initial State: {id(self)}")
...         print(self.__dict__)
...
...     def __del__(self):
...         print(f"The object is getting deleted: {self}")
...
... 
... person = Person("John")
... 
Person __new__ gets called
Person Instance Memory Address: 4505147856
{}
Person __init__ gets called
Set Initial State: 4505147856
{'name': 'John'}

The instantiation involves two key steps.

实例化涉及两个关键步骤。

  1. Creation of the instance object in the memory. This step involves calling the __new__() method, the name of which simply means creating a new instance object. The key operation here is to allocate memory to the object that you’re constructing. After creating the object, you can see that a memory slot has been associated with the object by calling the id() function, which reveals the memory address of the underlying object with the default CPython implementation.

    在内存中创建实例对象。 此步骤涉及调用__new__()方法,该方法的名称仅表示创建一个新的实例对象。 此处的关键操作是为正在构造的对象分配内存。 创建对象之后,您可以通过调用id()函数来查看与该对象相关联的内存插槽,该函数使用默认的CPython实现显示基础对象的内存地址。

  2. Set the initial state of the object. Once the object is created in the memory, Python will call the __init__() method to complete the initialization process for the object. If you compare the instance before and after setting the name attribute, you’ll notice that the new object didn’t have any attributes to start with, and after initialization, it has the expected name attribute. Notably, we’re still working on the same object that is created using the __new__() method, which has the same memory address.

    设置对象的初始状态。 在内存中创建对象后,Python将调用__init__()方法来完成对象的初始化过程。 如果在设置name属性之前和之后比较实例,您会注意到新对象没有任何属性开始,并且在初始化之后,它具有预期的name属性。 值得注意的是,我们仍在使用__new__()方法创建的同一对象,该方法具有相同的内存地址。

在命名空间中有效 (Active in Namespaces)

Once the instance is created, it will become part of a namespace so it can be used by other variables and functions. To get to know what variables are tracked, we can use the globals() function, which will return key-value pairs showing us the tracked labels and the associated objects. If you try calling this function, you may notice that the print-out can be very long. For the purpose of demonstration, I’ll just show you the keys for the mapping in the code snippet below.

创建实例后,它将成为命名空间的一部分,因此它可以被其他变量和函数使用。 为了了解要跟踪哪些变量,我们可以使用globals()函数,该函数将返回键值对,向我们显示跟踪的标签和关联的对象。 如果尝试调用此函数,可能会注意到打印输出可能很长。 出于演示目的,我将在下面的代码片段中向您显示映射的键。

>>> globals().keys()
dict_keys(['__name__', '__doc__', '__package__', 
           '__loader__', '__spec__', '__file__', 
           '__builtins__', 'sys', 'Person', 'person'])

As you can see, the last item of the keys is the person variable that we’ve just created. When we access this variable later in the code, the mapping object is used to resolve the object for us. As shown below, we can actually retrieve the object manually ourselves.

如您所见,键的最后一项是我们刚刚创建的person变量。 当我们稍后在代码中访问此变量时,将使用映射对象为我们解析该对象。 如下所示,我们实际上可以自己手动检索对象。

>>> globals()['person']
<__main__.Person object at 0x10c8719d0>
>>> id(globals()['person'])
4505147856
>>> hex(id(person))
'0x10c8719d0'
  • We can retrieve the person variable’s associated object using the ‘person’ key from the mapping dictionary of the global namespace.

    我们可以使用'person'键从全局名称空间的映射字典中检索person变量的关联对象。

  • The retrieved object is shown by its memory address expressed in hexadecimal value. (Notice the 0x prefix?) To verify that, we can use the built-in hex() function to reproduce the same hexadecimal value from the same object.

    检索到的对象以十六进制值表示其内存地址。 (注意0x前缀吗?)要验证这一点,我们可以使用内置的hex()函数从同一对象中再现相同的十六进制值。

When an object is active in the namespace, Python also tracks how many objects hold reference to the object for memory management purposes. To find out the reference count, we can take advantage of the functionalities provided by the sys and gc modules. As shown below, the following code shows you that the object has just one reference for now.

当对象在名称空间中处于活动状态时,Python还将跟踪多少个对象持有对该对象的引用以用于内存管理。 为了找出参考计数,我们可以利用sysgc模块提供的功能。 如下所示,以下代码向您显示该对象目前只有一个引用。

>>> import sys
... import gc
... 
>>> # With the sys module
>>> sys.getrefcount(person)
2
>>> # With the gc module
>>> len(gc.get_referrers(person))
1

Right now, you should expect only one reference to the object underlying the person variable, as shown by the len(gc.get_referrers(person)) call. However, please don’t be surprised by the result that we get from the sys.getrefcount(person) call, because it also includes a temporary reference to the object of the function call itself, and this behavior is expected, as discussed by the official documentation.

现在,您应该只期望对person变量下面的对象进行一次引用,如len(gc.get_referrers(person))调用所示。 但是,请不要对我们从sys.getrefcount(person)调用中得到的结果感到惊讶,因为它还包括对函数调用本身的对象的临时引用,并且这种行为是预期的,如官方文件 。

sys.getrefcount(object)

sys.getrefcount ( object )

Return the reference count of the object. The count returned is generally one higher than you might expect because it includes the (temporary) reference as an argument to getrefcount().

返回对象的引用计数。 返回的计数通常比您预期的高一,因为它包含(临时)引用作为getrefcount()的参数。

>>> # Create an object that references to the person variable
>>> family = {'household head': person}
>>> len(gc.get_referrers(person))
2
>>> # Delete the object that references to the person variable
>>> del family
>>> len(gc.get_referrers(person))
1

The above code shows you that Python tracks the reference count for us. First, we create another object that holds a reference to the object underlying the person variable, which results in the object’s reference count becoming 2. Second, we delete the object from the memory, and as expected, the reference count goes back to 1.

上面的代码向您显示Python为我们跟踪了引用计数。 首先,我们创建另一个对象,该对象持有对该person变量下面的对象的引用,这导致该对象的引用计数变为2。其次,我们从内存中删除了该对象,并且正如预期的那样,引用计数返回到1。

毁灭 (Destruction)

When the reference count of an object drops to zero, it will be removed from the memory, and this is the process of destructing the instance object. In normal cases, we don’t need to worry about these things, because Python manages memory for us automatically. However, for the purpose of understanding these processes, we can manually make the reference count to zero by using the del statement. We used this in the last section, but we didn’t explain what it did exactly. The following code shows you what’s happening exactly.

当一个对象的引用计数降至零时,它将被从内存中删除,这就是销毁实例对象的过程。 在正常情况下,我们无需担心这些事情,因为Python会自动为我们管理内存。 但是,出于理解这些过程的目的,我们可以使用del语句手动将引用计数设为零。 我们在上一节中使用了它,但是我们没有解释它的确切作用。 以下代码向您展示了正在发生的事情。

>>> 'person' in globals()
True
>>> del person
The object is getting deleted: <__main__.Person object at 0x10c8719d0>
>>> 'person' in globals()
False
  • Before deleting the object, the variable person, which references to the underlying object, is in the global namespace.

    在删除对象之前,引用基础对象的变量person位于全局名称空间中。

  • When we delete the variable using the del statement, the __del__() method is getting called. You can refer to the previous code snippet showing you how the Person class is defined, which includes the implementation of the __del__ method.

    当我们使用del语句删除变量时,将__del__()方法。 您可以参考前面的代码片段,其中显示了Person类的定义方式,其中包括__del__方法的实现。

  • After the deletion, the variable person is no longer part of the global namespace.

    删除后,变量person不再是全局名称空间的一部分。

One thing to note is that because the global namespace doesn’t have the person variable, we can’t check the reference count of the object by using the person label, which is expected behavior because Python can’t resolve the person variable.

需要注意的一件事是,因为全局名称空间没有person变量,所以我们无法使用person标签检查对象的引用计数,这是预期的行为,因为Python无法解析person变量。

>>> len(gc.get_referrers(person))
Traceback (most recent call last):
File "", line 1, in
NameError: name 'person' is not defined

One thing that may be confusing to some Python beginners is what the del statement actually does. If you remember that I made a distinction between variable and object, the following code will be easier for you to understand.

del语句的实际作用可能使某些Python初学者感到困惑。 如果您还记得我在变量和对象之间进行了区分,那么下面的代码将使您更容易理解。

>>> person0 = Person("John")
Person __new__ gets called
Person Instance Memory Address: 4505147184
{}
Person __init__ gets called
Set Initial State: 4505147184
{'name': 'John'}
>>> person1 = person0
>>> id(person0) == id(person1)
True
>>> del person0
>>> del person1
The object is getting deleted: <__main__.Person object at 0x10c871730>
  • The above code shows you that we create one object but have two variables that reference the same object, as revealed by the same memory address using the id() function.

    上面的代码向您展示了我们创建了一个对象,但是有两个引用同一对象的变量,如使用id()函数通过相同的内存地址所揭示的那样。

  • Notably, deleting one of them doesn’t trigger the calling of the __del__ method that we implemented for the Person class. However, when the underlying object is about to be deleted because the object’s reference count is zero after deleting the second variable.

    值得注意的是,删除其中一个不会触发我们为Person类实现的__del__方法的调用。 但是,当要删除基础对象时,因为在删除第二个变量后该对象的引用计数为零。

In other words, the del statement only removes the variable label from the corresponding namespace, while the __del__ method is concerned about the underlying object when it is to be destructed from the memory.

换句话说, del语句仅从相应的名称空间中删除变量标签,而__del__方法在要从内存中销毁该对象时会关注其基础对象。

结论 (Conclusions)

In this article, we reviewed the life cycle of Python custom instance objects. It applies to most built-in data instances too, with some exceptions, such as internalization of small integers. Understanding these underlying mechanisms may help you develop more in-depth knowledge about how things work under the hood. These basic concepts are necessary for you to understand how memory is managed in the context of garbage collection, which is a more advanced topic that we can cover in the future.

在本文中,我们回顾了Python自定义实例对象的生命周期。 它也适用于大多数内置数据实例,但有一些例外,例如小整数的内部化。 了解这些潜在的机制可能有助于您更深入地了解事物的工作原理。 这些基本概念对于您了解如何在垃圾回收的上下文中管理内存是必要的,这是我们将来可以讨论的更高级的主题。

I hope that this article helped you.

希望本文对您有所帮助。

翻译自: https://medium.com/better-programming/the-life-cycle-of-python-instance-objects-4a719fb4e925

python实例化对象

你可能感兴趣的:(python)