pablowilks2
pablowilks2

Reputation: 339

what does self refer to in Property class creating code?

In the below code that emulates property creation code:

class Property:
    "Emulate PyProperty_Type() in Objects/descrobject.c"

    def __init__(self, fget=None, fset=None, fdel=None, doc=None):
        self.fget = fget
        self.fset = fset
        self.fdel = fdel
        if doc is None and fget is not None:
            doc = fget.__doc__
        self.__doc__ = doc

    def __get__(self, obj, objtype=None):
        if obj is None:
            return self
        if self.fget is None:
            raise AttributeError("unreadable attribute")
        return self.fget(obj)

    def __set__(self, obj, value):
        if self.fset is None:
            raise AttributeError("can't set attribute")
        self.fset(obj, value)

    def __delete__(self, obj):
        if self.fdel is None:
            raise AttributeError("can't delete attribute")
        self.fdel(obj)

    def getter(self, fget):
        return type(self)(fget, self.fset, self.fdel, self.__doc__)

    def setter(self, fset):
        return type(self)(self.fget, fset, self.fdel, self.__doc__)

    def deleter(self, fdel):
        return type(self)(self.fget, self.fset, fdel, self.__doc__)

in __get__, self is the instance of the property class?

Also, what does obj refer to? Why is obj passed to self.fget?

When I test it I get the following error:

class A:
    func = Property(fget=func)
a = A()
a.func

TypeError: 'Property' object is not callable

How does it work then if it's not callable? I was under the impression that when defining a decorator as an instance you need to make sure it implements the __call__ method?

What am I missing?

Upvotes: 1

Views: 46

Answers (1)

chepner
chepner

Reputation: 532238

self is the instance of Property itself. Its main purpose is to simply hold references to the getter, setter, and/or deleter so that you can call them when the property is accessed. When those functions are called, they get a reference to the object from which the property was accessed, so in a.func, self is the instance of Property and obj is a.


The Property instance itself doesn't need to be callable, because it's not what gets called. Property.__get__ only returns self if you access it from the class, not an instance of the class. The built-in property works the same way.

>>> class A:
...   func = property(lambda self: 3)
...
>>> A.func
<property object at 0x10a33bb88>
>>> A().func
3

func is a class attribute whose value is an instance of property. The result of accessing the attribute differs depending on whether you look it up on the class itself or an instance of the class.

When accessed via the class, you get back the property itself.

A.func == A.__dict__['func'].__get__(None, A)
       == A.__dict__['func']

When accessed via an instance, you get back the result of calling the original function passed to property.

a = A()
a.func == A.__dict__['func'].__get__(a, A)
       == A.__dict__['func'].fget(a)
       == (lambda self: 3)(a)
       == 3

Upvotes: 1

Related Questions