Reading Great Code

Commom Features of great code

每个定义的函数包含的代码大多不超过20行;含有很多空行;对于交互型代码(例如Requests和Flask等),都有大量的doctoring或者comment,综合下来五分之一的代码内容是可以作为文档使用的。不过像HowDoI这种直接的不是用语交互的代码,并没有必要去包含大量的comment。

下面,我们就跟着学习下如何读不同风格的代码。

HowDoI

HowDoI,code代码总数只有300行左右,可以作为阅读代码的首选。

Reading a single-file script

scirpt通常有一个清晰的starting point和一个清晰的ending point,以及其他定义清晰的操作。这使得sciprt会比一般的提供API的库 更容易follow。 关于howdoi,可以Google下,然后从github下载到。

安装: pip install --editable . 

unit test: python test_howdoi.py

Read howdoi's documentation

HowDoI的文档是README.rst文件,从中可以看出 HowDoI是一个很小的命令行应用,它可以允许使用者从互联网上获取关于编程问题的答案。


In [2]:
!which howdoi


/Users/ywfang/miniconda3/envs/plotenv/bin/howdoi

In [3]:
!howdoi --help   #注意我这里是在jupyter notebook里面直接使用的,所以需要加感叹号。如果是在terminal上,不需要加叹号。


usage: howdoi [-h] [-p POS] [-a] [-l] [-c] [-n NUM_ANSWERS] [-C] [-v]
              [QUERY [QUERY ...]]

instant coding answers via the command line

positional arguments:
  QUERY                 the question to answer

optional arguments:
  -h, --help            show this help message and exit
  -p POS, --pos POS     select answer in specified position (default: 1)
  -a, --all             display the full text of the answer
  -l, --link            display only the answer link
  -c, --color           enable colorized output
  -n NUM_ANSWERS, --num-answers NUM_ANSWERS
                        number of answers to return
  -C, --clear-cache     clear the cache
  -v, --version         displays the current version of howdoi

通过帮助文档,我们可以了解到HowDoI大概的工作模式以及它的一些功能,例如可以colorize the output,get multiple answers, keep answers in a cache that can be clared等。

Use HowDoI


In [5]:
!howdoi --num-answers 3 python lambda function list comprehension


★  Answer from https://stackoverflow.com/questions/6076270/python-lambda-function-in-list-comprehensions ★
[(lambda x: x*x)(x) for x in range(10)]

================================================================================

★  Answer from https://stackoverflow.com/questions/28268439/python-list-comprehension-with-lambdas ★
In [177]: bases = [lambda x, i=i: x**i for i in range(3)]

In [178]: print([b(5) for b in bases])
[1, 5, 25]

================================================================================

★  Answer from https://stackoverflow.com/questions/452610/how-do-i-create-a-list-of-python-lambdas-in-a-list-comprehension-for-loop ★
def square(x): return lambda : x*x
listOfLambdas = [square(i) for i in [1,2,3,4,5]]
for f in listOfLambdas: print f()

In [ ]:


In [7]:
!howdoi --num-answer 3 python numpy array create


★  Answer from https://stackoverflow.com/questions/568962/how-do-i-create-an-empty-array-matrix-in-numpy ★
>>> import numpy
>>> a = numpy.zeros(shape=(5,2))
>>> a
array([[ 0.,  0.],
   [ 0.,  0.],
   [ 0.,  0.],
   [ 0.,  0.],
   [ 0.,  0.]])
>>> a[0] = [1,2]
>>> a[1] = [2,3]
>>> a
array([[ 1.,  2.],
   [ 2.,  3.],
   [ 0.,  0.],
   [ 0.,  0.],
   [ 0.,  0.]])

================================================================================

★  Answer from https://stackoverflow.com/questions/4535374/initialize-a-numpy-array ★
numpy.zeros

================================================================================

★  Answer from https://stackoverflow.com/questions/21088133/how-to-construct-a-ndarray-from-a-numpy-array-python ★
>>> x = np.array([1, 2, 3])
>>> type(x)
<type 'numpy.ndarray'>

Read HowDoI's code

在howdoi的目录中,除了pycache之外其实只有两个文件,即init.py 和 howdoi.py。 前者只有一行,包含了版本信息;而后者则是我们即将精读的代码。


In [3]:
!ls /Users/ywfang/FANG/git/howdoi_ywfang/howdoi


__init__.py __pycache__ howdoi.py

通过浏览howdoi.py,我们发现这里面定义了很多新的函数,而且每个函数都会在之后的函数中被引用,这是的我们可以方便follow。 其中的main function,即 command_line_runner()接近于 howdoi.py的底部


In [8]:
!sed -n '70,120p' /Users/ywfang/FANG/git/howdoi_ywfang/howdoi/howdoi.py


XDG_CACHE_DIR = os.environ.get('XDG_CACHE_HOME',
                               os.path.join(os.path.expanduser('~'), '.cache'))
CACHE_DIR = os.path.join(XDG_CACHE_DIR, 'howdoi')
CACHE_FILE = os.path.join(CACHE_DIR, 'cache{0}'.format(
    sys.version_info[0] if sys.version_info[0] == 3 else ''))
howdoi_session = requests.session()


def get_proxies():
    proxies = getproxies()
    filtered_proxies = {}
    for key, value in proxies.items():
        if key.startswith('http'):
            if not value.startswith('http'):
                filtered_proxies[key] = 'http://%s' % value
            else:
                filtered_proxies[key] = value
    return filtered_proxies


def _get_result(url):
    try:
        return howdoi_session.get(url, headers={'User-Agent': random.choice(USER_AGENTS)}, proxies=get_proxies(),
                                  verify=VERIFY_SSL_CERTIFICATE).text
    except requests.exceptions.SSLError as e:
        print('[ERROR] Encountered an SSL Error. Try using HTTP instead of '
              'HTTPS by setting the environment variable "HOWDOI_DISABLE_SSL".\n')
        raise e


def _add_links_to_text(element):
    hyperlinks = element.find('a')

    for hyperlink in hyperlinks:
        pquery_object = pq(hyperlink)
        href = hyperlink.attrib['href']
        copy = pquery_object.text()
        if (copy == href):
            replacement = copy
        else:
            replacement = "[{0}]({1})".format(copy, href)
        pquery_object.replace_with(replacement)

def get_text(element):
    ''' return inner text in pyquery element '''
    _add_links_to_text(element)
    return element.text(squash_space=False)


def _extract_links_from_bing(html):
    html.remove_namespaces()

In [ ]: