[Python-ideas] Re: Descriptor __get__ and __set__ argument discrepancy

2023-10-19 Thread dn via Python-ideas

On 19/10/2023 20.43, Dom Grigonis wrote:

On 19 Oct 2023, at 10:27, dn via Python-ideas  wrote:
On 19/10/2023 19.50, Dom Grigonis wrote:

Thank you,
Good information, thank you. Was not aware of __set_name__.


IIRC that was one of the updates/improvements. Thanks to whomsoever...!

The:

instance.__dict__[self.name] = value

may require a bit of thought before it feels comfortable, but it is 
significantly easier to understand than what we had to do 'before'.

I am using `setattr(instance, self.name, value)`. But I see that 
instance.__dict__ is more appropriate in this case.


IIRC that's the way we used to have to do things, ie 'turn ourselves in 
knots'!


Because coding a Custom Descriptor is still something of a mind-bender, 
I coded a 'library' ABC/Super-class ("something I put in the oven 
earlier"*) which is slightly more sophisticated that the code-example 
(see earlier) PLUS a call to, and outline-code for an @abstractmethod 
called validate(). Care to guess its purpose!?


This means that when a Custom Descriptor is useful, it is a matter of 
(trust and) sub-classing, eg NonNegativeInteger( SuperClassName ), which 
provides an appropriate, concrete, validate() method.


"Re-use" = no fuss, no muss!
- more to the point, my future-self doesn't have to remember the 
intricacies of the 'new' (v3.6+) internal mechanisms...


(the ABC is about 40-lines of code. I'll post it upon demand, or perhaps 
better off-list...)



* this is a saying originating in (British) television cooking-shows, to 
explain the temporal distortion of how the cook went from a mix of 
uncooked ingredients to the finished article, without the waiting-time 
for it to cook - as we would in real-life.




Another surprise, and I've assumed you're asking in the context of [Custom] 
Descriptors, is in how many places/functions Python makes use of a 
descriptor/descriptor protocol. Yet few of us seem to make use of them in our 
application code...
(YMMV!)

I use them more and more.


+1
I use them to abstract-away almost all data-item data-validation.



However, I was more interested, why doesn't __set__ have an `owner` argument, 
while `__get__` does. I am aware that this is not an issue at all as one can 
simply do `inst.__class__`, but I am just curious about the reason for 
inconsistency.


@Antoine has given a technical explanation.


The next contribution to your thinking is probably to mention/remind 
that the "owner" argument (in the 'getter') is optional - can provide it 
in __set_name__() and record it there. Thus, the effective-signatures 
become:


def __get__( self, instance, )
...
def __set__( self, instance, value, )
...

Is this a more familiar pattern?


Perhaps the 'mistake' is not in an apparent lack of consistency, but in 
our expectation of a 'tidy' pattern? There's always going to be a 
difference in the two signatures, because the 'setter' must be provided 
with a value, whereas the 'getter' returns (cf accepts) a data-value.



Still have questions?
Let's take a step (or two) backwards:-

Most of us start (down this road) by being introduced to @property. A 
favorite (realistic) example is "age". Despite (sub-standard) text-books 
featuring personnel records that use such a field, no professional ever 
does! We record the person's date-of-birth, and thereafter compute the 
difference from today() to arrive at (today's) age. The neat 'discovery' 
is that when using object.age as a property, although the mechanism has 
been coded as a method, its use is indistinguishable from a 'normal' 
data-attribute. Smooth!


The next step in one's education is to add a 'setter'. The 
tutorial-example here might be a physical quantity, eg how many 
bars/tablets/blocks of chocolate you are buying (to give to me, of 
course). This must be a positive number (there's a (in)famous Amazon bug 
where they went 'live' allowing negative-quantities!!!). In the case of 
chocolate, it might be an integer. In the case of other products it 
might be a decimal/float, eg 1.5KG/lbs of flour. (yes, that would be to 
mix-up a chocolate cake!).


This extension to the first @property use-case, is likely the 
performance of some-sort of 'validation routine' to make sure that the 
data is fit-for-purpose ("data-cleaning", "data-validation", etc). The 
original @property mechanism swings into action when we ask for the 
value. In this case, the mechanism applies when we set the data-value. 
Thus, 5 is an acceptable quantity, but "five" is not - and the code is 
designed to test for such.


You know all this, and that an @property is 'syntactic sugar' for a 
Descriptor as the underlying mechanism.


So, now extend that final step, so that instead of an @property with 
'getter' and 'setter', code the data-item (actually, its type) as a 
Descriptor.


Now, remember that a data-item which is a Descriptor is just like any 
other. To set its value, the code is:


instance.quantity = 1.5  # ie 1.5KG of flour

An 

[Python-ideas] Re: Descriptor __get__ and __set__ argument discrepancy

2023-10-19 Thread Dom Grigonis
Thank you, makes sense. This one has been bugging me every time I worked with 
descriptors.

I even had __set__ method in my Descriptor Base for class attributes, but never 
needed it so didn’t realise.

> On 19 Oct 2023, at 12:09, Antoine Rozo  wrote:
> 
> Hi,
> 
> The __get__ method of descriptors can be called at the class level (that's 
> how methods work) and in that case instance would be None, but owner will 
> always reference the current class.
> 
> __set__ can only be called for instances on that class (`Cls.attr = ...` 
> would redefine the class-attribute and not call the descriptor), so instance 
> can never be None and owner is type(instance).
> 
> Le jeu. 19 oct. 2023 à 09:45, Dom Grigonis  > a écrit :
> 
> > On 19 Oct 2023, at 10:27, dn via Python-ideas  > > wrote:
> > 
> > On 19/10/2023 19.50, Dom Grigonis wrote:
> >> Thank you,
> >> Good information, thank you. Was not aware of __set_name__.
> > 
> > IIRC that was one of the updates/improvements. Thanks to whomsoever...!
> > 
> > The:
> > 
> >instance.__dict__[self.name ] = value
> > 
> > may require a bit of thought before it feels comfortable, but it is 
> > significantly easier to understand than what we had to do 'before'.
> I am using `setattr(instance, self.name , value)`. But I 
> see that instance.__dict__ is more appropriate in this case.
> 
> 
> > Another surprise, and I've assumed you're asking in the context of [Custom] 
> > Descriptors, is in how many places/functions Python makes use of a 
> > descriptor/descriptor protocol. Yet few of us seem to make use of them in 
> > our application code...
> > (YMMV!)
> I use them more and more.
> 
> 
> However, I was more interested, why doesn't __set__ have an `owner` argument, 
> while `__get__` does. I am aware that this is not an issue at all as one can 
> simply do `inst.__class__`, but I am just curious about the reason for 
> inconsistency.
> 
> Although, answers that I got were very useful.
> 
> DG
> ___
> Python-ideas mailing list -- python-ideas@python.org 
> 
> To unsubscribe send an email to python-ideas-le...@python.org 
> 
> https://mail.python.org/mailman3/lists/python-ideas.python.org/ 
> 
> Message archived at 
> https://mail.python.org/archives/list/python-ideas@python.org/message/64MSMP4FFIT4FHPJQ66RW3OWXSP7RUFC/
>  
> 
> Code of Conduct: http://python.org/psf/codeofconduct/ 
> 
> 
> 
> -- 
> Antoine Rozo

___
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/JYWMEHUBWSCKS7KAJP3DW46OKJ7BV3IS/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: Descriptor __get__ and __set__ argument discrepancy

2023-10-19 Thread Antoine Rozo
Hi,

The __get__ method of descriptors can be called at the class level (that's
how methods work) and in that case instance would be None, but owner will
always reference the current class.

__set__ can only be called for instances on that class (`Cls.attr = ...`
would redefine the class-attribute and not call the descriptor), so
instance can never be None and owner is type(instance).

Le jeu. 19 oct. 2023 à 09:45, Dom Grigonis  a
écrit :

>
> > On 19 Oct 2023, at 10:27, dn via Python-ideas 
> wrote:
> >
> > On 19/10/2023 19.50, Dom Grigonis wrote:
> >> Thank you,
> >> Good information, thank you. Was not aware of __set_name__.
> >
> > IIRC that was one of the updates/improvements. Thanks to whomsoever...!
> >
> > The:
> >
> >instance.__dict__[self.name] = value
> >
> > may require a bit of thought before it feels comfortable, but it is
> significantly easier to understand than what we had to do 'before'.
> I am using `setattr(instance, self.name, value)`. But I see that
> instance.__dict__ is more appropriate in this case.
>
>
> > Another surprise, and I've assumed you're asking in the context of
> [Custom] Descriptors, is in how many places/functions Python makes use of a
> descriptor/descriptor protocol. Yet few of us seem to make use of them in
> our application code...
> > (YMMV!)
> I use them more and more.
>
>
> However, I was more interested, why doesn't __set__ have an `owner`
> argument, while `__get__` does. I am aware that this is not an issue at all
> as one can simply do `inst.__class__`, but I am just curious about the
> reason for inconsistency.
>
> Although, answers that I got were very useful.
>
> DG
> ___
> Python-ideas mailing list -- python-ideas@python.org
> To unsubscribe send an email to python-ideas-le...@python.org
> https://mail.python.org/mailman3/lists/python-ideas.python.org/
> Message archived at
> https://mail.python.org/archives/list/python-ideas@python.org/message/64MSMP4FFIT4FHPJQ66RW3OWXSP7RUFC/
> Code of Conduct: http://python.org/psf/codeofconduct/
>


-- 
Antoine Rozo
___
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/VPNJEO2A36LKQVSK764TJPXXSBE2QKLI/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: Descriptor __get__ and __set__ argument discrepancy

2023-10-19 Thread Dom Grigonis


> On 19 Oct 2023, at 10:27, dn via Python-ideas  wrote:
> 
> On 19/10/2023 19.50, Dom Grigonis wrote:
>> Thank you,
>> Good information, thank you. Was not aware of __set_name__.
> 
> IIRC that was one of the updates/improvements. Thanks to whomsoever...!
> 
> The:
> 
>instance.__dict__[self.name] = value
> 
> may require a bit of thought before it feels comfortable, but it is 
> significantly easier to understand than what we had to do 'before'.
I am using `setattr(instance, self.name, value)`. But I see that 
instance.__dict__ is more appropriate in this case.


> Another surprise, and I've assumed you're asking in the context of [Custom] 
> Descriptors, is in how many places/functions Python makes use of a 
> descriptor/descriptor protocol. Yet few of us seem to make use of them in our 
> application code...
> (YMMV!)
I use them more and more.


However, I was more interested, why doesn't __set__ have an `owner` argument, 
while `__get__` does. I am aware that this is not an issue at all as one can 
simply do `inst.__class__`, but I am just curious about the reason for 
inconsistency.

Although, answers that I got were very useful.

DG
___
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/64MSMP4FFIT4FHPJQ66RW3OWXSP7RUFC/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: Descriptor __get__ and __set__ argument discrepancy

2023-10-19 Thread dn via Python-ideas

On 19/10/2023 19.50, Dom Grigonis wrote:

Thank you,

Good information, thank you. Was not aware of __set_name__.


IIRC that was one of the updates/improvements. Thanks to whomsoever...!

The:

instance.__dict__[self.name] = value

may require a bit of thought before it feels comfortable, but it is 
significantly easier to understand than what we had to do 'before'.


Another surprise, and I've assumed you're asking in the context of 
[Custom] Descriptors, is in how many places/functions Python makes use 
of a descriptor/descriptor protocol. Yet few of us seem to make use of 
them in our application code...

(YMMV!)

--
Regards,
=dn

___
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/KWIJS6L4DRI7333U2I2YYSXOJBKFCJQD/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: Descriptor __get__ and __set__ argument discrepancy

2023-10-19 Thread Dom Grigonis
Thank you,

Good information, thank you. Was not aware of __set_name__.

DG

> On 19 Oct 2023, at 09:28, dn via Python-ideas  wrote:
> 
> On 19/10/2023 18.29, Dom Grigonis wrote:
>> def  __get__(self, instance, owner):
>> def  __set__(self, instance, value):
>> Is there a reason why `__set__` does not have owner in it’s arguments while 
>> `__get__` does?
> 
> Is this a Python Idea?
> 
> You may have only given us a couple of lines, when the scope of the question 
> is much wider...
> 
> Be careful because these mechanisms were updated relatively-recently - and 
> thus there are differences between Python versions!
> 
> 
> From my notes (please see code-example which appears to answer your question):
> 
> The __set_name__() method is a special method in Python that is used in the 
> context of descriptors. It was introduced in Python 3.6 as a part of the 
> Descriptor Protocol.
> 
> The purpose of the __set_name__() method is to allow descriptors to 
> automatically determine and store the name of the attribute they are assigned 
> to within the class. This method is called once during the creation of the 
> descriptor instance, and it receives two arguments: the owner class and the 
> name of the attribute.
> 
> By implementing the __set_name__() method in a descriptor, you can access and 
> store the name of the attribute to which the descriptor is assigned. This can 
> be useful when you want to associate the descriptor with the attribute name 
> or perform any additional setup based on the attribute name.
> 
> Here's an example to illustrate the usage of __set_name__():
> 
> ```python
> class Descriptor:
>def __set_name__(self, owner, name):
>self.name = name
> 
>def __get__(self, instance, owner):
>if instance is None:
>return self
>return instance.__dict__.get(self.name)
> 
>def __set__(self, instance, value):
>instance.__dict__[self.name] = value
> 
> class MyClass:
>attribute = Descriptor()
> 
> obj = MyClass()
> obj.attribute = 42
> print(obj.attribute)  # Output: 42
> ```
> 
> In the above code, the Descriptor class defines the __set_name__() method. 
> When the attribute descriptor is assigned to the attribute attribute of the 
> MyClass class, the __set_name__() method is automatically called with the 
> owner class (MyClass) and the attribute name (attribute). Inside this method, 
> we store the attribute name in the descriptor instance.
> 
> Later, when we set obj.attribute = 42, the descriptor's __set__() method is 
> called, and the value is stored in the instance's __dict__ attribute using 
> the previously stored attribute name.
> 
> By using __set_name__(), descriptors can dynamically associate themselves 
> with the attribute names they are assigned to, providing more flexibility and 
> customization.
> 
> 
> Web.Refs:
> https://docs.python.org/3/howto/descriptor.html
> https://docs.python.org/3/reference/datamodel.html#descriptors
> 
> -- 
> Regards,
> =dn
> ___
> Python-ideas mailing list -- python-ideas@python.org
> To unsubscribe send an email to python-ideas-le...@python.org
> https://mail.python.org/mailman3/lists/python-ideas.python.org/
> Message archived at 
> https://mail.python.org/archives/list/python-ideas@python.org/message/L7WZA7GEW3TPYUA4NF6POHDL2FSF5TIY/
> Code of Conduct: http://python.org/psf/codeofconduct/

___
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/GXXHZ4KPC5OJDKGMMOWSE6SH44EAHJYK/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: Descriptor __get__ and __set__ argument discrepancy

2023-10-19 Thread dn via Python-ideas

On 19/10/2023 18.29, Dom Grigonis wrote:

def  __get__(self, instance, owner):
def  __set__(self, instance, value):

Is there a reason why `__set__` does not have owner in it’s arguments 
while `__get__` does?


Is this a Python Idea?

You may have only given us a couple of lines, when the scope of the 
question is much wider...


Be careful because these mechanisms were updated relatively-recently - 
and thus there are differences between Python versions!



From my notes (please see code-example which appears to answer your 
question):


The __set_name__() method is a special method in Python that is used in 
the context of descriptors. It was introduced in Python 3.6 as a part of 
the Descriptor Protocol.


The purpose of the __set_name__() method is to allow descriptors to 
automatically determine and store the name of the attribute they are 
assigned to within the class. This method is called once during the 
creation of the descriptor instance, and it receives two arguments: the 
owner class and the name of the attribute.


By implementing the __set_name__() method in a descriptor, you can 
access and store the name of the attribute to which the descriptor is 
assigned. This can be useful when you want to associate the descriptor 
with the attribute name or perform any additional setup based on the 
attribute name.


Here's an example to illustrate the usage of __set_name__():

```python
class Descriptor:
def __set_name__(self, owner, name):
self.name = name

def __get__(self, instance, owner):
if instance is None:
return self
return instance.__dict__.get(self.name)

def __set__(self, instance, value):
instance.__dict__[self.name] = value

class MyClass:
attribute = Descriptor()

obj = MyClass()
obj.attribute = 42
print(obj.attribute)  # Output: 42
```

In the above code, the Descriptor class defines the __set_name__() 
method. When the attribute descriptor is assigned to the attribute 
attribute of the MyClass class, the __set_name__() method is 
automatically called with the owner class (MyClass) and the attribute 
name (attribute). Inside this method, we store the attribute name in the 
descriptor instance.


Later, when we set obj.attribute = 42, the descriptor's __set__() method 
is called, and the value is stored in the instance's __dict__ attribute 
using the previously stored attribute name.


By using __set_name__(), descriptors can dynamically associate 
themselves with the attribute names they are assigned to, providing more 
flexibility and customization.



Web.Refs:
https://docs.python.org/3/howto/descriptor.html
https://docs.python.org/3/reference/datamodel.html#descriptors

--
Regards,
=dn
___
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/L7WZA7GEW3TPYUA4NF6POHDL2FSF5TIY/
Code of Conduct: http://python.org/psf/codeofconduct/