Python tips

Jul 25, 2020 00:00 · 2186 words · 11 minute read Python

A few Python3 programming tips that help you being more professional and efficient.

1.Common tips

  • A quick “rule of thumb” is that all “empty” values are considered false, so 0, None, [], {}, ''all evaluate as false in a boolean context.
  • Use TODO comments for code that is temporary, a short-term solution, or good-enough but not perfect.
# TODO(kl@gmail.com): Use a "*" here for string repetition.
# TODO(Zeke) Change this to use relations.
  • Function names, variable names, and filenames should be descriptive; eschew abbreviation. In particular, do not use abbreviations that are ambiguous or unfamiliar to readers outside your project, and do not abbreviate by deleting letters within a word. module_name, package_name, ClassName, method_name, ExceptionName, function_name, GLOBAL_CONSTANT_NAME, global_var_name, instance_var_name, function_parameter_name, local_var_name.

  • File naming conventions

2. Strings

Avoid using the + and += operators to accumulate a string within a loop. Since strings are immutable, this creates unnecessary temporary objects and results in quadratic rather than linear running time. Instead, add each substring to a list and ''.join the list after the loop terminates

     x = a + b
     x = '%s, %s!' % (imperative, expletive)
     x = '{}, {}'.format(first, second)
     x = 'name: %s; score: %d' % (name, n)
     x = 'name: {}; score: {}'.format(name, n)
     x = f'name: {name}; score: {n}'  # Python 3.6+

Regular expression:

# \   Used to drop the special meaning of character following it
# []  Represent a character class
# ^   Matches the beginning
# $   Matches the end
# .   Matches any character except newline
# ?   Matches zero or one occurrence.
# |   Means OR (Matches with any of the characters
#     separated by it.
# *   Any number of occurrences (including 0 occurrences)
# +   One or more occurrences
# {}  Indicate number of occurrences of a preceding RE
#     to match.
# ()  Enclose a group of REs
# \d   Matches any decimal digit, this is equivalent to the set class [0-9].
# \D   Matches any non-digit character.
# \s   Matches any whitespace character.
# \S   Matches any non-whitespace character
# \w   Matches any alphanumeric character, this is
#      equivalent to the class [a-zA-Z0-9_].
# \W   Matches any non-alphanumeric character.
# Module Regular Expression is imported using __import__().
import re

# compile() creates regular expression character class [a-e],
# which is equivalent to [abcde].
# class [abcde] will match with string with 'a', 'b', 'c', 'd', 'e'.
p = re.compile('[a-e]')

# findall() searches for the Regular Expression and return a list upon finding
print(p.findall("Aye, said Mr. Gibenson Stark"))
#['e', 'a', 'd', 'b', 'e', 'a']


import re

# \d is equivalent to [0-9].
p = re.compile('\d')
print(p.findall("I went to him at 11 A.M. on 4th July 1886"))

# \d+ will match a group on [0-9], group of one or greater size
p = re.compile('\d+')
print(p.findall("I went to him at 11 A.M. on 4th July 1886"))
# ['1', '1', '4', '1', '8', '8', '6']
# ['11', '4', '1886']

import re

# Regular Expression pattern 'ub' matches the string at "Subject" and "Uber".
# As the CASE has been ignored, using Flag, 'ub' should match twice with the string
# Upon matching, 'ub' is replaced by '~*' in "Subject", and in "Uber", 'Ub' is replaced.
print(re.sub('ub', '~*' , 'Subject has Uber booked already', flags = re.IGNORECASE))

# Consider the Case Sensitivity, 'Ub' in "Uber", will not be reaplced.
print(re.sub('ub', '~*' , 'Subject has Uber booked already'))

# As count has been given value 1, the maximum times replacement occurs is 1
print(re.sub('ub', '~*' , 'Subject has Uber booked already', count=1, flags = re.IGNORECASE))

# 'r' before the patter denotes RE, \s is for start and end of a String.
print(re.sub(r'\sAND\s', ' & ', 'Baked Beans And Spam', flags=re.IGNORECASE))
# S~*ject has ~*er booked already
# S~*ject has Uber booked already
# S~*ject has Uber booked already
# Baked Beans & Spam

3. Function and method decorators

Decorators are very powerful and useful tool in Python since it allows programmers to modify the behavior of function or class. Decorators allow us to wrap another function in order to extend the behavior of wrapped function, without permanently modifying it.

  • Use decorators judiciously when there is a clear advantage. Avoid @staticmethod and limit use of @classmethod.
  • One common decorator is @property, used for converting ordinary methods into dynamically computed attributes. However, the decorator syntax allows for user-defined decorators as well. Specifically, for some function my_decorator, this:
class C:
    @my_decorator
    def method(self):
        # method body ...

The special syntax *args in function definitions in python is used to pass a variable number of arguments to a function. It is used to pass a non-keyworded, variable-length argument list.

The syntax is to use the symbol * to take in a variable number of arguments; by convention, it is often used with the word args. What *args allows you to do is take in more arguments than the number of formal arguments that you previously defined. With *args, any number of extra arguments can be tacked on to your current formal parameters (including zero extra arguments).

The special syntax **kwargs in function definitions in python is used to pass a keyworded, variable-length argument list. We use the name kwargs with the double star. The reason is because the double star allows us to pass through keyword arguments (and any number of them).

def myFun(**kwargs):  
    for key, value in kwargs.items():
        print ("%s == %s" %(key, value))

# Driver code
myFun(first ='Geeks', mid ='for', last='Geeks')  

The yield statement suspends function’s execution and sends a value back to the caller, but retains enough state to enable function to resume where it is left off. When resumed, the function continues execution immediately after the last yield run. This allows its code to produce a series of values over time, rather than computing them at once and sending them back like a list.

Return sends a specified value back to its caller whereas Yield can produce a sequence of values. We should use yield when we want to iterate over a sequence, but don’t want to store the entire sequence in memory.

Protected members are those members of the class which cannot be accessed outside the class but can be accessed from within the class and it’s subclasses. To accomplish this in Python, just follow the convention by prefixing the name of the member by a single underscore “_”.

Private members are similar to protected members, the difference is that the class members declared private should neither be accessed outside the class nor by any base class. In Python, there is no existence of Private instance variables that cannot be accessed except inside a class. However, to define a private member prefix the member name with double underscore “__”.

4. Comments and docstrings

def fetch_smalltable_rows(table_handle: smalltable.Table,
                          keys: Sequence[Union[bytes, str]],
                          require_all_keys: bool = False,
                         ) -> Mapping[bytes, Tuple[str]]:
    """Fetches rows from a Smalltable.

    Retrieves rows pertaining to the given keys from the Table instance
    represented by table_handle.  String keys will be UTF-8 encoded.

    Args:
        table_handle: An open smalltable.Table instance.
        keys: A sequence of strings representing the key of each table
          row to fetch.  String keys will be UTF-8 encoded.
        require_all_keys: Optional; If require_all_keys is True only
          rows with values set for all keys will be returned.

    Returns:
        A dict mapping keys to the corresponding table row data
        fetched. Each row is represented as a tuple of strings. For
        example:

        {b'Serak': ('Rigel VII', 'Preparer'),
         b'Zim': ('Irk', 'Invader'),
         b'Lrrr': ('Omicron Persei 8', 'Emperor')}

        Returned keys are always bytes.  If a key from the keys argument is
        missing from the dictionary, then that row was not found in the
        table (and require_all_keys must have been False).

    Raises:
        IOError: An error occurred accessing the smalltable.
    """
class SampleClass:
    """Summary of class here.

    Longer class information....
    Longer class information....

    Attributes:
        likes_spam: A boolean indicating if we like SPAM or not.
        eggs: An integer count of the eggs we have laid.
    """

    def __init__(self, likes_spam=False):
        """Inits SampleClass with blah."""
        self.likes_spam = likes_spam
        self.eggs = 0

    def public_method(self):
        """Performs operation blah."""
class SampleClass:
   pass


class OuterClass:

   class InnerClass:
       pass

5. Collections

5.1 Heap

A Heap is a special Tree-based data structure in which the tree is a complete binary tree. Generally, Heaps can be of two types:

  • Max-Heap: In a Max-Heap the key present at the root node must be greatest among the keys present at all of it’s children. The same property must be recursively true for all sub-trees in that Binary Tree.
  • Min-Heap: In a Min-Heap the key present at the root node must be minimum among the keys present at all of it’s children. The same property must be recursively true for all sub-trees in that Binary Tree. heap

Heap data structure is mainly used to represent a priority queue. In Python, it is available using “heapq” module. The property of this data structure in python is that each time the smallest of heap element is popped(min heap). Whenever elements are pushed or popped, heap structure in maintained. The heap[0] element also returns the smallest element each time.

Operations on heap :

  • heapify(iterable):convert the iterable into a heap data structure. i.e. in heap order.
  • heappush(heap, ele):insert the element mentioned in its arguments into heap. The order is adjusted, so as heap structure is maintained.
  • heappop(heap):remove and return the smallest element from heap. The order is adjusted, so as heap structure is maintained.
  • heappushpop(heap, ele):combines the functioning of both push and pop operations in one statement, increasing efficiency. Heap order is maintained after this operation.
  • heapreplace(heap, ele):inserts and pops element in one statement, but it is different from above function. In this, element is first popped, then the element is pushed.i.e, the value larger than the pushed value can be returned. heapreplace() returns the smallest value originally in heap regardless of the pushed element as opposed to heappushpop().
  • nlargest(k, iterable, key = fun): return the k largest elements from the iterable specified and satisfying the key if mentioned.
  • nsmallest(k, iterable, key = fun): return the k smallest elements from the iterable specified and satisfying the key if mentioned.
# Python code to demonstrate working of
# heappushpop() and heapreplce()

# importing "heapq" to implement heap queue
import heapq

# initializing list 1
li1 = [5, 7, 9, 4, 3]

# initializing list 2
li2 = [5, 7, 9, 4, 3]

# using heapify() to convert list into heap
heapq.heapify(li1)
heapq.heapify(li2)

# using heappushpop() to push and pop items simultaneously
# pops 2
print ("The popped item using heappushpop() is : ",end="")
print (heapq.heappushpop(li1, 2))

# using heapreplace() to push and pop items simultaneously
# pops 3
print ("The popped item using heapreplace() is : ",end="")
print (heapq.heapreplace(li2, 2))

5.2 Counter

Counter is a container included in the Python collections module. It is a subclass of dict, providing elements and corresponding numbers.

from collections import Counter

print(Counter(['A', 'A', 'B']))
# {'A': 2, 'B': 1}

# Use update method
coun = Counter()
coun.update(Data)

# Python program to demonstrate that counts in  
# Counter can be 0 and negative
c1 = Counter(A=4,  B=3, C=10)
c2 = Counter(A=10, B=3, C=4)   
c1.subtract(c2)
print(c1)
#Counter({'c': 6, 'B': 0, 'A': -6})

5.3 ChainMap

ChainMap encapsulates many dictionaries into one unit.

# importing collections for ChainMap operations
import collections

# initializing dictionaries
dic1 = { 'a' : 1, 'b' : 2 }
dic2 = { 'b' : 3, 'c' : 4 }
# initializing ChainMap
chain = collections.ChainMap(dic1, dic2)
# printing chainMap using maps
print ("All the ChainMap contents are : ")
print (chain.maps)
# printing keys using keys()
print ("All keys of ChainMap are : ")
print (list(chain.keys()))
# printing keys using keys()
print ("All values of ChainMap are : ")
print (list(chain.values()))
# All the ChainMap contents are :
# [{'b': 2, 'a': 1}, {'c': 4, 'b': 3}]
# All keys of ChainMap are :
# ['a', 'c', 'b']
# All values of ChainMap are :
# [1, 4, 2]

5.4 Deque

Deque (Doubly Ended Queue) is preferred over list in the cases where we need quicker append and pop operations from both the ends of container, as deque provides an O(1) time complexity for append and pop operations as compared to list which provides O(n) time complexity. Common operations include append(), appendleft(), pop(), popleft(), insert(i, a), remove(), count() etc.

The functionality of both dictionaries and defualtdict are almost same except for the fact that defualtdict never raises a KeyError. It provides a default value for the key that does not exists.

6. Exception Handling

Exceptions are raised when the program is syntactically correct but the code resulted in an error. This error does not stop the execution of the program, however, it changes the normal flow of the program. List of Exception Errors :

  • IOError : if file can’t be opened
  • KeyboardInterrupt : when an unrequired key is pressed by the user
  • ValueError : when built-in function receives a wrong argument
  • EOFError : if End-Of-File is hit without reading any data
  • ImportError : if it is unable to find the module
try:
  amount = 1999
  if amount < 2000:
    raise ValueError("please add more money")
  else:
    print("good")
except ValueError as e:
  print(e)

References:

tweet Share

Related Articles