Skip to content

Refactoring Python Applications for Simplicity

重构并精简Python应用1

Metrics

行数/Lines of Code

  • Linux
# file
$ wc -l file.py
# folder
$ find . -name \*.py | xargs wc -l
  • windows
Get-ChildItem -Path *.py -Recurse | Measure-Object –Line

链路复杂度/Cyclomatic Complexity

  • tool

    pip install radon
    
    radon cc cyclomatic_example.py -s
    
  • explain

  • F means function, M means method, and C means class.

  • main is the name of the function.
  • 4 is the line the function starts on.
  • B is the rating from A to F. A is the best grade, meaning the least complexity.
  • The number in parentheses, 6, is the cyclomatic complexity of the code.

难度度量元/Halstead Metrics

  • 4 Measures
  • Operands are values and names of variables.
  • Operators are all of the built-in keywords, like if, else, for or while.
  • Length (N) is the number of operators plus the number of operands in your program.
  • Vocabulary (h) is the number of unique operators plus the number of unique operands in your a program.

  • 3 metrics

  • Volume (V) represents a product of the length and the vocabulary.

  • Difficulty (D) represents a product of half the unique operands and the reuse of operands.
  • Effort (E) is the overall metric that is a product of volume and difficulty.

  • explain

    import sys  # import (operator), sys (operand)
    
    if len(sys.argv) > 1:
    ...
    # 5 operators
    -  if
    -  (
    -  )
    -  >
    -  :
    # 2 operands
    - sys.argv
    - 1
    
  • commands

radon hal cyclomatic_example.py
# time = effort/18
# bugs = volumn/3000

Maintainability Index/MI

  • command
radon mi cyclomatic_example.py -s
  • explian

    • lower than 25: hard to maintain
    • over 75: easy to maintain

wily: Capture and Track Complexity

  • command
$ pip install wily
  • explain

  • wily build: iterate through the Git history and analyze the metrics for each file

  • wily report: see the historical trend in metrics for a given file or folder
  • wily graph: graph a set of metrics in an HTML file

example: wily

$ git clone https://github.com/requests/requests
$ cd requests
$ ls

# build
$ wily build requests
# Lines/MI/Cyclomatic Complexity
$ wily report requests/api.py
#
$ wily list-metrics
# report
$ wily report requests/api.py maintainability.rank raw.sloc
#graph
$ wily graph requests/sessions.py maintainability.mi
# html
$ wily graph requests/sessions.py maintainability.mi -o my_report.html

Pre-commit Hook

  • shell

    $ wily diff requests/api.py
    
    $ pip install pre-commit
    

  • .pre-commit-config.yaml

    repos:
    -   repo: local
        hooks:
        -   id: wily
            name: wily
            entry: wily diff
            verbose: true
            language: python
            additional_dependencies: [wily]
    

  • shell

$ pre-commit install

Refactoring

  • Using rope
pip install rope
from rope.base.project import Project
proj = Project('requests')

[f.name for f in proj.get_files()]

api = proj.get_file('api.py')

from rope.refactor.rename import Rename
change = Rename(proj, api).get_changes('new_api')
proj.do(change)
  • Using VSCode
  • Using Pycharm
  • etc.

Anti-Patterns

  • Functions Be Objects
  • Objects Be Functions
  • "Triangular" code=>Flat Code
  • Handling Complex Dictionaries With Query Tools

    # pip install jmespath
    import jmespath
    jmespath.search("network.lines", data)
    
  • Using attrs and dataclasses to Reduce Code


  1. https://realpython.com/python-refactoring/#using-wily-to-capture-and-track-your-projects-complexity