dongweiming的博客
前言
我這個博客一直都是一些技術分享,show code的地方,我歷來沒有寫過我的生活或者情感雜談,固然我也歷來沒有談論過我對什麼東西的喜惡. 不少人喜歡噴XX語言,喜歡談論XX和YY的優缺,甚至湊了一本不知所云的書…好吧,我以爲沒有使用一門語言超過10年,沒有對一個技術研究個5,6年, 很差意思說本身懂(天才除外).我也以爲我沒有資格討論什麼,也許我有個人觀點見解,可是我懷着懷疑的心態看本身,生怕本身理解錯了. 下文純屬我的吐槽,也許沒有指定路怎麼走,只是但願提個醒…html
使用python2年,可喜的是python被愈來愈多的人接受,甚至前端工程師…可是卻有點爛大街的感受:感受出門不聊幾句python都很差意思和人打招呼.可是你真的懂python嘛?前端
你會python真的不重要
python實在太好學習了,假如你會其它的語言,可能搞本書翻一翻,一週後就能寫很高端的python程序,因爲web框架的普及,你甚至可讓一個網站應用跑起來. 你會我也會,你有什麼競爭力?python
你知道python怎麼用嘛?
在何時須要使用OOP?
在何時使用類裝飾器?
你用過元類嘛?
在何時用靜態方法何時使用類方法?
你瞭解那些管理屬性? call , init , __new__都是在何時被觸發?__getattr__和__getattribute__應用有什麼不一樣?
你知道標準庫裏面的多少個模塊?你能在須要的時候知道這個功能其實標準庫裏面已經實現了?
何時用回調?
何時用signal?假如你會django你知道django的signal是什麼?你瞭解orm嘛?
asyncore,contextlib, functools, collections, heapq,itertools, SocketServer, weakref,operator(知道3個就算)這些你會幾個?
python的多態是什麼?
在什麼場景能夠嘗試python的設計模式中的XX(能想到2個場景就算)?
在何時可使用Mixin?
在何時可使用python的閉包?
你曾經用過yield嘛?生成器和迭代器的區別和應用場景是什麼?
在什麼可使用python的函數式編程?
__future__模塊裏面都有什麼定義的用法?
提筆想了這上面16點我認爲體現python的東西,假如你不能有效的回答上面1/4, 好吧不要和我說你原來是會python的,踏實下來..你的路還很長.假如你回答不超過一半,我提醒你-你只是剛入行而已(這是個人角度)git
假如我是一個入職後的帶新人的引導者
學好git… 呵呵
假如新人還不熟悉python,python_koans是和不錯的入門選擇
首先就是嚴格的代碼規範,加上團隊的文化以及風格.
我會給一個任務,好比一週內寫個多線程的socket命令行聊天程序,支持羣組,加好友,羣聊,發送文件等功能,看新人能力而定
然後把項目一部分略棘手的工做教給他,注意這裏是生產環境,在他完成任務的過程當中會熟悉咱們的上線/code review/代碼風格等東西
我但願整個團隊一塊兒貢獻一個基礎的公共庫,包含一些經常使用的功能,而後新人首先學習這些東西,之後就不須要浪費時間造輪子,可是能夠修改完善公共庫, 這個公共庫能夠在新服務器部署時候直接使用pypi或者ubuntuPPA安裝進來
什麼算是好的python代碼?
假如你的代碼沒使用pep8檢驗過,你已經無敵了. 最差你也要使用autopep8格式化差勁的代碼吧?若是你想對本身的代碼質量有要求,我強烈建議你瞭解什麼是pythonic:程序員
doughellmann 的做者 an-introduction-to-the-zen-of-python
be-pythonic
代碼易懂可是堆和代碼難懂可是精煉的取捨
我想不少對代碼有追求的人都發現一件事情?看見項目中存在大量,沒有被重用的函數,似曾類似的方法甚至方法的名字… 我是極爲見不得ugly或者華而不實的代碼的人, 可是有個問題. 我封裝的代碼很不直觀,難懂..原來的代碼貌似極爲好懂.每一個人都有本身的理解吧.就象個人團隊裏面有人說django代碼太難懂,由於它們有django項目組的文化… celery代碼寫的很差這樣的安慰似的評論…可是我不這樣認爲,我還在讀celery代碼,我也認可裏面是有做者風格的取名或者實現的方式,可是我學到了不少.github
經過看開源代碼,好比django,requests,flask. 我會發現和總結不少別人的用法,思考別人爲啥這樣用.好比項目代碼目錄結構,解決一些問題使用的方式. 還有一些pep8中沒有提到的規範. 好比一些代碼實用的風格,舉個例子:web
咱們的代碼引用其它模塊是這樣的:面試
from test import long_long_long_test1
from test import long_long_long_test2
form test import long_long_long_test3
省略20多行,很鬧殘吧? 有一種風格是算法
from test import long_long_long_test1, long_long_long_test2, long_long_long_test3
說一個技巧:當我不知道該用什麼,去看很NB項目怎麼用 結果django和requests是這樣的風格django
from test import (long_long_long_test1,
long_long_long_test2,
long_long_long_test3)
怎麼樣進步?不是閉門造車..先去看看別人是怎麼用的..
好比我之前拼接一個文件路徑都是這樣:
'{0}/{1}'.fotmat(dir, filename)
其實人家原本有:
os.path.join(dir, filename)
很慚愧.而後花了半個小時,把之前我這樣用的地方全改了
這是我說的重要的一點: 知道了什麼是對的 就要改….
對於這個主題個人答案: 我喜歡難懂可是精煉的代碼. 境界就是你看的懂就能寫的出來. 假如你連這點代碼都看不懂.你看不了開源項目的作法.你會一直是個堆代碼的碼農..你會一直在堆着垃圾的代碼.你會增長將來接手人的維護成本
怎麼樣提升python可讀性和質量
如下是個人想法
首先給函數/類/方法取個好懂的名字(我這點很失敗,英語太爛…是否是應該加一個學好英語)
當一個差很少的操做出現了三次,不要繼續堆代碼,要抽象出來
我傾向於寫FIXME,TODO, 寫文件/函數的用途的註釋,在不是很好理解的代碼上面註釋做用,標明輸入和輸出都是什麼(若是不是要修改維護你的代碼,沒人在意你的算法多NB)
上面說的,請不要讓別人須要仔細研究你的代碼才明白是什麼意思.. 我寫代碼頗有壓力,由於我不想接受我帶嘛的人罵我.
不要炫技,請不要亂用函數式編程/閉包.我在意的是性能和簡單粗暴的實現功能
多用標準庫的實現,若是不知道有這個功能實現前先google.
多讀有名的項目,github上面有不少.思考別人爲何這樣用
….
咱們是封裝開源項目仍是直接修改開源代碼給本身用
其實我這樣描述,好比有個項目由於歷史緣由是一個很早的版本.可是和其它新的版本組件有兼容問題以及咱們業務的特殊須要.我看了源碼發現須要改動幾個地方. 問題改動後就須要本身維護這個項目,對於新部署的環境甚至其它版本我還繼續須要這個變更. 還有一種聲音是」你不能修改XX源碼」,你要在上面封裝出一個新的東西, 也就是不直接調用XX,而是在個人本身的項目對XX有了個封裝YY,而後咱們的調用YY.
我以爲這個東西本身部署是一個可行的方案,首先這個修改不是一個patch,不是主流的修改.只能算是咱們業務的二次開發而已,封裝只是在掩耳盜鈴. 着讓我想起一個問題:爲何中國鮮有好的開源項目:中國人不缺好的idea?是由於中國人以爲這件事情作不了,是由於它們以爲別人實現的就是很牛比的, 本身改了就會有問題…其實這是自卑..首先是代碼就會有bug,tornado/flask/requests不仍是在開發和解決問題嘛?bug一直在只是你沒有發現和注意. 我以爲開源項目的代碼看懂了,瞭解了就能夠修改..沒什麼可擔憂的…我指的是角度.我以爲每一個人學了一門語言看了某個項目的源碼只要你有膽量, 你有一個懷疑的善於發現和思考的心,那麼你都能貢獻你的代碼,作你的二次開發.
和本文相關: 若是你沒有作過這件事,你怎麼能夠說你會python?
個人感想
我不贊同」作好工做就行了」的調調.對你我的來講你明年今天作的事情和如今是同樣的,不一樣的是你老了一歲. 若是是爲何完成工做而完成工做. 其實你這個代碼就是線上運行的代碼,而且是之後很長時間再用的版本,你隨意的一些代碼會在好久以後很難的變更.. 我也不一樣意一上來就把你的程序寫的能承受千萬級PV的架構.我認爲對於如今項目狀態,我要思考大約將來一年可能的發展,它如何簡單的擴展就行了..
前言
對我來講,之前每次面試是我審視本身,檢驗本身的一種方式。每次準備面試,以及被面試官問住的時候纔會發現,其實我python我學的還不夠好。工做中也是,能夠從其餘的同事那裏得到成長。可是我今天說的是,我也在本身總結和思考最佳實踐這件事。
我想不少人都會有意識的去讀一些PEP(Python Enhancement Proposals)。瞭解語言設計者當時的考慮,這些文案也是通過很長時間的討論最後才實施的。既然想用好這門語言,必然須要理解設計之美。好比我據說gvanrossum使用emacs做爲編輯器,我也使用emacs,就是但願我能夠更貼近一些python。
本文根據 The Best of the Best Practices (BOBP) Guide for Python 和 Khan’s style-guides中對於開發中一些事物的理解和見解,有出至PEP,也有一些python界知名開發者,我加入了一些我本身的理解和見解。
價值觀
「Build tools for others that you want to be built for you.」 – Kenneth Reitz (Requests等知名庫做者)
你本身都不想用的東西作出來有什麼意義呢?
「Simplicity is alway better than functionality.」 – Pieter Hintjens (ZeroMQ)
我對函數式編程的見解一直是看場景,甚至於我常常會對比性能,義無反顧的使用性能最好的,可是代碼又不難懂和繁瑣的
「Fit the 90% use-case. Ignore the nay sayers.」 – Kenneth Reitz
程序員都有完美主義情懷,可是其實每每咱們是在偏激的看事情 – 用戶其實不case
「Beautiful is better than ugly.」 – PEP 20
開發參考
「Explicit is better than implicit」 – PEP 20
不要留坑,我常常看到一些複雜的代碼,這些代碼的做者寫的時候明顯知道本身在作什麼,可是別人很難維護和看懂.
因此我對本身的職業的基本要求就是: 那天我離職了,後來接手的人不會常常罵我
「Readability counts.」 – PEP 20
「Anybody can fix anything.」 – Khan’s style-guides
我如今更多不是代碼炫技,我常常思考的怎麼讓最少的代碼,最簡單的設計結構知足當前需求,也能給將來一段時間裏也有擴展性
Fix each broken window (bad design,wrong decision,or poor code) as soon as it is discovered.
咱們改bug有個原則 – 測試要覆蓋到出bug的地方。每一個人心裏都有很高的代碼質量的要求
「Now is better than never.」 – PEP 20
明日復明日,明日何其多。咱們在代碼review的時候,問題須要在提出的時候就去改,永遠不會說下一次再說,由於下一次大多時候是沒有下一次了
Test ruthlessly. Write docs for new features.
Even more important that Test-Driven Development–Human-Driven Development
一些細節
PEP8
不少人是排斥的,假如你想讓將來部門有本身的風格,習慣。讓新人立刻上手接受,PEP8是一個很是明智的選擇
文件開頭
新的文件的開頭須要加一些docstring。描述文件的做用,編輯者,修改緣由和日期等幫助閱讀者的描述.
不要添加#!/usr/bin/python(除非這個文件將來是一個可執行的文件),copyright,__author__或者其餘內容.
第一行建議添加# coding-utf-8
命名
Variables,functions,methods,packages,moduleslower_case_with_underscores
Classes and ExceptionsCapWords
Protected methods and internal functions_single_leading_underscore(self,…)
Private methods__double_leading_underscore(self,…)
ConstantsALL_CAPS_WITH_UNDERSCORES
Avoid one-letter variables (esp. l,O,I).永遠不要使用沒有意義的單字符做爲變量名
PS: 這點能夠折中,假如一個代碼塊代碼邏輯很清晰,而 這個短的便令也只是過程當中的一個間接變量之類的狀況下是能夠接受的
Good or Bad
列舉一些正確和錯誤的用法.
Avoid redundant labeling.
複製代碼
import audio
core=audio.Core()
controller=audio.Controller()
import audio
core=audio.AudioCore()
controller=audio.AudioController()
複製代碼
不要使用重複意義的標籤
Prefer 「reverse notation」.
複製代碼
elements=...
elements_active=...
elements_defunct=...
elements=...
active_elements=...
defunct_elements...
複製代碼
Avoid getter and setter methods.
person.age=42
person.set_age(42)
Indentation
永遠不要Tab和空格混用。使用4個空格做爲python縮進
Imports
Import entire modules instead of individual symbols within a module.
PS: 這個時候能夠參考tornado的代碼用法.
好比如今有這樣一個包
$tree
└──canteen
├──init.py
├──sessions.py
複製代碼
import canteen
import canteen.sessions
from canteen import sessions
from canteen import get_user # Symbol from canteen/init.py
from canteen.sessions import get_session # Symbol from canteen/sessions.py
複製代碼
PS: 除非這個第三方模塊的文檔顯式的要求這些寫
Splitting tricky lines
複製代碼
badge_name=badges.topic_exercise_badges.TopicExerciseBadge.name_for_topic_key_name(self.key().name())
badge_name=(badges.topic_exercise_badges.TopicExerciseBadge
.name_for_topic_key_name(self.key().name()))
self.redirect("/class_profile?selected_graph_type=%s
複製代碼
我添加的規則
from … import …
複製代碼
from aa import alonglonglonglonglong,alonglonglonglonglonglonglonglonglong,
alonglonglonglonglonglong
from aa import(alonglonglonglonglong,alonglonglonglonglonglonglonglonglong,
alonglonglonglonglonglong)
from aa import(alonglonglonglonglong,alonglonglonglonglonglonglonglonglong,
alonglonglonglonglonglong,alonglonglonglonglonglonglong,
alonglonglonglonglonglong2) # Good。 當引入的函數/類/變量不少時,也能夠選擇空 4 個空格的方式,而不須要和首行的左括號後對齊
複製代碼
相對引用(relative import) 和 絕對引用(absolute import)
複製代碼
$cat xx/models/user/consts.py # 若是想引用這個變量
TMP=1
$cat xx/views/user.py
from xx.models.user.consts import TMP # recommended。
$cat xx/models/user/main.py # 須要和 consts.py 在一個目錄下才能夠
from consts import TMP # Bad
from.consts import TMP # Good
複製代碼
我Python 最佳實踐指南
quoniammm
quoniammm
3 人讚了該文章
閱讀這份指南我所學到的
pip install pycodestyle
pycodestyle .py
pip install autopep8
autopep8 --in-place optparse.py
my_very_big_string = (
"For a long time I used to go to bed early. Sometimes, "
"when I had put out my candle, my eyes would close so quickly "
"that I had not even time to say 「I’m going to sleep.」"
)
init(self, *arg) # 構造函數
str(self) #@override return str_name
static attribute # 定義在 init 函數外 全部類實例共享
class son(Parent):
pass
# super().method_name # 調用父類方法
"""
name gives us the means for our module to detect whether it has been run
as a script or imported into another module or the REPL.
"""
print(name)
if name == 'main':
function()
"""description
Args:
description
Raises:
IOError: ......
Returns:
description
"""
a = [1, 2]
def replace(f):
f = [4, 5]
replace(a) # 結果: a 的值仍是 [1, 2] 這其實仍是一種閉包 (closure)
# Static # Dynamic
Strong # # Python
Weak # # JavaScript
解釋上圖:
Local Enclosing Global Built-in(LEGB)
count = 0
def show_count():
print("count = ", count)
def set_count(c):
global count # 不加這一行會出錯
count = c
heterogeneous(各類各樣的) immutable sequence
("www", 123, True) # in or not in
homogeneous(同類的,同性質的) immutable sequence of Unicode codepoints(characters)
.split() .join()
.partition() # divide a string into three around a seperator:
arithmetic(算數,算法) progression of integers
heterogeneous mutable sequence
.index(item) # ValueError if not found
.insert(index, item)
.extend(list)
.reverse()
.sort() # reverse=True
sorted(list)
list(reversed(list))
{}
dict([(), ()])
dicr(a=b, c=d)
.copy()
.update()
.values() # 忽略 keys
.keys() # 同上
.items() # 同上
from pprint import pprint as pp # pretty print 美化做用
unordered collection of unique, immutable objects
{1, 4, 67, 8999}
s = set([list])
.remove()
.discard()
.copy()
.union() # 並集
.intersection() # 交集
.difference() # 交集的補集
.symmetric_difference() # 補集
.issubset() # 子集
.issuperset() # 父集
.isdisjoint() # 不相交
Rasie/Handle/Unhandled exceptions/Exception objects
try:
except ValueError: # except (ValueError, TypeError) as e:
# str(e)
(finally)
return
iter() # 得到 iterator
next() # 得到當前元素
from itertools import islice, count
any()
zip()
unittest
pdb
淺拷貝
3.1 Python 包(Package)與模塊(Module)
簡單理解包和模塊的區別是:包是一組模塊的集合
reader/init.py
reader/reader.py
touch reader/init.py
import reader
print(type(reader))
reader.__file__ # 結果: './reader/init.py'
reader/compressed/init.py
reader/compressed/bzipped.py
reader/compressed/gzipped.py
import reader
import reader.compressed
import reader.compressed.bzipped
from ..a import A
from . import common
project_name/
project_name/main.py
project_name/project_name/init.py
project_name/project_name/subpackage/init.py
project_name/project_name/test/init.py
project_name/setup.py
3.2 深刻理解 Python 函數
call() # 一個 built_in 的函數 理念是 class 能夠像 function 同樣被執行
result = true_value if condition else false_value
callable() # bulit_in 函數 檢測是否可調用
t = (11, 12, 13, 14)
f(*t)
f(arg1, arg2, *arg3)
int('ff', base=16) # <=>
trace(int, "ff", base=16)
tansposed = list(zip(*daily)) # 神奇 cool
def sort_by_last_letter(strings):
x = 'clo'
def last_letter(s): # 每次的區域函數並不相同,沒有 cahe
return s[-1] + x
return sorted(strings, key=last_letter)
sort_by_last_letter.last_letter(s)
test = sort_by_last_letter('ert')
test.__closure__l
def raise_to(exp):
def raise_to_exp(x):
return pow(x, exp)
return rasie_to_exp
def escape_unicode(f):
def wrap(*args, **kwargs):
x = f(*args, **kwargs)
return ascii(x)
return wrap
@escape_unicode
def china_city():
return '西安'
class My_class:
def init(self, f):
self.f = f
self.count = 0
def call(self, *args, **kwargs):
self.count += 1
return self.f(*args, **kwargs)
@My_class
def hello(name):
print('Hello, {}'.format(name))
hello.count
class Trace:
def init(self):
self.enabled = True
def call(self, f):
def wrap(*args, **kwargs):
if self.enabled:
print('Calling {}'.format(f))
return f(*args, **kwargs)
return wrap
tracer = Trace()
@tracer
def rolate_list(l):
return l[1:] + [l[0]]
@tracer
@escape_unicode
def china_island_maker(name):
return name + '島'
class IslandMaker:
def init(self, suffix):
self.suffix = suffix
@tracer def make_island(self, name) return name + self.suffix
import functools
@functools.wraps()
def check_non_negative(index):
def validator(f):
def wrap(args):
if args[index] < 0:
raise ValueError(
'Argument {} must be non-negative.'.format(index))
)
return f(args)
return wrap
return validator
@check_non_negative(1)
def create_list(value, size):
return [value] * size
3.3 深刻理解 Python 面向對象編程
@property
def attr1(self):
return self._attr1
@attr1.setter
def attr1(self, value):
self._attr1 = value
class subClass(Base1, Base2, ...):
pass
className.__bases__
className.__mro__ (.mro()) # 見下圖
super(ClassName, self).__init__() <=> super().__init()
3.4 深刻理解 Python 語言特性
assert 5 > 2, "This is a error"
raise("some words")
except ValueError as e:
print("payload:", e.args)
3.5 單元測試(Python 測試)
python3 -m unittest -v
with pytest.raises(KeyError):
className.method(*arg)
pytest.skip("")
@pytest.fixture
def phonebook(request):
phonebook = Phonebook()
def cleanup_phonebook():
phonebook.clear()
request.add_finalizer(cleanup_phonebook)
return phonebook
pip install coverage
pip install pytest-cov
python3 -m pytest --cov-report term-missing --cov tennis
python3 -m pytest --cov-report html --cov tennis