美文网首页
python-12 小技巧

python-12 小技巧

作者: 巴巴11 | 来源:发表于2020-04-13 22:50 被阅读0次

条件表达式

if x > 0:
    y = math.log(x)
else:
    y = float('nan')

y = math.log(x) if x > 0 else float('nan')

def factorial(n):
    if n == 0:
        return 1
    else:
        return n * factorial(n-1)
def factorial(n):
    return 1 if n == 0 else n * factorial(n-1)


条件表达式的另一个用处是处理可选参数。
def __init__(self, name, contents=None):
    self.name = name
    if contents == None:
        contents = []
    self.pouch_contents = contents
我们可以像这样重写:
def __init__(self, name, contents=None):
    self.name = name
    self.pouch_contents = [] if contents == None else contents

列表推导式

def capitalize_all(t):
    res = []
    for s in t:
        res.append(s.capitalize())
    return res

def capitalize_all(t):
    return [s.capitalize() for s in t]

方括号操作符表示,我们正在构造一个新列表。方括号中的表达式指定列表中的元素,for 子句表示我们要遍历的序列。

列表推导式的语法有点奇怪,因为此例中的循环变量 s 在定义之前就出现了。

列表推导式也可以用于筛选。例如,这个函数只选择 t 中为大写的元素,并返回一个新列表:

def only_upper(t):
    res = []
    for s in t:
        if s.isupper():
            res.append(s)
    return res
我们可以使用列表推导式重写这个函数:

def only_upper(t):
    return [s for s in t if s.isupper()]
列表推导式非常简洁、易读,至少对简单的表达式是这样的。而且通常比对应的 for 循环要更快,有时要快很多。

列表推导式的调试难度更大,因为你不能在循环中添加打印语句。建议只在计算足够简单、第一次就能写出正确代码的前提下使用。

生成器表达式

生成器表达式与列表推导式类似,但是使用的是圆括号,而不是方括号:

>>> g = (x**2 for x in range(5))
>>> g
<generator object <genexpr> at 0x7f4c45a786c0>
结果是一个表达式对象,该对象知道如何遍历一个值序列。但与列举推导式不同的是,它不会一次性计算出所有的值;而是等待求值请求。内建函数 next 从生成器获取下一个值:

>>> next(g)
0
>>> next(g)
1
抵达序列的末尾时,next 会抛出 StopIteration 异常。你还可以使用 for 循环遍历这些值:

>>> for val in g:
...     print(val)
4
9
16
生成器对象会记录其在序列中的位置,因此 for 循环是从 next 结束的地方开始的。一旦生成器被消耗完,它会抛出 StopException 。

>>> next(g)
StopIteration
生成器表达式常与 sum 、max 和 min 等函数一起使用:

>>> sum(x**2 for x in range(5))
30

any 和 all

Python提供了一个内建函数 `any`,它接受一个布尔值序列,如果其中有任意一个值为 `True` 则返回 `True` 。它也适用于列表:
>>> any([False, False, True])
True


但是它通常用于生成器表达式:
>>> any(letter == 't' for letter in 'monty')
True


上面这个例子不是很有用,因为它的功能和 in 操作符一样。
可以像这样编写 `avoids` 函数:
def avoids(word, forbidden):
    return not any(letter in forbidden for letter in word)


上面的函数读取来和英语没什么区别:“word avoids forbidden if there are not any forbidden letters in word.”(如果某个词中没有任何禁用字母,那么该词就算避免了使用禁用词。)

将 `any` 与生成器表达式结合使用的效率较高,因为它只要一遇到真值就会终止,所以不会对整个序列进行计算。

Python还提供了另一个内建函数 `all`,如果序列中的每个元素均为 `True` 才会返回 `True` 。

集合



uses_only 函数:
def uses_only(word, available):
    for letter in word:
        if letter not in available:
            return False
    return True
uses_only 检查 word 中的所有字符也在 available 中。我们可以像这样重写该函数:
def uses_only(word, available):
    return set(word) <= set(available)
操作符 <= 检查某个集合是否是另一个集合的子集或本身,包括了二者相等的可能性。如果 word 中所有的字符都出现在 available 中,则返回 True 。

计数器

计数器(Counter)类似集合,区别在于如果某个元素出现次数超过一次,计数器就会记录其出现次数。

计数器定义在叫做 collections 的标准模块中,因此你必须首先导入该模块。你可以通过字符串、列表或任何支持迭代的数据结构来初始化计数器:

>>> from collections import Counter
>>> count = Counter('parrot')
>>> count
Counter({'r': 2, 't': 1, 'o': 1, 'p': 1, 'a': 1})
计数器的行为与字典有很多相似的地方:它们将每个键映射至其出现的次数。与字典一样,键必须是可哈希的。

与字典不同的是,如果你访问一个没有出现过的元素,计数器不会抛出异常,而只是返回 0 :

>>> count['d']
0

如果两个单词是变位词,那么它们会包含相同的字符,而且字符的计数也相同,因此它们的计数器也是等价的。

计数器提供了执行类似集合操作的方法和操作符,包括集合添加、差集、并集和交集。另外,还提供了一个通常非常有用的方法 most_common ,返回一个由值-频率对组成的列表,按照频率高低排序:

>>> count = Counter('parrot')
>>> for val, freq in count.most_common(3):
...     print(val, freq)
r 2
p 1
a 1

defaultdict

collections 模块中还提供了一个 defaultdict ,它类似字典,但是如果你访问一个不存在的键,它会临时生成一个新值。

在创建 defaultdict 时,你提供一个用于创建新值的函数。这个用于创建对象的函数有时也被称为 工厂 。用于创建列表、集合和其他类型的内建函数也可以用作工厂:

>>> from collections import defaultdict
>>> d = defaultdict(list)
请注意,这里的实参是 list ,它是一个类对象,而不是 list() ,后者是一个新列表。你提供的函数只有在访问不存在的键时,才会被调用。

>>> t = d['new key']
>>> t
[]
新列表 t 也被添加至字典中。因此如果我们修改 t ,改动也会出现在 d 中。

>>> t.append('new value')
>>> d
defaultdict(<class 'list'>, {'new key': ['new value']})
如果你要创建一个列表组成的字典,通常你可以使用 defaultdict 来简化代码。

命名元组

当你像下面这样定义类时,你通常先开始定义 init 和 str 方法:

class Point:

    def __init__(self, x=0, y=0):
        self.x = x
        self.y = y

    def __str__(self):
        return '(%g, %g)' % (self.x, self.y)
但是编写了这么多代码,却只传递了很少的信息。Python提供了一个更简洁的实现方式:

from collections import namedtuple
Point = namedtuple('Point', ['x', 'y'])
第一个实参是你希望创建的类的名称。第二个实参是 Point 对象应该具备的属性列表,以字符串的形式指定。 namedtuple 的返回值是一个类对象:

>>> Point
<class '__main__.Point'>
这里的 Point 自动提供了像 __init__ 和 __str__ 这样的方法,你没有必须再自己编写。

如果想创建一个 Point 对象,你可以将 Point 类当作函数使用:

>>> p = Point(1, 2)
>>> p
Point(x=1, y=2)
init 方法将实参赋值给你提供的属性。str 方法打印 Point 对象的字符串呈现及其属性。

你可以通过名称访问命令元组的元素:

>>> p.x, p.y
(1, 2)
但是你也可以把命名元组当作元组使用:

>>> p[0], p[1]
(1, 2)

>>> x, y = p
>>> x, y
(1, 2)
命名元组是定义简单类的一种便捷方式。缺点是这些简单类不会一成不变。之后你可能会发现想要给命名元组添加更多的方法。在这种情况下,你可以定义一个继承自命名元组的新类:

class Pointier(Point):
    # add more methods here
或者使用传统的类定义方式。

汇集关键字实参

在可变长度参数元组中,我们学习了如何编写一个将实参汇集到元组的函数:
def printall(*args):
    print(args)

你可以使用任意数量的位置实参(即不带关键字的参数)调用该函数:
>>> printall(1, 2.0, '3')
(1, 2.0, '3')

不过 `*` 星号操作符无法汇集关键字参数:
>>> printall(1, 2.0, third='3')
TypeError: printall() got an unexpected keyword argument 'third'

如果要汇集关键字参数,你可以使用 `**` 双星号操作符:
>def printall(*args, **kwargs):
    print(args, kwargs)

可以给关键字汇集形参取任意的名称,但是 `kwargs` 是常用名。上面函数的结果是一个将关键字映射至值的字典:
>>> printall(1, 2.0, third='3')
(1, 2.0) {'third': '3'}

如果你有一个有关键字和值组成的字典,可以使用分散操作符(scatter operator) `**` 调用函数:
>>> d = dict(x=1, y=2)
>>> Point(**d)
Point(x=1, y=2)

如果没有分散操作符,函数会将 `d` 视为一个位置实参,因此会将 `d` 赋值给 `x` 并报错,因为没有给 `y` 赋值:
>>> d = dict(x=1, y=2)
>>> Point(d)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: __new__() missing 1 required positional argument: 'y'

在处理有大量形参的函数时,通常可以创建指定了常用选项的字典,并将其传入函数。

相关文章

网友评论

      本文标题:python-12 小技巧

      本文链接:https://www.haomeiwen.com/subject/rpwomhtx.html