While significant whitespace is the most obvious difference between Python and C#/VB, the biggest difference between them lies in the type system. Python shares many high-level concepts with these .NET languages, such as imperative code, functions, classes, and objects. However, C# and VB are statically typed, which means that these languages fix the capabilities of individual types at compile time. After you determine the fields, properties, and methods of your type and run the code through the compiler, those capabilities can never change, ever
. If you need to change them, your only option is to throw away your original compiled binary and recompile a new one.
Dynamic typing, on the other hand, does not fix type capabilities at compile time. Rather, types are completely mutable and you can manipulate them at run time in much the same way that you have manipulated type instances in the past. For example, at runtime, you can create new types, add fields or methods to a type, remove fields or methods from a type, add fields or methods to a specific instance of a type, or even change the inheritance hierarchy of a type.
If you come from a static type language background, the idea of being able to manipulate types at run-time may seem strange, or even dangerous. Static advocates typically highlight type safety as one of the primary benefits of using static typing. When using static types, the compiler can do quite a bit of validation to ensure that the types you create and the methods you call all exist, that the fields and parameters are of the right types, etc. We've all had to fix compiler errors because we misspelled a method or type name. But that's not possible in dynamic languages; instead, In Python, such errors become run-time exceptions.
However, I'd like to put this type safety benefit in perspective. In any system of significant scale, there are essentially an infinite number of ways the application can be wrong. Type safe languages eliminate just one
of this infinite number of ways. But that still means that a static language compiler won't catch the overwhelming majority of errors and bugs you might make when developing your system. If it could, then all successfully-compiled applications would automatically be bug free! The fact is, you need some other mechanism to catch these other errors, typically automated unit tests. So while it's true that static types do provide a safety net, it's just not a very big one.
This isn't to say type safe languages are bad. Rather, I look at it as a tradeoff of static type safety vs. dynamic type flexibility. There is no universally right decision—only the right decision for you and your project. The good news is that with the addition of dynamic languages like IronPython to Microsoft's language stable, you can make that tradeoff decision while still staying in the .NET realm.
One somewhat frustrating aspect of C# is the amount of structure that gets forced on the developer. Some of this structure is related to static typing, such as specifying variable types and casting operators. However, there are also no stand-alone functions in C#, much less just a bunch of code in a simple script. All functions have to be attached to an object, meaning the simplest possible implementation of "Hello, World!" in C# looks like this:
static void Main()
Contrast that with the Python version:
print "Hello, World"
Obviously, you don't judge a language completely by its implementation of "Hello, World," but the real point is that Python scales well with complexity. If you need only a simple script, you don't have to add unneeded class and function constructs for the code to live inside. If you want to make a function, it can stand alone; you don't need to find a class to attach it to (such as the bubble_sort
example above). If you really need
classes and objects, you can build them, but you don't have to take on that complexity unless you really need it.
Polymorphism—the ability to treat different types the same way—is a staple of object-oriented programming. Statically typed languages have a variety of mechanisms designed to enable polymorphism, including inheritance, interfaces and generics. Because Python is dynamically typed, it doesn't need any of these mechanisms to provide polymorphism. (Python does support inheritance, but not to enable polymorphism.) Instead, Python determines type compatibility at run time based on the type's capabilities rather than on the declared types (a process commonly known as "duck typing"). This feature enables code that's more flexible and reusable in a wider variety of situations.
To see this in action, revisit the bubble_sort
function shown earlier. It takes a single parameter, ar
, which represents the collection to sort. A closer look shows that there the object instance ar
must have three capabilities to be compatible with the bubble_sort
- You must be able to ask the ar object for its length.
- You must be able to get items in the ar object by numeric index.
- You must be able to set items in the ar object by numeric index.
To write the equivalent bubble sort method in C#, you would have had to declare the ar
parameter as a specific type—either an interface or a base class—that implements the capabilities listed above. The C# compiler would validate that any code that called bubble sort would pass a parameter that implements that parameter type. Passing any other type as a parameter would result in a compile error, even if the type in question implemented all the required capabilities
Because Python does all the type checking at run time, so if the type instance passed in as the ar
parameter is missing an implementation of one of the three methods listed above, Python will throw a TypeError when it executes the bubble sort code. For the bubble sort code, the three required methods have special names: __len__
returns the length of the collection, while __getitem__
get and set values by index, respectively. Python defines quite a few special names useful in a variety of circumstances.
Here's an example of a custom linked list class that implements these methods and is thus compatible with the bubble sort code above.
self.data = value
self.next = None
self.head = None
def insert(self, value):
n = linked_list.node(value)
n.next = self.head
self.head = n
cur = self.head
while cur != None:
cur = cur.next
count = 0
for n in self:
count += 1
def find_node(self, key):
cur = self.head
for x in range(key):
cur = cur.next
def __getitem__(self, key):
def __setitem__(self, key, value):
self.find_node(key).data = value
Of course, linked lists don't usually provide indexed access to their members. But the point is that because this
custom class implements __len__
, you can pass an instance of this class to the bubble_sort
function without any changes at all.