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.

You might be also interested in the article:

What exactly can you do with Python?

What exactly can you do with Python?

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.

>>> Need custom software solution? Explore our agile-powered software services.

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.

>>> See how scrum can boost your software development projects

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.

You might be also interested in the article:

Digitalizing renewable energy

Digitalizing renewable energy

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.

>>> Interested in hiring a software development team? See our outsourcing guide!

“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.