python常用的小技巧

今天給大家整理一些在別人博客文章中看到的python的小技巧,有些確實很常用哦!

python常用的小技巧

1、Unpacking(拆箱)

>>> a,b,c = 1,2,3
>>> a,b,c
(1, 2, 3)
>>> a,b,c = [1,2,3]
>>> a,b,c
(1, 2, 3)
>>> a,b,c = (2 * i + 1 for i in range(3))
>>> a,b,c
(1, 3, 5)
>>> a,(b,c),d = [1,(2,3),4]
>>> a,b,c,d
(1, 2, 3, 4)

更多參考:

python3特性一:高級拆箱

Packing and Unpacking Arguments in Python

有沒有想過你在Python函數中看到的*args和**kwargs是什麼意思?

*和**運算符都根據它們的使用位置執行兩種不同但互補的操作。在方法定義中使用時,如下所示:

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

他們執行一項名為“packing(打包)”的操作。它的作用是將這個方法調用的所有參數打包(pack)成一個單獨的變量,一個名為args的元組。當然,你可以使用你想要的任何變量名,但是args似乎是最常見和更加Pythonic way。

一旦你有了這個'packed'變量,就可以使用普通元組(normal tuple)來做一些事情。比如args[0]和args[1]分別會給你第一個和第二個參數。如果將args元組轉換為列表,您還可以修改,刪除和重新排列其中的items。

那麼如何將這些packed的參數傳遞給另一個方法呢?這就是我們的unpacking了:

def __init__(self, *args, **kwargs):
# you can do something
super(AwesomeClass, self).__init__(self, *args, **kwargs)
# ^
# LOOK HERE!

所以再次使用相同的*運算符,但這次是在方法調用的前後中。它現在所做的就是分解args數組並調用該方法,就像您已經分別輸入每個變量一樣。

請看下面:

def func1(x, y, z):
print x
print y
print z
def func2(*args):
# Convert args tuple to a list so we can modify it
args = list(args)
args[0] = 'Hello'
args[1] = 'awesome'
func1(*args)
func2('Goodbye', 'cruel', 'world!')
# Will print
# > Hello
# > awesome
# > world!

之所以會出現上面的結果,是因為我們在將它們傳遞給func1之前更改了前兩個參數。

管理方法定義的常規規則適用於此……調用func2('a'、'b'、'c'、'd')將引發一個錯誤,因為它將調用func1,並帶有四個參數,這是func1意料不到的。

同樣的原則也適用於**kwargs,除了在這種情況下它適用於關鍵字參數,並且kwargs結果是dict。

結合packing和unpacking可以讓你做很多事情,比如:

  • 在傳遞參數之前驗證它們
  • 設置位置參數的默認值
  • 為不同的代碼/庫創建適配器
  • 等等

2、Unpacking for swapping variables(拆箱變量交換)

>>> a,b = 1,2
>>> a,b = b,a
>>> a,b
(2, 1)

3、Extended unpacking(Python 3 only)(擴展拆箱)

>>> a,*b,c = [1,2,3,4]
>>> a
1
>>> b
[2, 3]

>>> c
4

4、Negative indexing(負數索引)

(從-1開始,-1表示最後一個)

>>> a = [0,1,2,3,4,5,6,7,8,9]
>>> a[-1]
9
>>> a[-3]
7

5、List slices(a[start:end])(切割列表)

(不包括end那個點,從0開始索引)

>>> a = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> a[1:3]
[1, 2]

6、List slices with negative indexing(負數索引切割列表)

>>> a = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> a[-4:-2]
[6, 7]

7、List slices with step (a[start:end:step])(指定步長切割列表)

[::]表示取所有行所有列

>>> a = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> a[::2]

[0, 2, 4, 6, 8]
>>> a[::3]
[0, 3, 6, 9]
>>> a[2:8:2]
[2, 4, 6]

8、List slices with negative step(負數步長切割列表)

>>> a = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> a[::-1]
[9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
>>> a[::-2]
[9, 7, 5, 3, 1]

9、List slice assignment(列表切割賦值)

>>> a = [1,2,3,4,5]
>>> a[2:3]
[3]
#給這個位置賦值一個列表進去
>>> a[2:3] = [0,0]
>>> a
[1, 2, 0, 0, 4, 5]
>>> a[1:1]
[]
>>> a[1:1] = [6,7]
>>> a
[1, 6, 7, 2, 0, 0, 4, 5]
>>> a[1:-1]
[6, 7, 2, 0, 0, 4]
>>> a[1:-1] = []
>>> a
[1, 5]

10、Naming slices (slice(start, end, step))(命名列表切割方式)

>>> a = [0,1,2,3,4,5]
>>> lastThree = slice(-3,None)

>>> lastThree
slice(-3, None, None)
>>> a[lastThree]
[3, 4, 5]

11、Iterating over list index and value pairs (enumerate)

>>> a = ["hello","world","!"]
>>> for (i,x) in enumerate(a):
... print("{}:{}".format(i,x))
...
0:hello
1:world
2:!

12、Iterating over dictionary key and value pairs (dict.iteritems)

>>> m = {'a':1,'b':2,'c':3,'d':4}
>>> for (k, v) in m.items():
... print("{} : {}".format(k,v))
...
a : 1
b : 2
c : 3
d : 4

注意:在python3.x中,iteritems方法已經被廢除了,使用items方法替代。

Python字典中items()和iteritems()區別

13、Zipping and unzipping lists and iterables(列表以及迭代器的壓縮和解壓縮)

>>> a = [1,2,3]
>>> m = ['a','b','c']
>>> zipped = zip(a,b) #返回一個對象
>>> zipped


>>> list(zipped) #list()轉換為列表
[(1, 'a'), (2, 'b'), (3, 'c')]
>>> a1,a2 = zip(*zipped) # 與 zip 相反,zip(*) 可理解為解壓,返回二維矩陣
>>> a1
(1, 2, 3)
>>> a2
('a', 'b', 'c')

Python3 zip() 函數

14、Grouping adjacent list items using zip(列表相鄰元素壓縮器)

>>> a = [1,2,3,4,5,6]
>>> # Using iterators
>>> groupAdjacent = lambda a,k : zip(*([iter(a)] * k))
>>> groupAdjacent(a,3)

>>> list(groupAdjacent(a,3))
[(1, 2, 3), (4, 5, 6)]
>>> list(groupAdjacent(a,2))
[(1, 2), (3, 4), (5, 6)]
>>> list(groupAdjacent(a,1))
[(1,), (2,), (3,), (4,), (5,), (6,)]

上面代碼的理解:

首先關於python的迭代器。iter()能把一個序列生成為一個迭代器,迭代器的特點是可以用for in語句迭代,原理是迭代器對象有一個next方法,可以每次移動迭代的指針,一旦迭代完,沒有下一個元素的時候,會應發一個StopIteration異常。

在我們迭代了一次之後,指針就移動了,不會自動回溯。比如:

>>> a = [1,2,3]
>>> for m in a:
... print(m)
...
1
2
3
>>> x = iter(a)
>>> for i in x:
... print(i)
...
1
2
3
>>> for i in x:
... print(x) # x 已經被迭代過了,是迭代的指針沒有回去,理解為被清空了。
...

我們可以用for in循環列表a無數次,但是對於x,我們只能for in一次,因為迭代指針到達了迭代器的尾部。

第二個就是關於zip函數,它可以將兩個序列對應著打包,而我們提過關於*的用法,它是python函數可變參數的一種表示方式,加入*表示傳入一個元組對象進行解包。

最後是lambda函數,

lambda express: express 返回一個可以調用的對象,可以理解為函數
double = lambda x : return x * 2

上面等價於

def double(x):
retrun x * 2

所以關於

group_adjacent = lambda a, k: zip(*([iter(a)] * k))

我們首先看(k取3):

>>>[iter(x)]*3
[, , ]

可以看到,列表中的3個迭代器實際上是同一個迭代器!!!

怎麼解釋呢?首先看下面的一段代碼:

>>> a = [1,2,3,4,5,6]
>>> x = iter(a)
>>> t = [a,a]
>>> t
[[1, 2, 3, 4, 5, 6], [1, 2, 3, 4, 5, 6]]
>>> zip(*t) #這是一

>>> list(zip(*t))
[(1, 1), (2, 2), (3, 3), (4, 4), (5, 5), (6, 6)]
>>> tx = [x,x]
>>> list(zip(*tx)) #這是二
[(1, 2), (3, 4), (5, 6)]

上面的一:這裡的含義表示zip傳了兩個參數a,a1,a2都是a,所以我們執行了打包操作,打包了這兩個序列。

上面的二:這裡因為x是迭代器對象,迭代就調用next方法。也就是 zip執行打包的過程先調用第一個參數的x的next方法得1,然後調用第二個參數的x的next,因為這兩個x對象實際上是一樣的,調用第二個x的next方法的時候,迭代的指針已經移動,實際得到的時2,以次類推,過程模擬如下表示

1 x.next -> 1
2 x.next -> 2
3 zip(x.next(), x.next()) ---> zip(1, 2)
4 x.next -> 3
5 x.next -> 4
6 zip(x.next(), x.next()) ---> zip(3, 4)
....

等價於下面的方式:

zip([1, 3, 5], [2, 4, 6])

所以上面的也就明瞭了。我們有三個一樣的迭代器,也就是會打包成元組,每個元組包含3個元素,就是我們的結果了。

group_adjacent = lambda a, k: zip(*([iter(a)] * k))

所以上面的代碼表示:定義一個匿名函數,參數是 a和k,並綁定變量 group_adjacent。匿名函數的主體內容是,用iter將序列迭代化,然後用zip打包這個迭代器對象。

>>> groupAdjacent = lambda a, k: zip(*(a[i::k] for i in range(k)))
>>> groupAdjacent(a,3)


>>> list(groupAdjacent(a,3))
[(1, 2, 3), (4, 5, 6)]

首先關於a[i::k]。我們知道a[::k],表示每k個值取一個,所以我們的a[i::k]就表示在我們劃分的新的列表中,取第幾個,比如:

>>> a = [1,2,3,4,5,6]
>>> a[0::3] #取第一個
[1, 4]
>>> a[1::3] #取第二個
[2, 5]
>>> a[2::3] #取第三個
[3, 6]
>>>
>

所以zip(*(a[i::k] for i in range(k)))的意思就很明瞭了。如果我們的k為3,那麼我們便得到了([1,4],[2,5],[3,6]),然後用zip函數打包。

更多參考:

python列表相鄰元素壓縮器

python 使用zip合併相鄰的列表項

15、Sliding windows (nn -grams) using zip and iterators(在列表中用壓縮器和迭代器滑動取值窗口)

>>> def n_grams(a, n):
... z = [iter(a[i:]) for i in range(n)]
... return zip(*z)
...
>>> n_grams(a,3)


>>> list(n_grams(a,3))
[(1, 2, 3), (2, 3, 4), (3, 4, 5), (4, 5, 6)]
>>> list(n_grams(a,2))
[(1, 2), (2, 3), (3, 4), (4, 5), (5, 6)]

16、Inverting a dictionary using zip(用壓縮器反轉字典)

>>> m = {'a': 1, 'b': 2, 'c': 3, 'd': 4}
>>> m.items()
dict_items([('a', 1), ('b', 2), ('c', 3), ('d', 4)])
>>> zip(m.values(),m.keys())

>>> list(zip(m.values(),m.keys()))
[(1, 'a'), (2, 'b'), (3, 'c'), (4, 'd')]
>>> mi = dict(zip(m.values(),m.keys()))
>>> mi
{1: 'a', 2: 'b', 3: 'c', 4: 'd'}

17、Flattening lists(列表展開)

>>>import itertools
>>> a = [[1, 2], [3, 4], [5, 6]]
>>> list(itertools.chain.from_iterable(a))
[1, 2, 3, 4, 5, 6]
>>> sum(a, [])
[1, 2, 3, 4, 5, 6]
>>> [x for l in a for x in l]
[1, 2, 3, 4, 5, 6]
>>> a = [[[1, 2], [3, 4]], [[5, 6], [7, 8]]]
>>> [x for l1 in a for l2 in l1 for x in l2]
[1, 2, 3, 4, 5, 6, 7, 8]
>>> a = [1, 2, [3, 4], [[5, 6], [7, 8]]]
>>> flatten = lambda x: [y for l in x for y in flatten(l)] if type(x) is list else [x]
>>> flatten(a)
[1, 2, 3, 4, 5, 6, 7, 8]

注意:根據Python關於sum的文檔,itertools.chain.from_iterable是首選方法。

18、Generator expressions(生成器表達式)

>>> g = (x ** 2 for x in range(10))
>>> g
at 0x00000268994B6150>
>>> next(g)
0
>>> next(g)
1
>>> sum(x ** 2 for x in range(10))
285
>>>

19、Dictionary comprehensions(字典推導)

>>> m = {x: x ** 2 for x in range(5)}
>>> m
{0: 0, 1: 1, 2: 4, 3: 9, 4: 16}
>>> m = {x: 'A' + str(x) for x in range(10)}
>>> m
{0: 'A0', 1: 'A1', 2: 'A2', 3: 'A3', 4: 'A4', 5: 'A5', 6: 'A6', 7: 'A7', 8: 'A8', 9: 'A9'}

20、 Inverting a dictionary using a dictionary comprehension(用字典推導反轉字典)

>>> m = {'a': 1, 'b': 2, 'c': 3, 'd': 4}
>>> m
{'a': 1, 'b': 2, 'c': 3, 'd': 4}
>>> {v : k for k,v in m.items()}
{1: 'a', 2: 'b', 3: 'c', 4: 'd'}
>>>

21、Named tuples (collections.namedtuple)(命名元組)

>>> import collections 

>>> Point = collections.namedtuple('Point',['x','y'])
>>> p = Point(x=1.0,y=2.0)
>>> p
Point(x=1.0, y=2.0)
>>> p.x
1.0
>>> p.y
2.0

22、Inheriting from named tuples(繼承命名元組)

import collections
>>> class Point(collections.namedtuple('PointBase', ['x', 'y'])):
... __slots__ = ()
... def __add__(self, other):
... return Point(x=self.x + other.x, y=self.y + other.y)
...
>>> p = Point(x=1.0, y=2.0)
>>> q = Point(x=2.0, y=3.0)
>>> p + q
Point(x=3.0, y=5.0)

23、Sets and set operations(操作集合)

>>> A = {1,2,3,3}
>>> A
{1, 2, 3}
>>> B = {3,4,5,6,7}
>>> A | B
{1, 2, 3, 4, 5, 6, 7}
>>> A & B
{3}
>>> A - B
{1, 2}
>>> B - A
{4, 5, 6, 7}
>>> A ^ B
{1, 2, 4, 5, 6, 7}

小補充:

臨時性變量名稱:

_ 作為臨時性的名稱使用。這樣,當其他人閱讀你的代碼時將會知道,你分配了一個特定的名稱,但是並不會在後面再次用到該名稱。例如,下面的例子中,你可能對循環計數的實際值並不感興趣,此時就可以使用_。

n = 3
>>> for _ in range(n):
... print("hello world")
...
hello world
hello world
hello world

更多的參考:

30個有關Python的小技巧

30 Python Language Features and Tricks You May Not Know About

文章到此就結束了!!可以關注加收藏哦!!博客排版更好(這裡參考鏈接沒給出)

博客地址:https://0leo0.github.io/2018/normal_02.html



分享到:


相關文章: