Good languages stop you from writing stupid codes; bad languages allow you to write more stupid codes. Do not live on ancient code until death!

Even though I think Python is slow, it does not mean that I cannot learn it or learn from it. Now it seems more and more obvious to me that there are some neat advanced tricks you can do in Python.

A perfect intermediate python course: Python for Scientific Computing — Python for Scientific Computing documentation It is not about the language itself, but more about how to use it efficiently as a researcher.

Ok, now we move on to talk about some cool stuffs and tricks in Python.

Function arguements

The flexibility of Python can be reflected from the fact how function arguments work. You are allowed to mix position args, keyword args, and varargs all together. Check this video for more!

Decorators

This is really cool stuff. In Julia they are called macros, but essentially the same thing. In computer science, they belong to the category of metaprogramming. Decorator allows you to modify the raw code before the interpreter comes in to “decorate” your code. This applies to, for instance, the implementation of memoization, dataclass after Python 3.7, logging wrapper and many more. I have also seen this in ParaViews’ Python interface.

To be a master in Python, you have to use it elegantly.

@property
@total_ordering
@dataclass

Type hint

After Python 3.5, you can now add type hints to function arguments. This will help you guarantee that the correct argument types have been passed.

我惊讶地发现,Python从3.5版本以后也支持函数参数的类型指定了。看来MATLAB和Python也都在逐步改进啊。但是这个只是用来做标记的,实际运行的时候不会检查;所以我们需要一个static type checker。在3.8版本以后有专门的原生库支持这项功能。

Virtual Environments

It is possible to handle your customized Python packages with Virtual Environment. The basic workflow is:

  • Find a specific Python version
  • Create a directory for your packages, e.g python352
  • virtualenv python352 to start the virtual environment
  • source bin/activate to activate the virtual environment
  • module load Python/3.8.6-GCCcore-10.2.0
  • pip install --prefix python352 mypackage to install the required packages

To leave virtual environments, just say deactivate.

module load Python/3.8.2-GCCcore-9.3.0
virtualenv ~/proj/virtual_python3.8.2/
source /home/hongyang/proj/virtual_python3.8.2/bin/activate

However, see [Conda][#miniconda] for a better approach.

Miniconda

I found conda, or miniconda a more reliable way to handle packages. Even miniconda is pretty large though (claimed to be 300 MB when first installed, but quickly became 3GB+). Miniconda comes with its own Python version and pip tool.

Scripts VS Methods

For a script, alway add

If __name__ == '__main__':
    main()

to the end! This is a good practice

  • To tell users that this is a script that can actually run, but not a library
  • To avoid accidental global variables
  • To make your script runs faster (because it’s inside a function)

fstrings

This is introduced after 3.6, which is a new way to handle string outputs.

name = "Eric"
age = 74
print(f"Hello, {name}. You are {age}.")
print(f"{2 * 37}")
print(f"{name.lower()} is funny.")

# Multiline f-Strings
name = "Eric"
profession = "comedian"
affiliation = "Monty Python"
message_oneline = (
    f"Hi {name}. "
    f"You are a {profession}. "
    f"You were in {affiliation}."
)

message_multiline = f"""
    Hi {name}. 
    You are a {profession}. 
    You were in {affiliation}.
"""

DataClasses

Wow, this is a great alternative in many situations to the traditional classes after 3.7! The main advantage is to avoid boilerplate codes.

from dataclasses import dataclass

@dataclass
class Person:
    name: str
    address: str
    active: bool = True
    email_addresses: list[str] = field(default_factory=list)

def main() -> None:
    person = Person(name="John, address="123 Main St")
    print(person)

Bonus tip: Python class properties can be read-only by making them immutable.

from dataclasses import dataclass

@dataclass(frozen=True)
class Person:
    name: str
    address: str

Special Numbers

If I remember correctly, the integers -5-256 are treated differently (hard-coded, i.e. always refer to the same constants):

a = 2
b = 2
a is b # true
c = 257
d = 257
c is d # false

Note that this is NOT the case in Julia. (Check with ===.)