4 ms·
As an implementor of Hy (a homoiconic lisp frontend to Python) I've found certain Python idioms to be rather infuriating of late. In particular: >>> 0 ==
by agentultra 13y ago
As an implementor of Hy (a homoiconic lisp frontend to Python) I've found certain Python idioms to be rather infuriating of late.
In particular:
>>> 0 == False
True
Which makes the idiom of testing truthiness quite annoying in parsing code such as:
def is_digit_char(s):
""" Return a parsed integer from 's'."""
try:
return int(s)
except (ValueError, TypeError):
return None
Which is harmless enough except that as a predicate it sucks because parsing "0" will return False in a context where I'd rather know whether I parsed an integer or not... which leads to non-idiomatic code.
This is mainly because True/False are essentially aliases for 1/0 and as such won't identify so:
>>> 0 is False
False
>>> 0 is 0
True
So it's important to remember another Tim Peters-ism: Although practicality beats purity. As read in the Zen of Python it seems he's referring to special cases which this might be.
As a shameless aside, you should see what we're working on in Hy. There will likely come a point where we'll be able to do kibit-style suggestions of idiomatic transformations to your Python code.
Update: I ran into this while trying to write token parsers for a parser-combinator lib in Hy.
- icebraining 13y agoWell, it's not really Python's behaviour, just the behaviour of that type. You can always use your own type: class I(int): def __nonzero__(self): return True def is_digit_char(s): try: return I(s) except (ValueError, TypeError): return None >>> if is_digit_char('0'): print "True" True
- riquito 13y agoYour function is_digit_char(s) is peculiar. From the name I would expect it to return a boolean, instead it returns a number or None. To write it like that and have a problem with the falseness of 0 it means that you use it in a way to both use it as conditional expression and an integer value, e.g. digit = is_digit_char('0') if digit: # fail print(digit) else: print('not a digit') You should then check for his equality to None digit = is_digit_char('0') if digit is None: # pass print('not a number') else: print(digit) But I would argue that you were in search for troubles when you wrote an is_something() function that doesn't return a boolean. That is not idiomatic. p.s. Hy is too crazy :-)
- aiiane 13y agoLogged in to post basically the same comment, then saw you already had. Essentially, no one who knows what they're doing in Python would write the function that way.
- agentultra 13y agoIt's a trivial example. I've been writing Python since 2.3... maybe I should stop.
- agentultra 13y agoIt's a trivial example and I wouldn't focus on it too much. It's not that peculiar -- instead of parsing the same character twice you simply return the value that you parsed or None. My inspiration was from the CLHS predicate function, DIGIT-CHAR-P [0]. The real point I was making is that Python has warts that make writing idiomatic code impractical in some situations. I suggest that practicality take precedence over purity. There are some situations that lead to non-idiomatic code and that's okay. [0] http://clhs.lisp.se/Body/f_digi_1.htm http://clhs.lisp.se/Body/f_digi_1.htm Update: forgot the link. :) Update update: Perhaps peculiar to Python because all values of integers are not False except for 0 whereas in another language that doesn't have this wart, anything that isn't False is True... even 0. In other words, anything that isn't False is True. :D
- masklinn 13y ago> In other words, anything that isn't False is True. :D Except for `nil` (lisps, ruby), and possibly a host of other things depending on the language (empty strings in javascript).
- agentultra 13y agoScheme is the only PL I know that has an explicit #f value. CL, for all intents and purposes, treats nil as False... but some find the conflation of nil and the empty list runs into the same issue when operating on s-exprs. CL-USER> (if '() 1 0) 0 vs scheme> (if '() 1 0) 1
- js2 13y agoI'm surprised by your definition for a function named is_digit_char. I'd expect such a function to be something like: return len(s) == 1 and s[0] in string.digits Or return re.search('^\d$', s) Then it can be used idiomatically. The function you defined I'd call parse_int: def parse_int(s) "Return a parsed integer from 's' or None if it's not an int.""" ... Which would then be used as: i = parse_int(s) if i is None: ...your function definition... $0.02.
- agentultra 13y agoI might try this... it wasn't actually named is_digit_char; I was actually writing a token parser for a "monadic" parser combinator library in Hy and my function was more idiomatically named there, integer-char? (after the CLHS function DIGIT-CHAR-P).
- balloob 13y agoWhy not use the Python built-in method "anystring".isdigit() ?
- drdaeman 13y agoanystring in string.digits
- nilkn 13y agoThis reminds me, somewhat tangentially, of a bug I ran into once with Tcl (yes, Tcl) where a function returned an integer in the form "08" and Tcl had a convention that any integer with a 0 prepended would be treated as octal in any arithmetic expressions. Naturally, 8 is not a valid octal digit, so this broke everything when it happened. Ultimately it was my fault for not being better acquainted with the language, but that one still caught me by surprise.