Python common tips

Modules

Note that from myModule import * shouldn’t be used. If done, all the global variables defined in myModule will appear in the current namespace. Modulus should not pollute each other.

Lists and npArrays

1- Lists are (much) faster than npArrays when accessing or appending elements. However, npArrays are faster when doing linear algebra operations. It is more efficient to manipulate data in the form of lists and then convert them into npArrays when dealing with large data.

Set

1- A set of sets: frozenset()

listOfSets1 = [{5},{1,2},{3,4},{5,7,9}]
listOfSets2 = [{11},{1,2},{12,16},{5,7,9},{7,23,13}]
setOfSets1 = {frozenset(eachSet) for eachSet in listOfSets1}
setOfSets2 = {frozenset(eachSet) for eachSet in listOfSets2}
setOfSets1.intersection(setOfSets2)
Out[0]: {frozenset({5, 7, 9}), frozenset({1, 2})}

Functions

1- Positional vs keyword arguments

A function receives its arguments either in the form of positional or keyword arguments where order matters for positional arguments and argument’s name matters for keyword arguments. A keyword argument is recognized and used by its keyword/name in a function call. Examples are as follows.

def myFunction (arg1, arg2): # (position 1, position 2)
  # do something
# Equivalent function calls
myFunction (value1forArg1, value2forArg2) # position matters
myFunction (arg1 = value1forArg1, arg2 = value2forArg2)
myFunction (value1forArg1, arg2 = value2forArg2)
myFunction (arg2 = value2forArg2, arg1 = value1forArg1) # position does not matter

Keyword-only arguments

Some or all of the arguments of a function can be defined and forced to be keyword arguments; so, the function call is restricted to only use those arguments in the form of keyword arguments. To this ends, an * is typed in the function definition before the arguments to be as keyword arguments. In the following example, arg1 and arg2 are positional and argA, argB and argC are keyword-only arguments.

def myFunction (arg1, arg2, *, argA, argB, argC):
  # do something
# a function call
myFunction (valueForArg1, valueForArg2, argB = someVal1, argC = someVal2, argA = someVal3)

Note that keyword-only arguments only accept the keyword form of input. A function with all its arguments as keyword-only arguments is as follows.

def myFunction (*,argA, argB, argC, argD, argE):
  # do something

Positional-only arguments

To have all of the arguments of a function defined as positional, we use / in the function definition. For example

def myFunction (arg1, arg2, arg3, /):
  # do something

None of the arguments can be passed in the form of a keyword argument.

2- Type hinting

def myFunction(text: str, flag: bool, name: str = "Aristotle") -> str:
  pass

Operators

1- The results of different operators on objects. Objects (e.g. lists, dictionaries, other class objects, etc.) with the same value are usually stored at separate memory addresses.

L1 = [1,2,3]
L2 = [1,2,3]
print(L1==L2)
print(L1 is L2)
print(L1 is not L2)
L1 = [1,2,3]
L2 = [6,7,8]
print(L1 != L2)
print(L1 is not L2)
# output
True
False
True
True
True

Useful Functions

zip() pair up iteratable and gives and iterator (https://realpython.com/python-zip-function/)

Prefix Operators * and **

1- Asterisks for unpacking into function call
The * operator can be used to unpack an iterable into the arguments in the function call.
The ** operator takes a dictionary of key-value pairs and unpack it into keyword arguments (or positional arguments but by their names) in a function call.

Example:

letters = ['a', 'b', 'c']
numbers = [2, 1, 3, 4, 7]
print(*numbers, *fruits)

# merging dictionaries
first_dict = {"A": 1, "B": 2}
second_dict = {"C": 3, "D": 4}
merged_dict = {**first_dict, **second_dict}

# unpacking into positional arguments by their names
def test_args_kwargs(name, surname, city):
    print(name + ' ' + surname + ' from ' + city)
info = {'name': 'J', 'surname': 'K', 'city': 'L'}
test_args_kwargs(**info)

2- Asterisks for packing arguments given to function
Arbitrary positional arguments: when defining a function, we can use the * operator to capture an unlimited number of positional arguments given to the function. These arguments are captured into a tuple; so the function accepts a sequence of any number of (positional) arguments.

def f(*names):
  for name in names: print name
f('a', 'bb', 'ddh')

Arbitrary keyword arguments: the operator ** is used when defining a function to capture any number of keyword arguments. The keyword arguments will be captured into a dictionary.

def displayWord(word, **attributes):
    if 'color' in attributes:
        print(word + ' is ' + attributes['color'])
    if 'font' in attributes:
        print(word + ' is written in ' + attributes['font'])
displayWord('Jam', color='red', font='Times')

Note: Order of using formal args, *args and **kwargs is def funcName(args, *args, **kwargs)

I/O

Save an array to a text file.
Method 1: use np.savetxt()