【Kaggle】Python - 7 使用外部库
使用外部库
在本教程中你将学习 Python 的 导入(imports),获得一些处理不熟悉库(以及它们返回的对象)的小技巧,并深入了解 运算符重载(operator overloading)。
Imports
到目前为止我们讨论的是语言内置的类型和函数。
但是 Python 的一大优点(特别是对数据科学家)是有大量高质量的第三方库可用。
有些库属于“标准库”,无论在哪里运行 Python 都能找到;
其他库可以很容易地安装,即便默认不会随 Python 一起发布。
无论哪种情况,我们都通过 imports 来访问这些代码。
下面先从标准库里的 math 模块导入做示例。
import math
print("It's math! It has type {}".format(type(math)))
math 是一个模块。
模块只是别人的一组变量(如果你愿意可以叫作*命名空间*)。
我们可以用内置函数 dir() 查看 math 中所有的名字。
import math
print(dir(math))
我们可以用点语法访问这些变量。
其中一些是简单的值,比如 math.pi:
import math
print("pi to 4 significant digits = {:.4}".format(math.pi))
但模块中大部分是函数,例如 math.log:
import math
math.log(32, 2)
当然,如果我们不知道 math.log 是干什么的,可以对它调用 help():
help(math.log)
我们也可以对模块本身调用 help()。
这会给出模块中所有函数和值的综合文档(以及模块的概述)。
点击“输出”可以查看完整的 math 帮助页。
help(math)
其他导入语法
如果我们知道会频繁使用 math 中的函数,可以给它取一个短一点的别名以减少敲字量(尽管在这个例子里 “math” 已经很短了)。
import math as mt
print(mt.pi)
你可能见过对某些流行库这样做的习惯,如 Pandas、Numpy、Tensorflow、Matplotlib。
比如常见的约定是import numpy as np和import pandas as pd。
as 只是给导入的模块改了个名。
它等价于下面这种写法:
import math
mt = math
如果我们希望直接使用 math 模块里的变量(即不用写 math.pi 或 mt.pi,而直接写 pi),那也可以。
from math import *
print(pi, log(32, 2))
import * 会把模块的所有变量直接导入到当前命名空间(无需点前缀)。
坏消息是:一些代码整洁主义者可能会因此抱怨你。
更糟的是:他们确实有道理。
from math import *
from numpy import *
print(pi, log(32, 2))
发生了什么?
上面原本能工作的代码现在有问题了!
这种“星号导入(star imports)”有时会导致奇怪且难以调试的情况。
本例的问题是 math 和 numpy 都有名为 log 的函数,但它们语义不同。
因为我们第二个导入了 numpy,它的 log 会覆盖(或“遮蔽”)从 math 导入的 log。
一个折衷的好办法是只从每个模块导入我们需要的具体名字:
from math import log, pi
from numpy import asarray
子模块(Submodules)
我们看到模块包含变量,这些变量可以是函数或值。
还要注意的是,模块也可以包含指向其他模块的变量。
import numpy
print("numpy.random is a", type(numpy.random))
print("it contains names such as...", dir(numpy.random)[-15:])
因此如果我们像上面那样导入 numpy,调用 random “子模块”中的函数就需要两个点。
import numpy
# Roll 10 dice
rolls = numpy.random.randint(low=1, high=6, size=10)
print(rolls)
你会遇到的各种对象
经过前面几课,你应该已经熟悉了 int、float、bool、list、string 和 dict(对吧?)。
不过这还不是全部。随着你使用各种专门库,会发现它们定义了自己的类型,你需要学会与之交互。
例如,图形库 matplotlib 会有代表子图、图形、刻度和注释的对象;pandas 的函数会产出 DataFrame 和 Series。
在本节中,我想给你一个快速的生存指南,帮助你应对陌生类型。
理解陌生对象的三把工具
在上面的代码中,我们看到调用 numpy 的函数返回了一个“数组(array)”。
我们以前没见过这种类型(至少在本课程里没有),但别慌:我们有三个熟悉的内置函数可以帮忙。
- type()(这是什么东西?)
type(rolls)
- dir()(我能对它做什么?)
print(dir(rolls))
如果我想要平均掷骰结果,看到有个 mean 方法看起来很有用…
rolls.mean()
或者我只是想把数组变成列表,可以用 tolist
rolls.tolist()
- help()(告诉我更多)
help(rolls.ravel)
好吧,既然好奇,告诉我关于 numpy.ndarray 的一切:
help(rolls)
当然,你也可以查看在线文档
运算符重载(Operator overloading)
下面这个表达式的值是多少?
[3, 4, 1, 2, 2, 1] + 10
很显然会报错。
但那下面呢?
rolls + 10
我们可能认为 Python 严格控制其核心语法的行为,例如 +、<、in、==,或者用于索引和切片的方括号。
但实际上,Python 采取了较为放手的策略。当你定义一个新类型时,可以选择如何实现加法,或者定义类型的对象在与另一个对象比较是否相等。
列表的设计者决定不允许把列表与数字相加,而 numpy 数组的设计者则走了另一条路(把数字加入到数组的每个元素上)。
下面还有一些 numpy 数组与 Python 操作符交互的例子(或至少与列表的行为不同的例子)。
# At which indices are the dice less than or equal to 3?
rolls <= 3
import numpy
xlist = [[1,2,3],[2,4,6],]
# Create a 2-dimensional array
x = numpy.asarray(xlist)
print("xlist = {}\nx =\n{}".format(xlist, x))
# Get the last element of the second row of our numpy array
x[1,-1]
# Get the last element of the second sublist of our nested list?
xlist[1,-1]
numpy 的 ndarray 类型擅长处理多维数据,因此它为索引定义了自己的逻辑,允许用元组来指定每一维的索引。
什么时候 1 + 1 不等于 2?
情况还能更奇怪。你可能听说过(或使用过) tensorflow,这是一个流行的用于深度学习的 Python 库。
它大量使用了运算符重载。
import tensorflow as tf
# Create two constants, each with value 1
a = tf.constant(1)
b = tf.constant(1)
# Add them together to get...
a + b
a + b 并不是 2,而是(引用 tensorflow 文档):
一个指向某个 Operation 输出的符号句柄。
它不保存该操作输出的值,而是提供在一个 TensorFlowtf.Session中计算这些值的手段。
重要的是要意识到这种事情是可能的:库经常以不直观或看起来很“魔法”的方式使用运算符重载。
理解 Python 的操作符在 int、string、list 上的行为并不能保证你立刻理解它们在 tensorflow 的 Tensor、numpy 的 ndarray 或 pandas 的 DataFrame 上是如何工作的。
一旦你开始接触 DataFrame,下面这种表达式会变得直观且很受欢迎:
# Get the rows with population over 1m in South America
df[(df['population'] > 10**6) & (df['continent'] == 'South America')]
但它为什么能工作?
上面可能用了大约 5 个不同的被重载的操作符。
每个操作符在背后做了什么?
当问题出现时,知道答案是有帮助的。
想知道它们是如何工作的?
当你对一个对象调用 help() 或 dir() 时,会看到很多以双下划线开始和结束的名字,你可能会想它们是干什么的。
print(dir(list))
这与运算符重载直接相关。
当 Python 程序员想定义他们类型上的运算符行为时,会实现以两个下划线开头和结尾的特殊方法,例如 __lt__、__setattr__ 或 __contains__。
通常,遵循这种双下划线格式的名字对 Python 有特殊含义。
例如,表达式 x in [1, 2, 3] 实际上在背后调用了列表的方法 __contains__,等价于(看起来更丑的写法)[1, 2, 3].__contains__(x)。
如果你想深入了解,可以查看 Python 官方文档,里面描述了很多很多这样的特殊“下划线”方法。
我们在本课程里不会定义自己的类型(要是有时间就好了!),但希望你未来有机会体验定义自己奇妙又古怪类型的乐趣。
更多推荐


所有评论(0)