只有python3才有的特性
只有python3才有的特性
python2转python3必看
特性 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):
pass
def f(*args):
a, b, *args = args
特性 2: 关键字参数
def f(a, b, *args, option=True):
pass
选项来自* args。 访问它的唯一方法是显式调用f(a,b,option = True) 如果你不想收集* args,你可以只写一个*。
def f(a, b, *, option=True):
pass
少了一个参数
# 错误写法
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
或者,“我重新排序了函数的关键字参数,但是有些隐含地传递的参数要有正确的顺序“
Example:
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)]
在不使用关键字的情况下,会失败。
>>> 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中 return [‘a’, ‘ab’, ‘ac’], 见 特性 6). 我们用 maxall(iterable, *, key=None) 就不会犯错.
可以使API“未来变更” Stupid example:
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]
在Python 3, 可以这样写
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)))
可以这样用 extendto(10, shorter=a, longer=b) 也可以 extendto(10, longer=b, shorter=a).
添加新的关键字参数而不破坏API。 Python 3在标准库中完成了这项工作。 例如,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)不起作用。 让你不要以为它是一个双参数函数。
在Python 2中,你必须使用** kwargs并自己进行处理。 你的函数顶部有很多丑陋的选项= kwargs.pop(True)。 不能自我文档化。 如果您以某种方式编写仅用于Python 3的代码库,我强烈建议您创建所有关键字 仅限arguments关键字,尤其是表示“options”的关键字参数。
特性 3: Chained exceptions
- 情况: 通过except处理一个exception,然后抛出另外一个exception
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 会显示全部的exceptions链:
mycopy('noway', 'noway2') Traceback (most recent call last): File "<stdin>", line 3, in mycopy File "/Users/anybody/anaconda3/lib/python3.3/shutil.py", line 243, in copy2 copyfile(src, dst, follow_symlinks=follow_symlinks) File "/Users/anybody/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
特性 4: 保证OSError子类安全
不那么正确的例子(python2n)
OSError 可以做很多事情 (file not found, is a directory, is not a directory, broken pipe, …)
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")
特性 5: 一切皆是iterator
- 在python2中也有iterator,(xrange, itertools.izip, dict.itervalues, …)
- Python 3, range, zip, map, dict.values, … 都是 iterators.
特性 6: 不同类型不再能做比较
Python 2
>>> max(['one', 2]) # One *is* the loneliest number
'one'
>>> 'abc' > 123
True
>>> None > all
False
Python 3:
```py3
>>> 'one' > 2
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unorderable types: str() > int()
这在一定程度上减少了bugs
特性 7: yield from
Pretty great if you use generators Instead of writing 更优雅的写法:
for i in gen():
yield i
可以替换成:
yield from gen()
使用yield很容易把其它类型封装到generator:
#Bad
def dup(n):
A = []
for i in range(n):
A.extend([i, i])
return A
#Good
def dup(n):
for i in range(n):
yield i
yield i
#Better
def dup(n):
for i in range(n):
yield from [i, i]
generators的优势:
- 一次只计算一个值。 内存影响低(参见上面的范围示例)。
- 可以在运行中随时中断。 不必计算所有的值,只计算你需要什么。
- 如果您需要一个列表(例如,用于切片),只需在生成器上调用list()。
- 在yield之间可以保存状态,这就带来了coroutine
特性 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: 标准库
ipaddress
>>> ipaddress.ip_address('192.168.0.1')
IPv4Address('192.168.0.1')
>>> ipaddress.ip_address('2001:db8::')
IPv6Address('2001:db8::')
functools.lru_cache
@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,Python 3.4 及以上版本
>>> from enum import Enum
>>> class Color(Enum):
... red = 1
... green = 2
... blue = 3
>>> class Shape(Enum):
... square = 2
... square = 3
...
Traceback (most recent call last):
...
TypeError: Attempted to reuse key: 'square'
特性 10: 支持unicode变量名, 函数类型标记
unicode变量名
>>> 中国 = "knows Python"
>>> π = math.pi
函数类型标记
def f(a: stuff, b: stuff = 2) -> result:
pass
类型标记是python对象的一个属性,除了把标记放入到__annotations__ dictionary外不做任何其它动作,不影响解释速度,不是静态类型。类型标记可以提供给编辑器类型提示用,有兴趣可以了解下mypy项目。
>>> def f(x: int) -> float:
... pass
...
>>> f.__annotations__
{'return': <class 'float'>, 'x': <class 'int'>}
特性 11: Unicode 和 bytes
- 在Python 2中,str就像数据bytes一样,还有unicode类型来表示Unicode字符串。
- 在Python 3中,str是一个字符串,bytes是bytes, 没有unicode, str字符串是Unicode。
特性 12: Matrix Multiplication
Python 3.5
>>> a = np.array([[1, 0], [0, 1]])
>>> b = np.array([[4, 1], [2, 2]])
>>> np.dot(a, b)
array([[4, 1],
[2, 2]])
>>> a = np.array([[1, 0], [0, 1]])
>>> b = np.array([[4, 1], [2, 2]])
>>> a @ b
array([[4, 1],
[2, 2]])
任何类型可以使用@重载__matul__。