Life is short, you need Python.
---- Bruce Eckel
Zen of Python
Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Flat is better than nested.
Sparse is better than dense.
Readability counts.
Special cases aren't special enough to break the rules.
Although practicality beats purity.
Errors should never pass silently.
Unless explicitly silenced.
In the face of ambiguity, refuse the temptation to guess.
There should be one-- and preferably only one --obvious way to do it.
Although that way may not be obvious at first unless you're Dutch.
Now is better than never.
Although never is often better than *right* now.
If the implementation is hard to explain, it's a bad idea.
If the implementation is easy to explain, it may be a good idea.
Namespaces are one honking great idea -- let's do more of those!
参考:
https://www.python.org/dev/peps/pep-0020/#id3
http://www.tuicool.com/articles/NNnuQ3v
Python基础
Python拥有很好的扩充性,可以非常轻松地用其他语言编写模块供调用,用Python编写的模块也可以通过各种方式轻松被其他语言调用。所以一种常见的Python使用方式是,底层复杂且对效率要求高的模块用C/C++等语言实现,顶层调用的API用Python封装,这样可以通过简单的语法实现顶层逻辑,故而Python又被称为“胶水语言”。这种特性的好处是,无需花费很多时间在编程实现上,更多的时间可以专注于思考问题的逻辑。尤其是对做算法和深度学习的从业人员,这种方式是非常理想的,所以如今的深度学习框架中,除了MATLAB,或是Deeplearning4j这种摆明了给Java用的,其他框架基本上要么官方接口就是Python,要么支持Python接口。
模块导入
因为提到了对象名覆盖和import,所以简单讲一下。import是利用Python中各种强大库的基础,比如要计算cos(π)的值,可以有下面4种方式:
# 直接导入Python的内置基础数学库
import math
print(math.cos(math.pi))
# 从math中导入cos函数和pi变量
from math import cos, pi
print(cos(pi))
# 如果是个模块,在导入的时候可以起个别名,避免名字冲突或是方便懒得打字的人使用
import math as m
print(m.cos(m.pi))
# 从math中导入所有东西
from math import *
print(cos(pi))
一般来说最后一种方式不是很推荐,因为不知道import导入的名字里是否和现有对象名已经有冲突,很可能会不知不觉覆盖了现有的对象。
集合
集合是一种很有用的数学操作,比如列表去重,或是理清两组数据之间的关系,集合的操作符和位操作符有交集,注意不要弄混:
A = set([1, 2, 3, 4])
B = {3, 4, 5, 6}
C = set([1, 1, 2, 2, 2, 3, 3, 3, 3])
print(C) # 集合的去重效果,set([1, 2, 3])
print(A | B) # 求并集,set([1, 2, 3, 4, 5, 6])
print(A & B) # 求交集,set([3, 4])
print(A - B) # 求差集,属于A但不属于B的,set([1, 2])
print(B - A) # 求差集,属于B但不属于A的,set([5, 6])
print(A ^ B) # 求对称差集,相当于(A-B)|(B-A),set([1, 2, 5, 6])
for循环
如果for循环中的if块内的语句没有被触发,则通过else执行指定操作:
wusuowei = ["I", "don't", "give", "a", "shit"]
for x in wusuowei:
if x == "f**k":
print("What the f**k!")
hexie = False
break
else: # for循环中if内语句未被触发
print("Harmonious society!") # 和谐社会!
这样不需要一个标记是否和谐的状态变量,语句简洁了很多。
函数
def say_hello():
print('Hello!')
def greetings(x='Good morning!'):
print(x)
say_hello() # Hello!
greetings() # Good morning!
greetings("What's up!") # What's up!
a = greetings() # 返回值是None
def create_a_list(x, y=2, z=3): # 默认参数项必须放后面
return [x, y, z]
b = create_a_list(1) # [1, 2, 3]
c = create_a_list(3, 3) # [3, 3, 3]
d = create_a_list(6, 7, 8) # [6, 7, 8]
def traverse_args(*args):
for arg in args:
print(arg)
traverse_args(1, 2, 3) # 依次打印1, 2, 3
traverse_args('A', 'B', 'C', 'D') # 依次打印A, B, C, D
def traverse_kargs(**kwargs):
for k, v in kwargs.items():
print(k, v)
traverse_kargs(x=3, y=4, z=5) # 依次打印('x', 3), ('y', 4), ('z', 5)
traverse_kargs(fighter1='Fedor', fighter2='Randleman')
def foo(x, y, *args, **kwargs):
print(x, y)
print(args)
print(kwargs)
# 第一个pring输出(1, 2)
# 第二个print输出(3, 4, 5)
# 第三个print输出{'a': 3, 'b': 'bar'}
foo(1, 2, 3, 4, 5, a=6, b='bar')
括号里面定义参数,参数可以有默认值,且默认值不能在无默认值参数之前。
Python中的返回值用return定义,如果没有定义返回值,默认返回值是None。
参数的定义可以非常灵活,可以有定义好的固定参数,也可以有可变长的参数(args: arguments)和关键字参数(kargs: keyword arguments)。
如果要把这些参数都混用,则固定参数在最前,关键字参数在最后。
Python中万物皆对象,所以一些情况下函数也可以当成一个变量似的使用。例如:
moves = ['up', 'left', 'down', 'right']
def move_up(x): # 定义向上的操作
x[1] += 1
def move_down(x): # 定义向下的操作
x[1] -= 1
def move_left(x): # 定义向左的操作
x[0] -= 1
def move_right(x): # 定义向右的操作
x[0] += 1
# 动作和执行的函数关联起来,函数作为键对应的值
actions = {
'up': move_up,
'down': move_down,
'left': move_left,
'right': move_right
}
coord = [0, 0]
for move in moves:
actions[move](coord)
print(coord)
Python yield 使用浅析
参考:
https://www.ibm.com/developerworks/cn/opensource/os-cn-python-yield/
您可能听说过,带有 yield 的函数在 Python 中被称之为 generator(生成器),何谓 generator ?
我们先抛开 generator,以一个常见的编程题目来展示 yield 的概念。
如何生成斐波那契數列
斐波那契(Fibonacci)數列是一个非常简单的递归数列,除第一个和第二个数
外,任意一个数都可由前两个数相加得到。用计算机程序输出斐波那契數列的
前 N 个数是一个非常简单的问题,许多初学者都可以轻易写出如下函数:
清单 1. 简单输出斐波那契數列前 N 个数
def fab(max):
n, a, b = 0, 0, 1
while n < max:
print b
a, b = b, a + b
n = n + 1
执行 fab(5),我们可以得到如下输出:
>>> fab(5)
1
1
2
3
5
结果没有问题,但有经验的开发者会指出,直接在 fab 函数中用 print 打印数字
会导致该函数可复用性较差,因为 fab 函数返回 None,其他函数无法获得该函
数生成的数列。
要提高 fab 函数的可复用性,最好不要直接打印出数列,而是返回一个 List。
以下是 fab 函数改写后的第二个版本:
清单 2. 输出斐波那契數列前 N 个数第二版
def fab(max):
n, a, b = 0, 0, 1
L = []
while n < max:
L.append(b)
a, b = b, a + b
n = n + 1
return L
可以使用如下方式打印出 fab 函数返回的 List:
>>> for n in fab(5):
... print n
...
1
1
2
3
5
改写后的 fab 函数通过返回 List 能满足复用性的要求,但是更有经验的开发者
会指出,该函数在运行中占用的内存会随着参数 max 的增大而增大,如果要控
制内存占用,最好不要用 List来保存中间结果,而是通过 iterable 对象来迭
代。例如,在 Python2.x 中,代码:
清单 3. 通过 iterable 对象来迭代
for i in range(1000): pass
会导致生成一个 1000 个元素的 List,而代码:
for i in xrange(1000): pass
则不会生成一个 1000 个元素的 List,而是在每次迭代中返回下一个数值,内存空间占用很小。因为 xrange 不返回 List,而是返回一个 iterable 对象。
利用 iterable 我们可以把 fab 函数改写为一个支持 iterable 的 class,以下是第三个版本的 Fab:
清单 4. 第三个版本
class Fab(object):
def __init__(self, max):
self.max = max
self.n, self.a, self.b = 0, 0, 1
def __iter__(self):
return self
def next(self):
if self.n < self.max:
r = self.b
self.a, self.b = self.b, self.a + self.b
self.n = self.n + 1
return r
raise StopIteration()
Fab 类通过 next() 不断返回数列的下一个数,内存占用始终为常数:
>>> for n in Fab(5):
... print n
...
1
1
2
3
5
然而,使用 class 改写的这个版本,代码远远没有第一版的 fab 函数来得简
洁。如果我们想要保持第一版 fab 函数的简洁性,同时又要获得 iterable 的效
果,yield 就派上用场了:
清单 5. 使用 yield 的第四版
def fab(max):
n, a, b = 0, 0, 1
while n < max:
yield b
# print b
a, b = b, a + b
n = n + 1
第四个版本的 fab 和第一版相比,仅仅把 print b 改为了 yield b,就在保持简洁性的同时获得了 iterable 的效果。
调用第四版的 fab 和第二版的 fab 完全一致:
>>> for n in fab(5):
... print n
...
1
1
2
3
5
**
简单地讲,yield 的作用就是把一个函数变成一个 generator,带有 yield 的函数不再是一个普通函数,Python 解释器会将其视为一个 generator,调用 fab(5) 不会执行 fab 函数,而是返回一个 iterable 对象!在 for 循环执行时,每次循环都会执行 fab 函数内部的代码,执行到 yield b 时,fab 函数就返回一个迭代值,下次迭代时,代码从 yield b 的下一条语句继续执行,而函数的本地变量看起来和上次中断执行前是完全一样的,于是函数继续执行,直到再次遇到 yield。
**
也可以手动调用 fab(5) 的 next() 方法(因为 fab(5) 是一个 generator 对象,该对象具有 next() 方法),这样我们就可以更清楚地看到 fab 的执行流程:
清单 6. 执行流程
>>> f = fab(5)
>>> f.next()
1
>>> f.next()
1
>>> f.next()
2
>>> f.next()
3
>>> f.next()
5
>>> f.next()
Traceback (most recent call last):
File "", line 1, in
StopIteration
当函数执行结束时,generator 自动抛出 StopIteration 异常,表示迭代完成。在 for 循环里,无需处理 StopIteration 异常,循环会正常结束。
我们可以得出以下结论:
**
一个带有 yield 的函数就是一个 generator,它和普通函数不同,生成一个 generator 看起来像函数调用,但不会执行任何函数代码,直到对其调用 next()(在 for 循环中会自动调用 next())才开始执行。虽然执行流程仍按函数的流程执行,但每执行到一个 yield 语句就会中断,并返回一个迭代值,下次执行时从 yield 的下一个语句继续执行。看起来就好像一个函数在正常执行的过程中被 yield 中断了数次,每次中断都会通过 yield 返回当前的迭代值。
yield 的好处是显而易见的,把一个函数改写为一个 generator 就获得了迭代能力,比起用类的实例保存状态来计算下一个 next() 的值,不仅代码简洁,而且执行流程异常清晰。
**
如何判断一个函数是否是一个特殊的 generator 函数?可以利用 isgeneratorfunction 判断:
清单 7. 使用 isgeneratorfunction 判断
>>> from inspect import isgeneratorfunction
>>> isgeneratorfunction(fab)
True
要注意区分 fab 和 fab(5),fab 是一个 generator function,而 fab(5) 是调用 fab 返回的一个 generator,好比类的定义和类的实例的区别:
清单 8. 类的定义和类的实例
>>> import types
>>> isinstance(fab, types.GeneratorType)
False
>>> isinstance(fab(5), types.GeneratorType)
True
fab 是无法迭代的,而 fab(5) 是可迭代的:
>>> from collections import Iterable
>>> isinstance(fab, Iterable)
False
>>> isinstance(fab(5), Iterable)
True
每次调用 fab 函数都会生成一个新的 generator 实例,各实例互不影响:
>>> f1 = fab(3)
>>> f2 = fab(5)
>>> print 'f1:', f1.next()
f1: 1
>>> print 'f2:', f2.next()
f2: 1
>>> print 'f1:', f1.next()
f1: 1
>>> print 'f2:', f2.next()
f2: 1
>>> print 'f1:', f1.next()
f1: 2
>>> print 'f2:', f2.next()
f2: 2
>>> print 'f2:', f2.next()
f2: 3
>>> print 'f2:', f2.next()
f2: 5
return 的作用
在一个 generator function 中,如果没有 return,则默认执行至函数完毕,如果在执行过程中 return,则直接抛出 StopIteration 终止迭代。
另一个例子
另一个 yield 的例子来源于文件读取。如果直接对文件对象调用 read() 方法,会导致不可预测的内存占用。好的方法是利用固定长度的缓冲区来不断读取文件内容。通过 yield,我们不再需要编写读文件的迭代类,就可以轻松实现文件读取:
清单 9. 另一个 yield 的例子
def read_file(fpath):
BLOCK_SIZE = 1024
with open(fpath, 'rb') as f:
while True:
block = f.read(BLOCK_SIZE)
if block:
yield block
else:
return
以上仅仅简单介绍了 yield 的基本概念和用法,yield 在 Python 3 中还有更强大的用法,我们会在后续文章中讨论。
类(Class)
Python中的类的概念和其他语言相比没什么不同,比较特殊的是protected和private在Python中是没有明确限制的,一个惯例是用单下划线开头的表示protected,用双下划线开头的表示private:
class A:
"""Class A"""
def __init__(self, x, y, name):
self.x = x
self.y = y
self._name = name
def introduce(self):
print(self._name)
def greeting(self):
print("What's up!")
def __l2norm(self):
return self.x**2 + self.y**2
def cal_l2norm(self):
return self.__l2norm()
a = A(11, 11, 'Leonardo')
print(A.__doc__) # "Class A"
a.introduce() # "Leonardo"
a.greeting() # "What's up!"
print(a._name) # 可以正常访问
print(a.cal_l2norm()) # 输出11*11+11*11=242
print(a._A__l2norm()) # 仍然可以访问,只是名字不一样
print(a.__l2norm()) # 报错: 'A' object has no attribute '__l2norm'
类的初始化使用的是init(self,),所有成员变量都是self的,所以以self.开头。可以看到,单下划线开头的变量是可以直接访问的,而双下划线开头的变量则触发了Python中一种叫做name mangling的机制,其实就是名字变了下,仍然可以通过前边加上“_类名”的方式访问。也就是说Python中变量的访问权限都是靠自觉的。类定义中紧跟着类名字下一行的字符串叫做docstring,可以写一些用于描述类的介绍,如果有定义则通过“类名.doc”访问。这种前后都加双下划线访问的是特殊的变量/方法,除了doc和init还有很多,这里就不展开讲了。
Python中的继承也非常简单,最基本的继承方式就是定义类的时候把父类往括号里一放就行了。
map, reduce和filter
map可以用于对可遍历结构的每个元素执行同样的操作,批量操作:
map(lambda x: x**2, [1, 2, 3, 4]) # [1, 4, 9, 16]
map(lambda x, y: x + y, [1, 2, 3], [5, 6, 7]) # [6, 8, 10]
reduce则是对可遍历结构的元素按顺序进行两个输入参数的操作,并且每次的结果保存作为下次操作的第一个输入参数,还没有遍历的元素作为第二个输入参数。这样的结果就是把一串可遍历的值,减少(reduce)成一个对象:
reduce(lambda x, y: x + y, [1, 2, 3, 4]) # ((1+2)+3)+4=10
filter顾名思义,根据条件对可遍历结构进行筛选:
filter(lambda x: x % 2, [1, 2, 3, 4, 5]) # 筛选奇数,[1, 3, 5]
需要注意的是,对于filter和map,在Python2中返回结果是列表,Python3中是生成器。
文件操作和pickle
在Python中,推荐用上下文管理器(with-as)来打开文件,IO资源的管理更加安全,而且不用老惦记着给文件执行close()函数。还是举例子来说明,考虑有个文件name_age.txt,里面存储着名字和年龄的关系,格式如下:
Tom,8
Jerry,7
Tyke,3
...
读取文件内容并全部显示:
with open('name_age.txt', 'r') as f: # 打开文件,读取模式
lines = f.readlines() # 一次读取所有行
for line in lines: # 按行格式化并显示信息
name, age = line.rstrip().split(',')
print('{} is {} years old.'.format(name, age))
open()的第一个参数是文件名,第二个参数是模式。文件的模式一般有四种,读取(r),写入(w),追加(a)和读写(r+)。如果希望按照二进制数据读取,则将文件模式和b一起使用(wb, r+b…)。
再考虑一个场景,要读取文件内容,并把年龄和名字的顺序交换存成新文件age_name.txt,这时可以同时打开两个文件:
with open('name_age.txt', 'r') as fread, open('age_name.txt', 'w') as fwrite:
line = fread.readline()
while line:
name, age = line.rstrip().split(',')
fwrite.write('{},{}\n'.format(age, name))
line = fread.readline()
有的时候我们进行文件操作是希望把对象进行序列化,那么可以考虑用pickle模块:
import pickle
lines = [
"I'm like a dog chasing cars.",
"I wouldn't know what to do if I caught one...",
"I'd just do things."
]
with open('lines.pkl', 'wb') as f: # 序列化并保存成文件
pickle.dump(lines, f)
with open('lines.pkl', 'rb') as f: # 从文件读取并反序列化
lines_back = pickle.load(f)
print(lines_back) # 和lines一样
注意到,序列化的时候就得使用b模式了。Python2中有个效率更高的pickle叫cPickle,用法和pickle一样,在Python3中就只有一个pickle。
多进程
深度学习中对数据高效处理常常会需要并行,这时多进程就派上了用场。考虑这样一个场景,在数据准备阶段,有很多文件需要运行一定的预处理,正好有台多核服务器,我们希望把这些文件分成32份,并行处理:
from multiprocessing import Process#, freeze_support
def process_data(filelist):
for filepath in filelist:
print('Processing {} ...'.format(filepath))
# 处理数据
...
if __name__ == '__main__':
# 如果是在Windows下,还需要加上freeze_support()
#freeze_support()
# full_list包含了要处理的全部文件列表
...
n_total = len(full_list) # 一个远大于32的数
n_processes = 32
# 每段子列表的平均长度
length = float(n_total) / float(n_processes)
# 计算下标,尽可能均匀地划分输入文件列表
indices = [int(round(i*length)) for i in range(n_processes+1)]
# 生成每个进程要处理的子文件列表
sublists = [full_list[indices[i]:indices[i+1]] for i in range(n_processes)]
# 生成进程
processes = [Process(target=process_data, args=(x,)) for x in sublists]
# 并行处理
for p in processes:
p.start()
for p in processes:
p.join()
os模块
深度学习中的数据多是文件,所以数据处理阶段和文件相关的操作就非常重要。除了文件IO,Python中一些操作系统的相关功能也能够非常方便地帮助数据处理。想象一下我们有一个文件夹叫做data,下边有3个子文件夹叫做cat,dog和bat,里面分别是猫,狗和蝙蝠的照片。为了训练一个三分类模型,我们先要生成一个文件,里面每一行是文件的路径和对应的标签。定义cat是0,dog是1,bat是2,则可以通过如下脚本:
import os
# 定义文件夹名称和标签的对应关系
label_map = {
'cat': 0,
'dog': 1,
'bat': 2
}
with open('data.txt', 'w') as f:
# 遍历所有文件,root为当前文件夹,dirs是所有子文件夹名,files是所有文件名
for root, dirs, files in os.walk('data'):
for filename in files:
filepath = os.sep.join([root, filename]) # 获得文件完整路径
dirname = root.split(os.sep)[-1] # 获取当前文件夹名称
label = label_map[dirname] # 得到标签
line = '{},{}\n'.format(filepath, label)
f.write(line)
其中,os.sep是当前操作系统的路径分隔符,在Unix/Linux中是'/',Windows中是'\'。有的时候我们已经有了所有的文件在一个文件夹data下,希望获取所有文件的名称,则可以用os.listdir():
filenames = os.listdir('data')
os也提供了诸如拷贝,移动和修改文件名等操作。同时因为大部分深度学习框架最常见的都是在Unix/Linux下使用,并且Unix/Linux的shell已经非常强大(比Windows好用太多),所以只需要用字符串格式化等方式生成shell命令的字符串,然后通过os.system()就能方便实现很多功能,有时比os,还有Python中另一个操作系统相关模块shutil还要方便:
import os, shutil
filepath0 = 'data/bat/IMG_000001.jpg'
filepath1 = 'data/bat/IMG_000000.jpg'
# 修改文件名
os.system('mv {} {}'.format(filepath0, filepath1))
#os.rename(filepath0, filepath1)
# 创建文件夹
dirname = 'data_samples'
os.system('mkdir -p {}'.format(dirname))
#if not os.path.exists(dirname):
# os.mkdir(dirname)
# 拷贝文件
os.system('cp {} {}'.format(filepath1, dirname))
#shutil.copy(filepath1, dirname)
网络编程
套接字:通讯端点
套接字是一种具有之前所说的“通讯端点”概念的计算机网络数据结构。网络化的应用程序在开始任何通讯之前都必需要创建套接字。
套接字起源于 20 世纪 70 年代加利福尼亚大学伯克利分校版本的 Unix,即人们所说的 BSD Unix。 因此,有时人们也把套接字称为“伯克利套接字”或“BSD 套接字”。一开始,套接字被设计用在同 一台主机上多个应用程序之间的通讯。这也被称进程间通讯,或 IPC。套接字有两种,分别是基于文 件型的和基于网络型的。
Unix 套接字是我们要介绍的第一个套接字家族。其“家族名”为 AF_UNIX表示“地址家族:UNIX”。
另一种套接字是基于网络的,它有自己的家族名字:AF_INET,或叫“地址家族:Internet”。
Python 2.5 中加入了一种 Linux 套接字的支 持:AF_NETLINK套接字家族让用户代码与内核代码之间的 IPC 可以使用标准 BSD 套接字接口。
Python 只支持 AF_UNIX,AF_NETLINK,和 AF_INET 家族。
套接字地址:主机与端口
一个 Internet 地址由网 络通讯所必需的主机与端口组成。
合法的端口号范围为 0 到 65535。其中,小于 1024 的端口号为系统保留端口。如果你所使用的 是 Unix 操作系统,保留的端口号(及其对应的服务/协议和套接字类型)可以通过/etc/services 文件获得。
面向连接与无连接
面向连接
面向连接的套接字,即在通讯之 前一定要建立一条连接,就像跟朋友打电话时那样。这种通讯方式也被称为“虚电路”或“流套接 字”。
面向连接的通讯方式 供了顺序的,可靠的,不会重复的数据传输,而且也不会被加上数据边 界。这也意味着,每一个要发送的信息,可能会被拆分成多份,每一份都会不多不少地正确到达目 的地。然后被重新按顺序拼装起来,传给正在等待的应用程序。
实现这种连接的主要协议就是传输控制协议(即 TCP)。要创建 TCP 套接字就得在创建的时候, 指定套接字类型为 SOCK_STREAM。TCP 套接字采用 SOCK_STREAM 这个名字,表达了它做为流套接字的 特点。由于这些套接字使用 Internet 协议(IP)来查找网络中的主机,这样形成的整个系统,一般 会涉及两个协议(TCP 和 IP),即 TCP/IP。
无连接
与虚电路完全相反的是数据报型的无连接套接字。这意味着,无需建立连接就可以进行通讯。 但这时,数据到达的顺序,可靠性及数据不重复性就无法保证了。数据报会保留数据边界,这就表 示,数据不会像面向连接的协议那样被拆分成小块。
既然数据报有这么多缺点,为什么还要使用它呢? 由于面向连接套接字要 供一些保证,以及要维持虚电路连接,这都是很重的额外负担。数据报没有这 些负担,所以它更“便宜”。通常能 供更好的性能,更适合某些应用场合。
实现这种连接的主要协议就是用户数据报协议(即 UDP)。要创建 UDP 套接字就得在创建的时候, 指定套接字类型为 SOCK_DGRAM。由于这些套接字使用 Internet 协议来查找网络中的主机,这样形成的整个系统,一般会涉及这两个协议(UDP 和 IP),即 UDP/IP。
编程TIP
关于这句 from __future__ import absolute_import
的作用
Python提供了future模块,把下一个新版本的特性导入到当前版本,于是我们就可以在当前版本中测试一些新版本的特性。
absolute_import直观地看就是说”加入绝对引入这个新特性”。说到绝对引入,当然就会想到相对引入。那么什么是相对引入呢?比如说,你的包结构是这样的:
pkg/
pkg/init.py
pkg/main.py
pkg/string.py
如果你在main.py中写import string,那么在Python 2.4或之前, Python会先查找当前目录下有没有string.py, 若找到了,则引入该模块,然后你在main.py中可以直接用string了。如果你是真的想用同目录下的string.py那就好,但是如果你是想用系统自带的标准string.py呢?那其实没有什么好的简洁的方式可以忽略掉同目录的string.py而引入系统自带的标准string.py。这时候你就需要from future import absolute_import了。这样,你就可以用import string来引入系统的标准string.py, 而用from pkg import string来引入当前目录下的string.py了。
from __future__ import division
如果你想在Python 2.7的代码中直接使用Python 3.x的除法,可以通过future模块的division实现:
from __future__ import division
print '10 / 3 =', 10 / 3
print '10.0 / 3 =', 10.0 / 3
print '10 // 3 =', 10 // 3
结果:
10 / 3 = 3.33333333333
10.0 / 3 = 3.33333333333
10 // 3 = 3