Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How does a descriptor with __set__ but without __get__ work?

I read somewhere about the fact you can have a descriptor with __set__ and without __get__.

How does it work?

Does it count as a data descriptor? Is it a non-data descriptor?

Here is a code example:

class Desc:
    def __init__(self, name):
        self.name = name
    def __set__(self, inst, value):
        inst.__dict__[self.name] = value
        print("set", self.name)

class Test:
    attr = Desc("attr")

>>>myinst = Test()
>>> myinst.attr = 1234
set attr
>>> myinst.attr
1234
>>> myinst.attr = 5678
set attr
>>> myinst.attr
5678
like image 439
Bharel Avatar asked Sep 26 '16 19:09

Bharel


People also ask

What is __ get __ in Python?

Python __get__ Magic Method. Python's __get__() magic method defines the dynamic return value when accessing a specific instance and class attribute. It is defined in the attribute's class and not in the class holding the attribute (= the owner class).

How do you use descriptors in Python?

Creating descriptors using property typeWith the property(), it is easy to create a usable descriptor for any attribute. The syntax for creating property() is property(fget=None, fset=None, fdel=None, doc=None) where: fget – attribute get method. fset – attribute set method.

Which special method must be defined to implement a data descriptor?

Invoking descriptor : Descriptors are invoked automatically whenever it receives the call for a set() method or get() method. For example, obj. gfg looks up gfg in the dictionary of obj . If gfg defines the method __get__() , then gfg.

What is the difference between properties and descriptors?

The Cliff's Notes version: descriptors are a low-level mechanism that lets you hook into an object's attributes being accessed. Properties are a high-level application of this; that is, properties are implemented using descriptors.


1 Answers

The descriptor you've given in the example is a data descriptor.

Upon setting the attribute, as any other data descriptor, it takes the highest priority and is called like so:

type(myinst).__dict__["attr"].__set__(myinst, 1234)

This in turn, adds attr to the instance dictionary according to your __set__ method.

Upon attribute access, the descriptor is checked for having the __get__ method but fails, causing for the search to be redirected to the instance's dictionary like so:

myinst.__dict__["attr"]

If it is not found in the instance dictionary, the descriptor itself is returned.

This behavior is shortly documented in the data model like so:

If it does not define __get__(), then accessing the attribute will return the descriptor object itself unless there is a value in the object’s instance dictionary.

Common usecases include avoiding {instance: value} dictionaries inside the descriptors, and caching values in an efficient way.


In Python 3.6, __set_name__ was added to the descriptor protocol thus eliminating the need for specifying the name inside the descriptor. This way, your descriptor can be written like so:

class Desc:
    def __set_name__(self, owner, name):
        self.name = name
    def __set__(self, inst, value):
        inst.__dict__[self.name] = value
        print("set", self.name)

class Test:
    attr = Desc()
like image 106
Bharel Avatar answered Sep 20 '22 17:09

Bharel