python变量类型怎么决定的_Python数据类型提示痛点的解决方案探讨
幾個月前,你寫了一段Python代碼,當時只有你和上帝能看懂。幾個月后,這段代碼就只有上帝能看懂了。
痛點是什么
Python是一門弱類型的動態語言,在看其他人寫的一些Python項目的代碼、特別是大型項目的代碼的時候,是不是會有這樣的體驗:很多函數的參數超多,而且很難看出來每個參數代表什么含義、是什么數據類型的、字典類型的參數究竟可以傳哪些參數等等等。如果寫這些代碼的人習慣還不好,沒有寫什么注釋或注釋寫的比較糟糕,那看這種代碼簡直就是一種折磨了,這樣的情況下一開篇所描述的情景就并不算夸張了。這樣的代碼可讀性、可維護性幾乎為0。
解決方案
PEP 484類型提示語法
那么怎么解決上述痛點呢?Python 3.5版本在語言層面加入了一個新特性:類型提示(Type Hints,詳情見開篇引文),可以以一種非常優雅的方式來對代碼中變量的類型進行提示,大大提高了Python代碼的可讀性,例如下面這段代碼:
def hello(name:str, age:int) -> str:
return 'hello, {0}! your age is: {1}'.format(name, age)
在IPython中輸入這段代碼,然后用help函數查看hello函數的幫組文檔,顯示內容如下:
In [10]: help(hello)
Help on function hello in module __main__:
hello(name: str, age: int) -> str
類型提示語法中,在變量或函數形參后面加:類型名稱可以指明變量的數據類型,在函數定義def語句的后面加上-> 類型名稱可以指明函數返回值的數據類型,非常方便。
PEP 484類型提示的不足之處
要注意的是,PEP 484的類型提示語言特性只在Python 3.5或更高版本才支持,而且僅僅是作為注釋性的語法元素進行解析,在編譯的時候類型提示相關的代碼都是會被當做注釋而忽略掉的,Python編譯器并不會因為有了類型提示而對代碼做任何類型檢查。
更通用的解決方案:文檔注釋
那么還有沒有更好、更通用的辦法來解決上述類型提示痛點呢?有的,文檔注釋就是一個非常好的解決辦法。
文檔注釋的優點
清晰、準確、完善的文檔注釋可以讓Python代碼的可讀性、可維護性非常高。
不受Python語言版本的限制。
按照一定約定規范書寫的文檔注釋,可以被一些第三方IDE或語法檢查工具利用,從而可以對代碼做靜態類型檢查,在語法層面檢查代碼是否有類型錯誤,也可以做一些代碼補全方面的事情。
PyCharm風格的文檔注釋
這是一種個人比較推崇的文檔注釋格式,PyCharm IDE可以識別這種文檔注釋格式,從而進行代碼靜態語法類型檢查、代碼自動補全等操作。
下面是一個簡單的示例,在示例中用文檔注釋標注了hello函數的功能描述、每個形參變量的含義和類型、函數的返回值類型等信息:
def hello(name, age, habits = []):
'''
Say hello to someone, and print some info.
:param name: name of someone
:type name: str
:param age: age of someone
:type age: int
:param habits: habits of someone
:type habits: List[str]
:return: a hello sentence
:rtype: str
'''
return 'Hello, {name}! Your age is {age}, and your habits are {habits}.'.format(name = name, age = age, habits = habits)
將上述代碼輸入到IPython中,然后執行hello?命令來查看hello函數的文檔注釋,結果如下:
In [28]: hello?
Signature: hello(name, age, habits=[])
Docstring:
Say hello to someone, and print some info.
:param name: name of someone
:type name: str
:param age: age of someone
:type age: int
:param habits: habits of someone
:type habits: List[str]
:return: a hello sentence
:rtype: str
可以看出函數的功能描述、每個參數的含義和數據類型、函數返回值的含義和數據類型都非常清晰明了。
numpy風格的文檔注釋
還有一種numpy項目中所使用的文檔注釋風格,也非常不錯,可以參考。一個示例如下:
def squeeze(self, axis=None):
"""
Return a possibly reshaped matrix.
Refer to `numpy.squeeze` for more documentation.
Parameters
----------
axis : None or int or tuple of ints, optional
Selects a subset of the single-dimensional entries in the shape.
If an axis is selected with shape entry greater than one,
an error is raised.
Returns
-------
squeezed : matrix
The matrix, but as a (1, N) matrix if it had shape (N, 1).
See Also
--------
numpy.squeeze : related function
Notes
-----
If `m` has a single column then that column is returned
as the single row of a matrix. Otherwise `m` is returned.
The returned matrix is always either `m` itself or a view into `m`.
Supplying an axis keyword argument will not affect the returned matrix
but it may cause an error to be raised.
Examples
--------
>>> c = np.matrix([[1], [2]])
>>> c
matrix([[1],
[2]])
>>> c.squeeze()
matrix([[1, 2]])
>>> r = c.T
>>> r
matrix([[1, 2]])
>>> r.squeeze()
matrix([[1, 2]])
>>> m = np.matrix([[1, 2], [3, 4]])
>>> m.squeeze()
matrix([[1, 2],
[3, 4]])
"""
return N.ndarray.squeeze(self, axis=axis)
Emacs編輯器自動插入PyCharm風格的文檔注釋模板
如果你平時經常使用Emacs編輯器寫Python代碼,那么我寫了一個簡單的函數,可以方便地在python-mode一鍵插入上面所講的PyCharm風格的文檔注釋。只需將下面的elisp代碼加入你的Emacs配置文件中,然后在python-mode寫完一個函數的def語句后,光標在def語句所在的行時按快捷鍵C-c C-a即可快速插入文檔注釋模板:
(defun insert-doc-annotation-below-current-line ()
"Insert doc annotations for python class or function below current line."
(interactive)
;; first, get current line text
(let ((cur-line-str (buffer-substring-no-properties (line-beginning-position) (line-end-position))))
(cond
;; judge whether current line text match python function define pattern
((string-match "^[[:space:]]*def.+?(\\(.*\\))[[:space:]]*:" cur-line-str)
;; first capture group of regex above is params list string
(setq params-str (match-string 1 cur-line-str))
;; split params list string to list, and do some strip operation
(setq params (split-string params-str ",[[:space:]]*" t "[[:space:]]*=.+?"))
;; go to end of current line and go to new line and indent
(goto-char (line-end-position))
(newline-and-indent)
;; insert head of doc annotation `'''`
(insert "'''")
(goto-char (line-end-position))
(newline-and-indent)
;; record current line number, jump back to here after inserting annotation
(setq annotation-top-line (line-number-at-pos))
(newline-and-indent)
(newline-and-indent)
;; insert each params annotation
(while params
;; NOT insert param `self`
(when (not (string-equal (car params) "self"))
(progn
(setq param (car params))
;; insert param name annotation line
(insert (format ":param %s: " param))
(newline-and-indent)
;; insert param type annotation line
(insert (format ":type %s: " param))
(newline-and-indent)
(newline-and-indent)))
(setq params (cdr params)))
;; insert return and return type annotation line
(insert ":return: ")
(newline-and-indent)
(insert ":rtype:")
(newline-and-indent)
;; insert tail of doc annotation
(insert "'''")
;; jump back to the position of annotation top
(goto-line annotation-top-line)
(indent-for-tab-command))
((string-match "^[[:space:]]*class.+?:" cur-line-str)
(goto-char (line-end-position))
(newline-and-indent)
;; insert head of doc annotation `'''`
(insert "'''")
(goto-char (line-end-position))
(newline-and-indent)
;; record current line number, jump back to here after inserting annotation
(setq annotation-top-line (line-number-at-pos))
(newline-and-indent)
;; insert tail of doc annotation
(insert "'''")
;; jump back to the position of annotation top
(goto-line annotation-top-line)
(indent-for-tab-command))
(t (message "current line NOT match neither function nor class!")))))
(add-hook 'python-mode-hook
(lambda ()
(local-set-key (kbd "C-c C-a") 'insert-doc-annotation-below-current-line)))
總結
最終決定采用文檔注釋這種更通用的方式在Python代碼中做類型提示,雖然無法做到要求其他的人都能為他們自己的Python代碼提供完備、清晰的文檔注釋,但提升代碼的可讀性可以先從自身做起,起碼可以防止那種自己也讀不懂自己的代碼、只有上帝才能看懂的尷尬局面的發生。
總結
以上是生活随笔為你收集整理的python变量类型怎么决定的_Python数据类型提示痛点的解决方案探讨的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 如何关联QQ号?关联QQ号有什么作用?
- 下一篇: python standardscale