Python進階學習之代碼閱讀

原由

最近在公司的任務是寫一些簡單的運營工具,由於是很小的工具,因此就用了github上面的一個開源項目Flask-admin,能夠省去不少的事情。python

可是,這個開源項目是我的維護的項目,因此文檔相對簡單,網上的資料相對較少,遇到一些產品經理要求具體功能並不能直接經過文檔和例子中的代碼找到答案。因此,我只能經過閱讀源代碼,重寫相關類以及方法實現了具體的需求。在這個過程當中,學習到了一些東西,同時整理了本身之前的一些收穫,而後分享給你們,有不對的地方還望海涵、指正。git

閱讀代碼有助於處理bug

閱讀代碼是一項更重要的技能,在大學編程語言的考試中也有相關的考察——代碼填空、代碼查錯。在工做中用到的地方更多:程序員

1. 查找buggithub

2. 參與到已有的項目web

3. 接手別人的工做面試

4. 開源項目的二次開發編程

python是解釋性語言,不須要反編譯就能夠看到源代碼,利於查找bug。在找bug的時候,最重要的是定位bug的位置,比較直觀的bug是經過閱讀異常能夠定位到bug的位置。而有的異常信息,例如:AttributeError: 'NoneType' object has no attribute 'get' 會讓你以爲很費解,由於你本覺得這個確定有值,怎麼就是None了呢?這個時候,你能夠在異常定位的位置前面,把這個object的值打印出來,從新調試。察看這個值究竟是什麼,而後一步步的向上找到,是什麼地方操做了這個對象使得它的值爲None,形成了這個異常(固然也可使用經過ide調試模式進行排查,本文重點是閱讀代碼,因此就不介紹 打斷點決解bug的方法 了)。flask

綜合上面的講的:閱讀代碼,定位到問題的位置,而後打印出來!這樣有利於分析問題,解決問題。ruby

爲什要先說這個技能,由於當咱們用一個咱們不熟悉、文檔不徹底的庫、類、方法或者函數的時候,一般會遇到問題,經過上面的方法,定位到問題,經過輸出值,閱讀代碼。退後推敲出問題的緣由,就能夠很快的找到解決辦法。固然,這個方法也不是什麼bug可以解決的,可是經過上面的方法嘗試解決不成功後,再拿着這個bug去問別人的時候,就能夠具體到某個方法,精確的提問。你們的時間都很寶貴,若是你提出一個泛泛的問題、沒有本身嘗試解決過的問題,那麼誰也沒法給你一個好的解答(提問的智慧。同時能夠減小被批評的次數。。。。😓(注意看異常信息很重要,我曾經就拿不少低級問題去問我師父,我師傅走過來一看:你把這個異常提示給我翻一下。bash

def open_url(url:複製代碼
^複製代碼
SyntaxError: invalid syntax複製代碼

原來是忘了寫')'。。。。😅)

閱讀代碼有助於提升本身的編程水平

閱讀源代碼也是提升編碼能力的一種途徑,就像臨摹大師的畫同樣。能夠經過觀摩理解,吸取別人的智慧與技巧提升本身的能力。由於,工做上須要用flask,由於最開始本身學習flask的時候就對flask中的全局變量:g、request、session等,全局變量以爲很奇怪。request是全局變量,可是每一個請求的request都是不同,在我調用request對象的時候並無指定是那個請求的request,flask怎就能給我當前請求的request?經過查閱資料,再加上本身閱讀flask的代碼:

class Local(object):    ## request對象是Local的實例 __slots__ = ('__storage__', '__ident_func__') def __init__(self):複製代碼
複製代碼
複製代碼
複製代碼
複製代碼

## object.__setattr__如此賦值,並不調用實例的__setattr__方法

object.__setattr__(self, '__storage__', {})

## 這裏的__ident_func__存的的是當前進程/協程的惟一id

object.__setattr__(self, '__ident_func__', get_ident)

def __iter__(self):

return iter(self.__storage__.items())

def __call__(self, proxy):

"""Create a proxy for a name."""

return LocalProxy(self, proxy)

def __release_local__(self):

self.__storage__.pop(self.__ident_func__(), None)

def __getattr__(self, name):

try:

## 每次調用request就是調用當前進程/協程下的request

return self.__storage__[self.__ident_func__()][name]

except KeyError:

raise AttributeError(name)

def __setattr__(self, name, value):

ident = self.__ident_func__()

storage = self.__storage__

try:

storage[ident][name] = value

except KeyError:

storage[ident] = {name: value}

def __delattr__(self, name):

try:

del self.__storage__[self.__ident_func__()][name]

except KeyError:

raise AttributeError(name)

經過閱讀flask的內部實現就明白了究竟是如何優雅的實現:使用這些全局變量的時候,你啥都不用管只要調用就好了。這就是python的優雅的一 面。而優秀的代碼就是相似於這種的優雅的實現,多多‘臨摹’高手的代碼,能夠學到更多優雅技巧:裝飾器、協程、生成器、魔法方法等。而不是光學會概念、寫一些例子。閱讀代碼中看到實際的應用代碼片斷,更加有助於本身之後用到本身的代碼中。

閱讀代碼有助於養成優秀的代碼風格

「優秀的代碼不須要文檔」,這句話雖說的有些誇張的成份,可是也並沒有必定道理。優秀的項目中的代碼,註釋佔的比重是至關大的。比方tornado框架中的代碼:

class HTTPServer(TCPServer, Configurable,

httputil.HTTPServerConnectionDelegate):

r"""A non-blocking, single-threaded HTTP server.

A server is defined by a subclass of `.HTTPServerConnectionDelegate`,

or, for backwards compatibility, a callback that takes an

`.HTTPServerRequest` as an argument. The delegate is usually a

`tornado.web.Application`.

`HTTPServer` supports keep-alive connections by default

(automatically for HTTP/1.1, or for HTTP/1.0 when the client

requests ``Connection: keep-alive``).

If ``xheaders`` is ``True``, we support the

``X-Real-Ip``/``X-Forwarded-For`` and

``X-Scheme``/``X-Forwarded-Proto`` headers, which override the

remote IP and URI scheme/protocol for all requests. These headers

are useful when running Tornado behind a reverse proxy or load

balancer. The ``protocol`` argument can also be set to ``https``

if Tornado is run behind an SSL-decoding proxy that does not set one of

the supported ``xheaders``.

To make this server serve SSL traffic, send the ``ssl_options`` keyword

argument with an `ssl.SSLContext` object. For compatibility with older

versions of Python ``ssl_options`` may also be a dictionary of keyword

arguments for the `ssl.wrap_socket` method.::

ssl_ctx = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)

ssl_ctx.load_cert_chain(os.path.join(data_dir, "mydomain.crt"),

os.path.join(data_dir, "mydomain.key"))

HTTPServer(applicaton, ssl_options=ssl_ctx)

`HTTPServer` initialization follows one of three patterns (the

initialization methods are defined on `tornado.tcpserver.TCPServer`):

1. `~tornado.tcpserver.TCPServer.listen`: simple single-process::

server = HTTPServer(app)

server.listen(8888)

IOLoop.current().start()

In many cases, `tornado.web.Application.listen` can be used to avoid

the need to explicitly create the `HTTPServer`.

2. `~tornado.tcpserver.TCPServer.bind`/`~tornado.tcpserver.TCPServer.start`:

simple multi-process::

server = HTTPServer(app)

server.bind(8888)

server.start(0) # Forks multiple sub-processes

IOLoop.current().start()

When using this interface, an `.IOLoop` must *not* be passed

to the `HTTPServer` constructor. `~.TCPServer.start` will always start

the server on the default singleton `.IOLoop`.

3. `~tornado.tcpserver.TCPServer.add_sockets`: advanced multi-process::

sockets = tornado.netutil.bind_sockets(8888)

tornado.process.fork_processes(0)

server = HTTPServer(app)

server.add_sockets(sockets)

IOLoop.current().start()

The `~.TCPServer.add_sockets` interface is more complicated,

but it can be used with `tornado.process.fork_processes` to

give you more flexibility in when the fork happens.

`~.TCPServer.add_sockets` can also be used in single-process

servers if you want to create your listening sockets in some

way other than `tornado.netutil.bind_sockets`.

.. versionchanged:: 4.0

Added ``decompress_request``, ``chunk_size``, ``max_header_size``,

``idle_connection_timeout``, ``body_timeout``, ``max_body_size``

arguments. Added support for `.HTTPServerConnectionDelegate`

instances as ``request_callback``.

.. versionchanged:: 4.1

`.HTTPServerConnectionDelegate.start_request` is now called with

two arguments ``(server_conn, request_conn)`` (in accordance with the

documentation) instead of one ``(request_conn)``.

.. versionchanged:: 4.2

`HTTPServer` is now a subclass of `tornado.util.Configurable`.

"""

def __init__(self, *args, **kwargs):

# Ignore args to __init__; real initialization belongs in

# initialize since we're Configurable. (there's something

# weird in initialization order between this class,

# Configurable, and TCPServer so we can't leave __init__ out

# completely)

pass

上面的註釋包含了:類的說明、例子、版本主要改動。

優秀的代碼風格:看到名字就能知道它是用來幹什麼的(顧名思義)、結構清晰、代碼風格統一(命名規則、格式)

這些優秀的特質都是爲了:可讀性、容易理解。正如:代碼主要是給人看的,讓計算機運行是次要的

若是是在閱讀了很差的代碼,若是你內心在罵:「這代碼簡直是一坨💩」,必定要注意:本身寫的代碼,不能讓人在背後罵啊。因此寫代碼的時候不要圖一時爽,爲了快沒有了原則。沒準一個月後你本身看的時候,內心還在想這是誰寫的,這麼屎,最後發現是本身的‘傑做’。。。。

因此,本身的優秀的編碼風格也是成爲一個合格的程序員必備的一項技能(面試要求會有這一項),經過閱讀代碼學習,模仿優秀的代碼風格,有助於本身寫出‘漂亮’、整潔的代碼。

利用工具閱讀

由於我是個pythoner,經常使用語言是python(實際上是別的語言都不會。。😅),我推薦一款IDE——PyCharm,好的工具可讓你事半功倍。

下面介紹幾個快捷鍵和設置,有助於幫助閱讀提升閱讀代碼的效率:

1. 設置:在項目文件目錄中展現打開文件的位置

2. cmd b :跳轉到變量、方法、類等定義的位置(最好完成了步驟1設置)

便於查看相關定義


3. cmd +/- :展開/摺疊代碼塊(當前位置的:函數,註釋等)--加shift是所有

更加清晰的展現


4. alt F7 :查找該函數在何處被調用——便於察看相關調用

5. cmd f :在當前文件中查找 --加shift是在本項目中查找——查找某字段的位置

以上快捷鍵適用於mac,其它系統能夠參考

最後

本文介紹了閱讀代碼的好處,以及基本的方法。我但願看完這篇文章後,若是讀者以爲有對的地方,能夠在本身的日常工做和編程中實踐這些技能。在閱讀源代碼後把學到的技巧,總結、吸取、應用,相信久而久之,編程能力必定會獲得提升!

想學習交流的同窗能夠加Python羣705673780,羣內有許多免費資料可供學習哦~

進階,是一段很長的路,每遇到一個問題都是一個提升的機會,再難的問題、很差理解的代碼只要努力去探索、堅持去研究、尋找解決的方法。最終必定會搞懂的。

最後:

不積跬步,無以致千里

共勉!

相關文章
相關標籤/搜索