​如何愉快地迁移到 Python 3


Posted in Python onApril 28, 2019

引言

如今 Python 成为机器学习和大量使用数据操作的科学领域的主流语言; 它拥有各种深度学习框架和完善的数据处理和可视化工具。但是,Python 生态系统在 Python2 和 Python3 中共存,而Python2 仍在数据科学家中使用。到2019年底,也将停止支持 Python2。至于numpy,2018年9月之后任何新功能版本都将只支持Python3。同样的还包括pandas, matplotlib, ipython, jupyter notebook and jupyter lab。所以迁移到python3刻不容缓,当然不止是这些,还有些新特性让我们跟随后面到文章一一进行了解。

使用pathlib处理更好的路径

pathlib 是 Python3 中的一个默认模块,可以帮助你避免使用大量的 os.path.join。

from pathlib import Path
dataset = 'wiki_images'
datasets_root = Path('/path/to/datasets/')
#Navigating inside a directory tree,use:/
train_path = datasets_root / dataset / 'train'
test_path = datasets_root / dataset / 'test'
for image_path in train_path.iterdir():
 with image_path.open() as f: # note, open is a method of Path object
  # do something with an image

不要用字符串链接的形式拼接路径,根据操作系统的不同会出现错误,我们可以使用/结合 pathlib来拼接路径,非常的安全、方便和高可读性。

pathlib 还有很多属性,具体的可以参考pathlib的官方文档,下面列举几个:

from pathlib import Path
a = Path("/data")
b = "test"
c = a / b
print(c)
print(c.exists()) # 路径是否存在
print(c.is_dir()) # 判断是否为文件夹
print(c.parts) # 分离路径
print(c.with_name('sibling.png')) # 只修改拓展名, 不会修改源文件
print(c.with_suffix('.jpg')) # 只修改拓展名, 不会修改源文件
c.chmod(777) # 修改目录权限
c.rmdir() # 删除目录

类型提示现在是语言的一部分

     一个在 Pycharm 使用Typing的例子:

   引入类型提示是为了帮助解决程序日益复杂的问题,IDE可以识别参数的类型进而给用户提示。

关于Tying的具体用法,可以看我之前写的:python类型检测最终指南--Typing的使用

运行时类型提示类型检查

     除了之前文章提到 mypy 模块继续类型检查以外,还可以使用 enforce 模块进行检查,通过 pip 安装即可,使用示例如下:

import enforce
@enforce.runtime_validation
def foo(text: str) -> None:
 print(text)
foo('Hi') # ok
foo(5) # fails

输出

Hi
Traceback (most recent call last):
 File "/Users/chennan/pythonproject/dataanalysis/e.py", line 10, in <module>
 foo(5) # fails
 File "/Users/chennan/Desktop/2019/env/lib/python3.6/site-packages/enforce/decorators.py", line 104, in universal
 _args, _kwargs, _ = enforcer.validate_inputs(parameters)
 File "/Users/chennan/Desktop/2019/env/lib/python3.6/site-packages/enforce/enforcers.py", line 86, in validate_inputs
 raise RuntimeTypeError(exception_text)
enforce.exceptions.RuntimeTypeError: 
 The following runtime type errors were encountered:
  Argument 'text' was not of type <class 'str'>. Actual type was int.

使用@表示矩阵的乘法

   下面我们实现一个最简单的ML模型——l2正则化线性回归(又称岭回归)

# l2-regularized linear regression: || AX - y ||^2 + alpha * ||x||^2 -> min
# Python 2
X = np.linalg.inv(np.dot(A.T, A) + alpha * np.eye(A.shape[1])).dot(A.T.dot(y))
# Python 3
X = np.linalg.inv(A.T @ A + alpha * np.eye(A.shape[1])) @ (A.T @ y)

使用@符号,整个代码变得更可读和方便移植到其他科学计算相关的库,如numpy, cupy, pytorch, tensorflow等。

**通配符的使用

在 Python2 中,递归查找文件不是件容易的事情,即使是使用glob库,但是从 Python3.5 开始,可以通过**通配符简单的实现。

import glob
# Python 2
found_images = (
 glob.glob('/path/*.jpg')
 + glob.glob('/path/*/*.jpg')
 + glob.glob('/path/*/*/*.jpg')
 + glob.glob('/path/*/*/*/*.jpg')
 + glob.glob('/path/*/*/*/*/*.jpg'))
# Python 3
found_images = glob.glob('/path/**/*.jpg', recursive=True)

更好的路径写法是上面提到的 pathlib ,我们可以把代码进一步改写成如下形式。

# Python 3
import pathlib
import glob
found_images = pathlib.Path('/path/').glob('**/*.jpg')

Print函数

虽然 Python3 的 print 加了一对括号,但是这并不影响它的优点。

使用文件描述符的形式将文件写入

print >>sys.stderr, "critical error"  # Python 2
print("critical error", file=sys.stderr) # Python 3

不使用 str.join 拼接字符串

# Python 3
print(*array, sep=' ')
print(batch, epoch, loss, accuracy, time, sep=' ')

重新定义 print 方法的行为

既然 Python3 中的 print 是一个函数,我们就可以对其进行改写。

# Python 3
_print = print # store the original print function
def print(*args, **kargs):
 pass # do something useful, e.g. store output to some file

注意:在 Jupyter 中,最好将每个输出记录到一个单独的文件中(跟踪断开连接后发生的情况),这样就可以覆盖 print 了。

@contextlib.contextmanager
def replace_print():
 import builtins
 _print = print # saving old print function
 # or use some other function here
 builtins.print = lambda *args, **kwargs: _print('new printing', *args, **kwargs)
 yield
 builtins.print = _print
with replace_print():
 <code here will invoke other print function>

虽然上面这段代码也能达到重写 print 函数的目的,但是不推荐使用。

print 可以参与列表理解和其他语言构造

# Python 3
result = process(x) if is_valid(x) else print('invalid item: ', x)

数字文字中的下划线(千位分隔符)

在 PEP-515 中引入了在数字中加入下划线。在 Python3 中,下划线可用于整数,浮点和复数,这个下划线起到一个分组的作用

# grouping decimal numbers by thousands
one_million = 1_000_000
# grouping hexadecimal addresses by words
addr = 0xCAFE_F00D
# grouping bits into nibbles in a binary literal
flags = 0b_0011_1111_0100_1110
# same, for string conversions
flags = int('0b_1111_0000', 2)

也就是说10000,你可以写成10_000这种形式。

简单可看的字符串格式化f-string

Python2提供的字符串格式化系统还是不够好,太冗长麻烦,通常我们会写这样一段代码来输出日志信息:

# Python 2
print '{batch:3} {epoch:3} / {total_epochs:3} accuracy: {acc_mean:0.4f}±{acc_std:0.4f} time: {avg_time:3.2f}'.format(
 batch=batch, epoch=epoch, total_epochs=total_epochs,
 acc_mean=numpy.mean(accuracies), acc_std=numpy.std(accuracies),
 avg_time=time / len(data_batch)
)
# Python 2 (too error-prone during fast modifications, please avoid):
print '{:3} {:3} / {:3} accuracy: {:0.4f}±{:0.4f} time: {:3.2f}'.format(
 batch, epoch, total_epochs, numpy.mean(accuracies), numpy.std(accuracies),
 time / len(data_batch)
)

输出结果为

120  12 / 300  accuracy: 0.8180±0.4649 time: 56.60

在 Python3.6 中引入了 f-string (格式化字符串)

print(f'{batch:3} {epoch:3} / {total_epochs:3} accuracy: {numpy.mean(accuracies):0.4f}±{numpy.std(accuracies):0.4f} time: {time / len(data_batch):3.2f}')

关于 f-string 的用法可以看我在b站的视频[https://www.bilibili.com/video/av31608754]

'/'和'//'在数学运算中有着明显的区别

     对于数据科学来说,这无疑是一个方便的改变

data = pandas.read_csv('timing.csv')
velocity = data['distance'] / data['time']

Python2 中的结果取决于“时间”和“距离”(例如,以米和秒为单位)是否存储为整数。在python3中,这两种情况下的结果都是正确的,因为除法的结果是浮点数。

另一个例子是 floor 除法,它现在是一个显式操作

n_gifts = money // gift_price # correct for int and float arguments
nutshell
>>> from operator import truediv, floordiv
>>> truediv.__doc__, floordiv.__doc__
('truediv(a, b) -- Same as a / b.', 'floordiv(a, b) -- Same as a // b.')
>>> (3 / 2), (3 // 2), (3.0 // 2.0)
(1.5, 1, 1.0)

值得注意的是,这种规则既适用于内置类型,也适用于数据包提供的自定义类型(例如 numpy 或pandas)。

严格的顺序

下面的这些比较方式在 Python3 中都属于合法的。

3 < '3'
2 < None
(3, 4) < (3, None)
(4, 5) < [4, 5]

对于下面这种不管是2还是3都是不合法的

(4, 5) == [4, 5]

如果对不同的类型进行排序

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

虽然上面的写法在 Python2 中会得到结果 [2, 3, '1'],但是在 Python3 中上面的写法是不被允许的。

检查对象为 None 的合理方案

if a is not None:
 pass
if a: # WRONG check for None
 pass
NLP Unicode问题
s = '您好'
print(len(s))
print(s[:2])

输出内容

Python 2: 6
Python 3: 2

您好.

还有下面的运算

x = u'со'
x += 'co' # ok
x += 'со' # fail

Python2 失败了,Python3 正常工作(因为我在字符串中使用了俄文字母)。

在 Python3 中,字符串都是 unicode 编码,所以对于非英语文本处理起来更方便。

一些其他操作

'a' < type < u'a' # Python 2: True
'a' < u'a'     # Python 2: False

再比如

from collections import Counter
Counter('Möbelstück')

在 Python2 中

 Counter({'Ã': 2, 'b': 1, 'e': 1, 'c': 1, 'k': 1, 'M': 1, 'l': 1, 's': 1, 't': 1, '¶': 1, '¼': 1})

在 Python3 中

 Counter({'M': 1, 'ö': 1, 'b': 1, 'e': 1, 'l': 1, 's': 1, 't': 1, 'ü': 1, 'c': 1, 'k': 1})

虽然可以在 Python2 中正确地处理这些结果,但是在 Python3 中看起来结果更加友好。

保留了字典和**kwargs的顺序

在CPython3.6+ 中,默认情况下,dict 的行为类似于 OrderedDict ,都会自动排序(这在Python3.7+ 中得到保证)。同时在字典生成式(以及其他操作,例如在 json 序列化/反序列化期间)都保留了顺序。

import json
x = {str(i):i for i in range(5)}
json.loads(json.dumps(x))
# Python 2
{u'1': 1, u'0': 0, u'3': 3, u'2': 2, u'4': 4}
# Python 3
{'0': 0, '1': 1, '2': 2, '3': 3, '4': 4}

这同样适用于**kwargs(在Python 3.6+中),它们的顺序与参数中出现的顺序相同。当涉及到数据管道时,顺序是至关重要的,以前我们必须以一种繁琐的方式编写它

from torch import nn
# Python 2
model = nn.Sequential(OrderedDict([
     ('conv1', nn.Conv2d(1,20,5)),
     ('relu1', nn.ReLU()),
     ('conv2', nn.Conv2d(20,64,5)),
     ('relu2', nn.ReLU())
    ]))

而在 Python3.6 以后你可以这么操作

# Python 3.6+, how it *can* be done, not supported right now in pytorch
model = nn.Sequential(
  conv1=nn.Conv2d(1,20,5),
  relu1=nn.ReLU(),
  conv2=nn.Conv2d(20,64,5),
  relu2=nn.ReLU())
)

可迭代对象拆包

类似于元组和列表的拆包,具体看下面的代码例子。

# handy when amount of additional stored info may vary between experiments, but the same code can be used in all cases
model_paramteres, optimizer_parameters, *other_params = load(checkpoint_name)
# picking two last values from a sequence
*prev, next_to_last, last = values_history
# This also works with any iterables, so if you have a function that yields e.g. qualities,
# below is a simple way to take only last two values from a list
*prev, next_to_last, last = iter_train(args)

提供了更高性能的pickle

Python2

import cPickle as pickle
import numpy
print len(pickle.dumps(numpy.random.normal(size=[1000, 1000])))
# result: 23691675
Python3
import pickle
import numpy
len(pickle.dumps(numpy.random.normal(size=[1000, 1000])))
# result: 8000162

空间少了三倍。而且要快得多。实际上,使用 protocol=2 参数可以实现类似的压缩(但不是速度),但是开发人员通常忽略

这个选项(或者根本不知道)。

注意:pickle 不安全(并且不能完全转移),所以不要 unpickle 从不受信任或未经身份验证的来源收到的数据。

更安全的列表推导

labels = <initial_value>
predictions = [model.predict(data) for data, labels in dataset]
# labels are overwritten in Python 2
# labels are not affected by comprehension in Python

更简易的super()

在python2中 super 相关的代码是经常容易写错的。

# Python 2
class MySubClass(MySuperClass):
  def __init__(self, name, **options):
    super(MySubClass, self).__init__(name='subclass', **options)
# Python 3
class MySubClass(MySuperClass):
  def __init__(self, name, **options):
    super().__init__(name='subclass', **options)

这一点Python3得到了很大的优化,新的 super() 可以不再传递参数。

同时在调用顺序上也不一样。

IDE能够给出更好的提示

使用Java、c#等语言进行编程最有趣的地方是IDE可以提供很好的建议,因为在执行程序之前,每个标识符的类型都是已知的。

在python中这很难实现,但是注释会帮助你

这是一个带有变量注释的 PyCharm 提示示例。即使在使用的函数没有注释的情况下(例如,由于向后兼容性),也可以使用这种方法。

Multiple unpacking

如何合并两个字典

x = dict(a=1, b=2)
y = dict(b=3, d=4)
# Python 3.5+
z = {**x, **y}
# z = {'a': 1, 'b': 3, 'd': 4}, note that value for `b` is taken from the latter dict.

我在b站同样发布了相关的视频[https://www.bilibili.com/video/av50376841]
同样的方法也适用于列表、元组和集合(a、b、c是任何迭代器)

[*a, *b, *c] # list, concatenating
(*a, *b, *c) # tuple, concatenating
{*a, *b, *c} # set, union

函数还支持*arg和**kwarg的多重解包

# Python 3.5+
do_something(**{**default_settings, **custom_settings})
# Also possible, this code also checks there is no intersection between keys of dictionaries
do_something(**first_args, **second_args)
Data classes

Python 3.7引入了Dataclass类,它适合存储数据对象。数据对象是什么?下面列出这种对象类型的几项特征,虽然不全面:

它们存储数据并表示某种数据类型,例如:数字。对于熟悉ORM的朋友来说),数据模型实例就是一个数据对象。它代表了一种特定的实体。它所具有的属性定义或表示了该实体。
它们可以与同一类型的其他对象进行比较。例如:大于、小于或等于。
当然还有更多的特性,下面的这个例子可以很好的替代namedtuple的功能。

dataclass装饰器实现了

@dataclass
class Person:
  name: str
  age: int
@dataclass
class Coder(Person):
  preferred_language: str = 'Python 3'

几个魔法函数方法的功能(__init__,__repr__,__le__,__eq__)

关于数据类有以下几个特性:

数据类可以是可变的,也可以是不可变的
支持字段的默认值
可被其他类继承
数据类可以定义新的方法并覆盖现有的方法
初始化后处理(例如验证一致性)
更多内容可以参考官方文档。
自定义对模块属性的访问

在Python中,可以用getattr和dir控制任何对象的属性访问和提示。因为python3.7,你也可以对模块这样做。

一个自然的例子是实现张量库的随机子模块,这通常是跳过初始化和传递随机状态对象的快捷方式。numpy的实现如下:

# nprandom.py
import numpy
__random_state = numpy.random.RandomState()
def __getattr__(name):
  return getattr(__random_state, name)
def __dir__():
  return dir(__random_state)
def seed(seed):
  __random_state = numpy.random.RandomState(seed=seed)

也可以这样混合不同对象/子模块的功能。与pytorch和cupy中的技巧相比。

除此之外,还可以做以下事情:

使用它来延迟加载子模块。例如,导入tensorflow时会导入所有子模块(和依赖项)。需要大约150兆内存。

在应用编程接口中使用此选项进行折旧

在子模块之间引入运行时路由

内置的断点

在python3.7中可以直接使用breakpoint给代码打断点

# Python 3.7+, not all IDEs support this at the moment
foo()
breakpoint()
bar()

在python3.7以前我们可以通过import pdb的pdb.set_trace()实现相同的功能。

对于远程调试,可尝试将breakpoint()与web-pdb结合使用.

Math模块中的常数

# Python 3
math.inf # Infinite float
math.nan # not a number
max_quality = -math.inf # no more magic initial values!
for model in trained_models:
  max_quality = max(max_quality, compute_quality(model, data))

整数类型只有int

Python 2提供了两种基本的整数类型,一种是int(64位有符号整数)一种是long,使用起来非常容易混乱,而在python3中只提供了int类型这一种。

isinstance(x, numbers.Integral) # Python 2, the canonical way
isinstance(x, (long, int))   # Python 2
isinstance(x, int)       # Python 3, easier to remember

在python3中同样的也可以应用于其他整数类型,如numpy.int32、numpy.int64,但其他类型不适用。

总结

以上所述是小编给大家介绍的愉快地迁移到 Python 3,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对三水点靠木网站的支持!
如果你觉得本文对你有帮助,欢迎转载,烦请注明出处,谢谢!

Python 相关文章推荐
python显示天气预报
Mar 02 Python
在Python的Django框架中编写错误提示页面
Jul 22 Python
python如何在循环引用中管理内存
Mar 20 Python
使用Python读取安卓手机的屏幕分辨率方法
Mar 31 Python
python3 中文乱码与默认编码格式设定方法
Oct 31 Python
Django3.0 异步通信初体验(小结)
Dec 04 Python
python模式 工厂模式原理及实例详解
Feb 11 Python
Python Handler处理器和自定义Opener原理详解
Mar 05 Python
python中append函数用法讲解
Dec 11 Python
Python Selenium异常处理的实例分析
Feb 28 Python
python 破解加密zip文件的密码
Apr 22 Python
Python 数据可视化神器Pyecharts绘制图像练习
Feb 28 Python
python学习开发mock接口
Apr 28 #Python
Python简单基础小程序的实例代码
Apr 28 #Python
python实现Excel文件转换为TXT文件
Apr 28 #Python
Python3.5模块的定义、导入、优化操作图文详解
Apr 27 #Python
Python3.5内置模块之time与datetime模块用法实例分析
Apr 27 #Python
Python3.5内置模块之os模块、sys模块、shutil模块用法实例分析
Apr 27 #Python
Python3.5内置模块之shelve模块、xml模块、configparser模块、hashlib、hmac模块用法分析
Apr 27 #Python
You might like
使用sockets:从新闻组中获取文章(二)
2006/10/09 PHP
PHP 常用数组内部函数(Array Functions)介绍
2013/06/05 PHP
PHP获取MySql新增记录ID值的3种方法
2014/06/24 PHP
php查询mssql出现乱码的解决方法
2014/12/29 PHP
PHP实现APP微信支付的实例讲解
2018/02/10 PHP
PHP+Session防止表单重复提交的解决方法
2018/04/09 PHP
PHP中十六进制颜色与RGB颜色值互转的方法
2019/03/18 PHP
window.addEventListener来解决让一个js事件执行多个函数
2012/12/26 Javascript
jquery入门必备的基本认识及实例(整理)
2013/06/24 Javascript
JavaScript中双叹号(!!)作用示例介绍
2014/04/10 Javascript
jQuery多条件筛选如何实现
2015/11/04 Javascript
NodeJS实现客户端js加密
2017/01/09 NodeJs
详解如何配置vue-cli3.0的vue.config.js
2018/08/23 Javascript
JS实现的点击按钮图片上下滚动效果示例
2019/01/28 Javascript
vue表单数据交互提交演示教程
2019/11/13 Javascript
[33:42]LGD vs OG 2018国际邀请赛小组赛BO2 第一场 8.16
2018/08/17 DOTA
python单例模式实例分析
2015/04/08 Python
Python的MongoDB模块PyMongo操作方法集锦
2016/01/05 Python
python3实现TCP协议的简单服务器和客户端案例(分享)
2017/06/14 Python
基于python socketserver框架全面解析
2017/09/21 Python
python实现批量解析邮件并下载附件
2018/06/19 Python
python 批量修改/替换数据的实例
2018/07/25 Python
python3中利用filter函数输出小于某个数的所有回文数实例
2019/11/24 Python
Pycharm新手使用教程(图文详解)
2020/09/17 Python
python 如何将office文件转换为PDF
2020/09/22 Python
python爬取2021猫眼票房字体加密实例
2021/02/19 Python
利用HTML5 Canvas制作一个简单的打飞机游戏
2015/05/11 HTML / CSS
英国知名美妆护肤在线商城:Zest Beauty
2018/04/24 全球购物
Vision Direct比利时:在线订购隐形眼镜
2019/08/27 全球购物
公司授权委托书范本
2014/04/03 职场文书
离婚协议书的范本
2015/01/27 职场文书
女性健康知识讲座主持词
2015/07/04 职场文书
公司考勤管理制度
2015/08/04 职场文书
团结主题班会
2015/08/13 职场文书
详解CSS玩转图片Base64编码
2021/05/25 HTML / CSS
Go语言安装并操作redis的go-redis库
2022/04/14 Golang