首先启动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 "", 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]:
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: }
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)
in
----> 1 func2(1)
in func2(x)
5 a = x
6 b = x - 1
----> 7 return func1(a, b)
8
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 "", line 1, in
func2(1)
File "", line 7, in func2
return func1(a, b)
File "", line 2, in func1
return a / b
ZeroDivisionError: division by zero
接下来显示Verbose
In [7]: func2(1)
---------------------------------------------------------------------------
ZeroDivisionError Traceback (most recent call last)
in
----> 1 func2(1)
global func2 =
in func2(x=1)
5 a = x
6 b = x - 1
----> 7 return func1(a, b)
global func1 =
a = 1
b = 0
8
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
> (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
> (2)func1()
1 def func1(a, b):
----> 2 return a / b
3
4 def func2(x):
5 a = x
ipdb> up
> (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
> (1)()
----> 1 func2(1)
ipdb> down
> (7)func2()
4 def func2(x):
5 a = x
6 b = x - 1
----> 7 return func1(a, b)
8
ipdb> down
> (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 中的提示框后面的(2)func1()
这种, 表示运行的栈的区域,上面的就是表示运行在func1区域。
如果你希望在任何异常时都自动启动调试器,可以用%pdb魔法函数来启动这个自启动过程
In [15]: %pdb on
Automatic pdb calling has been turned ON
In [16]: func2(1)
ZeroDivisionError: division by zero
> (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) 跳到程序的下一步
重复前一个命令
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 :4()
5 0.041 0.008 0.041 0.008 {built-in method builtins.sum}
1 0.028 0.028 0.521 0.521 :1(sum_of_lists)
1 0.009 0.009 0.530 0.530 :1()
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:
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