On 19/10/2023 20.43, Dom Grigonis wrote:
On 19 Oct 2023, at 10:27, dn via Python-ideas <python-ideas@python.org> 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 "expression" where the name of the data-item is on the left-hand-side, and the value is the RHS.

Whereas, when we come to utilise same, eg

    line_price = quantity * unit_price

data-items on the RHS only need to be identified by their name!

So, the 'setter' provides two inputs - name and value.
Whereas, the 'getter' only needs to know which name (applies to that data-item).


The 'pattern' or (necessary) consistency is only the name/id of the data-item. Its value is 'inconsistent' in that it is either being passed-in ('setter') or pulled-out ('getter'). In one case it is an argument and in the other a return-value.


Although, answers that I got were very useful.

Thanks!

--
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/24Z4FIPXPKGATDAXC6VPIRHTXP2WFXUF/
Code of Conduct: http://python.org/psf/codeofconduct/

Reply via email to