Coolboy
Coolboy

Reputation: 51

Is Python function immutable?

I have

import copy
class A:
   _member_a: dict[str, str]
   _member_b: Callable

   def set_b(self, func):
       self._member_b = func

   def deep_copy(self):
       return copy.deepcopy(self)

I can set the value of _member_b with something like:

a = A()
a.set_b(some_actual_function_in_other_files) 

and in another file, I have:

def some_actual_function_in_other_files(self):
    # Implementation

def some_actual_function_in_other_files_2(self):
    # Implementation

so in my test, I wrote:

def test(self):
   a = A()
   a._member_a = {"test", "test"}
   
   a.set_b(some_actual_function_in_other_files)
   
   copy = a.deep_copy()
   self.assertIsNot(a._member_a, copy._member_a)  # pass, because dictionary is mutable and deepcopy is not using the same reference
   self.assertIsNot(a._member_b, copy._member_b)  # fail, which means `a._member_b` and `copy._member_b` shares the same reference

I understand in copy.deepcopy(), if the object is immutable, Python just use copy the reference(e.g., integers). So in this case, is _member_b immutable?

I believe so, because later I updated the copy._member_b and I found a._member_b still has the original value which means it's not affected by the change of the copy. Is it true?

Upvotes: 2

Views: 111

Answers (2)

Barmar
Barmar

Reputation: 782105

Functions are mutable. You can see this in the following:

def foo():
    pass

foo.x = 1

Assigning to foo.x modifies the function object.

But copy.deepcopy() doesn't make a copy of functions.

bar = copy.deepcopy(foo)
print(id(foo) == id(bar)) # prints True

It's a simplification to say that deepcopy() makes a copy of all mutable objects. It's true for container types, but not for various system types.

While functions are mutable, we rarely use them as mutable containers. If you need to associate data with a function, you normally use a class; the data goes in instance variables, and the function can access them using self.attribute. Then you make a copy of the instance to get independent objects.

Upvotes: 3

Ry-
Ry-

Reputation: 225125

Normal functions are mutable in Python, but copy.deepcopy doesn’t copy them anyway:

This module does not copy types like module, method, stack trace, stack frame, file, socket, window, or any similar types. It does “copy” functions and classes (shallow and deeply), by returning the original object unchanged; this is compatible with the way these are treated by the pickle module.

import copy

def func():
    pass

func2 = copy.deepcopy(func)
func.is_mutable = True   # no error
print(func2.is_mutable)  # True, not copied
print(func is func2)     # True, not copied

Upvotes: 5

Related Questions