Python vs C# - a detailed comparison

Coders nowadays have quite a wide array of tools and languages to choose from when faced with the task of writing an application. Not all programming languages are made equal, though. In this article we learn about two specimens from two seemingly different worlds - Python and C#.

Python vs C# - a detailed comparison
Contributor Photo - Krzysztof Miczkowski

Krzysztof Miczkowski

.NET developer

Python vs C# - high level comparison

Tl;dr: both are cool 😎

But if such a laconic statement doesn’t satisfy you, I am happy to report that C# is a general-purpose high-level statically-typed programming language and Python is a general-purpose high-level dynamically-typed* programming language.

Sounds more or less similar but some caveats have popped up - that’s because in both cases your mileage may vary. If you need to write low level pointer arithmetic software in C#, nothing is stopping you. Similarly, if you would like type checks in Python, you can have them as of version 3.6. All in all, the way you write your code comes down to personal preference, or the team’s coding standards.

Differences between C# and Python

Were the two programming languages the same, this comparison would be very brief indeed. Let’s explore some of their quirks that a keen observer can use to tell them apart.

Hello world!

A mandatory and customary point in every such comparison - how do you print a piece of text data to the console? Let’s have a look at the code you would usually write to achieve that.

In Python:

print("Hello world!")

And in C#:

using System;

namespace MyApp
{
    public static class App
    {
        public static void Main()
        {
            Console.WriteLine("Hello world!");
        }
    }
}

Character-wise you can immediately see that Python’s software is about brevity - no curly braces, no semicolons, no classes, namespaces, all that fluff, only the thing that counts - greeting the world like a polite person would.

But those two pieces of code are not equal at all! In C# we’re declaring a namespace, a class, a method and then calling our print function. That’s a lot of boilerplate for a simple hello world app, do we really need it?

No, we do not. The following is a valid C# script:

Console.WriteLine("Hello world!");

The programming language is the same but most of the code has vanished. We can achieve that by not doing the usual thing™ and putting the code through a regular compiler but instead using the C# interactive compiler or dotnet-script tool, although, to be fair, the namespace, class and Main() method are still defined, just not explicitly by us, which I believe to be the point of it all anyway.

developers

Whitespaces

Python uses semantic whitespace to delimit blocks of code as seen in the snippet below:

text = input()
for c in text:
    if c == " ":
        print("Found space!")

If we were to reduce the indentation level of the print function call, the interpreter will refuse to work:

File ".\whitespaces\whitespace.py", line 4
  print("Found space!")
      ^
IndentationError: expected an indented block

On the other hand, C# doesn’t give a damn about indentation and while the following is a well-formatted snippet according to the coding conventions,

string text = Console.ReadLine();
foreach (char c in text)
{
    if (c == ' ')
    {
        Console.WriteLine("Found space!");
    }
}

the alternative is also valid, though painful to behold:

string text=Console.ReadLine();
foreach(char c in text)
{
if(c==' ')
{
Console.WriteLine("Found space!");
}
}

As a bonus fact, it is possible to omit the curly braces if the loop or if-statement body consists of a single statement, allowing for a quite Python-y C# snippet:

string text = Console.ReadLine();
foreach (char c in text)
    if (c == ' ')
        Console.WriteLine("Found space!");

On the other hand, it might be worth typing these anyway. And while I do admit to being notorious for breaking this rule for the sake of brevity, adding those few lines is not a big price to pay for the correctness of your code and the reliability of your software.

Typing

It was said in our introduction to these two programming languages, C# is statically typed, Python is dynamically typed. And this holds true most of the time, as seen here:

greeting = "Hello, world"
greeting = 100

Completely fine.

string greeting = "Hello world";
greeting = 100;

Causes a compiler error:

Cannot implicitly convert type 'int' to 'string' [static.csx].

You can also write the following:

dynamic greeting = "Hello world";
greeting = 100;

Which will do as advertised - dynamically change the runtime type with no errors.

As well as this:

def print_greeting(g: str) -> None:
    print(g)

greeting = "Hello world"
print_greeting(greeting)
greeting = 100
print_greeting(greeting)

Which will… also run just like any correct Python code would but with some help from mypy, or a checker bundled with an IDE like PyCharm, you can get some type errors! More details can be found in this article.

You might be also interested in the article:

Speeding up e-commerce content moderation with Machine Learning based on Amazon Web Services

Speeding up e-commerce content moderation with Machine Learning based on Amazon Web Services

Execution model

With C# being a compiled language and Python an interpreted one there doesn’t seem to be much room for a comparison. They are just different, right?

Taking a closer look, we learn they are not that far apart. In both cases we begin with source code in the form of a text file (sometimes more than one). The C# compiler, when fed such files, emits .exe or .dll files which contain IL - intermediate language understood by the .NET runtime.

IL can be generated from any .NET compliant language - C#, F#, Visual C++, VB.NET and many more. These files can be executed using the runtime wich uses a JIT compiler to create native machine code as it runs the IL for the first time. Any subsequent call to the code that has been JITed will use the native instructions instead.

On the other hand, Python interpreter turns the source scripts into Python VM bytecode which is then executed, skipping the whole JIT business. That is, unless you are using a different Python implementation - PyPy - which comes with its own JIT compiler which helps t achieve better performance, particularly for long-running, pure Python (i.e. not calling C/C++ code) programs.

In Microsoft’s .NET framework world, it is also worth mentioning that there is a project called CoreRT which aims to enable the creation of standalone, native binaries out of .NET code. It is in an early stage of development but if it succeeds, the world of framework-free, compact, high-performance applications will open for the .NET devs.

“is” keyword and == operator

False friends, you might call them, because both exist in either programming language but have different meanings, depending on the context.

In C#, the is keyword can be used to check whether a variable is of a certain type, whereas in the object oriented Python it is for checking if two variables refer to the same object. To check the latter in C# you would use the == operator but for reference types only - if used with built-in value types, it will check value equality.

This behavior can be changed by overriding the operator for a type like it has been done for string, a reference type, where comparison usng == checks the actual values.

This is also the case for this operator in Python - you need to override the __eq__ method in a class for the comparison to work. It is worth noting that in Python you can override __eq__ and leave __ne__ as is and it will be just fine, while in C# it is a compile-time error to override just one of the == and != operators.

I feel I should mention that there is more to equality in C# than described here but it is also heavily tied to CLR internals and as such is well beyond the scope of this article.

Similarities between languages

A great number of concepts and features sets the two programming languages apart, though there are a few striking similarities between the two. Besides classes, ifs, and fors, which can be found in any language that supports object-oriented, imperative programming, there are some more unique features to be found in the two languages in question.

async/await

As of C# 5 and Python 3.5, both programming languages support async/await keywords which help make asynchronicity and concurrency in code more manageable. Here’s how the two languages compare:

import random
import asyncio async def greet_after(who, when):
    await asyncio.sleep(when)
    print(f"Hello, {who}")


async def main():
    tasks = [asyncio.create_task(greet_after(x, random.random())) for x in ["John", "Jill", "Jane", "Jake"]]
    await asyncio.wait(tasks)

asyncio.run(main())

And for C#:

async Task GreetAfter(string who, int when)
{
    await Task.Delay(when);
    Console.WriteLine($"Hello, {who}");
}

var random = new Random();
var taks = new[] { "John", "Jill", "Jane", "Jake" }.Select(x => GreetAfter(x, random.Next(0, 999)));
await Task.WhenAll(taks);

Those two snippets greet the people from the list in parallel but will wait a random amount of time before each name. If these two snippets got any more alike, would we even consider them written in separate languages? Probably not, given that C# is quite adamant about its semicolons.

Tuples

Tuples are lightweight data objects often used to return multiple values from a function without having to wrap them in an aggregating record, like a class. Comparing tuples in the two languages, they can be near identical syntactically (unless you omit the braces), with some differences in how they behave.

In Python they are simple collections (like a list) which are immutable (unlike a list), while in C# ValueTuples are increasingly more versatile objects with some dedicated syntax to create them out of other objects.

Here’s some basic Python code:

# cannot have named fields by default - use namedtuple for that
tup = ("this", "is", "a", "tuple")

for t in tup: # can iterate over items
    print(t)

# t[0] = "that" # error - Python tuples are immutable

def makeTuple():
    return "The answer is", 42

t = makeTuple()
print(t) # prints ('The answer is', 42)

t1, t2 = makeTuple() # tuple deconstruction
print(t1, t2) # prints The answer is 42

print(t + t)

And here’s some marginally less basic C#:

// C# tuples can have named items
var tup = ("this", "is", "a", literallyTuple: "tuple");
// Named items can still be accessed by the .ItemX property
Console.WriteLine($"{tup.Item4} is the same as {tup.literallyTuple}");

// foreach(var item in tup) // error - tuples are not iterable
// {
//     Console.WriteLine(item);
// }

tup.Item1 = "that"; // values can be reassigned

(string, int) MakeTuple() => ("The answer is", 42);

var t = MakeTuple();
Console.WriteLine(t); // prints (The answer is, 42)
var (t1, t2) = MakeTuple(); // tuple deconstruction
Console.WriteLine($"{t1} {t2}"); // prints The answer is 42

class TheAnswer
{
    public int Answer { get; set; }
    public string Description { get; set; }

    // classes can have deconstructors (not to be confused with finalizers)
    // you can have as many as you like, out parameters define the resulting tuple
    public void Deconstruct(out int answer, out string description)
    {
        answer = Answer;
        description = Description;
    }
}

var theAnswer = new TheAnswer
{
    Answer = 42,
    Description = "The ultimate answer to life, universe and everything"
};

var (a, d) = theAnswer;
Console.WriteLine($"{d} is {a}.");
// unsurprisingly prints The ultimate answer to life, universe and everything is 42.

This feature is available from C# 7 and is separate from generic Tuple<>. If you see a compiler error along the lines of Predefined type ‘System.ValueTuple`2’ is not defined or imported, make sure that you have referenced the System.ValueTuple NuGget package.

Ranges and indices

As of C# 8, the language supports two new types - Range and Index. They were inspired by a similar syntax in languages like Swift, Perl and, obviously, Python, and their purpose is to simplify any code that operates on collection subsets. In Python, one would write code similar to the following:

myList = [n for n in range(0, 10)]
print("Whole:", myList)
# [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
print("From index 2 onward:", myList[2:])
# [2, 3, 4, 5, 6, 7, 8, 9]
print("From index 2 onward, skip every other:", myList[2::2])
# [2, 4, 6, 8]
print("From index 2 to 4:", myList[2:4])
# [2, 3]
print("From index 1 to second from the end:", myList[1:-1])
# [1, 2, 3, 4, 5, 6, 7, 8]
print("Last: ", myList[-1])

Whereas in C# the syntax is near identical, although does not support declaring ranges which skip a number of elements. Moreover, at the time of writing this article, only Arrays and Spans can make use of this feature, but it may change before the final release.

var myArray = Enumerable.Range(0, 10).ToArray();
PrintArray(myArray, "Whole");
// [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
PrintArray(myArray[2..], "From index 2 onward");
// [2, 3, 4, 5, 6, 7, 8, 9]
// PrintList(myArray[2..^0:2], "This does not work");
PrintArray(myArray[2..4], "From index 2 to 4");
// [2, 3]
PrintArray(myArray[1..^1], "From index 1 to second from the end");
// [2, 3, 4, 5, 6, 7, 8]
Console.WriteLine($"Last: {myArray[^1]}");
// 9

static void PrintArray<T>(IEnumerable<T> list, string description) =>
    Console.WriteLine($"{description}: [{string.Join(", ", list)}]");

Open source? Cross-platform?

This is not really a language feature but it is well worth mentioning that both Python and C# are fully open source, which, for the former, is not a new affair but quite so for the latter. With the advent of .NET Core, not only can we have a sneak peek at what is going on under the hood of the compiler, but the runtime and BCL as well.

You can also run the code written in both programming languages on all major platforms like Windows, Linux, MacOS, Android and iOS. More on that later.

Developers

Frameworks and applications

In the following section, we will learn about a number of different scenarios where both languages can be applied and see how well-suited they are for a given task.

Cross platform apps

WPF, WinForms, UWP - all of these come from the .NET framework world and can be used to write Windows desktop apps. With the addition of projects like AvaloniaUI and the upcoming Xamarin.Forms support for desktop scenarios, a C# developer is able to target any major desktop platform out there.

For Python devs, there’s Qt (or PySide with a more permissive license), Kivy, BeeWare, PyGTK/PyGObject, and TkInter, the latter being bundled with the default Python installation. All of them allow the making of cross-platform desktop apps, so if you know Python and need to write such an app, you are covered.

Mobile apps

Xamarin, Kivy and BeeWare all serve as mobile frameworks as well as desktop ones. We could mention UWP as well but given that Microsoft’s Windows 10 Mobile was discontinued, it is safe to ignore it for mobile scenarios. If you need your app to run on Android or iOS, you have some choice as a Python developer, less so in C#, but both options remain viable.

Web

You could fill whole libraries with articles comparing various web frameworks with one another, so for the sake of being DRY, let’s just say that for any developer coding in any language, options do exist to make a web application. Just looking at the TechEmpower Fortunes benchmark can reveal that there are both C# and Python based tools readily available.

You can run the following JavaScript snippet in the browser’s console to filter out all the languages unrelated to the topic of this article:

var rows = document.querySelectorAll("div#bar-fortune table tr")
for(r of rows) r.style.display = r.children[6].innerText != "Py" ? "none" : "initial"; // or “C#”

Flask, Bottle, Django, Pyramid, to name just a few - all of them will help make a Python web app. NancyFX and ASP.NET, or in particular - ASP.NET Core, are there for you if you would rather use C# and might even be a better choice if performance is of critical priority as ASP.NET Core made it to the top 10 of the benchmark.

Serverless

As a bonus, it is worth mentioning that both AWS Lambda and Azure Functions support either language to write serverless code, with Azure’s support for Python being experimental. Google’s Cloud Functions can be written in Python but .NET is not supported.

Gamedev

As a gamer, I sometimes find myself wondering what programming language and frameworks were used in the making of the games I play. As it turns out, one of my all-time favourites, Mount and Blade, was written in Python!

PyGame or Panda is what you should become friends with if you are looking out to create game software using that language. On the other hand, C# can be used with MonoGame (an open source implementation of Microsoft’s XNA Framework), Unity3D, Xenko, or even CryEngine.

Machine Learning

This is the part of the programming world where Python reigns supreme. Numpy, SciPy, TensorFlow, PyTorch, Apache Spark, Keras, the choice of software available is staggering for a Python dev. With a metric ton of courses and tutorials available both free and paid on the web, it is arguably THE language for your ML needs.

As for C#, there’s a new kid on the block, ML.NET, as well as some older libraries like Accord.Net and bindings for TensorFlow. Microsoft’s Cognitive Toolkit (CNTK) also supports C# for both evaluation and training (since v2.2) but at the same time has a more polished Python api.

Conclusion

It is hard to decide which is top, really. Both languages have free, mature tooling, a plethora of frameworks and libraries, and active communities. They are also flexible enough to fit any task a programmer can come up against, and are truly cross platform, running on desktops, phones, in the cloud, and even IoT devices.

One is the king of Machine Learning, the other shines on the servers, and even, if you are feeling adventurous, in browsers via WebAssembly. Personally, I would not give up C#’s type system for Python’s concise syntax, but hey, that’s just me and as it was stated in the beginning - your mileage may vary.

Just use the tool that you feel is right for the task, and you should be good.

Contact:

Let's talk about
your business