《Python数据科学手册》抄书笔记,第一章: Ipython 超越Python

首先启动IPython shell 和Jupyter Notebook

在命令行输入ipython启动IPython

mac启动Jupyter使用Python3 -m IPython notebook

 

在IPython中,符号?用于浏览文档,??用于浏览源代码,而Tab用于自动补全。

 

每一个Python对象独有一个字符串的引用,该字符串即为docstring。大多数情况下,该字符串包含对象的简要介绍和使用方法。

内置的help()函数可以获取这些信息。

 

?可以查看对象的相关信息

In [5]: len?                                                                    
Signature: len(obj, /)
Docstring: Return the number of items in a container.
Type:      builtin_function_or_method

In [6]: l = []                                                                  

In [7]: l?                                                                      
Type:        list
String form: []
Length:      0
Docstring:  
Built-in mutable sequence.

If no argument is given, the constructor creates a new empty list.
The argument must be an iterable if specified.

In [8]: l.index?                                                                
Signature: l.index(value, start=0, stop=9223372036854775807, /)
Docstring:
Return first index of value.

Raises ValueError if the value is not present.
Type:      builtin_function_or_method

In [9]:     

 无论是函数,方法还是具体实例对象。

在自己编写函数的时候,写好相关docstring

 

1.2.2通过符号??获取源代码。

 

1.2.3用Tab补全的方式探索模块

1.对象内容的Tab自动补全(相对简单)

2.导入时的Tab自动补全

3.超越Tab自动补全:通配符适配

当想适配中间或者末尾的几个字符时,可以通过*符号来实现

列举除名字空间中以Warning结尾的所有对象。

In [11]: *Warning?                                                              
BytesWarning
DeprecationWarning
FutureWarning
ImportWarning
PendingDeprecationWarning
ResourceWarning
RuntimeWarning
SyntaxWarning
UnicodeWarning
UserWarning
Warning

 不要忘记了后面有一个?

寻找一个字符串方法,它的名字中有find

In [12]: str.*find*?                                                            
str.find
str.rfind

In [13]:     

 

1.3IPython shell中的快捷键

书中分为:导航快捷键、文本输入快捷键、命令历史快捷键和其他快捷键

 

1.3.1 导航快捷键

Crrl+a 光标移到本行开始处

Ctrl+e 光标移动本行结尾处

Ctrl+b(或左箭头键) 光标向后移动一个

Ctrl+f(或右箭头键) 光标向前移动一个

 

1.3.2 文本输入快捷键

Backspace键   删除前一个字符

Ctrl + d        删除后一个字符

Ctrl + k     从光标开始剪切至行的末尾

Ctrl + u     从行的开头剪切至光标

Ctrl + y     yank(即粘贴)之前剪切的文本

Ctrl + t     transpose(即交换)前两个字符

 

1.3.3 命令历史快捷键

Ctrl + p  (向上箭头)  获取前一个历史命令

Ctrl + n  (向下箭头)  获取下一个历史命令

Ctrl + r        对历史命令的反向搜索

 

这是一个非常好用的功能,对话框中输入关键字会自动匹配相关信息。

使用Ctrl + P与Ctrl + N 可以上下查看相关搜索记录。

使用上下箭头可以查看匹配到的相关信息。

 

1.3.4 其他快捷键

Ctrl + l 清楚终端屏幕的内容

Ctrl + c 中断当前的Python命令

Ctrl + d  退出IPython会话

 

1.4 IPython魔法命令

 

1.4.1粘贴代码块: %paste和%cpaste

当复制,粘贴的时候,出现了多于的符号,解释器报错

可以用%paste与%cpaste来进行复制,粘贴

 

1.4.2 执行外部代码: %run

执行外部的模块文件

In [2]: %run myscript.py                                                        
1 squared is 1
2 squared is 4
3 squared is 9

In [3]: square(55)                                                              
Out[3]: 3025

In [4]:      

 square函数定义在myscript.py模块中,这感觉也是一种比较好的加载模块内对象的方式。

myscript.py文件内容:

def square(x):
    """square a number"""
    return x ** 2

for N in range(1, 4):
    print(N, "squared is", square(N))

 

1.4.3 计算代码运行时间: %timeit

In [4]: %timeit L = [n ** 2 for n in range(1000)]                               
241 µs ± 4.27 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

In [5]:  

 这个命令会多次执行简短命令,以获取更加稳定的结果。

 

多行的输入测试,可以使用两个%%来实现.

%%timeit
   ...: L = []
   ...: for n in range(1000):
   ...:     L.append(n ** 2)

## -- End pasted text --

278 µs ± 2.05 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

 

1.4.4 魔法函数的帮助:?、%magic、 %lsmagic

 

获取文档注释

%timeit?

为了获得可用魔法函数的通用描述以及一些示例,可以输入一下命令

%magic

为了快速而简单地获取所有可用魔法函数的列表,可以输入以下命令:

%lsmagic

 

1.5 输入和输出历史

前面已经讲过可以使用Ctrl+p或者Ctrl+n来翻看历史

 

1.5.1IPython的输入和输出对象

In [1]: import math                                                             

In [2]: math.sin(2)                                                             
Out[2]: 0.9092974268256817

In [3]: math.cos(2)                                                             
Out[3]: -0.4161468365471424

In [4]: print(in)                                                               
  File "<ipython-input-4-7f51abe8dfc1>", line 1
    print(in)
           ^
SyntaxError: invalid syntax


In [5]: print(In)                                                               
['', 'import math', 'math.sin(2)', 'math.cos(2)', 'print(in)', 'print(In)']

In [6]: print(Out)                                                              
{2: 0.9092974268256817, 3: -0.4161468365471424}

In [7]:       

 Ipython里面有一对In与Out的输入与输出。,在Ipython中会自动添加In和Out的Python变量,其中In为list实例,Out为Dict实例

任何返回值是None的命令都不会加到Out变量中(所以print输出不会添加到Out中)。

In [7]: print()                                                                 


In [8]: print                                                                   
Out[8]: <function print>

In [9]: print(In)                                                               
['', 'import math', 'math.sin(2)', 'math.cos(2)', 'print(in)', 'print(In)', 'print(Out)', 'print()', 'print', 'print(In)']

In [10]: print(Out)                                                             
{2: 0.9092974268256817, 3: -0.4161468365471424, 8: <built-in function print>}

In [11]: 

 这样,如果利用之前的结果可以方便的拿出来使用。

 

1.5.2 下划线快捷和以前的输出

如果想获取上一次的输出可以用_来表示,倒数第二个用__,以此类推

通过_x,其中的x为Out输出中的key,开获取第几次的值

In [27]: Out[2]                                                                 
Out[27]: 0.9092974268256817

In [28]: _2                                                                     
Out[28]: 0.9092974268256817

 

1.5.3 禁止输出

就是在输入命令行中,在末尾添加;分号

In [32]: math.sin(2) + math.cos(2);                                             

In [33]: 32 in Out                                                              
Out[33]: False

In [34]:   

 在Out的输出字典中,根本没有key32的存在,说明没有输出。

 

1.5.4 相关的魔法命令

%history 来获取所有的历史命令输入。

通过-n可以显示命令行的输入索引编号,通过n-m开业获取n到m号的输入命令情况。

In [35]: %history 1-4                                                           
import math
math.sin(2)
math.cos(2)
print(in)

In [36]: %history -n 1-4                                                        
   1: import math
   2: math.sin(2)
   3: math.cos(2)
   4: print(in)

In [37]:        

 

1.6 IPython和shell命令

 

1.6.2 IPython中的shell命令

只需要在命令之前+!感叹号

In [45]: !pwd                                                                   
/Users/shijianzhong

In [46]: !echo "printing from the shell"                                        
printing from the shell

In [47]:  

 

1.6.3 在shell中传入或传出值

通过变量接收shell命令的输出值

 contents = !ls                                                         

In [48]: print(contents)                                                        
['Applications', 'Compute-Question', 'Desktop', 'Documents', 'Downloads', 'Java_Home', 'Library', 'Movies', 'Music', 'Pictures', 'Postman', 'Public', 'PycharmProjects', 'Untitled Folder', '__pycache__', 'books', 'c_study', 'codeintel', 'django_venv', 'dump.rdb', 'eclipse-workspace', 'exec_new3.sh', 'flasky', 'group_git_tips', 'image', 'java_error_in_pycharm.hprof', 'java_error_in_pycharm_958.log', 'learn_crystal', 'learn_django', 'learn_git', 'learn_ruby', 'learn_shell', 'learngit', 'mbox', 'mprun_demo.py', 'my_home', 'my_software', 'mycentos.tar', 'myscript.py', 'nohup.out', 'pyalgo_two', 'pyalgotrade', 'ruby_study', 'scrapy_venv', 'show_p_path.py', 'ssh_test', 'study', 'tmp', 'work_space']

In [49]: contents                                                               
Out[49]: 
['Applications',
 'Compute-Question',
 'Desktop',
 'Documents',
 'Downloads',
 'Java_Home',
 'Library',
 'Movies',
 'Music',
 'Pictures',
 'Postman',
 'Public',
 'PycharmProjects',
 'Untitled Folder',
 '__pycache__',
 'books',
 'c_study',
 'codeintel',
 'django_venv',
 'dump.rdb',
 'eclipse-workspace',
 'exec_new3.sh',
 'flasky',
 'group_git_tips',
 'image',
 'java_error_in_pycharm.hprof',
 'java_error_in_pycharm_958.log',
 'learn_crystal',
 'learn_django',
 'learn_git',
 'learn_ruby',
 'learn_shell',
 'learngit',
 'mbox',
 'mprun_demo.py',
 'my_home',
 'my_software',
 'mycentos.tar',
 'myscript.py',
 'nohup.out',
 'pyalgo_two',
 'pyalgotrade',
 'ruby_study',
 'scrapy_venv',
 'show_p_path.py',
 'ssh_test',
 'study',
 'tmp',
 'work_space']

In [50]: type(contents)                                                         
Out[50]: IPython.utils.text.SList

In [51]: message = "hello from the Python"                                      

In [52]: !echo {message}                                                        
hello from the Python

 变量接收的shell命令输出的对象是一个类似与列表的对象,Python中的变量名,也可以通过{}的形式传递给shell

 

1.7与shell相关的魔法方法

Notebook中的shell命令是在一个临时的分支shell中执行的。如果你希望以一种更持久的方式运行,可以使用%魔法命令

通过%后面执行的命令,不能与Python进行交互

一般的shell命令,会通过%automagic模范函数进行翻转。当automagic被打开,一些常用的命令直接省略%就可以跟shell中使用一样

cd cat cp ls man mkdir more mv pwd rm rmdir

In [76]: mkdir tmpe                                                             

In [77]: cp myscript.py tmp                                                     

In [78]: cp myscript.py tmpe/                                                   

In [79]: ls tmpe                                                                
myscript.py

In [80]:                                                                        

In [80]: rm -r tmpe                                                             

In [81]: 

 

1.8错误和调试

 

1.8.1控制异常:%xmode

当一个Python脚本未执行通过时,会抛出一个异常。当解释器捕获到这些异常中的一个时,可以在轨迹追溯(traceback)中找到引起这个错误的原因。

利用%xmode魔法函数,IPython允许你在异常发生时控制打印信息的数量。

In [1]: def func1(a, b): 
   ...:     return a / b 
   ...:  
   ...: def func2(x): 
   ...:     a = x 
   ...:     b = x - 1 
   ...:     return func1(a, b) 
   ...:                                                                         

In [2]: func2(1)                                                                
---------------------------------------------------------------------------
ZeroDivisionError                         Traceback (most recent call last)
<ipython-input-2-7cb498ea7ed1> in <module>
----> 1 func2(1)

<ipython-input-1-84841e495819> in func2(x)
      5     a = x
      6     b = x - 1
----> 7     return func1(a, b)
      8 

<ipython-input-1-84841e495819> in func1(a, b)
      1 def func1(a, b):
----> 2     return a / b
      3 
      4 def func2(x):
      5     a = x

ZeroDivisionError: division by zero

 

In [3]: %xmode?                                                                 
Docstring:
Switch modes for the exception handlers.

Valid modes: Plain, Context, Verbose, and Minimal.

If called without arguments, acts as a toggle.
File:      /Library/Python/3.7/site-packages/IPython/core/magics/basic.py

 xmode有4中选项,默认就时Context

接下来显示Plain

In [4]: %xmode Plain                                                            
Exception reporting mode: Plain

In [5]: func2(1)                                                                
Traceback (most recent call last):
  File "<ipython-input-5-7cb498ea7ed1>", line 1, in <module>
    func2(1)
  File "<ipython-input-1-84841e495819>", line 7, in func2
    return func1(a, b)
  File "<ipython-input-1-84841e495819>", line 2, in func1
    return a / b
ZeroDivisionError: division by zero

 接下来显示Verbose

In [7]: func2(1)                                                                
---------------------------------------------------------------------------
ZeroDivisionError                         Traceback (most recent call last)
<ipython-input-7-7cb498ea7ed1> in <module>
----> 1 func2(1)
        global func2 = <function func2 at 0x101fb9e18>

<ipython-input-1-84841e495819> in func2(x=1)
      5     a = x
      6     b = x - 1
----> 7     return func1(a, b)
        global func1 = <function func1 at 0x101fb9c80>
        a = 1
        b = 0
      8 

<ipython-input-1-84841e495819> in func1(a=1, b=0)
      1 def func1(a, b):
----> 2     return a / b
        a = 1
        b = 0
      3 
      4 def func2(x):
      5     a = x

ZeroDivisionError: division by zero

In [8]:                                                                         

 最后时Minimal模式

In [8]: %xmode Minimal                                                          
Exception reporting mode: Minimal

In [9]: func2(1)                                                                
ZeroDivisionError: division by zero

 显然Verbose时显示最丰富的。

 

1.8.2 调试:当阅读轨迹追溯不足以解决问题时

IPython的调试器时Ipdb,Python的时pdb,我现在还不是很熟悉pdb。。。

 

IPython中最方便的调试界面可能就时%debug魔法命令了。如果你在捕获异常后调用该调试器,它会在异常点自动打开一个交互式调试提示符。

ipdb提示符让你可以探索栈空间当前状态,探索可用变量,设置运行Python命令

In [10]: %debug                                                                 
> <ipython-input-1-84841e495819>(2)func1()
      1 def func1(a, b):
----> 2     return a / b
      3 
      4 def func2(x):
      5     a = x

ipdb> print(a)                                                                  
1
ipdb> print(b)                                                                  
0
ipdb>        

 这个交互式调试器的功能不止如此,我们甚至可以设置单步入栈和出栈来查看各变量的值

In [11]: %debug                                                                 
> <ipython-input-1-84841e495819>(2)func1()
      1 def func1(a, b):
----> 2     return a / b
      3 
      4 def func2(x):
      5     a = x

ipdb> up                                                                        
> <ipython-input-1-84841e495819>(7)func2()
      4 def func2(x):
      5     a = x
      6     b = x - 1
----> 7     return func1(a, b)
      8 

ipdb> print(x)                                                                  
1
ipdb> up                                                                        
> <ipython-input-9-7cb498ea7ed1>(1)<module>()
----> 1 func2(1)

ipdb> down                                                                      
> <ipython-input-1-84841e495819>(7)func2()
      4 def func2(x):
      5     a = x
      6     b = x - 1
----> 7     return func1(a, b)
      8 

ipdb> down                                                                      
> <ipython-input-1-84841e495819>(2)func1()
      1 def func1(a, b):
----> 2     return a / b
      3 
      4 def func2(x):
      5     a = x

ipdb> print(x)                                                                  
*** NameError: name 'x' is not defined
ipdb> quit       

 ipdb 中的提示框后面的<ipython-input-1-84841e495819>(2)func1()

这种, 表示运行的栈的区域,上面的就是表示运行在func1区域。

如果你希望在任何异常时都自动启动调试器,可以用%pdb魔法函数来启动这个自启动过程

In [15]: %pdb on                                                                
Automatic pdb calling has been turned ON

In [16]: func2(1)                                                               
ZeroDivisionError: division by zero

> <ipython-input-1-84841e495819>(2)func1()
      1 def func1(a, b):
----> 2     return a / b
      3 
      4 def func2(x):
      5     a = x

ipdb> print(b)                                                                  
0
ipdb> q        

 

部分调试命令

list    显示文件的当前路径

h(elp)   显示命令列表,或查找特定命令的帮助信息

q(uit)   退出调试器和程序

c(ontinue)    退出调试器,继续运行程序

n(ext)     跳到程序的下一步

<enter>  重复前一个命令

p(rint)    打印变量

s(tep)     步进子进程

r(eturn)     从子进程跳出

 

1.9 代码的分析和计时

%time

对单个语句的执行时间进行计时

%timeit

对单个语句的重复执行进行计时,以获取更高的精准度

%prun

利用分析器运行代码

%lprun

利用逐行分析器运行代码

%memit

测量单个语句的内存使用

%mprun

通过逐行的内存分析器运行代码

 

  • %time: Time the execution of a single statement
  • %timeit: Time repeated execution of a single statement for more accuracy
  • %prun: Run code with the profiler
  • %lprun: Run code with the line-by-line profiler
  • %memit: Measure the memory use of a single statement
  • %mprun: Run code with the line-by-line memory profiler

 

1.9.1 代码段计时:%timeit和%time

首先使用%timeit 使用很简单

In [1]: %timeit sum(range(100))                                                 
1.1 µs ± 27.4 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

 因为这个操作很快,所以%timeit自动让代码重复运行了很多次。对于慢的,会自动调整并减少重复执行的次数。

In [2]: %%timeit 
   ...: total = 0 
   ...: for i in range(1000): 
   ...:     for j in range(1000): 
   ...:         total += i * (-1) ** j 
   ...:                                                                         
303 ms ± 2.79 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

 

有时候重复操作并不是一个最佳选择。例如,如果有一个列表需要排序,我们可能会被重复操作误导。对一个预先排好序的列表进行排序,比对一个无序的列表进行排序要快,所以重复运作将使结果出现偏差。

In [16]: import random 
    ...: L = [random.random() for i in range(100000)] 
    ...: %timeit L.sort()                                                       
410 µs ± 4.27 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

 1000次循环,第一次排序了以后,后面的L就使一个有序的列表了。

 

这样的情况下,可以使用%time来尝试

In [16]: import random 
    ...: L = [random.random() for i in range(100000)] 
    ...: %timeit L.sort()                                                       
410 µs ± 4.27 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

In [17]: %time L.sort()                                                         
CPU times: user 896 µs, sys: 24 µs, total: 920 µs
Wall time: 970 µs

In [18]:  

 上面是对已经排序的列表再次进行排序,下面是全新的随机列表进行排序的时间

In [18]: L = [random.random() for i in range(100000)]                           

In [19]: %time L.sort()                                                         
CPU times: user 14.7 ms, sys: 49 µs, total: 14.7 ms
Wall time: 14.8 ms

 

%time就执行一次,由于%timeit会进行一些优化,比如阻止清理未利用的Python对象(即垃圾回收),所有通常情况下%timeit会比%time得到的结果要快

In [20]: %%time 
    ...: total = 0 
    ...: for i in range(1000): 
    ...:     for j in range(1000): 
    ...:         total += i * (-1) ** j 
    ...:                                                                        
CPU times: user 354 ms, sys: 1.57 ms, total: 356 ms
Wall time: 356 ms

 

1.9.2分析整个脚本

一个程序右很多的单个语句组成,有时候对整个脚本计时比单个语句计时更重要。IPython可以通过%prun这个魔法函数实现

In [24]: def sum_of_lists(N): 
    ...:     total = 0 
    ...:     for i in range(5): 
    ...:         L = [j ^ (j >> i) for j in range(N)] 
    ...:         total += sum(L) 
    ...:     return total 
    ...:                                                                        

In [25]: %prun sum_of_lists(1000000)                                            
         14 function calls in 0.530 seconds

   Ordered by: internal time

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
        5    0.452    0.090    0.452    0.090 <ipython-input-24-c8a2c5fe2cd3>:4(<listcomp>)
        5    0.041    0.008    0.041    0.008 {built-in method builtins.sum}
        1    0.028    0.028    0.521    0.521 <ipython-input-24-c8a2c5fe2cd3>:1(sum_of_lists)
        1    0.009    0.009    0.530    0.530 <string>:1(<module>)
        1    0.000    0.000    0.530    0.530 {built-in method builtins.exec}
        1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}

 

1.9.3 用%lprun进行逐行分析

用%prun对代码中的每个函数分析非常有用,但有时候逐行代码分析报告更加方便。该功能需要通过安装第三方的包来实现。首相安装line_profier包

pip install line_profiler

 

接着用IPython导入line_profiler包提供的IPython扩展

本来mac电脑装了老半天一直装不上去这个包,后面查寻后通过pip3 install Cython git+https://github.com/rkern/line_profiler.git --user进行了安装

 

In [3]: %load_ext line_profiler

In [5]: %lprun -f sum_of_lists sum_of_lists(5000)
Timer unit: 1e-06 s

Total time: 0.004989 s
File: <ipython-input-2-c8a2c5fe2cd3>
Function: sum_of_lists at line 1

Line #      Hits         Time  Per Hit   % Time  Line Contents
==============================================================
     1                                           def sum_of_lists(N):
     2         1          3.0      3.0      0.1      total = 0
     3         6          6.0      1.0      0.1      for i in range(5):
     4         5       4820.0    964.0     96.6          L = [j ^ (j >> i) for j in range(N)]
     5         5        159.0     31.8      3.2          total += sum(L)
     6         1          1.0      1.0      0.0      return total

 

1.9.4 用%memit和%mprun进行内存分析

与前面line_profiler差不多,首先进行导包,后面进行魔法函数进行运行

内存分析扩展包有两个有用的魔法函数:%memit魔法函数(它提供的内存消耗计算功能类似于%timeit)和%mprun魔法函数(它提供的内存消耗计算功能类似于%lprun)。

%menit的使用很简单

In [7]: %load_ext memory_profiler

In [8]: def sum_of_lists(N):
   ...:     total = 0
   ...:     for i in range(5):
   ...:         L = [j ^ (j >> i) for j in range(N)]
   ...:         total += sum(L)
   ...:     return total
   ...: 

In [9]: %memit sum_of_lists(1000000)
peak memory: 152.33 MiB, increment: 79.62 MiB

 这个函数消耗了150m内存

 

对于逐行代码内存消耗,可以用%mprun魔法函数。但很可惜,这个魔法函数仅仅对独立模块内部的函数有效,而对于Notebook本身不起作用。

所以后面先生成一个文件

In [14]: %%file mprun_demo.py
    ...: def sum_of_lists(N):
    ...:     total = 0
    ...:     for i in range(5):
    ...:         L = [j ^ (j >> i) for j in range(N)]
    ...:         total += sum(L)
    ...:         del L # remove reference to L
    ...:     return total
    ...: 
Overwriting mprun_demo.py

 

In [15]: from mprun_demo import sum_of_lists

In [17]: %mprun -f sum_of_lists sum_of_lists(10000)
Filename: /Users/shijianzhong/mprun_demo.py

Line #    Mem usage    Increment   Line Contents
================================================
     1     91.4 MiB     91.4 MiB   def sum_of_lists(N):
     2     91.4 MiB      0.0 MiB       total = 0
     3     91.4 MiB      0.0 MiB       for i in range(5):
     4     91.4 MiB      0.0 MiB           L = [j ^ (j >> i) for j in range(N)]
     5     91.4 MiB      0.0 MiB           total += sum(L)
     6     91.4 MiB      0.0 MiB           del L # remove reference to L
     7     91.4 MiB      0.0 MiB       return total

 

posted @ 2020-10-08 20:18  就是想学习  阅读(477)  评论(0编辑  收藏  举报