I hate NULL and all its variants ... quick hack to alleviate the pain in Python
blog.alexn.org
blog.alexn.org
def if_present?
# example: User.find_all_by_something.if_present? || User.find_all_by_something_else
self if present?
end
def send_if_respond_to(method, *args)
# example: string_or_nil.send_if_respond_to(:strip)
send(method, *args) if respond_to?(method)
endMessage-eating nils are one of my favorite features of Objective-C.
(Banana.find(id).plantation.state rescue nil)
Chain it if when falsity occurs do something else. banana = (Banana.find(id).plantation.state rescue nil) || Fruit.find(:banana,id).stateYes, in Haskell or F# (the 2 languages I've played with) ... monads are so natural you don't even notice them many times. But this goes both ways, what's natural to do in Python, isn't so in other languages.
Also in this particular case you're not programming around the language ... dynamic OOP languages are all about late-binding, that's their strength. If you're using something like Python as if it were Java, you're missing on lots of useful stuff.
find "table" [("class", "search-params")] soup >>=
findParent "form" >>=
find "td" [("class", "elem")] >>=
find "input" [] >>=
get "name"
If the result of any call in the chain is Nothing (equivalent to null), we get Nothing. Otherwise, it keeps on going and (if, for example, the name of the relevant element is "signup") we get Just "signup".Simulating Haskell's option type is actually the article's proposed solution.
It's really hard not to gloat.
soup.find('table', {'class':'search-params'}).findParent('form')\
.find('td', {'class': 'elem'})\
.find('input')\
.get('name') or raise WebUIError("Web interface specification changed")
Shouldn't the boolean expression short-circuit and only raise WebUIError in case the expression returns what he doesn't want to?This api fail drives me crazy when using BS. Esp since it's otherwise pretty slick.
It might require a few changes elsewhere as you would be getting None where you used to get an error, but relying on the error might have been some lazy programming to begin with.
It's really, really not what you want. Or at least not what the poor guy who has to maintain your poorly monkey patched code.
Though I would go an entirely different direction, personally, something like:
f = self.find
result = maybe_apply((f, ('table', {'class': ...})),
(f, ('td', {'class': 'elem'})),
(f, ('input',)),
(self.get, ('name',)))
Explicit is better than implicit. For bonus points, the "query" is now reusable and programmatically manipulable, which in my experience outweighs the ugly factor.You're basically using Python as if it were Lisp, which is kind of ugly :)
Sometimes the ugly tradeoff is OK, even in Python. In this case it is still less ugly than the code it is replacing, if you write correct code. Doing one little piddly-ass thing (running one method), then copy/pasting in the same four lines of error handling after every single one, well, it may be aesthetically pleasing on your screen but it's far uglier than what I've shown.
Python has many advantages, but it isn't perfect.