2

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?

10
  • 3
    “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.“ It sounds like you just overwrote the reference to a value, which is something you can do regardless of the value being referenced. For example, ints are immutable, but you can do a = 1 and then a = 2 no problem. Dicts are mutable, but a = b = {} and b = {"foo": "bar"} doesn’t set a. Commented Aug 15, 2024 at 22:45
  • 1
    Functions are mutable, but deepcopy() doesn't know how to make a copy of a function, so it just copies the reference. Commented Aug 15, 2024 at 22:47
  • @Ry- The real issue is the result of self.assertIsNot(a._member_b, copy._member_b) which fails. Commented Aug 15, 2024 at 22:48
  • @Barmar @Ry Does this mean if I want to have a deepcopy for my class A, I need to implement it myself rather than directly using copy.deepcopy()? I would like to have the ability to construct a deep copy of an instance of class A, and the new copy is completely independent from the original one. Commented Aug 15, 2024 at 22:54
  • 1
    @Coolboy What actually is the problem here? Why does the result of copy.deepcopy not work for you needs, precisely? Commented Aug 15, 2024 at 22:59

2 Answers 2

5

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
Sign up to request clarification or add additional context in comments.

2 Comments

Does this mean if I want to have a deepcopy for my class A, I need to implement it myself rather than directly using copy.deepcopy()? I would like to have the ability to construct a deep copy of an instance of class A, and the new copy is completely independent from the original one.
@Coolboy: What exactly are you worried about? Unless you're setting attributes on the function (extremely rare), or relying on terrible things like mutating a mutable default argument as some sort of weird state-preserving persistent state (good God, no!), copying means nothing to a plain function.
3

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.

2 Comments

Does this mean if I want to have a deepcopy for my class A, I need to implement it myself rather than directly using copy.deepcopy()? I would like to have the ability to construct a deep copy of an instance of class A, and the new copy is completely independent from the original one.
Why do you need to modify the function? Assigning to _member_b is not modifying the function, it's modifying the A instance.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.