[Tutor] Quacks like an object

Steven D'Aprano steve at pearwood.info
Tue Oct 25 10:07:09 CEST 2011


On Mon, Oct 24, 2011 at 07:02:28PM -0400, Christopher King wrote:
> Dear Tutors,
>     I am trying to make an object, which will appear exactly like an object
> of my choice. It will should be impossible to tell it is not the object.

Can't be done in Python. The only way to make something absolutely 
indistinguishable from another thing is to actually make them the same thing.

> This is because I am making an object that modifies it methods so that if
> the methods make a change to the object, it will sync those changes to a
> file, but I don't want it to be another object, so foreign functions will
> not mistake for not being the object.

Unfortunately, some functions are excessively finicky about their input. When 
writing a function, the author can:

(1) Just Try It, and if it fails, deal with the failure. Dealing with the 
failure may mean "do nothing, just let the exception happen", or it may mean
catching the exception and do something else.

(2) Check for an interface first, then perform the task, e.g.:

    if hasattr(obj, '__getitem__'): x = obj[i]

(3) Check the type using isinstance, which supports subtypes:

    if isinstance(obj, list): x = obj[i]

(4) Check for an exact type, which defeats subtyping:

    if type(obj) is list: x = obj[i]


There is very little you can do with functions that take the last approach, 
except report it back to the author as a bug.


> For example
> if, I used type on the object, it should return the class of the object it
> is trying to mimic. I have tried everything from modifying the object's get
> set attribute methods, to having making a new object that has get and set
> the same as the object except with a filter for methods, but I had no luck.
> In a nutshell, I want an object that walks like another object, quacks like
> another object, and basically is another object. Is this possible?

No. You have misunderstood duck typing. With duck typing, if your object 
swims like a duck and walks like a duck and quacks like a duck, who cares if 
it's actually a goose? The only way to make it exactly the same as a duck is
if it actually is a duck.

But you can get pretty close. The usual two approaches are:

(A) Delegation. Something like this (untested):

class Wrapper:
    def __init__(self, obj):
        self['_obj'] = obj
    def __getattr__(self, attr):
        return getattr(self._obj, attr)
    def __delattr__(self, attr):
        delattr(self._obj, attr)
    def __setattr__(self, attr, value):
        setattr(self._obj, attr, value)
    def spam(self, arg):
        # Save to a file
        somefile.write("whatever")
        self._obj.spam(arg)  # Call the original.


Delegation works really well for functions that don't use isinstance or type
checking.


(B) Inheritence.

class MySpam(Spam):
    # Subclass of Spam that saves to a file.
    def spam(self, arg):
        somefile.write("whatever")
        super(MySpam, self).spam(arg) 



Does this help?


-- 
Steven



More information about the Tutor mailing list