Programming Practices That Make Python More Like an Object Oriented Programming Language

This post was first published at Medium

Photo by Nicole De Khors from Burst
As a programmer who has been using Unity to develop games for a long time, the programming language I use is C#. As you know, C# is a strongly typed language and is usually programmed using an object-oriented paradigm (of course, it has other paradigms). Recently, I started to use Python for programming. It has been 5-6 years since I last used Python, and at that time I used Python 2.7. Therefore, I had the impression that Python is a process-oriented dynamic programming language without types.

Of course, now I know I am wrong. And I found that the following practices can make programmers like me using C# also very accustomed to using Python.

Type Annotation & Type Comments

My first attempt was to make Python a strongly typed language, at least to make it look like a strongly typed language. The good news is, since Python3.5, Python introduced type annotation through PEP 484.

For example, here is a simple function whose argument and return type are declared in the annotations:

def greeting(name: str) -> str:
    return 'Hello ' + name

As you can see in the example above, type annotation is accomplished by adding : <type> after a variable. By the way, if the function does not return, the return type should be None.

def retry(url: Url, retry_count: int) -> None: ...

But what if the type is more complex? For example, the type is a list whose elements are int. The typing module since Python 3.5 can be used for this purpose. This module provides runtime support for type hints as specified by PEP 484, PEP 526, PEP 544, PEP 586, PEP 589, and PEP 591. The most fundamental support consists of the types Any, Union, Tuple, Callable, TypeVar, and Generic.

With the help of typing, you can add type annotation like this:

from typing import List

def scale(scalar: float, vector: List[float]) -> List[float]:
    return [scalar * num for num in vector]

However, PEP 484 main focus was function annotations, a syntax for typing variables was added in Python 3.6 through PEP 526. There are two variants of the syntax, with or without an initializer expression:

from typing import Optional
foo: Optional[int]  # No initializer
bar: List[str] = []  # Initializer

Another way to add type hint is type comment, a comment-based annotation syntax for Python 2 code. There are some examples:

x = 1  # type: int
x = 1.0  # type: float
x = True  # type: bool
x = "test"  # type: str
x = u"test"  # type: unicode

Type comment is accomplished by adding # type: after a variable.

Interface/Abstract Class

The Python standard library abc (Abstract Base Class) module is often used to define and verify interfaces. It defines a metaclass ABCMeta and decorators @abstractmethod and @abstractproperty.

    from abc import ABCMeta, abstractmethod
    class AbstractStrategy(metaclass=ABCMeta):    
        @abstractmethod    
        def algorithm_interface(self):        
            pass
    class ConcreteStrategyA(AbstractStrategy):    
        def algorithm_interface(self):        
            print("ConcreteStrategyA")

And another alternative is to use the interface library. It is a library for declaring interfaces and for statically asserting that classes implement those interfaces.

    from interface import implements, Interface
    class MyInterface(Interface):
        def method1(self, x):
            pass
        def method2(self, x, y):
            pass
    class MyClass(implements(MyInterface)):
        def method1(self, x):
            return x * 2
        def method2(self, x, y):
            return x + y

Static Methods

You can use the @staticmethod decorator to define a static function in a class. A static method does not receive an implicit first argument(self). And there is an example:

    class C:
        @staticmethod
        def f(arg1, arg2, ...): ...

Now the static method f() can be called either on the class (such as C.f()) or on an instance (such as C().f()).

Generics

By using the typing module mentioned above, you can use generics in Python. The typing module provides several generic collections, such as List, Mapping, Dict. They are the generic version of list, Mapping, dict respectively.

    T = TypeVar('T', int, float)
    def vec2(x: T, y: T) -> List[T]:
        return [x, y]
    def keep_positives(vector: Sequence[T]) -> List[T]:
        return [item for item in vector if item > 0]
    def get_position_in_index(word_list: Mapping[str, int], word: str) -> int:
        return word_list[word]
    def count_words(text: str) -> Dict[str, int]:
        ...

A user-defined class can be defined as a generic class, too. The class only needs to inherit Generic. Generic is an abstract base class for generic types. For example, a generic user-defined class might be defined as:

    from typing import TypeVar, Generic
    from logging import Logger
    T = TypeVar('T')
    class LoggedVar(Generic[T]):
        def __init__(self, value: T, name: str, logger: Logger) -> None:
            self.name = name
            self.logger = logger
            self.value = value

The class LoggedVar takes a single type parameter T .

The LoggedVar class can then be used as follows:

    T = TypeVar('T')
    def foo(logged: LoggedVar[T]) -> None:

Well, through the above programming practices, I, a C# programmer, can use a familiar way to program by using the Python language, Yeah!

Thanks for reading, I hope this article is useful!


Subscribe To Jiadong Chen's Blog

Avatar
Jiadong Chen
Cloud Architect/Senior Developer

Cloud Architect at Company-X | Microsoft MVP, MCT | Azure Certified Solutions Architect & Cybersecurity Architect Expert | Member of .NET Foundation | Packt Author ㅣ Opinions = my own.

comments powered by Disqus