1

I'm trying to implement this: How to fake type with Python. I'm wrapping some .NET objects I don't control, so don't want __new__ to be called on them by the instantiation of the class in the wrapper. I've taken the code from the question above and built a simple example:

class WrappableInt(object):
    def __new__(cls,*args,**kwargs):
        print("Making a new wrapint")
        return super().__new__(cls)
    def __init__(self,x):
        self.x=x


def wrap_user(wrapee):
    class wrapped_user(type(wrapee)):
        def __new__(cls,*args,**kwargs):
            return object.__new__(cls)
        def __init__(self):
            pass
        def gimmieFive(self):
            return 5
        def __getattribute__(self, attr):
            self_dict = object.__getattribute__(type(self), '__dict__')
            if attr in self_dict:
                return self_dict[attr]
            return getattr(wrapee, attr)
    return wrapped_user()

When I run the following tests, everything is as expected:

>>> z=WrappableInt(3)
Making a new wrapint
>>> p=wrap_user(z)
>>> print(p.x)
3

However, when I run gimmieFive, I get the following:

>>> p.gimmieFive()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: gimmieFive() missing 1 required positional argument: 'self'

Why does python think gimmieFive is a static method, when it seems to know __getattribute__ is not? It will happily run e.g.:

>>> p.gimmieFive(1)
5
Community
  • 1
  • 1
Carbon
  • 3,828
  • 3
  • 24
  • 51

1 Answers1

2

You are bypassing the normal descriptor protocol here. __getattribute__ does more than just look up names on the class, it also binds them if the object retrieved supports binding.

Python function objects are descriptors, binding them to an instance produces a method object that, when called, passes in the bound instance as the first function argument. You are returning the original unbound function object.

Re-implementing binding behaviour is.. complex. In case of a conflict between a instance attribute and a descriptor object retrieved from the class, you'll need to check for local attributes, determine if the descriptor is a data descriptor or regural descriptor, and decide which one to return.

A simple and basic implementation would at the very least need to support __get__ binding:

if attr in self_dict:
    obj = self_dict[attr]
    if hasattr(obj, '__get__'):
        obj = obj.__get__(self, type(self))
    return obj

This may be enough for your usecase since your wrapped_user class doesn't have any data descriptors right now.

With the above change, your specific example works as expected:

>>> z=WrappableInt(3)
Making a new wrapint
>>> p=wrap_user(z)
>>> print(p.x)
3
>>> p.gimmieFive()
5
Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343