
PyContracts: declare constraints on function parameters and return values (2014) [pdf] - pmoriarty
http://censi.mit.edu/pub/research/201410-pycontracts/201410-pycontracts.pdf
======
lkrubner
I'll offer this qualifier up front: Python is a great language with many
interesting features and it has a fantastic spot in the world of data
analysis. Having said that, I wonder if contract programming is appropriate in
Python? I have a similar criticism of contract programming in Ruby and PHP and
Perl.

I'm a huge fan of "design by contract" and I strongly advocate for that style
when I'm working in any JVM language (Java, Groovy, Clojure, etc). But if you
find yourself needing this kind of run-time validation in Python, shouldn't
you stop and ask yourself "Is Python the right tool for this job?" (If anyone
here remembers reading some old posts of mine, you might recall that last year
I was involved in a startup focused on Natural Language Processing, which we
first considered using Python for, but then switched to Java.)

My criticism is stronger for Ruby and PHP. I realize that some people compile
their Python, which allows optimizations similar to what's available on the
JVM. My advice is less absolute when it comes to Python. Whereas with PHP, if
you find yourself needing runtime contracts, I would always, always suggest
you switch to a different language, I realize the situation is somewhat
different with Python. There are more compile and run time options.

The great advantage of the scripting languages is their ease-of-getting-
started. PHP and Ruby and Python all recommend themselves to beginners because
of the simplicity of the initial setup.

I am willing to believe that Python scales up better than PHP or Ruby. I've
seen enough disasters with Symfony and Rails that I'm willing to give Python
some credit here (though consider how many complete re-writes Zope undertook).

For my part, as soon as the run time is complicated enough that I feel I need
contracts for the runtime environment, I would consider switching to some
other language.

~~~
paulddraper
> I'm a huge fan of "design by contract" and I strongly advocate for that
> style when I'm working in any JVM language (Java, Groovy, Clojure, etc).

Is there an advantage for design-by-contact that Groovy or Clojure have that
Python doesn't?

Static typing (IMO) goes hand-in-hand with design-by-contract, but Groovy or
Closure -- normally -- don't have this.

~~~
p4wnc6
It's not so much static typing as functional purity that guarantees design by
contract. In a pure language like Haskell, you are forced to go out of your
way and do things with glaringly bad code smells to avoid designing by
contract. In, say, Scala, F#, or ML, it's easier to subvert by e.g. liberally
using mutation or references. In Clojure, it's even easier to avoid, since
there's no static type checking, and, arguably in Python it's even easier
still.

But even in Haskell or Idris, which require extreme lengths taken by the
programmer to violate these things, you still can, and you could violate them
in a small piece of code that gets wrapped and used all over, spreading the
impurity.

So at the end of the day, no matter what language you use, these things
_always_ boil down to _mere conventions_ that rely on trust of the system
implementers.

You can trade away some conveniences in order to add extra hoops for
convention-violators to jump through -- and you might interpret the existence
of the extra hoops as "safety" or some kind of "guarantee".

This suggests there should still be use cases when you want conveniences of
Python but also want to enforce some degree of hoop-jumping to deter
convention-violators. Switching to another language instead, because now
Python's lack of intrinsic hoop-jumping guarantees is a problem, comes with
it's own costs, and if those costs are significant and related to the major
wins that Python was bringing you, then it could easily instead be more
effective to just create a few abstractions (and accept performance hits or
whatever) to get the hoop-jumping you need in Python.

One very common instance where this arises in dynamic typing languages is with
subclassing and inheritance hierarchies. You might have a metaclass Foo and
you want isinstance(x, Foo) or issubclass(x, Foo) to evaluate True for a wide
range of x, some not directly inheriting from x.

You can monkeypatch the __instancecheck__ and __subclasshook__ methods on the
metaclass to override instance and subclass checking.

This is an example where you could get a lot of value out of it. By building
that machinery right, later on you can check isinstance and then raise a
TypeError, rather than try-excepting an attribute access or something, and you
morph a try-except block that might have had a lot of complexity and been a
target for code growth when new features get added, and turn it into
effectively a much simpler assert statement.

You definitely wouldn't want to move away from Python just to get this kind of
flexibility out of the box in some other language.

But as you ramp up the need for this kind of hoop jumping, say by building
some kind of complicated static type checker that uses argument type
annotations and fancy metaprogramming with the inspect and typing modules, now
you might step back and consider whether, if all that really is needed, you
wouldn't just prefer a statically typed language to begin with. And so on...

~~~
Chris2048
monkeypatching, instance checking and class-hook hacks aren't things I'd
particularly value in Python, they certainly aren't deal-breakers wrt "major
wins that Python was bringing you".

I've rarely needed to use these things - I'd sooner make use of multiple-
inheritance and use mix-ins.

Compared to a language like Java, first the possibility of Mixins exists, as
does Duck-typing.

Further, types are not mandatory, or statically checked. Is is the fact that
you _don 't need_ to do this is Python that is a win. The fact that (barring
some extension) _you can 't_ is not an advantage. Optional checking, and
dropping verbosity (no check by default, not having to declare something
unchecked) is the best of worlds.

> things always boil down to mere conventions that rely on trust of the system
> implementers

This doesn't mean you should try to _reduce_ dependance on this trust. The
less checking you do, the more likely a n issue slips through the net.
Furthermore, not all infractions are the same, we can forgive small things.
Not everyone should be able to modify import, or class-loading mechanisms
freely, or without supervision.

> some kind of complicated static type checker that uses argument type
> annotations

You can make this argument for, say, jumping to Julia. Does a complication in
implementing in Python outweigh the complication of an entire new language?
You could argue Python 3 is exactly this...

~~~
p4wnc6
> instance checking and class-hook hacks

These are not remotely "hacks" in Python. They are up-front, first class
patterns for Python metaclasses. I do agree that monkeypatching is a hack, and
is unfortunately relied upon too often in Python.

~~~
Chris2048
Depends which Python - Py3 is a bit more robust wrt metaclasses.

instance checking on a overridden magic method certainly is a hack, I don't
know what youy mean by "first class pattern" \- Why add the possibility that
the instanceof mechanism has been messed with, thereby throwing off alot of
reasonable assumptions about the check?

~~~
p4wnc6
I mean it is supported directly with __instancecheck__ or __subclasshook__ ..
explicitly to allow you to create metaclasses with custom implementations of
instance checking or subclassing. To call use of this a hack is similar to
saying that overriding __getitem__ is a hack. In Python, it's not a hack, it's
a normal, common way of doing many things.

You'd mess with the isinstance mechanism exactly when doing so made
engineering sense. In Python, making assumptions about what isinstance is
doing is a very bad idea, would indicate novice Python understanding -- just
like picking up some other class (like say, pandas DataFrame) and assuming
something "reasonable" about what __getitem__ does would be a bad idea --
__getitem__ is heavily overloaded for various design and performance reasons
in Pandas (and in many, many libraries).

You can't (and shouldn't want to) pick up any old class in Python and assume
things like operator overloads, hashing, instance checking, item getting,
serialization, iteration, etc. etc., work under "reasonable assumptions."
Either the class authors implemented the trickery in some way that virtually
never impacts anyone, or else you need to know the gory implementation details
to effectively reason about it. My experience has been that you should always
assume the latter until you have strong evidence of the former.

------
mk89
Funny that years and years ago, when I started learning python (2.5, in 2006),
design by contract in Python was considered a shameful practice of people
coming form different backgrounds and languages. TDD and "unit tests" were
already the keys to heaven at that time, considered the new way of developing
high-quality programs with less bugs.

I find it way clearer than having different tests due to the overhead brought
in by the dynamic nature of the language. I don't find it necessarily wrong or
not Pythonic. Afterall, in a static typed language you also don't have
contracts on parameters/return values (all you have is the type).

However, this will be seen, as it was years and years ago, as a bad practice.
Great agile developers write tests over tests to determine what a function
should return and what should not accept.

------
captaindiego
This looks quite interesting. I really like the documentation aspect of this.
I already leave myself similar notes in docstrings explaining the structure of
data going in and out when it's a bit complicated. This would allow me to
catch errors more quickly when I change code in one place and it effects
complex data types moving between things.

------
jononor
Looks very nice, will consider using for next Python project. Have been
working on something quite similar for JavaScript,
[http://agreejs.org/](http://agreejs.org/)

------
simoncarucci
Isn't Ethereum a better viable alternative?

~~~
icebraining
Not the same kind of contract:
[https://en.wikipedia.org/wiki/Design_by_contract](https://en.wikipedia.org/wiki/Design_by_contract)

