7.Working with External Libraries

在本课中,我将讨论Python中的imports,提供一些使用不熟悉的库(以及它们返回的对象)的技巧,并深入研究Python的内容,以及谈谈运算符重载。

Imports

到目前为止,我们已经讨论了内置于该语言的类型和函数。
但是关于Python的最好的事情之一(特别的如果你是一名数据科学家)是为它编写的大量高质量自定义库。
其中一些库位于“标准库”中,这意味着您可以在运行Python的任何位置找到它们。 其他库可以轻松添加,即使它们并不总是随Python一起提供。
无论哪种方式,我们都会使用导入访问此代码。
我们将从标准库中导入math开始我们的示例。

【1】

import math

print("It's math! It has type {}".format(type(math)))
It's math! It has type <class 'module'>

math是一个模块,模块只是其他人定义的变量集合。 我们可以使用内置函数dir()查看数学中的所有名称。

【2】

print(dir(math))
['__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'acos', 'acosh', 'asin', 'asinh', 'atan', 'atan2', 'atanh', 'ceil', 'copysign', 'cos', 'cosh', 'degrees', 'e', 'erf', 'erfc', 'exp', 'expm1', 'fabs', 'factorial', 'floor', 'fmod', 'frexp', 'fsum', 'gamma', 'gcd', 'hypot', 'inf', 'isclose', 'isfinite', 'isinf', 'isnan', 'ldexp', 'lgamma', 'log', 'log10', 'log1p', 'log2', 'modf', 'nan', 'pi', 'pow', 'radians', 'sin', 'sinh', 'sqrt', 'tan', 'tanh', 'tau', 'trunc']

我们可以使用点语法访问这些变量。 其中一些是指简单的值,比如math.pi:

【3】

print("pi to 4 significant digits = {:.4}".format(math.pi))
pi to 4 significant digits = 3.142

但是我们在模块中找到最多的是函数:像math.log:

[4]

math.log(32, 2)
5.0

当然,如果我们不了解math.log做什么,我们调用help()函数:

【5】

help(math.log)
Help on built-in function log in module math:

log(...)
    log(x[, base])
    
    Return the logarithm of x to the given base.
    If the base not specified, returns the natural logarithm (base e) of x.

我们也可以在模块本身上调用help()。 这将为我们提供模块中所有函数和值的组合文档(以及模块的高级描述)。 

【6】

help(math)

Other import syntax

如果我们知道我们将经常使用math中的函数,我们可以在较短的别名下导入它以节省一些输入(尽管在这种情况下“math”已经相当短)。

【7】

import math as mt
mt.pi
3.141592653589793

您可能已经看到了使用某些流行的库(如Pandas,Numpy,Tensorflow或Matplotlib)执行此操作的代码。 例如,import numpy as np 和 import pandas as pd是一种常见的约定。

as相当于重命名导入模块,等价于如下操作:

[8]

import math
mt = math

如果我们可以自己引用数学模块中的所有变量,那不是很好吗? 即如果我们可以只引用pi而不是math.pi或mt.pi? 好消息:我们可以做到这一点。

[9]

from math import *
print(pi, log(32, 2))
3.141592653589793 5.0

import *使您可以直接访问所有模块的变量。
坏消息:一些纯粹主义者可能会抱怨你这样做。
[10]

from math import *
from numpy import *
print(pi, log(32, 2))
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-10-9b512bc684f5> in <module>()
      1 from math import *
      2 from numpy import *
----> 3 print(pi, log(32, 2))

TypeError: return arrays must be of ArrayType

什么是什么? 但它以前工作过!
这些“star imports”偶尔会导致奇怪的,难以调试的情况。
这种情况下的问题是math和numpy模块都有称为log的函数,但它们具有不同的语义。 因为numpy导入在第二个,覆盖了我们从math导入的变量。
一个很好的折衷方案是只从每个模块导入我们需要的特定内容:

[11]

from math import log, pi
from numpy import asarray

Submodules

我们已经看到模块包含可以引用函数或值的变量。 需要注意的是,它们也可以包含引用其他模块的变量。

[12]

import numpy
print("numpy.random is a", type(numpy.random))
print("it contains names such as...",
      dir(numpy.random)[-15:]
     )
numpy.random is a <class 'module'>
it contains names such as... ['set_state', 'shuffle', 'standard_cauchy', 'standard_exponential', 'standard_gamma', 'standard_normal', 'standard_t', 'test', 'triangular', 'uniform', 'vonmises', 'wald', 'warnings', 'weibull', 'zipf']

如果我们导入numpy模块,调用randon子模块中的函数需要两个点号。

【13】

# Roll 10 dice
rolls = numpy.random.randint(low=1, high=6, size=10)
rolls
array([2, 1, 5, 2, 4, 5, 4, 5, 5, 5])

Oh the places you'll go, oh the objects you'll see

所以在经过6课之后,你对 整数,浮点数,布尔,列表,字符串和字典很熟悉了(对吧?)。
即使这是真的,也不会就此结束。 当您使用各种库进行专门任务时,您会发现他们定义了您自己必须学习使用的类型。 例如,如果您使用图形库matplotlib,您将接触到它定义的对象,这些对象代表子图,数字,TickMark和注释。 pandas函数将为您提供DataFrames和Series。
在本节中,我想与您分享一个快速生存指南,用于处理奇怪的类型。

Three tools for understanding strange objects

在上面的例子中,我们看到调用一个numpy函数给了我们一个“数组”。 我们以前从未见过这样的事情(不管怎么说都不是这样)。 但不要惊慌:我们有三个熟悉的内置函数来帮助我们。
1:type()(这是什么东西?)

【14】

type(rolls)

numpy.ndarray

2.dir()(我可以用它做什么?)

【15】

print(dir(rolls))
['T', '__abs__', '__add__', '__and__', '__array__', '__array_finalize__', '__array_interface__', '__array_prepare__', '__array_priority__', '__array_struct__', '__array_ufunc__', '__array_wrap__', '__bool__', '__class__', '__complex__', '__contains__', '__copy__', '__deepcopy__', '__delattr__', '__delitem__', '__dir__', '__divmod__', '__doc__', '__eq__', '__float__', '__floordiv__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '__iadd__', '__iand__', '__ifloordiv__', '__ilshift__', '__imatmul__', '__imod__', '__imul__', '__index__', '__init__', '__init_subclass__', '__int__', '__invert__', '__ior__', '__ipow__', '__irshift__', '__isub__', '__iter__', '__itruediv__', '__ixor__', '__le__', '__len__', '__lshift__', '__lt__', '__matmul__', '__mod__', '__mul__', '__ne__', '__neg__', '__new__', '__or__', '__pos__', '__pow__', '__radd__', '__rand__', '__rdivmod__', '__reduce__', '__reduce_ex__', '__repr__', '__rfloordiv__', '__rlshift__', '__rmatmul__', '__rmod__', '__rmul__', '__ror__', '__rpow__', '__rrshift__', '__rshift__', '__rsub__', '__rtruediv__', '__rxor__', '__setattr__', '__setitem__', '__setstate__', '__sizeof__', '__str__', '__sub__', '__subclasshook__', '__truediv__', '__xor__', 'all', 'any', 'argmax', 'argmin', 'argpartition', 'argsort', 'astype', 'base', 'byteswap', 'choose', 'clip', 'compress', 'conj', 'conjugate', 'copy', 'ctypes', 'cumprod', 'cumsum', 'data', 'diagonal', 'dot', 'dtype', 'dump', 'dumps', 'fill', 'flags', 'flat', 'flatten', 'getfield', 'imag', 'item', 'itemset', 'itemsize', 'max', 'mean', 'min', 'nbytes', 'ndim', 'newbyteorder', 'nonzero', 'partition', 'prod', 'ptp', 'put', 'ravel', 'real', 'repeat', 'reshape', 'resize', 'round', 'searchsorted', 'setfield', 'setflags', 'shape', 'size', 'sort', 'squeeze', 'std', 'strides', 'sum', 'swapaxes', 'take', 'tobytes', 'tofile', 'tolist', 'tostring', 'trace', 'transpose', 'var', 'view']

【16】

# What am I trying to do with this dice roll data? Maybe I want the average roll, in which case the "mean"
# method looks promising...
rolls.mean()
3.8

【17】

# Or maybe I just want to get back on familiar ground, in which case I might want to check out "tolist"
rolls.tolist()
[2, 1, 5, 2, 4, 5, 4, 5, 5, 5]

3.help()(告诉我更多)

【18】

# That "ravel" attribute sounds interesting. I'm a big classical music fan.
help(rolls.ravel)

Help on built-in function ravel:

ravel(...) method of numpy.ndarray instance
    a.ravel([order])
    
    Return a flattened array.
    
    Refer to `numpy.ravel` for full documentation.
    
    See Also
    --------
    numpy.ravel : equivalent function
    
    ndarray.flat : a flat iterator on the array.

【19】

# Okay, just tell me everything there is to know about numpy.ndarray
# (Click the "output" button to see the novel-length output)
help(rolls)

当然,你可能更喜欢查看在线文档

Operator overloading

下面表达式的值是多少?

【20】

[3, 4, 1, 2, 2, 1] + 10
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-20-1b8555d93650> in <module>()
----> 1 [3, 4, 1, 2, 2, 1] + 10

TypeError: can only concatenate list (not "int") to list

多么愚蠢的问题,当然会报错。

但是。。。

【21】

rolls + 10

array([12, 11, 15, 12, 14, 15, 14, 15, 15, 15])

我们可能会认为Python严格控制其核心语法的行为,例如+,<,in,==或方括号用于索引和切片。 但事实上,它需要一种非常不干涉的方法。 定义新类型时,可以选择添加的工作方式,或者该类型的对象与其他对象的含义相同。
列表的设计者决定不允许将它们添加到数字中。 numpy数组的设计者采用了不同的方式(将数字添加到数组的每个元素)。
下面是一些numpy数组如何与Python运算符交互(或者至少与列表不同)的示例。

【22】

# At which indices are the dice less than or equal to 3?
rolls <= 3
array([ True,  True, False,  True, False, False, False, False, False,
       False])

【23】

xlist = [[1,2,3],[2,4,6],]
# Create a 2-dimensional array
x = numpy.asarray(xlist)
print("xlist = {}\nx =\n{}".format(xlist, x))

xlist = [[1, 2, 3], [2, 4, 6]]
x =
[[1 2 3]
 [2 4 6]]

【24】

# Get the last element of the second row of our numpy array
x[1,-1]

6

【25】

# Get the last element of the second sublist of our nested list?
xlist[1,-1]

---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-25-f74d164f666a> in <module>()
      1 # Get the last element of the second sublist of our nested list?
----> 2 xlist[1,-1]

TypeError: list indices must be integers or slices, not tuple

numpy的ndarray类型专门用于处理多维数据,因此它定义了自己的索引逻辑,允许我们通过元组来指定每个维度的索引。

1 + 1何时不等于2?

事情可能比这更奇怪。 您可能听说过(甚至使用过)tensorflow,这是一种广泛用于深度学习的Python库。 它广泛使用了运算符重载。

【26】

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
<tf.Tensor 'add:0' shape=() dtype=int32>

a + b不是2,它是(引用tensorflow的文档)......
     操作的一个输出的符号句柄。 它不保存该操作的输出值,而是提供在TensorFlow tf.Session中计算这些值的方法。
重要的是要意识到这样的事情是可能的,并且库通常会以非显而易见或神奇的方式使用运算符重载。

理解Python的运算符在应用于整数,字符串和列表时的工作原理并不能保证您在应用与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(),并想知道所有带有双下划线的名字到底是什么?

【27】

print(dir(list))

['__add__', '__class__', '__contains__', '__delattr__', '__delitem__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '__iadd__', '__imul__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__reversed__', '__rmul__', '__setattr__', '__setitem__', '__sizeof__', '__str__', '__subclasshook__', 'append', 'clear', 'copy', 'count', 'extend', 'index', 'insert', 'pop', 'remove', 'reverse', 'sort']

结果与运算符重载直接相关。
当Python程序员想要定义操作符在其类型上的行为方式时,他们通过实现具有特殊名称的方法来实现,这些特征名称以2个下划线开头和结尾,例如__lt __,__ setattr __或__contains__。 通常,遵循此双下划线格式的名称对Python具有特殊含义。
因此,例如,[1,2,3]中的表达式x实际上是在幕后调用list方法__contains__。 它相当于(更加丑陋)[1,2,3] .__包含__(x)。
如果您想了解更多信息,可以查看Python的官方文档,其中描述了许多这些特殊的“下划线”方法。
我们不会在这些课程中定义我们自己的类型,但我希望你能够体验到后来定义你自己的奇妙,怪异类型的乐趣。

Your turn!

转到最后一个练习笔记本,再进行一轮涉及导入库,处理不熟悉的对象,当然还有更多的赌博的编程问题。

猜你喜欢

转载自blog.csdn.net/cg129054036/article/details/82216124