Reputation: 16720
I'm designing a base class, and I want it to define a base behaviour for copy.copy
.
This behaviour consists in printing a warning in the console, and then copying the instance as if it had no __copy__
attribute.
When one defines a blank Foo
class and copies an instance of it, the copy
function returns a new instance of that class, as shown by the following session:
>>> class Foo: pass
...
>>> foo = Foo()
>>> foo2 = copy(foo)
>>> foo is foo2
False
Now if the Foo
class defines a __copy__
instance method, the latter will be called when trying to pass an instance to copy
:
>>> class Foo:
... def __copy__(self):
... print("Copying")
...
>>> foo = Foo()
>>> copy(foo)
Copying
So as I understand it, the flow of execution of the copy
function would be:
__copy__
attributeBut now, I want to capture the copy
function's accessing the __copy__
attribute, through defining a __getattr__
method, and then simulate the absence of this attribute, by raising an AttributeError
:
>>> class Foo:
... def __getattr__(self, attr):
... if attr == '__copy__':
... print("Accessing '__copy__'")
... raise AttributeError
...
Then the __copy__
attribute does not seem to be accessed anymore:
>>> foo = Foo()
# Actual behaviour of copy(foo)
>>> copy(foo)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "C:\ProgramData\Miniconda3\lib\copy.py", line 96, in copy
rv = reductor(4)
TypeError: 'NoneType' object is not callable
# Expected behaviour of copy(foo)
>>> foo.__copy__()
Accessing '__copy__'
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 5, in __getattr__
AttributeError
What am I missing in the execution flow of the copy
function, with regards to the __copy__
attribute?
As far as I understand, given a foo
object with no attribute bar
, it should behave exactly the same, whether it has a __getattr__
method that fails on bar
, or it doesn't define anything.
Is this statement exact?
Upvotes: 1
Views: 66
Reputation: 140186
Short answer seems to be "no".
Within the copy.copy
method we find this (summarized)
cls = type(x)
...
copier = getattr(cls, "__copy__", None)
We see that the function uses getattr
on the class, not the instance.
And there's no way to have __getattr__
called when getattr
is called on the class (just because it's an instance method)
Demo (sorry, I would have prefered a working demo)
class Foo:
def __getattr__(self,attr):
if attr == '__copy__':
return "WORKED"
foo = Foo()
print(getattr(foo, "__copy__",None))
print(getattr(Foo, "__copy__",None))
this returns:
WORKED
None
So it's not possible to fool the original copy.copy
module into believing there's a __copy__
attribute without creating a __copy__
method.
Upvotes: 1