Even though it’s not a part of the Zen of Python, there is a widely accepted principle in Python community that reads:
It’s better to ask for forgiveness rather than permission.
For code, it generally means
trying to do something and seeing whether it succeeded, rather than checking
if it should succeed and then doing it. The first approach produces something like this:
and it’s preferred over the more cautious alternative:
What I realized recently is that there is much more general level of software development where the exact same principle can be applied. It’s not just about individual pieces of code, or catching exceptions versus checking preconditions. It extends to functions, modules, classes and packages – but more importantly, to the whole process of adding features, extending functionality or even conceiving totally new projects.
Taking a bit simplistic view, this process can be thought of having two extremes. On one end, there is a concept of pure “waterfall”. Every bit of work has to fit into predefined set of phases and no later phase can start before the previous one has completely finished. In this setting, design must be done upfront and all knowledge about the future product has to be gathered before coding starts.
By even this short description, it’s hard to stop thinking about all the hilarious ways the waterfall approach can end badly. Curiously, though, there are still companies that not only follow it but flaunt the fact publicly.
The other end is sometimes called “cowboy coding”, an almost derogatory term for churning out code without regard to pretty much any methodology whatsoever. Like in case of waterfall, it sometimes (very rarely) works, but it’s more so by accident rather than by design. And like pure waterfall, it also doesn’t really exist in nature, as long as more than one programmer is involved.
If we frame the spectrum like this, with the two completely opposite points at the edges, the natural tendency is to search for the middle ground. But if we subscribe to the titular principle of asking for forgiveness rather than permission, the right answer will clearly be shifted towards the “lean” side.
What is permission here? That’s a green light from design, architecture, requirements, UX and similar standpoints. You wouldn’t mind having all these before you start coding, but it is not absolutely crucial. Not having it, you can still do things: code, prototype, explore, and see how things pan out.
Even if you end up ripping it all apart, or maybe even fail to produce anything notable, you will still accrue useful knowledge. Say you have to throw away most of the week’s work because you’ve learned you need to do it differently. So what? With the insight you have gained along the way, it may very well take just a few hours now.
And so thou shalt be forgiven.
Many a hardships are constantly plaguing the hard working IT folks. For example, you may find out that the nearest fridge is out of your favorite drink! But among the so-called First World Problems, there are few that constitute actual, you know, problems – even if you should consider yourself very lucky facing them.
The prime example is this peculiar situation when you’re standing bathed in glaring sunlight, while your body is clearly telling you it’s the middle of the night – or the other way around. I’m talking, of course, about the physiological condition known as jet lag.
It’s pretty stubborn beast, too. There isn’t really a way to avoid it, short of reverting to old fashioned and slow means of transport. And you cannot count on having many innate adaptation to deal with it, either. It would require for our savannah ancestors to have been hopping continents in a few hours on regular basis, which – as far as we know – is not something they used to do.
Nevertheless, the symptoms of jet lag and their severity may vary greatly by person, so most of the advice about dealing with it will not necessarily work for everyone. But among the dubious statements you can find around the Internet, there are also some solid facts here and there.
Although it’s not technically the cause of jet lag, spending many long hours in closed, cramped, crowded space (also known as “taking a flight”) is certainly a factor that could make its symptoms worse. To reduce that impact, there are few things you can do.
The air in aircraft’s cabin is typically very dry. In such an environment, you will lose body water faster than on the ground and eventually experience dehydration. I suspect this is also the reason why many people feel the jet lag as a condition similar to hangover.
The solution is, obviously, to drink more. You should never refuse a cup of water whenever the flight attendant offers you one, and you may actually want to actively bug them for even more. This isn’t likely to win you friends but it’s totally worth it, and also conveniently coincides with the next point.
While on the plane, your options to move around are naturally quite limited. It’s never a bad idea, however, to get up, take a few steps and do some stretching. But what’s more important is to actually move around way before stepping on board.
As you probably know very well, the average airliner has a hermetically sealed cabin that keeps the air inside of it at much higher pressure than the surrounding atmosphere it soars through. Unfortunately, the pressure cannot be kept too high, lest the strain on fuselage’s integrity would be too severe and risk implosion. As a result, the density of air (and thus oxygen) in the cabin is usually comparable to that of about 2 kilometers above the sea level.
Unless you live at least that high up and are used to reduced oxygen pressure, you may experience some adverse effects which are curiously similar to the effects of jet lag. Though moving into some elevated area to make your breathing and circulation more efficient is certainly an option, the less radical way is simply to exercise regularly to achieve a comparable effect.
This is hint is a bit more controversial, as some people will recommend staying up for the whole travel. Personally I envy those who are in the position to even consider this as an option. But even then, getting into the more relaxed state by closing your eyes works as acceptable substitute should you choose to do so.
The practical effect of moving rapidly to a far away time zone is to make your “day” artificially shorter or longer. Coping with that change therefore requires a temporary adjustment period with altered sleep pattern. To put it simply, you will need to either sleep when you normally don’t, or stay awake when you normally do.
While this is really a matter of your typical habits, I’d wager that for the average reader – who is likely no stranger to coding sessions that drag well into the night – the latter path would be the one of less resistance. Note, however, that especially on eastward flights the total duration of “daytime” may exceed 30 or more hours. Taking a nap while still on the plane would then serve to cushion the blow of having the “evening” moved well past the normal time.
A vast majority of code is dealing with logical conditions by using three dedicated operators:
or (alternative). These are often written as
||, respectively, in C-like programming languages.
In principle, this is sufficient. Coupled with true and false, it’s enough to encode any boolean function of zero, one or two arguments. But language designers didn’t seem to be concerned with minimalism here, because it’s possible to replace those three operators with just one of the following binary functions:
If you can’t immediately see how, start with deriving negation first.
So we already have some redundancy for the sake of readability. While it’s surely a bad idea to try and incorporate all 22 before mentioned functions, isn’t there at least few others that would make sense as operators on their own?
I can probably think of one: the material implication (). It may seem like a weird choice at first, but there are certain common scenarios where such operator would make things more explicit.
Imagine for a second that some mainstream language (like Java) has been enhanced with operator
=> that acts as implication. Here’s one example of its straightforward usage:
Many situations involving “optionals” could take advantage of logical implication as an operator. Also note how in this case, the alternatives do not look very appealing at all. One could use an
if statement to produce equivalent construct:
but this makes a trivial one-liner suddenly look quite involved. We could also expand the implication using the equivalence law :
Reader would then have to perform the opposite transformation anyway, in order to restore the real meaning hidden behind the non-obvious
|| operators. Finally, we could be a little more creative:
and capture the intent almost perfectly… At least until along comes someone clever and “simplifies” the expression into the not-a-or-b form presented above.
Does any language actually have the implication operator? Not surprisingly, the answer is yes – but it’s most likely a language you wouldn’t want to code in. Older and scripting versions of Visual Basic had the
Imp operator, intended to evaluate the logical connective .
Besides provoking a few chuckles with its hilarious name, its usefulness was limited by the fact that it wasn’t short-circuiting. Both arguments were always evaluated, even if the first one turned out false. You may notice that in our
NameMatcher example, such a behavior would produce
NullPointerException when one of the names is
null. This is also the reason why implication implemented as a function:
would not work in most languages, for the arguments are all evaluated before executing function’s code.
As a fact of life, in bigger projects you often cannot just delete something – be it function, method, class or module. Replacing all its usages with whatever is the new recommendation – if any! – is typically outside of your influence, capabilities or priorities. By no means it should be treated as lost cause, though; any codebase would be quickly overwhelmed by kludges if there were no way to jettison them.
To reconcile those two opposing needs – compatibility and cleanliness – the typical approach involves a transition period. During that time, the particular piece of API shall be marked as deprecated, which is a slightly theatrical term for ‘obsolete’ and ‘not intended for new code’. How effective this is depends strongly on target audience – for publicly available APIs, someone will always wake up and start screaming when the transition period ends.
For in-project interfaces, however, the blow may be effectively cushioned by using certain features of the language, IDE, source control, continuous integration, and so on. As an example, Java has the
@Deprecated annotation that can be applied to functions or classes:
If the symbol is then used somewhere else, it produces a compiler warning (and visual cue in most IDEs). These can be suppressed, of course, but it’s something you need to do explicitly through a complementary language construct.
So I had this idea to try and add similar mechanism to Python. One part of it is already present in its standard library: we have the
warnings module and a built-in category of
DeprecationWarnings. These can be ignored, suppressed, caught or even made into errors.
They are also pretty powerful, as they allow to deprecate certain code paths and not just symbols, which can be useful when introducing new meanings for function parameters, among other things. At the same time, it means using them is irritatingly imperative and adds clutter:
And in this particular case, it also doesn’t work as intended, for reasons that will become apparent later on.
What we’d like instead is something similar to annotation approach that is available in Java:
Given that the
@-things in Python (decorators, that is) are significantly more powerful than the Java counterparts, it shouldn’t be a tough call to achieve this…
Surprisingly, though, it turns out to be very tricky and quite arcane. The problems lie mostly in the subtle issues of what exactly constitutes “usage” of a symbol in Python, and how to actually detect it. If you try to come up with a few solutions, you’ll soon realize how the one that may eventually require walking through the interpreter call stack turns out to be the least insane one.
But hey, we didn’t go to the Moon because it was easy, right? ;) So let’s see how at least we can get started.
Many are the quirks of shell scripting. Most are related to confusing syntax, but some come from certain surprising semantics of Bash as a language, as well as the way scripts are executed.
Consider, for example, that you’d like to list files that are within certain size range. This is something you cannot do with ls alone. And while there’s certainly some awk incantation that makes it trivial, let’s assume you’re a rare kind of scripter who actually likes their hacks readable:
So you use an explicit
while loop, obtain the file size using stat and compare it to given bounds using a straightforward
if statement. Pretty simple code that shouldn’t cause any troubles later on… right?
But as your needs grow, you find that you also want to count how many files fall within your range, and how many do not. Given that you have an explicit
if, it appears like a simple addition (in quite literal sense):
Why it doesn’t work, then? Because clearly this is not the output we’re looking for (ls_between is our script here):
It seems that neither matches nor misses are counted properly, even though it’s clear from the printed list that everything is fine with our
if statement and loop. Wherein lies the problem?
Last weekend I attended the PyGrunn conference, back in the good ol’ Netherlands. It was a very enjoyable and instructive event, featuring not only a few local speakers but also some prominent figures from the Python community – like Kenneth Reitz or Armin Ronacher. Overall, it was a weekend of some great pythonic fun.
…Except for a one small detail. As I’ve learned there, Python will apparently get to have enums in the 3.4 version of the language. To say that this was baffling to me would be a severe understatement. Not only I see very little need for such a feature, but I also have serious doubts whether it fits into the general spirit of Python language.
Why so? It’s mostly because of the main purpose of enumeration types, derived from their usage in many languages that already have them as a feature. That purpose is to turn arbitrary data – mostly integers and strings – into well-known, reliable entities that can be safely manipulated inside our programs. Enums act as border guardians, filtering out unexpected data and converting expected data into its safe representation.
What’s safety in this context? It’s type safety, of course. Thanks to enumeration types, we can be certain that a particular value belongs to specific and constrained set of elements. They clearly define all the variants that our code should handle, because everything else was already culled at the conversion stage.
Problem is, in Python there is nothing that would guarantee those safety promises are actually fulfilled. True, the basic property of enum types is retained: given an enum object, we know it must belong to a preordained set of entities. But there is nothing that ensures we are dealing with an enum object at all – short of us actually checking that ourselves:
How this is different from a straightforward membership check:
except for the latter looking cleaner and more explicit?…
This saying, I do not claim enums are completely out of place in Python. This is untrue, if simply because of the fact they are easily implemented through a rather simple metaclass. In fact, this is exactly how the proposed
enums.Enum base is supposed to work.
At the same time, it is also possible to provide some of the before-mentioned type safety. Just look into various libraries that enhance Python with support for contracts: a form of type safety which is even more powerful than what you’ll find in many statically typed languages. You are free to use them, and you should definitely do, if your project would benefit from such a functionality.
Incidentally, they fit right in with that new & upcoming enumeration types from Python 3.4. It remains to be seen what it means exactly for the overall direction of the language design. But with enums in place, the style of “checked” typing suddenly became a lot more pythonic than it was before.
And I can’t say I like it very much.
Much can be said about similarities between two popular, distributed version control systems: Git and Mercurial. For the most part, choosing between them can be a matter of taste. And because Git seems to have considerably more proponents in the Cool Kids camp, it doesn’t necessarily follow it’s a better option.
But I have found at least one specific and common scenario where Git clearly outshines Hg. Suppose you have coded yourself into a dead end: the feature you planned doesn’t pan out the way you wanted it; or you have some compatibility issues you cannot easily resolve; or you just need to escape the refactoring bathtub.
In any case, you just want to step back a few commits and pretend nothing happened, for now. The mishap might be useful later, though, so it’d be nice if we left it marked for the future.
In Git, this is easily done. You would start by creating a new branch that points to your dead end:
__my_feature__dead_end__ refer to the same, head commit. We would then move the former a little back, sticking it to one of the earlier hashes. Let’s find a suitable target:
If it looks right, we can reset the
my_feature branch so it points to this specific commit:
Our final situation would then looks like this:
which is exactly what we wanted. Note how any further commits starting from the referent of
my_feature would fork at that point, diverging from development line which has lead us into dead end.
Why the same thing is not so easily done in Mercurial?… In general, this is mostly because of its one fundamental design decision: every commit belongs to one branch, forever and for always. Branch designation is actually part of the changeset’s metadata, just like the commit message or diff. Moving things around – like we did above – is therefore equivalent to changing history and requires tools that are capable of doing so, such as