Python Tips for C# Developers
1. Introduction
Learning Python can be challenging for C# developers due to the significant differences between the two languages’ syntax and philosophy. Let’s review a few practical cases which C# developers will likely encounter when starting with Python.
2. LINQ
Most .NET developers would agree that LINQ is one of C#‘s best features. With LINQ, we can write functions in an intuitive, fluent and readable way.
Python doesn’t have an exact analog of LINQ. However, we can get something similar using some of Python’s built-in functions. Let’s implement two of the most commonly used LINQ methods in Python.
2.1 Where
Let’s write some C# code which uses the LINQ Where method. The following method filters an enumerable of integers to include only those greater than 100:
public IEnumerable<int> FilterGreaterThanHundred(IEnumerable<int> numbers) { return numbers.Where(n => n > 100); }
To implement the same method in Python, we can use the built-in function called filter. This function requires two parameters, a predicate and an iterable.
We can use a Python lambda function to define the predicate. The predicate should essentially be equivalent to our C# predicate in the previous code block: n => n > 100. The iterable will simply be the list of numbers we want to filter. Here is a Python function which does what we’ve described:
#numbers is a list of integers def filter_greater_than_hundred(numbers): return filter(lambda n: n>100, numbers)
2.2 Select
Let’s define a C# method which uses the LINQ Select method. It will return a modified enumerable of integers, with each entry incremented by 1:
public IEnumerable<int> GetIncrementedCopy(IEnumerable<int> numbers) { return numbers.Select(n => n + 1); }
Our Python implementation will be very similar to the previous example. Instead of filter, we will use a different built-in function called map. The map function also takes a function and an iterable as parameters.
Unlike filter, The function parameter for map need not be a predicate. The function will be return the incremented version of each element in the iterable parameter. It should be equivalent to the C# expression we used above: n => n + 1. Here is the our Python equivalent of the C# example:
#numbers is a list of integers def get_incremented_copy(numbers): return map(lambda n: n+1, numbers)
3. Private Members
Python doesn’t support private members for classes. However, there is a widely accepted naming convention among Python developers for indicating which class members which should be considered private.
To indicate a private member for a class, simply prefix the name with an underscore. Technically, this won’t stop external code from modifying the class’s private members. We must rely on the prudence of other developers not to abuse the class’s private members. If they do so, it is at their own risk.
Let’s define a C# class which heavily relies on a private member:
public class Ninja { private int numberOfShuriken; public Ninja() { numberOfShuriken = 3; } public void ThrowShuriken() { if (numberOfShuriken > 0) { Console.WriteLine("A shuriken was thrown"); numberOfShuriken--; } else { Console.WriteLine("No shurikens left"); } } }
It’s important that the numberOfShuriken member is private, because we don’t want external code to arbitrarily increase or decrease it. The ninja’s shuriken count should only change when he throws them.
The equivalent Python implementation will look similar to the C# version. However, there is a key difference. Python does not let you mark class members as private. The best we can do is to prefix the class member with an underscore.
class Ninja: def __init__(self): # _numberOfShuriken is a private variable is thus prefixed with _ self._numberOfShuriken = 3 def throw_shuriken(self): if self._numberOfShuriken > 0: print("A shuriken was thrown") self._numberOfShuriken -= 1 else: print("No shurikens left")
4. Packages and Namespaces
If a .NET application depends on an external library, it must contain a reference to that library’s dll file. In Python, these external libraries are referred to as modules. Python modules are defined within Python files.
When building a .NET app, references to external libraries are added to the project rather than to the individual C# files. However, an individual Python file must reference the module it requires.
For example, if we want to use the machine learning library PyTorch, we would insert the following line at the top of our Python file.
import torch
We would now like to use the manual_seed function from the torch module. If we simply prefix the function’s name with the module’s name, we will be able to use it:
torch.manual_seed(1)
However, this is unnecessarily verbose. We don’t want to prefix the module name every time we use the manual_seed function. When using .NET, we would normally solve this by adding a namespace declaration at the beginning of the file.
We can do something similar when using Python. We can import the manual_seed function into the parent module’s namespace by adding the following line:
from torch import manual_seed
We can now call the manual_seed function without having to prefix it with the module name:
manual_seed(1)