Home > Mobile >  Method object implementation in Python | Ambiguous statements from the documentation
Method object implementation in Python | Ambiguous statements from the documentation

Time:12-24

I have some confusion about the method implementation in Python. Any help in that regard would be appreciated.

I found this closely-related post, which is quite useful, but not answering my questions.

My problem: The following statements from the last paragraph of Section 9.3.4 from Python documentation are not quite clear to me.

When a non-data attribute of an instance is referenced, the instance’s class is searched. If the name denotes a valid class attribute that is a function object, a method object is created by packing (pointers to) the instance object and the function object just found together in an abstract object: this is the method object. When the method object is called with an argument list, a new argument list is constructed from the instance object and the argument list, and the function object is called with this new argument list.

My initial understanding from the doccumentation was very similar to that of @vaughn-mcgill-adami as described in their post: when a class instance object calls its method object, a search would be done on the class object for the corresponding function object. If found, then calling that function owned by the class object with the class instance object as the first parameter.

However, after some coding, it seems to me that my understanding may not be correct. The reason is that one could delete the class object. Yet, calling the method object can still be done with no problem, whereas passing the class instance object to the function object from the class object would result in an error, since there would be no class object anymore after its deletion.

The following code snippet aims at better explaining my questions:

class myclass:
    x=0
    
    def inc(self):
        self.x  =1
        print(self.x)
myobj = myclass()

type(myobj.inc)   #output: method
type(myclass.inc) #output: function

# 1. calling method object multiple times
# 2. calling function object and passing the instance object multiple times
print(myobj.x)
for i in range(3):
    myobj.inc()
for i in range(3):
    myclass.inc(myobj)
#-------------
# output:
#-------------
# 0
# 1
# 2
# 3
# 4
# 5
# 6
#-------------

# deleting the class object
del myclass

# calling method object multiple times
for i in range(3):
    myobj.inc()
#-------------
# output:
#-------------
# 7
# 8
# 9
#-------------

# calling function object and passing the instance object
for i in range(3):
    myclass.inc(myobj)

#-------------
# output:
#-------------

---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
<ipython-input-177-86f094e96103> in <module>
      1 for i in range(3):
----> 2     myclass.inc(myobj)

NameError: name 'myclass' is not defined

My understanding from the attached code snippet is that after instantiating a class object, the method objects are attached to the class instance object. The method objects and the class instance object will then be independent of the class object.

Here are some ambiguities I have from the documentation:

  • What does it mean "...the instance’s class is searched"? I believe it should be "instance class object", but it does not seem to be a typo, since in the following sentence it says "If the name denotes a valid class attribute that is a function object...". Again, here I think the "method object" should replace the "function object".

  • "...a method object is created...". Is the method object created once it is called, or at the same time that the class instance object is created?

  • "by packing (pointers to) the instance object and the function object just found". Again, "function object" does not seem correct to me.

Basically, if the class object is deleted, as in my code snippet, then there is no class object to be searched on in the first place. There will be no function object to be pointed to either.

There is a good chance that I am missing something here. I would be grateful if anyone could please advise. Thank you

CodePudding user response:

Basically, if the class object is deleted, as in my code snippet, then there is no class object to be searched on in the first place. There will be no function object to be pointed to either.

This is your fundamental misunderstanding.

del does not delete objects.

Indeed, Python the language provides no way to manually delete objects (or manually manage memory, i.e. it is a memory-managed language).

del removes names from namespaces.

When you del myclass, you simply removed that name from that module's namespace. But the class object still exists, for one, it is being referenced by all instances of that class, my_object.__class__, and like any other object, will continue to exist as long as references to it exist.

So, consider the following example with del:

>>> x = [1,2,3]
>>> y = x
>>> print(x is y)
True
>>> print(id(x), id(y))
140664200270464 140664200270464
>>> x.append(1337)
>>> x
[1, 2, 3, 1337]
>>> y
[1, 2, 3, 1337]
>>> del x
>>> print(y)
[1, 2, 3, 1337]

So del x did not delete the object, it simply deleted the name x from that namespace. So if I do:

>>> print(x)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'x' is not defined

del can only indirectly lead to the deletion of an object, if the name you remove from the namespace is the last reference to that object, then that object is free for garbage collection (and in CPython, which uses reference counting as it's main garbage collections strategy, it will be reclaimed immediately, but this may not be true in other implementations, e.g, JYthon, which uses the Java runtime's garbage collector).

Some relevant documentation for the del statement is here:

Deletion of a name removes the binding of that name from the local or global namespace, depending on whether the name occurs in a global statement in the same code block. If the name is unbound, a NameError exception will be raised.

Note, the other part,

Deletion of attribute references, subscriptions and slicings is passed to the primary object involved; deletion of a slicing is in general equivalent to assignment of an empty slice of the right type (but even this is determined by the sliced object).

Is basically saying that a statement like

del obj[item]

is delegated to

type(obj).__delitem__

i.e., it is just sugar for a call to a method.

  • Related