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



分享到:


相關文章: