Home > Software engineering >  Why did the class get called twise in this python code?
Why did the class get called twise in this python code?

Time:01-23

import copyreg, pickle

class cars:
    def __init__(self, name):
        self.name = name

def myfunc(x):
    return cars , (x.name,)

copyreg.pickle(cars, myfunc)


test = cars('hello')

print(pickle.dumps(test))

1 - Why did the class get called twise in this code ?

def myfunc(x):
   #why does it need to be called here too ?
    return cars , (x.name,)

#we already called the class here!!!
copyreg.pickle(cars, myfunc)

2 - and why comma in the tuple ?

CodePudding user response:

The following is solely based on the documentations of copyreg.pickle and pickle dispatch_table.

The copyreg module offers a way to define functions used while pickling specific objects. The pickle and copy modules use those functions when pickling/copying those objects. The module provides configuration information about object constructors which are not classes.

This is mentioned in the copyreg.pickle docs, it basically tells that the function defined is used when pickling specified objects, here, it is cars.

A pickler object’s dispatch table is a registry of reduction functions of the kind which can be declared using copyreg.pickle(). It is a mapping whose keys are classes and whose values are reduction functions.

This is mentioned in the pickle.dispatch_table docs. It basically tells us that cars is the class being used as a key, and myfunc as the reduction function used as its value.

Here, to your doubts, copyreg is not calling your class. It is just creating/editing the dispatch_table with the keys(class) and values(func).

If you are still confused, here's a little experiment, where I added a print statement in the function:

import copyreg, pickle

class cars:
    def __init__(self, name):
        self.name = name

def myfunc(x):
    print("This is a test")
    return cars, (x.name,)
    
copyreg.pickle(cars, myfunc)


test = cars('hello')
print(pickle.dumps(test))

Output:

This is a test b'\x80\x04\x95!\x00\x00\x00\x00\x00\x00\x00\x8c\x08__main__\x94\x8c\x04cars\x94\x93\x94\x8c\x05hello\x94\x85\x94R\x94.'

Now, if I just remove the copyreg.pickle line. The dispatch_table won't be created:

import copyreg, pickle

class cars:
    def __init__(self, name):
        self.name = name

def myfunc(x):
    print("This is a test")
    return cars, (x.name,)

test = cars('hello')
print(pickle.dumps(test))

Ouput:

b'\x80\x04\x95 \x00\x00\x00\x00\x00\x00\x00\x8c\x08__main__\x94\x8c\x04cars\x94\x93\x94)\x81\x94}\x94\x8c\x04name\x94\x8c\x05hello\x94sb.'

As you can see, the function is not at all triggered. Because the relation is not yet created.

To summarize:

  1. copyreg.pickle just creates a dispatch_table with class as key and func as value, and the function defined is used when pickling specified class.
  2. For the tuple, the comment by @Jean-FrançoisFabre is the most suitable.

EDIT: Upon more research, here are the addons.

copyreg.pickle makes the myfunc function act as a reduction function of cars. You can skip that part if you define __reduce__ method in the class itself.

Reduction functions are used by pickle for instruction on how to reconstruct the original object from the pickle object if it fails automatically.

import copyreg, pickle

class cars:
    def __init__(self, name):
        self.name = name
    
    def __reduce__(self):
        print("This is a test")
        return cars, (self.name,)
        

test = cars('hello')
print(pickle.dumps(test))

Output:

This is a test b'\x80\x04\x95!\x00\x00\x00\x00\x00\x00\x00\x8c\x08__main__\x94\x8c\x04cars\x94\x93\x94\x8c\x05hello\x94\x85\x94R\x94.'

You can skip the copyreg.pickle if a __reduce__ instance is mentioned in your class, and it will act the same.

If you want to know, why these values have to be returned in the function, this documentation will give you insight on it.

  • Related