
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