中文字幕在线观看,亚洲а∨天堂久久精品9966,亚洲成a人片在线观看你懂的,亚洲av成人片无码网站,亚洲国产精品无码久久久五月天

如何像Python高手(Pythonista)一樣編程

2018-07-20    來源:編程學(xué)習(xí)網(wǎng)

容器云強(qiáng)勢上線!快速搭建集群,上萬Linux鏡像隨意使用

  最近在網(wǎng)上看到一篇介紹Pythonic編程的文章:Code Like a Pythonista: Idiomatic Python,其實(shí)作者在2006的PyCon會(huì)議后就寫了這篇文章,寫這篇文章的主要原因是作者發(fā)現(xiàn)很多有經(jīng)驗(yàn)的Pythoner寫出的代碼不夠Pythonic。我覺得這篇文章很不錯(cuò),所以將它用中文寫了下來(不是逐字的翻譯,中間加了一些自己的理解),分享給大家。另:由于本人平時(shí)時(shí)間有限,這篇文章翻譯了比較長的時(shí)間,如果你發(fā)現(xiàn)了什么不對(duì)的地方,歡迎指出。。

  一、Python之禪(The Zen of Python) 

  The Zen of Python是Python語言的指導(dǎo)原則,遵循這些基本原則,你就可以像個(gè)Pythonista一樣編程。具體內(nèi)容你可以在Python命令行輸入import this看到:

The Zen of Python, by Tim Peters

Beautiful is better than ugly.
# 優(yōu)美勝于丑陋(Python以編寫優(yōu)美的代碼為目標(biāo))

Explicit is better than implicit.
# 明了勝于晦澀(優(yōu)美的代碼應(yīng)當(dāng)是明了的,命名規(guī)范,風(fēng)格相似)

Simple is better than complex.
# 簡潔勝于復(fù)雜(優(yōu)美的代碼應(yīng)當(dāng)是簡潔的,不要有復(fù)雜的內(nèi)部實(shí)現(xiàn))

Complex is better than complicated.
# 復(fù)雜勝于凌亂(如果復(fù)雜不可避免,那代碼間也不能有難懂的關(guān)系,要保持接口簡潔)

Flat is better than nested.
# 扁平勝于嵌套(優(yōu)美的代碼應(yīng)當(dāng)是扁平的,不能有太多的嵌套)

Sparse is better than dense.
# 間隔勝于緊湊(優(yōu)美的代碼有適當(dāng)?shù)拈g隔,不要奢望一行代碼解決問題)

Readability counts.
# 可讀性很重要(優(yōu)美的代碼是可讀的)

Special cases aren't special enough to break the rules.
Although practicality beats purity.
# 即便假借特例的實(shí)用性之名,也不可違背這些規(guī)則(這些規(guī)則至高無上)

Errors should never pass silently.
Unless explicitly silenced.
# 不要包容所有錯(cuò)誤,除非你確定需要這樣做(精準(zhǔn)地捕獲異常,不寫except:pass風(fēng)格的代碼)

In the face of ambiguity, refuse the temptation to guess.
# 當(dāng)存在多種可能,不要嘗試去猜測

There should be one-- and preferably only one --obvious way to do it.
# 而是盡量找一種,最好是唯一一種明顯的解決方案(如果不確定,就用窮舉法)

Although that way may not be obvious at first unless you're Dutch.
# 雖然這并不容易,因?yàn)槟悴皇?nbsp;Python 之父(這里的Dutch是指Guido)
Now is better than never.
Although never is often better than *right* now.
# 做也許好過不做,但不假思索就動(dòng)手還不如不做(動(dòng)手之前要細(xì)思量)
If the implementation is hard to explain, it's a bad idea.
If the implementation is easy to explain, it may be a good idea.
# 如果你無法向人描述你的方案,那肯定不是一個(gè)好方案;反之亦然(方案測評(píng)標(biāo)準(zhǔn))
Namespaces are one honking great idea -- let's do more of those!
# 命名空間是一種絕妙的理念,我們應(yīng)當(dāng)多加利用(倡導(dǎo)與號(hào)召)

  這首特別的“詩”開始作為一個(gè)笑話,但它確實(shí)包含了很多關(guān)于Python背后的哲學(xué)真理。Python之禪已經(jīng)正式成文PEP 20,具體內(nèi)容見:PEP 20

  二、PEP8: Python編碼規(guī)范(PEP8: Style Guide for Python Code) 

  Abelson & Sussman在《計(jì)算機(jī)程序的構(gòu)造和解釋》一書中說道:程序是寫來給人讀的,只是順帶讓機(jī)器執(zhí)行。所以,我們?cè)诰幋a時(shí)應(yīng)該盡量讓它更易讀懂。PEP8是Python的編碼規(guī)范,官方文檔見:PEP 8,PEP是Python Enhancement Proposal的縮寫。PEP8包括很多編碼的規(guī)范,下面主要介紹一下縮進(jìn)和命名等內(nèi)容。

  空格和縮進(jìn)(WhiteSpace and Indentation)

  空格和縮進(jìn)在Python語言中非常重要,它替代了其他語言中{}的作用,用來區(qū)分代碼塊和作用域。在這方面PEP8有以下的建議:

1、每次縮進(jìn)使用4個(gè)空格
2、不要使用Tab,更不要Tab和空格混用
3、兩個(gè)方法之間使用一個(gè)空行,兩個(gè)Class之間使用兩個(gè)空行
4、添加一個(gè)空格在字典、列表、序列、參數(shù)列表中的“,“后,以及在字典中的”:“之后,而不是之前
5、在賦值和比較兩邊放置一個(gè)空格(參數(shù)列表中除外)
6、緊隨括號(hào)后面或者參數(shù)列表前一個(gè)字符不要存在空格

  Python命名

  命名規(guī)范是編程語言的基礎(chǔ),而且大部分的規(guī)范對(duì)于高級(jí)語言來說都是一樣的,Python的基本規(guī)范如下:

1、方法 & 屬性:joined_lower
2、常量:joined_lower or ALL_CAPS
3、類:StudlyCaps
4、類屬性:interface, _internal, __private
5、camelCase only to conform to pre-existing conventions

  以上內(nèi)容只是對(duì)PEP8做了非常簡單的介紹,由于今天的主題不在于此,所以就不在這里多講。想要更加深入的了解Python編碼規(guī)范,可以閱讀PEP8官方文檔和Google Python編碼規(guī)范等內(nèi)容。

  三、交換變量值(Swap Values)

  在其他語言中,交換兩個(gè)變量值的時(shí)候,可以這樣寫:

temp = a
a = b
b = temp

  在Python中,我們可以簡單的這樣寫:

b, a = a, b

  可能你已經(jīng)在其他地方見過這種寫法,但是你知道Python是如何實(shí)現(xiàn)這種語法的嗎?首先,逗號(hào)(,)是Python中tuple數(shù)據(jù)結(jié)構(gòu)的語法;上面的語法會(huì)執(zhí)行一下的操作:

  1、Python會(huì)先將右邊的a, b生成一個(gè)tuple(元組),存放在內(nèi)存中;

  2、之后會(huì)執(zhí)行賦值操作,這時(shí)候會(huì)將tuple拆開;

  3、然后將tuple的第一個(gè)元素賦值給左邊的第一個(gè)變量,第二個(gè)元素賦值給左邊第二個(gè)變量。

  再舉個(gè)tuple拆分的例子:

In [1]: people = ['David', 'Pythonista', '15145551234']

In [2]: name, title, phone = people

In [3]: name
Out[3]: 'David'

In [4]: title
Out[4]: 'Pythonista'

In [5]: phone
Out[5]: '15145551234'

  這種語法在For循環(huán)中非常實(shí)用:

In [6]: people = [['David', 'Pythonista', '15145551234'], ['Wu', 'Student', '15101365547']]

In [7]: for name, title, phone in people:
   ...:     print name,  phone
   ...:     
David 15145551234
Wu 15101365547

  PS:在使用這種語法時(shí),需要確保左邊的變量個(gè)數(shù)和右邊tuple的個(gè)數(shù)一致,否則,Python會(huì)拋出ValueError異常。

  更多tuple的例子:

>>> 1,
(1,)
>>> (1,)
(1,)
>>> (1)
1
>>> value = 1,
>>> value
(1,)

  我們知道:逗號(hào)(,)在Python中是創(chuàng)建tuple的構(gòu)造器,所以我們可以按照上面的方式很方便的創(chuàng)建一個(gè)tuple;需要注意的是:如果聲明只有一個(gè)元素的tuple,末尾必須要帶上逗號(hào),兩個(gè)以上的元素則不需要。聲明tuple的語法很簡單,但同時(shí)它也比較坑:如果你發(fā)現(xiàn)Python中的變量不可思議的變成了tuple,那很可能是因?yàn)槟愣鄬懥艘粋(gè)逗號(hào)。。

  四、Python控制臺(tái)的"_"(Interactive "_")

  這是Python中比較有用的一個(gè)功能,不過有很多人不知道(我也是接觸Python很久之后才知道的)。。在Python的交互式控制臺(tái)中,當(dāng)你計(jì)算一個(gè)表達(dá)式或者調(diào)用一個(gè)方法的時(shí)候,運(yùn)算的結(jié)果都會(huì)放在一個(gè)臨時(shí)的變量 _ 里面。_(下劃線)用來存儲(chǔ)上一次的打印結(jié)果,比如:

>>> import math
>>> math.pi / 3
1.0471975511965976
>>> angle = _
>>> math.cos(angle)
0.50000000000000011
>>> _
0.50000000000000011

  PS:當(dāng)返回結(jié)果為None的時(shí)候,控制臺(tái)不會(huì)打印,_ 里面存儲(chǔ)的值也就不會(huì)改變。

  五、合并字符串(Building Strings from Sub strings)

  假如現(xiàn)在有一個(gè)list,里面是一些字符串,你現(xiàn)在需要將它們合并成一個(gè)字符串,最簡單的方法,你可以按照下面的方式去處理:

colors = ['red', 'blue', 'green', 'yellow']

result = ''
for s in colors:
    result += s

  但是,很快你會(huì)發(fā)現(xiàn):這種方法非常低效,尤其當(dāng)list非常大的時(shí)候。Python中的字符串對(duì)象是不可改變的,因此對(duì)任何字符串的操作如拼接,修改等都將產(chǎn)生一個(gè)新的字符串對(duì)象,而不是基于原字符串。所以,上面的方法會(huì)消耗很大的內(nèi)存:它需要計(jì)算,存儲(chǔ),同時(shí)扔掉中間的計(jì)算結(jié)果。正確的方法是使用Python中的join方法:

result = ','.join(colors)

  當(dāng)合并元素比較少的時(shí)候,使用join方法看不出太大的效果;但是當(dāng)元素多的時(shí)候,你會(huì)發(fā)現(xiàn)join的效率還是非常明顯的。不過,在使用的時(shí)候請(qǐng)注意:join只能用于元素是字符串的list,它不會(huì)進(jìn)行任何的強(qiáng)制類型轉(zhuǎn)換。連接一個(gè)存在一個(gè)或多個(gè)非字符串元素的list時(shí)將拋出異常。

  六、使用關(guān)鍵字in(Use in where possible)

  當(dāng)你需要判斷一個(gè)KEY是否在dict中或者要遍歷dict的KEY時(shí),最好的方法是使用關(guān)鍵字in:

d = {'a': 1, 'b': 2}
if 'c' in d:
    print True
# DO NOT USE
if d.has_key('c'):
    print True

for key in d:
    print key
# DO NOT USE
for key in d.keys():
    print key

  Python的dict對(duì)象是對(duì)KEY做過hash的,而keys()方法會(huì)將dict中所有的KEY作為一個(gè)list對(duì)象;所以,直接使用in的時(shí)候執(zhí)行效率會(huì)比較快,代碼也更簡潔。

  七、字典(Dictionary)

  dict是Python內(nèi)置的數(shù)據(jù)結(jié)構(gòu),在寫Python程序時(shí)會(huì)經(jīng)常用到。這里介紹一下它的get方法和defaultdict方法。

  1、get

  在獲取dict中的數(shù)據(jù)時(shí),我們一般使用index的方式,但是如果KEY不存在的時(shí)候會(huì)拋出KeyError。這時(shí)候你可以使用get方法,使用方法:dict.get(key, default=None),可以避免異常。例如:

d = {'a': 1, 'b': 2}
print d.get('c')        # None
print d.get('c', 14)    # 14

  2、fromkeys

  dict本身有個(gè)fromkeys方法,可以通過一個(gè)list生成一個(gè)dict,不過得提供默認(rèn)的value,例如:

# ?序列做 key,并提供默認(rèn)value
>>> dict.fromkeys(['a', 'b', 'c'], 1)
# {'a': 1, 'c': 1, 'b': 1}

  3、setdefault

  有些情況下,我們需要給dict的KEY一個(gè)默認(rèn)值,你可以這樣寫:

equities = {}
for (portfolio, equity) in data:
    if portfolio in equities:
        equities[portfolio].append(equity)
    else:
        equities[portfolio] = [equity]

  上面的實(shí)現(xiàn)方式很麻煩,使用dict的setdefault(key, default)方法會(huì)更簡潔,更效率。

equities = {}
for (portfolio, equity) in data:
    equities.setdefault(portfolio, []).append(equity)

  setdefault方法相當(dāng)于"get, or set & get",或者相當(dāng)于"set if necessary, then get"

  八、defaultdict

  defaultdict是Python2.5之后引入的功能,具體的用法我已經(jīng)在另外一篇文章中詳細(xì)介紹:Python的defaultdict模塊和namedtuple模塊

  九、字典的組裝和拆分(Building & Splitting Dictionaries)

  在Python中,你可以使用zip方法將兩個(gè)list組裝成一個(gè)dict,其中一個(gè)list的值作為KEY,另外一個(gè)list的值作為VALUE:

>>> given = ['John', 'Eric', 'Terry', 'Michael']
>>> family = ['Cleese', 'Idle', 'Gilliam', 'Palin']
>>> pythons = dict(zip(given, family))
>>> print pythons
{'John': 'Cleese', 'Michael': 'Palin', 'Eric': 'Idle', 'Terry': 'Gilliam'}

  相反的,你可以使用dict的keys()和values()方法來獲取KEY和VALUE的列表:

>>> pythons.keys()
['John', 'Michael', 'Eric', 'Terry']
>>> pythons.values()
['Cleese', 'Palin', 'Idle', 'Gilliam']

  需要注意的是:由于dict本身是無序的,所以通過keys()和values()方法獲得的list的順序已經(jīng)和原始的list不一樣了。。

  十、Python的True值(Truth Values)

  在Python中,判斷一個(gè)變量是否為True的時(shí)候,你可以這樣做:

# 這樣寫
if x:
    pass
# !不要這樣寫
if x == True:
    pass

# 對(duì)于list,要這樣寫
if items:
    pass
# !不要這樣寫
if len(items) == 0:
    pass

  Python中的真值對(duì)象有以下幾個(gè):

False True
False (== 0) True (== 1)
"" (空字符串) 除 "" 之外的字符串(" ", "anything")
0, 0.0 除 0 之外的數(shù)字(1, 0.1, -1, 3.14)
[], (), {}, set() 非空的list,tuple,set和dict ([0], (None,), [''])
None 大部分的對(duì)象,除了明確指定為False的對(duì)象

  對(duì)于自己聲明的class,如果你想明確地指定它的實(shí)例是True或False,你可以自己實(shí)現(xiàn)class的__nonzero__或__len__方法。當(dāng)你的class是一個(gè)container時(shí),你可以實(shí)現(xiàn)__len__方法,如下:

class MyContainer(object):

    def __init__(self, data):
        self.data = data

    def __len__(self):
        """ Return my length. """
        return len(self.data)

  如果你的class不是container,你可以實(shí)現(xiàn)__nonzero__方法,如下:

class MyClass(object):

    def __init__(self, value):
        self.value = value

    def __nonzero__(self):
        """ Return my truth value (True or False). """
        # This could be arbitrarily complex:
        return bool(self.value)

  在Python 3.x中,__nonzero__方法被__bool__方法替代。考慮到兼容性,你可以在class定義中加上以下的代碼:

__bool__ = __nonzero__

  十一、enumerate:索引和元素(Index & Item: enumerate)

  在Python中,我們?cè)诒闅v列表的時(shí)候,可以通過enumerate方法來獲取遍歷時(shí)的index,比如:

>>> items = 'zero one two three'.split()
>>> print list(enumerate(items))
[(0, 'zero'), (1, 'one'), (2, 'two'), (3, 'three')]
>>> for (index, item) in enumerate(items):
    print index, item

  enumerate方法是惰性方法,所以它只會(huì)在需要的時(shí)候生成一項(xiàng),也因此在上述代碼print的時(shí)候需要包裝一個(gè)list。enumerate其實(shí)是一個(gè)生成器(generator),這個(gè)下面會(huì)講到。使用enumerate之后,for循環(huán)變得很簡單:

for (index, item) in enumerate(items):
    print index, item

# compare:
index = 0
for item in items:
    print index, item
    index += 1

# compare:
for i in range(len(items)):
    print i, items[i]

  使用enumerate的代碼比其他兩個(gè)都短,而且更簡單,更容易讀懂。下面的例子可以說明一下enumerate實(shí)際返回的數(shù)據(jù):一個(gè)迭代器,

>>> enumerate(items)
<enumerate object at 0x011EA1C0>
>>> e = enumerate(items)
>>> e.next()
(0, 'zero')
>>> e.next()
(1, 'one')
>>> e.next()
(2, 'two')
>>> e.next()
(3, 'three')
>>> e.next()
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
StopIteration

  十二、Python中的變量 & 引用(variables & names)

  在很多其他高級(jí)語言中,給一個(gè)變量賦值時(shí)會(huì)將"value"放在一個(gè)"盒子"里:

int a = 1;

  如圖:

a1box.png

    現(xiàn)在,盒子"a"中包含了一個(gè)整數(shù) 1;將另外一個(gè)"value"賦值給同一個(gè)變量時(shí),會(huì)將"盒子"中的內(nèi)容替換掉:

a = 2;

    如圖:

a2box.png

    現(xiàn)在,盒子"a"中包含了一個(gè)整數(shù) 2;將變量賦值給其他一個(gè)變量時(shí),會(huì)將"value"拷貝一份放在一個(gè)新的"盒子"中:

int b = a;

    如圖:

b2box.png  a2box.png

  盒子"b"是第二個(gè)"盒子",里面是整數(shù) 2的一個(gè)拷貝,盒子"a"中是另外一個(gè)拷貝。

  在Python中,變量沒有數(shù)據(jù)類型,是附屬于對(duì)象的標(biāo)示符名稱,如下圖:實(shí)際,這段表明了像python,PHP這類動(dòng)態(tài)腳本語言中“變量”包含了兩個(gè)內(nèi)容:1 標(biāo)識(shí)符名稱 2 標(biāo)識(shí)符所對(duì)應(yīng)(引用)的值(對(duì)象),也就是說“變量”不在是一個(gè)容器。

a = 1

a1tag.png

    這里,整數(shù) 1 對(duì)象有一個(gè)名字為 "a" 的變量(tag)。如果我們給變量 "a" 重新賦值,對(duì)Python來說,只是將變量(tag) "a" 指向另外一個(gè)對(duì)象:

a = 2

 

a2tag.png    1.png

  現(xiàn)在,變量 "a" 是附屬在整數(shù)對(duì)象 2 上面。最初的整數(shù)對(duì)象 1 已經(jīng)沒有指向它的變量 "a",它可能還存在,但是我們已經(jīng)不能通過變量 "a"獲得。當(dāng)一個(gè)對(duì)象沒有了指向它的引用的時(shí)候,它將會(huì)被從內(nèi)存中刪除(垃圾回收)。如果我們將存在的變量賦值給一個(gè)新的變量,Python會(huì)在已經(jīng)存在的對(duì)象上加上一個(gè)指向自己的變量(tag)。

b = a

ab2tag.png

  變量 "a"和"b" 是指向同一個(gè)整數(shù)對(duì)象的。

  PS:Python中的變量,引用等設(shè)計(jì)和其他語言不同,這里只是將原文翻譯說明了一下,更多的介紹可以參看:Python中的變量、引用、拷貝和作用域

  十三、Python方法中參數(shù)的默認(rèn)值(Default Parameter Values)

  對(duì)于Python初學(xué)者來說,Python的方法默認(rèn)參數(shù)有一個(gè)很容易犯錯(cuò)的地方:在默認(rèn)參數(shù)中使用可變對(duì)象,甚至有不少Python老鳥也可能會(huì)在這個(gè)問題上掉坑里,如果他們不能理解Python的對(duì)象引用。。問題如下:

def bad_append(new_item, a_list=[]):
    a_list.append(new_item)
    return a_list

>>> print bad_append('one')
['one']
>>> print bad_append('two')
['one', 'two']

  這個(gè)問題的主要原因是:a_list參數(shù)的默認(rèn)值是一個(gè)空的list,它在函數(shù)定義的時(shí)候已經(jīng)被創(chuàng)建。所以,之后每次調(diào)用該函數(shù)的時(shí)候,a_list的默認(rèn)值都是這個(gè)list對(duì)象。List,dict和set是可變對(duì)象,如果想在函數(shù)中獲取一個(gè)默認(rèn)的list(dict or set)對(duì)象,正確的做法是在函數(shù)中創(chuàng)建:

def good_append(new_item, a_list=None):
    if a_list is None:
        a_list = []
    a_list.append(new_item)
    return a_list

  十四、字符串格式化(String Formatting)

    在許多編程語言中都包含有格式化字符串的功能,比如C語言中的格式化輸入輸出。Python中內(nèi)置有對(duì)字符串進(jìn)行格式化的操作符 "%" 以及str.format()方法。

  1、操作符 "%"

  Python中的 "%" 操作符和C語言中的sprintf類似。簡單來說,使用 "%" 來格式化字符串的時(shí)候,你需要提供一個(gè)字符串模板和用來插入的值。模板中有格式符,這些格式符為真實(shí)值預(yù)留位置,并說明真實(shí)數(shù)值應(yīng)該呈現(xiàn)的格式。Python用一個(gè)tuple將多個(gè)值傳遞給模板,每個(gè)值對(duì)應(yīng)一個(gè)格式符。注意:給定的值一定要和模板中的格式符一一對(duì)應(yīng)!

name = 'xianglong'
messages = 3
text = ('Hello %s, you have %i messages' % (name, messages))
print text

# Output: Hello xianglong, you have 3 messages

  在上面的例子中,"Hello %s, you have %i messages" 是字符串模板。%s為第一個(gè)格式符,表示一個(gè)字符串。%i為第二個(gè)格式符,表示一個(gè)十進(jìn)制整數(shù)。(name, messages)的兩個(gè)元素為替換%s和%i的真實(shí)值。在模板和tuple之間,有一個(gè)%號(hào)分隔,它代表了格式化操作。

  常用的格式符如下:

格式 描述
%% 百分號(hào) % 標(biāo)記
%s 字符串 (采用str()的顯示)
%r 字符串 (采用repr()的顯示)
%c 字符及其ASCII碼
%b 二進(jìn)制整數(shù)
%d 十進(jìn)制整數(shù) (有符號(hào)整數(shù))
%u 十進(jìn)制整數(shù) (無符號(hào)整數(shù))
%i 十進(jìn)制整數(shù) (有符號(hào)整數(shù))
%o 八進(jìn)制整數(shù) (無符號(hào)整數(shù))
%x 十六進(jìn)制整數(shù) (無符號(hào)整數(shù))
%X 十六進(jìn)制整數(shù) (無符號(hào)整數(shù))
%e 指數(shù) (基底寫為e)
%E 指數(shù) (基底寫為E)
%f 浮點(diǎn)數(shù)
%F 浮點(diǎn)數(shù),與上相同
%g 指數(shù)(e)或浮點(diǎn)數(shù) (根據(jù)顯示長度)
%G 指數(shù)(E)或浮點(diǎn)數(shù) (根據(jù)顯示長度)
%p 指針(用十六進(jìn)制打印值的內(nèi)存地址)
%n 存儲(chǔ)輸出字符的數(shù)量放進(jìn)參數(shù)列表的下一個(gè)變量中

  使用操作符 "%" 也可以通過字典格式化字符串:

values = {'name': name, 'messages': messages}
print ('Hello %(name)s, you have %(messages)i messages' % values)

# Output: Hello xianglong, you have 3 messages

  上面的代碼中,我們指定了用來格式化的值的名字,然后可以根據(jù)name在字典中查找相應(yīng)的value。其實(shí),上面的"name"和"messages"已經(jīng)在local命名空間中定義,所以,我們可以利用這一點(diǎn):

print ('Hello %(name)s, you have %(messages)i messages' % locals())

  locals()方法返回一個(gè)包含所有本地變量的字典。這個(gè)功能非常強(qiáng)大,你可以不必?fù)?dān)心提供的values是否和模板匹配;但是同時(shí)這個(gè)也是非常危險(xiǎn)的:你將會(huì)暴露整個(gè)本地命名空間給調(diào)用者,這一點(diǎn)需要你注意。

  在Python中,對(duì)象有一個(gè)__dict__屬性,你可以在格式化字符串的時(shí)候使用;

print ("We found %(error_count)d errors" % self.__dict__)

# 等同于
print ("We found %d errors" % self.error_count)

  另外,我們還可以用如下的方式,對(duì)字符串格式化進(jìn)一步的控制:%[(name)][flags][width].[precision]typecode,其中:

  (name)為命名

  flags可以有+,-,' '或0。+表示右對(duì)齊。-表示左對(duì)齊。' '為一個(gè)空格,表示在正數(shù)的左側(cè)填充一個(gè)空格,從而與負(fù)數(shù)對(duì)齊。0表示使用0填充。

  width表示顯示寬度

  precision表示小數(shù)點(diǎn)后精度

  比如:

print("%+10x" % 10)    # +a
print("%04d" % 5)    # 0005
print("%6.3f" % 2.3)    # 2.300

  上面的width, precision為兩個(gè)整數(shù)。我們可以利用*,來動(dòng)態(tài)代入這兩個(gè)量。比如:

print("%.*f" % (4, 1.2))    # 1.2000

  Python實(shí)際上用4來替換*。所以實(shí)際的模板為"%.4f"。

  2、str.format()方法

  str.format()方法是在Python 2.6中引入的,它通過 {} 和 : 來代替 % ,功能非常強(qiáng)大。具體的用法見下面的例子:

In [1]: name = 'xianglong'
In [2]: messages = 4

# 通過位置
In [3]: 'Hello {0}, you have {1} messages'.format(name, messages)
Out[3]: 'Hello xianglong, you have 4 messages'

# 通過關(guān)鍵字參數(shù)
In [4]: 'Hello {name}, you have {messages} messages'.format(name=name, messages=messages)
Out[4]: 'Hello xianglong, you have 4 messages'

# 通過下標(biāo)
In [5]: 'Hello {0[0]}, you have {0[1]} messages'.format([name, messages])
Out[5]: 'Hello xianglong, you have 4 messages'

# 格式限定符:填充與對(duì)齊
# ^、<、>分別是居中、左對(duì)齊、右對(duì)齊,后面帶寬度
# :號(hào)后面帶填充的字符,只能是一個(gè)字符,不指定的話默認(rèn)是用空格填充
In [6]: 'Hello {0:>14}, you have {1:>14} messages'.format(name, messages)
Out[6]: 'Hello      xianglong, you have              4 messages'

# 格式限定符:精度與類型f
In [7]: '{:.2f}'.format(321.33345)
Out[7]: '321.33'

# 格式限定符:b、d、o、x分別是二進(jìn)制、十進(jìn)制、八進(jìn)制、十六進(jìn)制
In [8]: '{:b}'.format(14)
Out[8]: '1110'

In [9]: '{:d}'.format(14)
Out[9]: '14'

In [10]: '{:o}'.format(14)
Out[10]: '16'

In [11]: '{:x}'.format(14)
Out[11]: 'e'

# 格式限定符:千位分隔符
In [12]: '{:,}'.format(1234567890)
Out[12]: '1,234,567,890'

  更多關(guān)于Python字符串格式化的介紹,可以參看:PEP 3101 -- Advanced String Formatting

  十五、迭代器(List comprehensions)

  List Comprehensions即迭代器(列表生成式),是Python內(nèi)置的非常簡單卻強(qiáng)大的可以用來創(chuàng)建list的生成式。在不使用迭代器的時(shí)候,創(chuàng)建一個(gè)新列表可以使用for和if來實(shí)現(xiàn):

new_list = []
for item in a_list:
    if condition(item):
        new_list.append(fn(item))

  使用迭代器的話:

new_list = [fn(item) for item in a_list if condition(item)]

  列表生成式非常簡潔的,不過是在某種程度上。你可以在列表生成式中使用多個(gè)for循環(huán)和多個(gè)if語句,但是兩個(gè)以上的for和if語句會(huì)讓列表生成式非常復(fù)雜,這時(shí)候建議直接用for循環(huán)。根據(jù)Zen of Python,選擇更容易讀的方式。下面是一些例子:

>>> [n ** 2 for n in range(10)]
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

>>> [n ** 2 for n in range(10) if n % 2]
[1, 9, 25, 49, 81]

  十六、生成器(Generator & Generator expressions)

  先出一個(gè)題:計(jì)算1 ~ 100的平方和。最簡單的方法就是使用一個(gè)for循環(huán):

total = 0
for num in range(1, 101):
    total += num * num

  其實(shí),我們可以使用Python內(nèi)置的sum方法計(jì)算:

# 迭代器(列表生成式)
total = sum([num * num for num in range(1, 101)])

# 生成器
total = sum(num * num for num in xrange(1, 101))

  生成器和上面提到的迭代器差不多,可以說:生成器是一種特殊的迭代器;但是它們之間有一個(gè)很大的區(qū)別:迭代器是貪婪的,而生成器是懶惰的,具體來說:迭代器會(huì)一次性的計(jì)算出整個(gè)結(jié)果列表,而生成器只在需要的時(shí)候計(jì)算一個(gè)值。這個(gè)特性在列表非常大,或者需要一步一步計(jì)算的時(shí)候非常有用。

  在上面的例子中,我們只需要平方和,不需要平方的list,所以我們使用生成器xrange。如果我們計(jì)算1 ~ 1000000000的平方和,使用迭代器的話會(huì)內(nèi)存溢出,但是生成器卻不會(huì):

total = sum(num * num for num in xrange(1, 1000000000))

  在語法上,迭代器會(huì)有一個(gè)"[]",但是生成器沒有;不過有時(shí)候,生成器需要"()",所以,最好每次都帶上。一些自定義的生成器例子:

# 過濾CSV文件中的空行
def filter_rows(row_iterator):
    for row in row_iterator:
        if row:
            yield row

data_file = open(path, 'rb')
irows = filter_rows(csv.reader(data_file))

# 文件讀。簅pen
datafile = open('datafile')
for line in datafile:
    do_something(line)

  PS:原文中作者舉了一些工作中的例子,這里不再贅述,想了解的可以到原文鏈接中查看。

  十七、排序(Sorting)

  在Python中對(duì)列表排序非常簡單,比如:

In [1]: a_list = ['Tommy', 'Jack', 'Smith', 'Paul']
In [2]: a_list.sort()
In [3]: a_list
Out[3]: ['Jack', 'Paul', 'Smith', 'Tommy']

  需要注意的是:list的sort()方法會(huì)直接在原list變量上排序,改變?cè)镜膌ist對(duì)象,并且該方法不會(huì)返回一個(gè)list對(duì)象。如果你需要不改變?cè)璴ist,并且返回新的list對(duì)象的話,可以使用Python的orted方法:

In [1]: a_list = ['Tommy', 'Jack', 'Smith', 'Paul']
In [2]: b_list = sorted(a_list)
In [3]: b_list
Out[3]: ['Jack', 'Paul', 'Smith', 'Tommy']
In [4]: a_list
Out[4]: ['Tom', 'Jack', 'Smith', 'Paul']

  但是,如果你想對(duì)一個(gè)list進(jìn)行排序,不過不想使用默認(rèn)的排序方式,比如你可能需要先根據(jù)第二行排序,再根據(jù)第四行排序。這時(shí)候,我們也可以使用sort()方法,但是得提供一個(gè)自定義的排序方法:

In [1]: def custom_cmp(item1, item2):
   ...:     return cmp((item1[1], item1[3]), (item2[1], item2[3]))
   ...: 
In [2]: a_list = ['Tommy', 'Jack', 'Smith', 'Paul']
In [3]: a_list.sort(custom_cmp)
In [4]: a_list
Out[4]: ['Jack', 'Paul', 'Smith', 'Tommy']

  這種方法可以實(shí)現(xiàn),但是在list比較大的情況下效率很低。下面介紹兩種其他的方法。

  1、DSU排序方法

  DSU即Decorate-Sort-Undecorate,中文就是"封裝-排序-解封"。DSU方法不會(huì)創(chuàng)建自定義的排序方法,而是創(chuàng)建一個(gè)輔助的排序列表,然后對(duì)這個(gè)列表進(jìn)行默認(rèn)排序。需要說明的是:DSU方法是一種比較老的方法,現(xiàn)在已經(jīng)基本上不使用了,不過這里還是給出一個(gè)簡單的例子說明一下:

# Decorate:
to_sort = [(item[1], item[3], item) for item in a_list]

# Sort:
to_sort.sort()

# Undecorate:
a_list = [item[-1] for item in to_sort]

  上述代碼第一行創(chuàng)建了一個(gè)tuple的list,tuple中的前兩項(xiàng)是用來排序的字段,最后一項(xiàng)是原數(shù)據(jù);第二行使用sort()方法對(duì)輔助的list進(jìn)行默認(rèn)的排序;最后一行是從已經(jīng)排序的輔助list中獲取原數(shù)據(jù),重新組成list。

  這種方法是使用復(fù)雜度和內(nèi)存空間來減少計(jì)算時(shí)間,比較簡單,也比較快,但是我們得復(fù)制原列表的數(shù)據(jù)。

  2、KEY方法

  自從Python 2.4之后,list.sort()和sorted()都添加了一個(gè)key參數(shù)用來指定一個(gè)函數(shù),這個(gè)函數(shù)作用于每個(gè)list元素,在做cmp之前調(diào)用。key參數(shù)是一個(gè)函數(shù),這個(gè)函數(shù)有一個(gè)參數(shù),返回一個(gè)用來排序的關(guān)鍵字。這種排序方法很快,因?yàn)閗ey方法在每個(gè)輸入的record上只執(zhí)行一次。你可以使用Python內(nèi)置的函數(shù)(len, str.lower)或者自定義函數(shù)作為key參數(shù),下面是一個(gè)簡單的例子:

In [1]: a_list = ['Tommy', 'Jack', 'Smith', 'Paul']
In [2]: def my_key(item):
   ...:     return (item[1], item[3])
   ...: 
In [3]: a_list.sort(key=my_key)
In [4]: a_list
Out[4]: ['Jack', 'Paul', 'Smith', 'Tommy']

  十八、EAFP vs LBYL

  檢查數(shù)據(jù)可以讓程序更健壯,用術(shù)語來說就是防御性編程。檢查數(shù)據(jù)的時(shí)候,有EAFP和LBYL兩種不同的編程風(fēng)格,具體的意思如下:

  LBYL: Look Before You Leap,即事先檢查;

  EAFP: It's Easier to Ask Forgiveness than Permission,即不檢查,出了問題由異常處理來處理。

  異常處理總是比事先檢查容易,因?yàn)槟愫茈y提前想到所有可能的問題。所以,一般情況下編碼時(shí)會(huì)傾向使用EAFP風(fēng)格,但它也不是適應(yīng)所有的情況。兩個(gè)風(fēng)格的優(yōu)缺點(diǎn)如下:

d = {}
words = ['a', 'd', 'a', 'c', 'b', 'z', 'd']
# LBYL
for w in words:
    if w not in d:
        d[w] = 0
    d[w] += 1

# EAFP
for w in words:
    try:
        d[w] += 1
    except KeyError:
        d[w] = 1

  對(duì)于LBYL,容易打亂思維,本來業(yè)務(wù)邏輯用一行代碼就可以搞定的。卻多出來了很多行用于檢查的代碼。防御性的代碼跟業(yè)務(wù)邏輯混在一塊降低了可讀性。而EAFP,業(yè)務(wù)邏輯代碼跟防御代碼隔離的比較清晰,更容易讓開發(fā)者專注于業(yè)務(wù)邏輯。不過,異常處理會(huì)影響一點(diǎn)性能。因?yàn)樵诎l(fā)生異常的時(shí)候,需要進(jìn)行保留現(xiàn)場、回溯traceback等操作。但其實(shí)性能相差不大,尤其是異常發(fā)生的頻率比較低的時(shí)候。

  另外,需要注意的是,如果涉及到原子操作,強(qiáng)烈推薦用EAFP風(fēng)格。比如我某段程序邏輯是根據(jù)redis的key是否存在進(jìn)行操作。如果先if exists(key),然后do something。這樣就變成2步操作,在多線程并發(fā)的時(shí)候,可能key的狀態(tài)已經(jīng)被其他線程改變了。而用EAFP風(fēng)格則可以確保原子性。

  PS:在使用EAFP風(fēng)格捕獲異常時(shí),盡量指明具體的異常,不要直接捕獲Exception。否則會(huì)捕獲到其他未知的異常,如果有問題,你會(huì)很難去定位(debug)。

  十九、引用(Importing)

  Python中的引用:

from module import *

  你可能在其他地方見過這種使用通配符*的引用方式,可能你也比較喜歡這種方式。但是,這里要說的是:請(qǐng)不要使用這種引用方式!

  通配符引用的方式屬于Python中的陰暗面,這種方式會(huì)導(dǎo)致命名空間污染的問題。你可能會(huì)在本地命名空間中得到意想不到的東西,而且這種方式引入的變量可能將你在本地定義的變量覆蓋,在這種情況下,你很難弄清楚變量來自哪里。所以,通配符引用的方式雖然方便,但可能會(huì)導(dǎo)致各種各樣奇怪的問題,在Python項(xiàng)目中盡量不要用這種方式。

  在Python中,大家比較認(rèn)同的import方式有以下幾個(gè)規(guī)則:

  1、通過模塊引用變量(Reference names through their module)

  這種方式直接import的是模塊,然后通過模塊訪問其中的變量,Class和方法。使用這種方式可以很清晰的知道變量來自哪里:

import module
module.name

  2、模塊名比較長時(shí)使用短名字(alias)

import long_module_name as mod
mod.name

  3、直接引用你需要的變量名

from module import name
name

  二十、模塊與腳本(Modules & Scripts)

  為了使一個(gè)Python文件既可以被引用,又可以直接執(zhí)行,你可以在Python文件中加上這樣的代碼:

if __name__ == '__main__':
    # script code here

  當(dāng)被引用時(shí),一個(gè)模塊(module)的__name__屬性會(huì)被設(shè)置為該文件的文件名(不包括.py后綴)。所以,上面代碼片段中if語句中的腳本在被引用的時(shí)候不會(huì)執(zhí)行。當(dāng)把Python文件作為一個(gè)腳本執(zhí)行的時(shí)候,__name__屬性則被設(shè)置為"__main__",這時(shí)候if語句中的腳本才會(huì)被執(zhí)行。

  最好不要在Python文件中直接寫可執(zhí)行的語句,應(yīng)該將這些代碼放在方法或類里面,必要的時(shí)候放在"if __name__  == '__main__':"中。一個(gè)Python文件的結(jié)構(gòu)可以參考下面的:

#!/usr/bin/env python
# -*- coding: utf-8 -*-
""" 文檔 module docstring """

# 引用 imports
# 常量 constants
# 異常 exception classes
# 方法 interface functions
# 類 classes
# 內(nèi)部方法和類 internal functions & classes

def main(...):
    ...

if __name__ == '__main__':
    status = main()
    sys.exit(status)

  二十一、命令行解析(Commend-Line Processing)

  Python是一種腳本語言,有時(shí)候我們會(huì)直接在命令行運(yùn)行Python文件,這時(shí)候可能需要解析命令行傳入的參數(shù),下面是一個(gè)例子:cmdline.py

#!/usr/bin/env python

"""
Module docstring.
"""

import sys
import optparse

def process_command_line(argv):
    """
    Return a 2-tuple: (settings object, args list).
    `argv` is a list of arguments, or `None` for ``sys.argv[1:]``.
    """
    if argv is None:
        argv = sys.argv[1:]

    # initialize the parser object:
    parser = optparse.OptionParser(
        formatter=optparse.TitledHelpFormatter(width=78),
        add_help_option=None)

    # define options here:
    parser.add_option(      # customized description; put --help last
        '-h', '--help', action='help',
        help='Show this help message and exit.')

    settings, args = parser.parse_args(argv)

    # check number of arguments, verify values, etc.:
    if args:
        parser.error('program takes no command-line arguments; '
                     '"%s" ignored.' % (args,))

    # further process settings & args if necessary

    return settings, args

def main(argv=None):
    settings, args = process_command_line(argv)
    # application code here, like:
    # run(settings, args)
    return 0        # success

if __name__ == '__main__':
    status = main()
    sys.exit(status)

  二十二、包(Packages)

  Python中包的設(shè)計(jì)與引用規(guī)則,包的設(shè)計(jì)例子:

package/
    __init__.py
    module1.py
    subpackage/
        __init__.py
        module2.py

  建議使用上面的方式來組織你的項(xiàng)目,盡量減小引用路徑,明確引用對(duì)象,避免引用沖突。引用示例:

import package.module1
from package.subpackage import module2
from package.subpackage.module2 import name

  我們可以通過__future__模塊使用Python 3.0的功能:absolute_import。方法如下:

from __future__ import absolute_import

  簡單介紹一下相對(duì)引入和絕對(duì)引入的概念:    

  相對(duì)導(dǎo)入:在不指明 package 名的情況下導(dǎo)入自己這個(gè) package 的模塊,比如一個(gè) package 下有 a.py 和 b.py 兩個(gè)文件,在 a.py 里 from . import b 即是相對(duì)導(dǎo)入 b.py。

  絕對(duì)導(dǎo)入:指明頂層 package 名。比如 import a,Python 會(huì)在 sys.path 里尋找所有名為 a 的頂層模塊。

  引入absolute_import之后不是支持了絕對(duì)引入,而是拒絕相對(duì)引入。

  簡單比復(fù)雜好

  調(diào)試程序的難度是寫代碼的兩倍。因此,只要你的代碼寫的盡可能的清楚,那么你在調(diào)試代碼時(shí)就不需要那么地有技巧。(Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it. ) -- Brian Kernighan。所以,盡量保持你的程序足夠簡單。

  不要重復(fù)造輪子

  在你寫代碼之前,你需要先看一下有沒有其他人已經(jīng)實(shí)現(xiàn)了類似的功能。你可以從下面的幾個(gè)地方尋找:

  1、Python標(biāo)準(zhǔn)庫

  2、Python第三方LIB, PYPI(Python Package Index),地址:PYPI

  3、搜索引擎,Google,百度等。。

  參考

  Code Like a Pythonista: Idiomatic Python

  writing idiomatic python 3

  LBYL與EAFP兩種防御性編程風(fēng)格

  Python高級(jí)編程

  Python補(bǔ)充05 字符串格式化 (%操作符)

  How to sorting

  探索 Python 的變量、類型和引用

  Over!

  via:xianglong.me

標(biāo)簽: b2b Google swap 代碼 腳本 開發(fā)者 搜索 搜索引擎

版權(quán)申明:本站文章部分自網(wǎng)絡(luò),如有侵權(quán),請(qǐng)聯(lián)系:west999com@outlook.com
特別注意:本站所有轉(zhuǎn)載文章言論不代表本站觀點(diǎn)!
本站所提供的圖片等素材,版權(quán)歸原作者所有,如需使用,請(qǐng)與原作者聯(lián)系。

上一篇:C++指針的概念解讀 超詳細(xì)

下一篇:Android Touch事件傳遞機(jī)制通俗講解