How to deal with Python ~ static typing? [closed]

家住魔仙堡 提交于 2019-11-29 06:15:05

Static type checking is undecidable in the general case. This means that there are programs which are statically type-safe but for which the type-checker cannot prove that they are statically type-safe, and thus the type-checker must reject those programs.

In other words: there are type-safe programs that the type-checker will not allow you to write. Or, even more succinctly: static typing prevents you from writing certain programs.

This applies to all static typing in general, not just to Java.

As to Java specifically: it has a rather crappy type system. Its type system is not expressive enough to express even very simple properties. For example: where in the type of static void java.util.Arrays.sort(Object[] a) does it actually say that the result has to be, you know, sorted? Or that the array elements have to be partially ordered?

Another problem with Java is that its type system has holes so big that you can drive a truck through:

String[] a = new String[1];
Object[] b = a;
b[0] = 1; // ArrayStoreException

The problem in this particular case are covariant arrays. It's simply not possible for arrays to be both covariant and type-safe.

Java combines all the hassle of static typing with none of the advantages. So, you might just as well get rid of the hassle.

However, note that this is not universal. There are other languages which have much better type systems for which the trade-offs are much less clear.

For example, here is the most stupid language benchmark of all time (Fibonacci) in Python:

def fib(n):
    if n < 2: return n
    return fib(n-2) + fib(n-1)

and Java:

int fib(int n) {
    if (n < 2) return n;
    return fib(n-2) + fib(n-1);
}

Note that there is quite a bit more clutter there, which is solely related to static typing. To make the comparison more fair, let's imagine a language with Python's syntax and Java's semantics:

def fib(n: int) -> int:
    if n < 2: return n
    return fib(n-2) + fib(n-1)

[Interesting side note: with the addition of optional static type annotations in Python 3.x, that is actually also valid Python code, although it is obviously still not statically type-safe, since the annotations are just that: annotations. They are never actually checked anywhere.]

There is some definite clutter there. However, in Haskell it looks like this:

fib n
  |     n < 2 = n
  | otherwise = fib (n-2) + fib (n-1)

Unlike the Python version, this is perfectly statically type-safe, but there is zero type-related clutter.

In this particular case, the question between the benefits of static and dynamic typing are much less clear.

By the way, a more idiomatic Haskell version would probably look like this:

fib 0 = 0
fib 1 = 1
fib n = fib (n-2) + fib (n-1)

or this:

fibs = 0 : 1 : zipWith (+) fibs (tail fibs)

Really, the much more important difference between Java and Python is not so much that Java is statically typed and Python is dynamically typed, but rather that Java is just not a good programming language, while Python is. So, Java is just always going to lose, not because it is statically typed, but because it is crap. Comparing BASIC with Haskell, Haskell clearly wins, but again, not because it is statically typed but because BASIC is crap.

A much more interesting comparison would be Java vs. BASIC or Python vs. Haskell.

I suspect that the vast majority of non-trivial Java programs have dynamic typing in them.

Every time in Java that you do a cast from Object to an explicit type you are doing dynamic type checking - this includes every use of a collection class before generics were introduced in 1.5. Actually Java generics can still defer some type checking until runtime.

Every time you use Java reflection you are doing dynamic type checking. This includes mapping from a class or method name in an text file to a real class or method - e.g. every time you use a Spring XML configuration file.

Does this make Java programs fragile and error prone? Do Java programmers spend a significant part of their time having to track down and fix problems with incorrect dynamic typing? Probably not - and neither do Python programmers.

Some of the advantages of dynamic typing:

  • Greatly reduced reliance on inheritance. I have seen Java programs with massive inheritance trees. Python programs often use little or no inheritance, preferring to use duck typing.
  • It is easy to write truly generic code. For example the min() and max() functions can take a sequence of any comparable type - integers, strings, floats, classes that have the appropriate comparison methods, lists, tuples etc.
  • Less code. A huge proportion of Java code contributes nothing to solving the problem at hand - it is purely there to keep the type system happy. If a Python program is a fifth the size of the equivalent Java program then there is one fifth the code to write, maintain, read and understand. To put it another way, Python has a much higher signal to noise ratio.
  • Faster development cycle. This goes hand in hand with less code - you spend less time thinking about types and classes and more time thinking about solving the problem you are working on.
  • Little need for AOP. I think there are aspect oriented libraries for Python but I don't know of anyone that uses them, since for 99% of what you need AOP for you can do with decorators and dynamic object modification. The wide use of AspectJ in the Java world suggests to me that there are deficiencies in the core Java language that have to be compensated for with an external tool.

Do you like it in Python?

It's part of Python. Liking it in Python is silly.

Do you have an example where it helped in a big project?

Yes. Every single day I rejoice that I can make changes and -- because of Duck typing -- they are reasonably localized, pass all the unit tests, pass all the integration tests, and nothing is disrupted elsewhere.

If this was Java, the changes would require endless refactoring to pull interfaces out of classes so that I could introduce variations that were still permitted under Java's static type checking.

Doesn't it a bit error prone?

Not any more than static typing is. A simple unit test confirms that the objects conform to the expected features.

It's easy to write a class in Java that (a) passes compile-time checks and (b) crashes horribly at run time. Casts are a good way to do this. Failing to meet the classes intent is a common thing -- a class may compile but still not work.

A lot of patterns (e.g. from GoF) are unnecessary or can be implemented with less efforts in dynamic-typed languages with functional flavor. In fact, a lot of patterns are "built-in" into python so if you write short and 'pythonic' code you will get all the benefits for free. You don't need Iterator, Observer, Strategy, Factory Method, Abstract Factory, and a bunch of other patterns that are common in Java or C++.

This means less code to write and (much more important) less code to read, understand and support. I think this is the main benefit of languages like python. And in my opinion this greatly outweighs the absence of static typing. Type-related errors are not often in python code and they are easy to catch with simple functional tests (and such tests are easier to write in python than in java for sure).

It's a load off your mind. You can think the color red as "Red" (a constant) as "255, 0, 0" (a tuple) or "#FF0000" (a string): three different formats, that would require three different types, or complex lookup and conversion methods in Java.

It makes code simpler.

For example, you can write functions to which you can pass an integer as well as a string or a list or a dictionary or whatever else, and it will be able to transparently handle all of them in appropriate ways (or throw an exception if it cannot handle the type). You can do things like that in other languages, too, but usually you have to resort to (ab)use things like pointers, references or typecasts, which opens holes for programming errors, and it's just plain ugly.

As you're from the Java world, the obvious answer would be that it's great not to be forced to write all that stuff you are forced to write, just to keep Java's type system happy.

Of course, there are other statically type checked languages that don't force you to write all that stuff that Java forces you to write.

Even C# does type inference for local method variables!

And there are other statically type checked languages that provide more compile time error checking than Java provides.

(The less obvious answers for - what is so great about dynamic typing in Python? - probably require more understanding of Python to understand.)

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!