Skip to content Skip to sidebar Skip to footer

How Can I Use Descriptors For Non-static Methods?

I am aware that I can use descriptors to change static property as if it were a normal property. However, when I try using descriptors for a normal class property, I end up changin

Solution 1:

Descriptors provide a simple mechanism for variations on the usual patterns of binding functions into methods.

To recap, functions have a __get__() method so that they can be converted to a method when accessed as attributes. The non-data descriptor transforms an obj.f(*args) call into f(obj, *args). Calling klass.f(*args) becomes f(*args).

This chart summarizes the binding and its two most useful variants:

Transformation  Called from an Object   Called from a Class
function            f(obj, *args)           f(*args)
staticmethod        f(*args)                f(*args)
classmethod         f(type(obj), *args)     f(klass, *args)

Static methods return the underlying function without changes. Calling either c.f or C.f is the equivalent of a direct lookup into

object.__getattribute__(c, "f") or object.__getattribute__(C, "f"). As a result, the function becomes identically accessible from either an object or a class.

Good candidates for static methods are methods that do not reference the self variable.

classRevealAccess(object):
    """A data descriptor that sets and returns values
       normally and prints a message logging their access.
    """def__init__(self, initval=None, name='var'):
        self.val = initval
        self.name = name

    def__get__(self, obj, objtype):
        print'Retrieving', self.name
        return self.val

    def__set__(self, obj, val):
        print'Updating', self.name
        self.val = val

>>> classMyClass(object):
...     x = RevealAccess(10, 'var "x"')
...     y = 5
...
>>> m = MyClass()
>>> m.x
Retrieving var "x"10>>> m.x = 20
Updating var "x">>> m.x
Retrieving var "x"20>>> m.y
5

Solution 2:

Considering method(param) returns a descriptor, you may invoke it manually with a property like so:

classSomeClass(object):def__init__(self):
        self._descriptor = method(param)

    @propertydefmy_attribute(self):
        returnself._descriptor.__get__(self, self.__class__)

    @my_attribute.setter
    defmy_attribute(self, value):
        self._descriptor.__set__(self, value)

This will allow you to create instance-specific descriptors instead of class-level descriptors.

I still do not see the reason for you to do that as descriptors should usuallty be class-level. That's their point. Else they are just regular objects which you can call using properties.

Solution 3:

Properties are "computed attributes". Properties are implemented using descriptors. If an object's attribute is looked up and a descriptor object is found, the descriptor's getter function will compute the value.

This special rule is valid only at the class level.

That's why:

classSomeClass():
    property = <a propertyobject here>

defines a property (computed attribute) for all SomeClass instances, but:

classSomeClass():
    def__init__(self):
        self.property = <a propertyobject here>

defines an ordinary attribute which happens to be of property object type, but is not treated specially in any way. Its getters and setters are ignored (unless called explicitely by the user code, of course).


There is no other way of using descriptors and properties as setting them in the class and not in an instance.

UPDATE - Example:

Small example: instead of creating several instances each with its own descriptor (approach which does not work), a new subclass is instantiated every time.

classBaseClass:
    pass

_class_number = 0defclass_factory(prop):
    global _class_number
    _class_number += 1returntype('Class' + str(_class_number), (BaseClass,), dict(prop=prop))

c1 = class_factory(property(fget=lambda self: "property1"))()
print(c1.prop)
c2 = class_factory(property(fget=lambda self: "property2"))()
print(c2.prop)

Solution 4:

guys,

Thanks for the help. Out of many great suggestions, I decided to wrap the set and get methods, thus losing the practicality of descriptors, while keeping the practicality of having a module completely programmed for me. I simply extended the class "PageElement" and created two new methods get() and set(), which call their correspondent implementations:

defget(self):
    returnself.__get__(self.owner_page, self.owner_page.__class__)

defset(self, value):
    self.__set__(self.owner_page, value) 

Thanks for the help! I opened up my eyes to a lot of details in the language that I still do not know. And I will keep digging.

Post a Comment for "How Can I Use Descriptors For Non-static Methods?"