Python3描述——10个很棒的Python特性

特性1: 高级解包

你以前可以这么做:

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

现在可以这样:

>>> a, b, *rest = range(10)
>>> a
0
>>> b
1
>>> rest
[2, 3, 4, 5, 6, 7, 8, 9]

*rest可以出现在任何位置:

>>> a, *rest, b = range(10)
>>> a
0
>>> b
9
>>> rest
[1, 2, 3, 4, 5, 6, 7, 8]
>>> *rest, b = range(10)
>>> rest
[0, 1, 2, 3, 4, 5, 6, 7, 8]
>>> b
9

获取文件第一行和最后一行:

>>> with open("using_python_to_profit") as f:
... first, *_, last = f.readlines()
>>> first
'Step 1: Use Python 3\n'
>>> last
'Step 10: Profit!\n'

函数重构:

def f(a, b, *args):
    stuff
def f(*args):
    a, b, *args = args
    stuff

特性2: 关键字唯一参数

def f(a, b, *args, option=True):
    ...

option 在*args后。

访问它的唯一方法是显式调用f(a, b, option=True)

可以只写一个 * 如果不想收集*args。

def f(a, b, *, option=True):
    ...

若不小心传递太多参数给函数,其中之一会被关键字参数接收。

def sum(a, b, biteme=False):
    if biteme:
        shutil.rmtree('/')
    else:
        return a + b
>>> sum(1, 2)
3
>>> sum(1, 2, 3)

替代写法。

def sum(a, b, *, biteme=False):
    if biteme:
        shutil.rmtree('/')
    else:
        return a + b
>>> sum(1, 2, 3)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: sum() takes 2 positional arguments but 3 were given

重新排序函数的关键词参数,但是有些是隐式传递的,例如:

def maxall(iterable, key=None):
    """
    A list of all max items from the iterable
    """
    key = key or (lambda x: x)
    m = max(iterable, key=key)
    return [i for i in iterable if key(i) == key(m)]
>>> maxall(['a', 'ab', 'bc'], len)
['ab', 'bc']

max内建函数支持max(a, b, c)。我们是否也要那么做。

def maxall(*args, key=None):
    """
    A list of all max items from the iterable
    """
    if len(args) == 1:
        iterable = args[0]
    else:
        iterable = args
    key = key or (lambda x: x)
    m = max(iterable, key=key)
    return [i for i in iterable if key(i) == key(m)]

刚刚打破了以往代码不使用关键词作为第二个参数来给key传值。

>>> maxall(['a', 'ab', 'ac'], len)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 10, in maxall
TypeError: unorderable types: builtin_function_or_method() > list()
  • (事实上在Python2会返回['a', 'ab', 'ac'],见特性6)。

  • 顺便说一句,max表明在Python2中已经可能,但只有当你用C写你的函数。

  • 显然,应该使用maxall(iterable, *, key=None)来入门。

使你的API“与时俱进”。

不建议的做法:

def extendto(value, shorter, longer):
    """
    Extend list `shorter` to the length of list `longer` with `value`
    """
    if len(shorter) > len(longer):
        raise ValueError('The `shorter` list is longer than the `longer` list')
    shorter.extend([value]*(len(longer) - len(shorter)))
>>> a = [1, 2]
>>> b = [1, 2, 3, 4, 5]
>>> extendto(10, a, b)
>>> a
[1, 2, 10, 10, 10]

在Python3中,你可以这样用:

def extendto(value, *, shorter=None, longer=None):
    """
    Extend list `shorter` to the length of list `longer` with `value`
    """
    if shorter is None or longer is None:
        raise TypeError('`shorter` and `longer` must be specified')
    if len(shorter) > len(longer):
        raise ValueError('The `shorter` list is longer than the `longer` list')
    shorter.extend([value]*(len(longer) - len(shorter)))
  • 现在,a和b必须像extendto(10, shorter=a, longer=b)这样传入。

  • 如果你愿意,也可以像这样extendto(10, longer=b, shorter=a)。

  • 不破坏API增加新的关键字参数。

  • Python3在标准库中做了这些。

  • 举例,在os模块中的函数有follow_symlinks选项。

  • 所以可以只使用os.stat(file, follow_symlinks=False)而不是os.lstat。

  • 可以这样做:

  • s = os.stat(file, follow_symlinks=some_condition)

    替代:

if some_condition:
    s = os.stat(file)
else:
    s = os.lstat(file)
  • 但是,os.stat(file, some_condition)不可以。

  • 不要想它是一个两个参数的函数。

  • 在Python2中,你必须使用**kwargs并且自己做处理。

  • 在你的函数头部会有许多丑陋的option = kwargs.pop(True)。

  • 不再自我记录。

  • 如果你正在写一个Python3代码库,我强烈建议你仅使用关键词参数,尤其关键词参数代表“选项(options)”。

特性3:连接异常

情境:你用except捕获了一个异常,做了一些事情,然后引发了一个不同的异常。

def mycopy(source, dest):
    try:
        shutil.copy2(source, dest)
    except OSError: # We don't have permissions. More on this later
        raise NotImplementedError("automatic sudo injection")

问题:您丢失了原始回溯

>>> mycopy('noway', 'noway2')
>>> mycopy(1, 2)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 5, in mycopy
NotImplementedError: automatic sudo injection

OSError发生了什么?

Python 3向您展示了整个异常链:

mycopy('noway', 'noway2')
Traceback (most recent call last):
File "<stdin>", line 3, in mycopy
File "/Users/aaronmeurer/anaconda3/lib/python3.3/shutil.py", line 243, in copy2
 copyfile(src, dst, follow_symlinks=follow_symlinks)
File "/Users/aaronmeurer/anaconda3/lib/python3.3/shutil.py", line 109, in copyfile
 with open(src, 'rb') as fsrc:
PermissionError: [Errno 13] Permission denied: 'noway'
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 5, in mycopy
NotImplementedError: automatic sudo injection

您还可以使用raise from手动执行此操作:

raise exception from e

>>> raise NotImplementedError from OSError
OSError
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NotImplementedError

特性4:细粒度的OSError子类

刚才给你们看的代码是错误的。

它捕获OSError并假设它是一个权限错误。

但是OSError可以是很多东西(文件没有找到,是一个目录,不是目录,断了管道,…)

但你真的必须这么做:

import errno

def mycopy(source, dest):
    try:
        shutil.copy2(source, dest)
    except OSError as e:
        if e.errno in [errno.EPERM, errno.EACCES]:
            raise NotImplementedError("automatic sudo injection")
        else:
            raise

哇。这糟透了。

Python 3通过添加大量新异常来修复这个问题。

你可以这样做:

def mycopy(source, dest):
    try:
        shutil.copy2(source, dest)
    except PermissionError:
        raise NotImplementedError("automatic sudo injection")

(别担心,从OSError获得的PermissionError子类仍然有.errno。旧代码仍然可以工作)。

特性5:一切都是迭代器

这是最难的。

Python 2中也存在迭代器。

但你必须使用它们。不要写范围或zip或dict.values ....

如果你这样做:

def naivesum(N):
    """
    Naively sum the first N integers
    """
    A = 0
    for i in range(N + 1):
        A += i
    return A
In [3]: timeit naivesum(1000000)
10 loops, best of 3: 61.4 ms per loop

In [4]: timeit naivesum(10000000)
1 loops, best of 3: 622 ms per loop

In [5]: timeit naivesum(100000000)

相反,取代一些变量(xrange, itertools)。izip dict.itervalues,……)。

不一致的API有人知道吗?

在Python 3中,range、zip、map、dict.values等都是迭代器。

如果您想要一个列表,只需将结果包装为list。

显性比隐性好。

编写不小心使用了太多内存的代码比较困难,因为输入比预期的要大。

特性6:不是一切都能比较

在python 2中,你可以这样做:

>>> max(['one', 2]) # One *is* the loneliest number
'one'

这是因为在python2里,你可以比较任何东西。

>>> 'abc' > 123
True
>>> None > all
False

在Python 3中,不能这么做。

>>> 'one' > 2
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unorderable types: str() > int()

这可以避免一些微妙的Bug,比如,非强制转换的所有类型,从int转换成str或者反过来。

尤其当你隐式使用>时,像max或sorted。

在Python2中:

>>> sorted(['1', 2, '3'])
[2, '1', '3']

特性7:yield from

如果你使用生成器,那就太棒了

而不是写

for i in gen():
    yield i

而是写成:

yield from gen()

简单地将generators重构成subgenerators。

将一切转换成生成器更简单了。看上面提到的“特性5: 一切皆迭代器”,了解为什么要这样做。

不要叠加生成一个列表,只要yield或yield from就可以了。

不会的做法:

def dup(n):
    A = []
    for i in range(n):
        A.extend([i, i])
    return A

好的做法:

def dup(n):
    for i in range(n):
        yield i
        yield i

更好的做法:

def dup(n):
    for i in range(n):
        yield from [i, i]

以防你不知道,生成器是极好的,因为:

  • 每次只有一个值被计算。低内存影响(见上面的range例子)。

  • 可以在中间断开。不需要计算一切只是为了找到你需要的。

  • 计算正是你需要的。如果你经常不需要它,你可以在这里获得很多性能。

  • 如果你需要一个列表(比如,切片(slicing)),在生成器上调用list() 。

  • 在yield期间,函数状态是“保存的”。

  • 这导致有趣的可能性,协程式的。。。

特性8:asyncio

使用新的协同程序特性和保存的生成器状态来执行异步IO。

# Taken from Guido's slides from “Tulip: Async I/O for Python 3” by Guido
# van Rossum, at LinkedIn, Mountain View, Jan 23, 2014
@coroutine
def fetch(host, port):
    r,w = yield from open_connection(host,port)
    w.write(b'GET /HTTP/1.0\r\n\r\n ')
    while (yield from r.readline()).decode('latin-1').strip():
        pass
    body=yield from r.read()
    return body

@coroutine
def start():
    data = yield from fetch('python.org', 80)
    print(data.decode('utf-8'))

特性9:标准库添加

faulthandler

显示(有限的)回溯(tracebacks),即使当Python因某种艰难方式挂掉了。
使用kill -9时不起作用,但segfaults起作用。

import faulthandler
faulthandler.enable()
def killme():
    # Taken from http://nbviewer.ipython.org/github/ipython/ipython/blob/1.x/examples/notebooks/Part%201%20-%20Running%20Code.ipynb
    import sys
    from ctypes import CDLL
    # This will crash a Linux or Mac system; equivalent calls can be made on
    # Windows
    dll = 'dylib' if sys.platform == 'darwin' else 'so.6'
    libc = CDLL("libc.%s" % dll)
    libc.time(-1) # BOOM!!

killme()

$python test.py
Fatal Python error: Segmentation fault

Current thread 0x00007fff781b6310:
File "test.py", line 11 in killme
File "test.py", line 13 in <module>
Segmentation fault: 11

或使用kill -6(程序请求异常终止)

通过python -X faulthandler也可以激活。

ipaddress

正是如此。IP地址。

>>> ipaddress.ip_address('192.168.0.1')
IPv4Address('192.168.0.1')
>>> ipaddress.ip_address('2001:db8::')
IPv6Address('2001:db8::')

只是另一件你自己不想做的事。

functools.lru_cache

为你的函数提供一个LRU缓存装饰器。

来自文档。

@lru_cache(maxsize=32)
def get_pep(num):
    'Retrieve text of a Python Enhancement Proposal'
    resource = 'http://www.python.org/dev/peps/pep-%04d/' % num
    try:
        with urllib.request.urlopen(resource) as s:
        return s.read()
    except urllib.error.HTTPError:
        return 'Not Found'

>>> for n in 8, 290, 308, 320, 8, 218, 320, 279, 289, 320, 9991:
... pep = get_pep(n)
... print(n, len(pep))

>>> get_pep.cache_info()
CacheInfo(hits=3, misses=8, maxsize=32, currsize=8)

enum

最后,标准库中的枚举类型。

>>> from enum import Enum
>>> class Color(Enum):
... red = 1
... green = 2
... blue = 3
...

使用一些仅在Python3中有用的魔法(由于元类的改变):

>>> class Shape(Enum):
... square = 2
... square = 3
...
Traceback (most recent call last):
...
TypeError: Attempted to reuse key: 'square'

特性10:乐趣

Unicode变量名

>>> résumé = "knows Python"
>>> π = math.pi

函数注释

def f(a: stuff, b: stuff = 2) -> result:
    ...

注释可以是任意的Python对象。

Python对注释不做任何处理,除了把它们放在一个__annotations__字典中。

>>> def f(x: int) -> float:
... pass
...
>>> f.__annotations__
{'return': <class 'float'>, 'x': <class 'int'>}

但是它为库作者做有趣的事情打开了可能性。

举例,IPython 2.0小工具。

从IPython git中运行IPython手册(Python3中)并且打开http://127.0.0.1:8888/notebooks/examples/Interactive%20Widgets/Image%20Processing.ipynb 

英文原文:http://asmeurer.github.io/python3-presentation/python3-presentation.pdf 

猜你喜欢

转载自blog.csdn.net/youzhouliu/article/details/83061164