Py学习  »  Python

比默认的 Python shell 好太多,IPython 实用小技巧合集

技术最前线 • 1 年前 • 208 次点击  

【导语】:IPython 是一个 Python的交互式shell,比默认的 Python shell好用得多。它有许多好用的特性,本文将对一些特性进行介绍。

介绍

IPython 是一个Python的交互式shell,比默认的 Python shell好用得多,支持变量自动补全,自动缩进,内置了许多很有用的功能和函数。在之前的项目中,我使用 IPython 较多,每当在开发中遇到问题时,我会打印每一步的输出,查找问题。完成项目后,我对之前使用 IPython 的一些经验作了总结。在本文,我将对之前的总结作以简单介绍。

此外,文中的部分命令配有 gif 图演示,但由于比较大,没法上传到微信公众号。如果有需要 gif 演示动图的小伙伴,请看文末获取方式。

1. 显示文档

In [1]: import re

In [2]: re.findall?
Signature: re.findall(pattern, string, flags=0)
Docstring:
Return a list of all non-overlapping matches in the string.

If one or more capturing groups are present in the pattern, return
a list of groups; this will be a list of tuples if the pattern
has more than one group.

Empty matches are included in the result.
File:      ~/.pyenv/versions/3.9.0/lib/python3.9/re.py
Type:      function

这是我最喜欢的功能之一。我们可以通过在任何函数、模块、变量的开头或结尾添加“?”来显示其文档。它被称为“动态对象检查”, 通过使用该特性,我们就可以在不退出终端的情况下来获取文档。在标准的Python交互式解释器中REPL中,使用help()函数也能获取文档信息, 但"?"输出的文档可读性更强。它能将函数签名对象signature、文档字符串等重要信息高亮显示。(由于我使用的语法高亮库不支持Ipython,因此在这里无法显示)

2. 显示源代码

In [1]: import pandas

In [2]: pandas.DataFrame??

Init signature:
pandas.DataFrame(
    data=None,
    index: Optional[Collection] = None,
    columns: Optional[Collection] = None,
    dtype: Union[ForwardRef('ExtensionDtype'), str, numpy.dtype, Type[Union[str, float, int, complex, bool]], NoneType] = None,
    copy: bool = False,
)
Source:
class DataFrame(NDFrame):
    """
    Two-dimensional, size-mutable, potentially heterogeneous tabular data.

    Data structure also contains labeled axes (rows and columns).
    Arithmetic operations align on both row and column labels. Can be
    thought of as a dict-like container for Series objects. The primary
    pandas data structure.

    Parameters
    ----------

假如我们想查看一个函数(或者类/模块)的源代码,可以使用两个问号,例如:function_name????function_name

3. %edit 函数

3_edit.gif

如果我们想在 IPython 中写一个功能较复杂的函数,可以使用 %edit 命令打开自己喜欢的编辑器(也可以用 $editor环境变量设置编辑器),在里面编写代码。当我们保存并关闭这个文件时,IPython 就会自动执行该文件。

我通常将它与vim编辑器结合使用,这样就不需要切换到代码编辑器了,当编写稍微长一点的函数时,效果很好。但是,如果函数体的代码过多,那么直接在 IPython中编写就比较麻烦了。

4. 使用%edit -p打开之前的文件

4_edit_p.gif

假如我们想修改上次编写的代码,可以使用%edit -p重新打开文件。

5. 通配符搜索

In [1]: import os

In [2]: os.*dir*?
os.__dir__
os.chdir
os.curdir
os.fchdir
os.listdir
os.makedirs
os.mkdir
os.pardir
os.removedirs
os.rmdir
os.scandir
os.supports_dir_fd

In [3]: os.chdir("/some/other/dir")

如果我们忘记了某个函数的名称,可以使用动态对象检查(“?”)和通配符(“*”)进行搜索。例如,我们知道os模块有一个可以改变当前目录的方法,但是忘记了该方法的具体名称。可以列出os模块中的所有函数,由于关于文件操作的函数名称中包含“dir”。因此,可以搜索os模块中名称包含“dir”的所有函数。

6. %debug

假如程序出现了异常,我们可以使用%debug命令开启调试。无需打断点,或者使用特殊的参数运行 IPython。

In [1]: from solver import solve

In [2]: solve()
IndexError: list index out of range

In [3]: %debug
> /Users/switowski/workspace/iac/solver.py(11)count_trees()
      9         x = (x + dx) % mod
     10         y += dy
---> 11         if values[y][x] == "#":
     12             count += 1
     13     return count

ipdb>

7. 自动开启调试

如果我们希望异常出现时,调试器自动启动,可以使用%pdb命令,再次使用该命令会关闭该功能。

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

In [2]: from solver import solve

In [3]: solve()
IndexError: list index out of range

> /Users/switowski/workspace/iac/solver.py(11)count_trees()
      9         x = (x + dx) % mod
     10         y += dy
---> 11         if values[y][x] == "#":
     12             count += 1
     13     return count

ipdb> y
1
ipdb> x
3
ipdb>

8. shell命令

如果想在 IPython 中使用shell命令,可以在命令前加上感叹号。像ls, pwd, cd这种常用shell命令,也可以不加感叹号。我主要使用shell命令来移动文件,或者在文件夹间移动。我们还可以在Ipython中为另一种编程语言开启REPL。

In [1]: !pwd
/Users/switowski/workspace/iac

In [2]: ls -al
total 8
drwxr-xr-x   5 switowski  staff   480 Dec 21 17:26 ./
drwxr-xr-x  55 switowski  staff  1760 Dec 22 14:47 ../
drwxr-xr-x   9 switowski  staff   384 Dec 21 17:27 .git/
drwxr-xr-x   4 switowski  staff   160 Jan 25 11:39 __pycache__/
-rw-r--r--   1 switowski  staff   344 Dec 21 17:26 solver.py

# Node REPL inside IPython? Sure!
In [3]: !node
Welcome to Node.js v12.8.0.
Type ".help" for more information.
> var x = "Hello world"
undefined
> x
'Hello world'
>

9. 使用%cd命令在文件系统间移动

我们可以使用%cd命令在文件系统中移动(按Tab键可以自动补全文件夹路径)。此外,还可以收藏一个文件夹或将一些文件夹移回历史记录(运行%cd?查看该命令的具体信息)。

In [1]: !pwd
/Users/switowski/workspace/iac/input_files/wrong/folder

In [2]: %cd ../..
/Users/switowski/workspace/iac/input_files

In [3]: %cd right_folder/
/Users/switowski/workspace/iac/input_files/right_folder

10. %autoreload

使用%autoreload命令可以在运行导入的函数前,重载该函数。当我们在 Python 中导入一个函数时,Python 会将其源代码保存在内存中(实际上,真实的情况比较复杂)。当我们更改了函数,Python 不会重新加载函数,而是继续使用之前的代码。

如果我们想创建一个函数或模块,并且希望在不重启 IPython 的情况下测试代码(可以使用importlib.reload()),就可以使用%autoreload。通过使用该命令,就可以实现重载函数的效果。如果你想了解更多信息,可以查看这篇文章[1]

10_autoreload.gif

11. 用%xmode展示详细的异常信息

默认情况下,IPython 抛出的异常信息量足够查找错误——至少对我来说是这样。但是,如果你想展示详细的异常信息,可以使用%xmode命令。它可以用四种形式展示异常信息,开发者按需选择。

  • Minimal
In [1]: %xmode
Exception reporting mode: Minimal

In [2]: solve()
IndexError: list index out of range 
  • Plain
In [3]: %xmode
Exception reporting mode: Plain

In [4]: solve()
Traceback (most recent call last):
  File "" , line 1, in 
    solve()
  File "/Users/switowski/workspace/iac/solver.py", line 27, in solve
    sol_part1 = part1(vals)
  File "/Users/switowski/workspace/iac/solver.py", line 16, in part1
    return count_trees(vals, 3, 1)
  File "/Users/switowski/workspace/iac/solver.py", line 11, in count_trees
    if vals[y][x] == "#":
IndexError: list index out of range
  • Context(默认设置)
In [5]: %xmode
Exception reporting mode: Context

In [6]: solve()
---------------------------------------------------------------------------
IndexError                                Traceback (most recent call last)
 in 
----> 1 solve()

~/workspace/iac/solver.py in solve()
     25 def solve():
     26     vals = getInput()
---> 27     sol_part1 = part1(vals)
     28     print(f"Part 1: {sol_part1}")
     29     print(f"Part 2: {part2(vals, sol_part1)}")

~/workspace/iac/solver.py in part1(vals)
     14
     15 def part1(vals: list) -> int:
---> 16     return count_trees(vals, 3, 1)
     17
     18 def part2(vals: list, sol_part1: int) -> int:

~/workspace/iac/solver.py in count_trees(vals, dx, dy)
      9         x = (x + dx) % mod
     10         y += dy
---> 11         if vals[y][x] == "#":
     12             cnt += 1
     13     return cnt

IndexError: list index out of range
  • Verbose (与context类似,但其中包含局部变量和全局变量)
In [7]: %xmode
Exception reporting mode: Verbose

In [8]: solve()
---------------------------------------------------------------------------
IndexError                                Traceback (most recent call last)
 in 
----> 1 solve()
        global solve = <function solve at 0x109312b80>

~/workspace/iac/solver.py in solve()
     25 def solve():
     26     values = read_input()
---> 27     part1 = solve1(values)
        part1 = undefined
        global solve1 = <function solve1 at 0x109f363a0>
        values = [['..##.......', ..., '.#..#...#.#']]
     28     print(f"Part 1: {part1}")
     29     print(f"Part 2: {solve2(values, part1)}")

~/workspace/iac/solver.py in solve1(values=[['..##.......', ..., '.#..#...#.#']])
     14
     15 def solve1(values: list) -> int:
---> 16     return count_trees(values, 3, 1)
        global count_trees = <function count_trees at 0x109f364c0>
        values = [['..##.......', ..., '.#..#...#.#']]
     17
     18 def solve2(values: list, sol_part1: int) -> int:

... and so on

IndexError: list index out of range

12.  rerun 命令

可以使用%rerun ~1/重新运行前一个会话中的所有命令。但该命令有一个很大的缺点——如果之前的会话中有任何异常,执行就会停止。因此,必须手动删除有异常的命令行。如果你正在使用Jupyter notebook,可以将出现异常的地方标记为“raising an exception”。如果使用rerun 命令,IPython 将忽略此异常。这不是一个完美的解决方案,假如可以在%rerun命令设置,效果会更好。

In [1]: a = 10

In [2]: b = a + 20

In [3]: b
Out[3]: 30

# Restart IPython

In [1]: %rerun ~1/
=== Executing: ===
a = 10
b = a + 20
b
=== Output: ===
Out[1]: 30

In [2]: b
Out[2]: 30

13. 在启动 IPython 时运行某些代码

如果你想在每次启动 IPython 时执行一些代码,只需在“startup”文件夹(~/.Ipython /profile_default/startup/)中创建一个新文件,并在其中书写代码。IPython将自动执行该文件。你也可以在该文件中导入一些模块,但如果文件中的代码过多,将会影响IPython的启动速度。

13_startup.gif

14. 使用不同的配置文件

如果我们想导入一些模块,并配置某些东西。例如在调试/分析时,我们想让输出的异常信息为verbose模式,并导入一些分析库。此时不能将其放入默认配置文件中,因为我们无需对每个文件都进行调试或分析。我们可以创建一个新的配置文件。配置文件就像IPython的不同用户帐户——每个帐户都有自己的配置文件和启动文件夹。

14_profile.gif

15.  保存表达式结果

如果你忘记了将表达式赋值给变量,可以使用var = _. _存储最后一个命令的输出(这在Python REPL中也适用)。这样前面所有命令的结果都存储在了:变量_1(第一个命令的输出),_2(第二个命令的输出),依次类推。

In [1]: sum(range(1000000))
Out[1]: 499999500000

In [2]: the_sum = _

In [3]: the_sum
Out[3]: 499999500000

In [4]: _1
Out[4]: 499999500000

16. 编辑任何函数或模块

你可以使用%edit编辑任何Python函数,包括:自定义的函数,用pip安装的第三方库中的函数,甚至是内置的函数。甚至不需要知道函数位于哪个文件中, 只需指定函数名称(必须先导入它),IPython 就会自动找到它。在下面的例子中,我修改了内置的randint()函数,让它始终返回42。

16_edit.gif

17. 分享代码

如果你想把代码分享给其他人,可以使用%pastebin命令,并指定共享的代码。IPython 将创建一个pastebin(类似于GitHub 的 gist),粘贴选定的行,并返回一个链接,你可以把它发送给别人。务必记住,这个链接会在7天后过期。

In [1]: welcome = "Welcome to my gist"

In [2]: welcome
Out[2]: 'Welcome to my gist'

In [3]: a = 42

In [4]: b = 41

In [5]: a - b
Out[5]: 1

In [6]: %pastebin 1-5
Out[6]: 'http://dpaste.com/8QA86F776'

18. 将 IPython 作为debugger

我们还可以把 IPython 当作debugger使用。IPython自带“ipdb”——它类似于内置的Python调试器“pdb”,但其中有一些IPython的特有特性(语法高亮显示、自动补全等)。通过设置PYTHONBREAKPOINT环境变量,调用breakpoint(),就可以在断点语句中使用ipdb了。但需要在 Python 3.7或更高版本中使用(Python3.7之后才引入了breakpoint()语句)。

18_debugger.gif

19. 执行用其他语言编写的代码

假设我们想要在不退出 IPython 的情况下,执行用另一种语言编写的一些代码。比如输入%%ruby,再编写一些Ruby代码,然后按两次Enter键,IPython就会运行这段代码。IPython 支持Ruby、Bash或JavaScript等语言。它也适用于Python2 (%%python2)。

In [1]: %%ruby
   ...: 1.upto 16 do |i|
   ...:   out = ""
   ...:   out += "Fizz" if i % 3 == 0
   ...:   out += "Buzz" if i % 5 == 0
   ...:   puts out.empty? ? i : out
   ...: end
   ...:
   ...:
1
2
Fizz
4
Buzz
Fizz
7
8
Fizz
Buzz
11
Fizz
13
14
FizzBuzz
16

20. 在会话中存储变量

IPython使用SQLite来存储之前的会话。我们也可以用它来存储自定义的数据。例如,使用%store命令,就可以在IPython的数据库中保存变量,使用estore -r可以另一个会话中恢复它们。您还可以在配置文件中设置c.StoreMagics.autorestore = True,以便在启动IPython时自动从数据库恢复所有变量。

In [1]: a = 100

In [2]: %store a
Stored 'a' (int)

# Restart IPython
In [1]: %store -r a

In [2]: a
Out[2]: 100

21. 将会话保存进一个文件

我们可以使用%save命令将IPython会话保存到一个文件中,然后对代码进行修改,无需手动复制粘贴到代码编辑器中。

In [1]: a = 100

In [2]: b = 200

In [3]: c = a + b

In [4]: c
Out[4]: 300

In [5]: %save filename.py 1-4
The following commands were written to file `filename.py`:
a = 100
b = 200
c = a + b
c

22. 清理>标记或者不正确的缩进

如果我们想清除不正确的缩进或“>”符号(例如,从git diff、文档字符串或电子邮件中复制的代码,都含有冗余信息),无需手动操作,可以复制代码并运行%paste。IPython将从剪贴板粘贴代码,修复缩进,并删除“>”符号(尽管它有时不能正常工作)。




    
# Clipboard content:
# >def greet(name):
# >    print(f"Hello {name}")

# Just pasting the code won't work
In [1]: >def greet(name):
   ...: >    print(f"Hello {name}")
  File "", line 1
    >def greet(name):
    ^
SyntaxError: invalid syntax


# But using %paste works
In [2]: %paste
>def greet(name):
>    print(f"Hello {name}")

## -- End pasted text --

In [3]: greet("Sebastian")
Hello Sebastian

23. 列出所有变量

使用%whos命令可以显示出当前会话的所有变量,包括变量类型和存储的数据。

In [1]: a = 100

In [2]: name = "Sebastian"

In [3]: squares = [x*x for x in range(100)]

In [4]: squares_sum = sum(squares)

In [5]: def say_hello():
   ...:     print("Hello!")
   ...:

In [6]: %whos
Variable      Type        Data/Info
-----------------------------------
a             int         100
name          str         Sebastian
say_hello     function    <function say_hello at 0x111b60a60>
squares       list        n=100
squares_sum   int         328350

24. 使用异步函数

可以使用异步函数提高代码的运行速度。要使用异步,必须先启动一个事件循环来调用它们。方便的是,IPython自带事件循环!这样,无需额外操作就能使用异步函数了。

In [1]: import asyncio

In [2]: async def worker():
   ...:     print("Hi")
   ...:     await asyncio.sleep(2)
   ...:     print("Bye")
   ...:

# The following code would fail in the standard Python REPL
# because we can't call await outside of an async function
In [3]: await asyncio.gather(worker(), worker(), worker())
Hi
Hi
Hi
Bye
Bye
Bye

25. IPython 脚本

我们可以在命令前加上!或者&,执行包含 IPython 特定语法的文件,这种文件的扩展名为“ipy”。

$ ls
file1.py    file2.py    file3.py    file4.py    wishes.ipy

$ cat wishes.ipy
files = !ls
# suffix 运行所有后缀为.py的文件
for file in files:
    if file.endswith(".py"):
        %run $file

$ ipython wishes.ipy
Have a
Very Merry
Christmas!
🎄🎄🎄🎄🎄🎄

结论

IPython 是我最喜欢的 Python 工具之一。它有许多好用的特性,本文分享了一些,如果你知道其他特性,可以留言分享!


参考资料
[1]

文章: https://switowski.com/blog/ipython-autoreload

[2]

参考原文: https://switowski.com/blog/25-ipython-tips-for-your-next-advent-of-code

- EOF -

推荐阅读  点击标题可跳转

1、一个 Python Bug 干倒了估值 1.6 亿美元的公司

2、使用 LSTM 对销售额预测(Python代码)

3、一位国外老程序员的反思:C、Python、Java 不可兼得,专心学好一门编程语言就行!


↓推荐关注↓

「Python开发者」日常分享 Python 相关的技术文章、实用案例、工具资源、精选课程、热点资讯等


点赞和在看就是最大的支持❤️

Python社区是高质量的Python/Django开发社区
本文地址:http://www.python88.com/topic/147198
 
208 次点击