On Thu, Jul 19, 2018 at 05:32:48PM +0900, Stephen J. Turnbull wrote: > To be honest, code transformations like this > > > class BaseUploadObject(object): > > def find_content_type(self, filename): > > ctype, encoding = mimetypes.guess_type(filename) > > if ctype is None: > > return 'application/octet-stream' > > else: > > return ctype > > to this > > > class BaseUploadObject(object): > > def find_content_type(self, filename): > > ctype, encoding = mimetypes.guess_type(filename) > > return ctype ?? 'application/octet-stream' > > make me cringe.
I don't think that's a good use of ?? and that method should either remain in the original form, or at most, be re-written using return ctype if ctypes is not None else 'application/octet-stream' (although I prefer the original form). There doesn't seem to be any advantage to ?? in this example except to transform a four-line imperative-style return statement to a one-line return statement, trading off vertical space for horizontal. But there's no improvement in expressiveness that I can see. > Exactly one of two things is true: > > 1. mimetypes.guess_type guarantees that the only falsie it will ever > return is None, or > > 2. it doesn't. > > In case 1, "ctype or 'application/octet-stream'" ALSO does the right > thing. It does the right thing, but it doesn't look like it does the right thing. The ?? operator promises to only transform None, and nothing but None. To the reader, the `or` operator promises to transform any falsey value. Is `or` too broad and do too much? There's no way of knowing. Instead of treating `guess_type` as a black-box, the reader now has to dig-deep into its documentation and/or implementation to find out which falsey values it might return and whether or not they ought to be treated the same as None. > In case 2, ONLY "ctype or 'application/octet-stream'" does the > right thing, as few callers of BaseUploadObject.find_content_type will > be prepared for "", (), [], or any variety of 0 as the return value. I doubt that guess_type (and hence find_content_type) will return 0 or [] etc as part of their documented interface (as opposed to a bug), but if they did, surely we ought to assume that the caller is prepared to handle its output. More likely is that it might return the empty string "". At face value I would expect that a better interface for guess_type would be to return the empty string rather than None. But perhaps there is a legitimate reason to treat the empty string as distinct from None, it should *not* be transformed. On perhaps the function is avoiding the "failure looks like success" anti-pattern. Who hasn't been bitten by writing something like this? chunk = text[text.find('spam')+4:] It looks okay at a glance: return the chunk of text immediately following "spam". But if "spam" isn't present at all, it returns the entire text minus the first three characters, which is probably not what you wanted. So there is a good argument to be made that guess_type ought to promise: - return None if it cannot guess the type; - otherwise return a non-empty string. (or that the empty string is itself a legitimate output that ought to be considered distinct from None and should *not* be transformed). In that case, `or` reads wrongly, because it either wrongly transforms "" into the default when it should not, or it suggests to the reader that it might. Either way, an explicit test for None -- whether we use "if ... is None" or the proposed ?? operator -- is better than a test for falsey. > So I would like to see various examples of code where > > 1. in the original code, treating a falsie that is not None the same > way that None is treated is a detectable bug; and I don't see that this is relevant. We already had this discussion, when the ternary if was introduced, and a million times when discussing why it is better to write "if spam is None" rather than "if not spam". In other words, we ought to be comparing the expressiveness of process(spam ?? something) versus: process(something if spam is None else spam) (and similar variations), not against process(spam if spam else something) process(spam or something) both of which do something different from the ?? operator. -- Steve _______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/