<jupyter_start><jupyter_text>Python数据科学分享——1.jupyter和python> Python数据科学生态中，Jupyter是一个非营利组织，旨在“为数十种编程语言的交互式计算开发开源软件，开放标准和服务”，目前主要包括三种交互式计算产品：Jupyter Notebook、JupyterHub和JupyterLab(Jupyter Notebook的下一代版本)。- toc: true - badges: true- comments: true- categories: [jupyter,Python,Data Science]   数据科学（统计学+计算机+行业经验）1. 统计学家的能力——建立模型和聚合（数量不断增大的）数据2. 计算机科学家的能力——设计并使用算法对数据进行高效存储、分析和可视化3. 专业领域的能力——在细分领域中经过专业的训练，既可以提出正确的问题，又可以作出专业的解答郁彬2020年2月发表论文《Verdical data science（靠谱的数据科学）》提出的数据科学三原则（PCS）:1. 可预测性(predictability)1. 可计算性（computability）1. 稳定性（stability ）<NAME> and <NAME> (2020) [Verdical data science](https://www.pnas.org/content/117/8/3920) PNAS. 117 (8), 3920-3929. QnAs with Bin Yu. > 郁彬：统计学家，美国艺术与科学学院院士、美国国家科学院院士，加州大学伯克利分校统计系和电子工程与计算机科学系终身教授  [REPL 与魔法函数](https://jupyter.org/) 1. Jupyter是一个非营利组织，旨在“为数十种编程语言的交互式计算开发开源软件，开放标准和服务”。2014年由<NAME>从IPython中衍生出来。jupyter-console==IPython2. Jupyter主要包括三种交互式计算产品：Jupyter Notebook、JupyterHub和JupyterLab(Jupyter Notebook的下一代版本)。3. Jupyter Notebook（前身是IPython Notebook）是一个基于Web的交互式计算环境，用于创建Jupyter Notebook文档(JSON文档)，由一组有序的输入/输出单元格列表构成，包含代码、文本（支持Markdown）、数学公式(Mathjax)、图表和富媒体，通常以“.ipynb”结尾扩展。 $\begin{align*}y = y(x,t) &= A e^{i\theta} \\&= A (\cos \theta + i \sin \theta) \\&= A (\cos(kx - \omega t) + i \sin(kx - \omega t)) \\&= A\cos(kx - \omega t) + i A\sin(kx - \omega t)  \\&= A\cos \Big(\frac{2\pi}{\lambda}x - \frac{2\pi v}{\lambda} t \Big) + i A\sin \Big(\frac{2\pi}{\lambda}x - \frac{2\pi v}{\lambda} t \Big)  \\&= A\cos \frac{2\pi}{\lambda} (x - v t) + i A\sin \frac{2\pi}{\lambda} (x - v t)\end{align*}$其中，$ \theta = kx - \omega t $<jupyter_code>import altair as alt
from vega_datasets import data

source = data.cars()

brush = alt.selection(type="interval")

points = (
    alt.Chart(source)
    .mark_point()
    .encode(
        x="Horsepower:Q",
        y="Miles_per_Gallon:Q",
        color=alt.condition(brush, "Origin:N", alt.value("lightgray")),
    )
    .add_selection(brush)
)

bars = (
    alt.Chart(source)
    .mark_bar()
    .encode(y="Origin:N", color="Origin:N", x="count(Origin):Q")
    .transform_filter(brush)
)

points & bars
from ipywidgets import HBox, VBox, IntSlider, interactive_output
from IPython.display import display


a = IntSlider()
b = IntSlider()
out = interactive_output(lambda a, b: print(f"{a} * {b} = {a*b}"), {"a": a, "b": b})
display(HBox([VBox([a, b]), out]))<jupyter_output><empty_output><jupyter_text>用符号`?`获取帮助文档Python内置的`help()`函数可以获取这些信息，并且能打印输出结果。例如，如果要查看内置的`sorted`函数的文档，可以按照以下步骤操作：<jupyter_code>help(sorted)<jupyter_output>Help on built-in function sorted in module builtins:

sorted(iterable, /, *, key=None, reverse=False)
    Return a new list containing all items from the iterable in ascending order.
    
    A custom key function can be supplied to customize the sort order, and the
    reverse flag can be set to request the result in descending order.<jupyter_text>IPython引入了`?`符号作为获取这个文档和其他相关信息的缩写：<jupyter_code>sorted?

# Signature: sorted(iterable, /, *, key=None, reverse=False)
# Docstring:
# Return a new list containing all items from the iterable in ascending order.

# A custom key function can be supplied to customize the sort order, and the
# reverse flag can be set to request the result in descending order.
# Type:      builtin_function_or_method<jupyter_output><empty_output><jupyter_text>也适用于自定义函数或者其他对象！下面定义一个带有docstring的小函数：<jupyter_code>def square(a):
    """返回a的平方"""
    return a ** 2
square?<jupyter_output><empty_output><jupyter_text>通过符号`??`获取源代码 Jupyter提供了获取源代码的快捷方式（使用两个问号`??`）：<jupyter_code>square??<jupyter_output>Object `square` not found.<jupyter_text>当查询C语言或其他编译扩展语言实现的函数时，`??`后缀=`?`后缀<jupyter_code>sorted??<jupyter_output><empty_output><jupyter_text>用Tab补全的方式探索模块Jupyter支持用Tab键自动补全和探索对象、模块及命名空间的内容（下面用``来表示Tab键）1. 对象内容的Tab自动补全1. 导入时的Tab自动补全1. 通配符\*匹配  对象内容的Tab自动补全每一个Python对象都包含各种属性和方法。Python有一个内置的`dir`函数，可以返回一个属性和方法的列表。Tab自动补全接口更简便：输入这个对象的名称，再加上一个句点（`.`）和Tab键：<jupyter_code>L = [1, 2, 3]
L.append<jupyter_output><empty_output><jupyter_text>为了进一步缩小整个列表，可以输入属性或方法名称的第一个或前几个字符，然后Tab键将会查找匹配的属性或方法：```   L.c      L.clear  L.copy   L.countL.co      L.copy   L.count```如果只有一个选项，按下Tab键将会把名称自动补全。例如，下面示例中的内容将会马上被`L.count`替换：```    L.cou  ```Python一般用前置下划线表示私有属性或方法。可以通过明确地输入一条下划线来把这些私有的属性或方法列出来：```   L._    L.__add__           L.__gt__            L.__reduce__    L.__class__         L.__hash__          L.__reduce_ex__```为了简洁起见，这里只展示了输出的前两行，大部分是Python特殊的双下划线方法（昵称叫作“dunder方法”）。  导入时的Tab自动补全Tab自动补全在从包中导入对象时也非常有用。下面用这种方法来查找`itertools`包中以`co`开头的所有可导入的对象：<jupyter_code>from itertools import co<TAB>
    combinations compress combinations_with_replacement  count
同样，你也可以用Tab自动补全查看系统中所有可导入的包：
    
import <TAB>
    
import h<TAB><jupyter_output><empty_output><jupyter_text>通配符*匹配Jupyter还提供了用`*`符号来实现的依赖中间或者末尾几个字符查询的通配符匹配方法。<jupyter_code>str.*find*?<jupyter_output><empty_output><jupyter_text>假设寻找一个字符串方法，它的名称中包含`find`，则可以这样做：```str.*find*?    str.find    str.rfind```> 这里的`*`符号匹配任意字符串，包括空字符串。**在实际应用过程中，灵活的通配符对于找命令非常有用。**  Jupyter shell中的快捷键 Jupyter通过GNU Readline库实现了四类shell快捷键，在IPython中提高工作效率1. 导航快捷键1. 文本输入快捷键1. 命令历史快捷键1. 其他快捷键  导航快捷键快捷键 | 动作  ---|---  Ctrl + a | 将光标移到本行的开始处Ctrl + e | 将光标移到本行的结尾处 Ctrl + b（或左箭头键）| 将光标回退一个字符  Ctrl + f（或右箭头键）| 将光标前进一个字符     文本输入快捷键快捷键 | 动作  ---|---  Backspace键 | 删除前一个字符  Ctrl + d | 删除下一个字符  Ctrl + k | 从光标开始剪切至行的末尾 Ctrl + u | 从行的开头剪切至光标Ctrl + y | Yank（即粘贴）之前剪切的文本  Ctrl + t | Transpose（即交换）前两个字符     命令历史快捷键Jupyter的命令历史都保存在配置文件路径下的SQLite数据库中，下面的快捷键实现历史搜索：快捷键 | 动作 ---|---  Ctrl + p（或向上箭头）| 获取前一个历史命令 Ctrl + n（或向下箭头）| 获取后一个历史命令  Ctrl + r | 对历史命令的反向搜索   其他快捷键快捷键 | 动作 ---|---  Ctrl + l | 清除终端屏幕的内容  Ctrl + c | 中断当前的Python命令  Ctrl + d | 退出Jupyter会话  Jupyter魔法命令Jupyter在普通Python语法基础之上的增强功能，被称作Jupyter**魔法命令**，都以`%`符号作为前缀。这些魔法命令设计用于解决数据分析中的各种常见问题。魔法命令有两种形式：1. 行魔法（line magic）：以单个`%`字符作为前缀，作用于单行输入；1. 单元魔法（cell magic）：以两个`%%`作为前缀，作用于多行输入。  粘贴代码块：`%paste`和`%cpaste`当你使用Jupyter解释器时，粘贴多行代码块可能会导致错误，尤其是其中包含缩进和解释符号时。Jupyter的`%paste`魔法函数可以解决这个包含符号的多行输入问题：    In [1]: %paste    >>> def donothing(x):    ...     return x         -- End pasted text --`%paste`命令同时输入并执行该代码，所以你可以看到这个函数现在被应用了：        In [2]: donothing(10)    Out[2]: 10另外一个作用类似的命令是`%cpaste`。该命令打开一个交互式多行输入提示，你可以在这个提示下粘贴并执行一个或多个代码块：          In [3]: %cpaste    Pasting code; enter '--' alone on the line to stop or use Ctrl-D.    :>>> def donothing(x):    :...     return x    :--  执行外部代码：`%run`在Jupyter会话中运行代码文件非常方便，不用在另一个新窗口中运行这些程序代码。通过`%run`魔法命令来实现。假设你创建了一个myscript.py文件，该文件包含以下内容：<jupyter_code>%%file myscript.py
def square(x):
    """求平方"""
    return x ** 2

for N in range(1, 4):
    print(N, "squared is", square(N))
ls myscript.py
%run myscript.py<jupyter_output>1 squared is 1
2 squared is 4
3 squared is 9<jupyter_text>当你运行了这段代码之后，该代码中包含的所有函数都可以在Jupyter会话中使用：<jupyter_code>square(5)<jupyter_output><empty_output><jupyter_text>计算代码运行时间：`%timeit``%timeit`会自动计算接下来一行的Python语句的执行时间。<jupyter_code>%timeit L = [n ** 2 for n in range(1000)]<jupyter_output>238 µs ± 24.6 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)<jupyter_text>`%timeit`会自动多次执行命令以获得更稳定的结果。对于多行语句，可以加入第二个`%`符号将其转变成单元魔法，以处理多行输入。例如，下面是`for`循环的同等结构：<jupyter_code>%%timeit
L = []
for n in range(1000):
    L.append(n ** 2)<jupyter_output>321 µs ± 37.6 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)<jupyter_text>从以上结果可以立刻看出，列表解析式比同等的`for`循环结构快约20%。<jupyter_code>%magic<jupyter_output><empty_output><jupyter_text>魔法函数的帮助文档`?`、`%magic`和`%lsmagic`和普通的Python函数一样，Jupyter魔法函数也有文档字符串，输入以下命令即可查询：    In [10]: %timeit?查询魔法函数的描述以及示例，可以输入命令：          In [11]: %magic获得所有可用魔法函数的列表，可以输入命令：            In [12]: %lsmagic也可以自定义魔法函数，详情查看官方文档。  输入和输出历史Jupyter在shell和Notebook中都提供了几种获得历史命令的输出方式：1. Jupyter的输入（In）和输出（Out）对象1. 下划线快捷键和历史输出1. 禁止输出，末尾加分号1. `%history`、`%rerun`和`%save`  Jupyter的输入和输出对象Jupyter的`In[1]:`/`Out[1]:`形式，并不仅仅是好看的装饰形式，还可以获取输入和输出历史：<jupyter_code>import math
math.sin(2)
math.cos(2)<jupyter_output><empty_output><jupyter_text>Jupyter实际上创建了叫作`In`和`Out`的Python变量，这些变量自动更新以反映命令历史：<jupyter_code># In
# Out<jupyter_output><empty_output><jupyter_text>`In`对象是一个列表，按照顺序记录所有的命令（列表中的第一项是一个占位符，以便`In[1]`可以表示第一条命令）：<jupyter_code>print(In[1])<jupyter_output>import math<jupyter_text>`Out`对象不是一个列表，而是一个字典。它将输入数字映射到相应的输出（如果有的话）：<jupyter_code>print(Out[2])<jupyter_output>0.9092974268256817<jupyter_text>> 不是所有操作都有输出，例如`import`语句和`print`语句不会加入Out。因为`print`函数的返回值是`None`。任何返回值是`None`的命令都不会加到`Out`变量中。如果想利用之前的结果，理解以上内容将大有用处。例如，利用之前的计算结果检查`sin(2) ** 2`和`cos(2) ** 2`的和，结果如下：<jupyter_code>Out[2] ** 2 + Out[3] ** 2<jupyter_output><empty_output><jupyter_text>下划线快捷键和历史输出标准的Python shell用变量`_`（单下划线）可以获得前一个命令输出结果，在Jupyter中也适用：<jupyter_code>print(_)<jupyter_output>1.0<jupyter_text>Jupyter可以用两条下划线获得倒数第二个历史输出，用三条下划线获得倒数第三个历史输出（跳过任何没有输出的命令）：<jupyter_code>print(__)
print(___)<jupyter_output>-0.4161468365471424<jupyter_text>另外，`Out[X]`可以简写成`_X`（即一条下划线加行号）：<jupyter_code>Out[2]
_2<jupyter_output><empty_output><jupyter_text>禁止输出要禁止一个命令的输出，在行末尾处添加一个分号：<jupyter_code>math.sin(2) + math.cos(2);<jupyter_output><empty_output><jupyter_text>> 这个结果被计算后，输出结果既不会显示在屏幕上，也不会存储在`Out`中：<jupyter_code>25 in Out<jupyter_output><empty_output><jupyter_text>其他历史魔法命令如果想一次性获取此前所有的输入历史，`%history`魔法命令会非常有用。在下面的示例中可以看到如何打印前4条输入命令：<jupyter_code>%history -n 1-4<jupyter_output>1: %load ../README.md
   2:
from ipywidgets import HBox, VBox, IntSlider, interactive_output
from IPython.display import display


a = IntSlider()
b = IntSlider()
out = interactive_output(lambda a, b: print(f"{a} * {b} = {a*b}"), {"a": a, "b": b})
display(HBox([VBox([a, b]), out]))
   3:
m = 5.1
if m < 5:
    print(f"{m}小于5")
elif m % 2 == 1:
    print(f"{m}是奇数")
elif m % 2 == 0:
    print(f"{m}是偶数")
else:
    print("不是整数")
   4: 5.1 % 2<jupyter_text>按照惯例，可以输入`%history?`来查看更多相关信息以及可用选项的详细描述。其他类似的魔法命令还有`%rerun`（该命令将重新执行部分历史命令）和`%save`（该命令将部分历史命令保存到一个文件中）。如果想获取更多相关信息，建议你使用`?`帮助功能（详情请参见1.2节）。  Jupyter和shell命令1. 在Jupyter中使用shell命令1. 在shell中传入或传出值1. 部分自动魔法  在Jupyter中使用shell命令Jupyter终端直接执行shell命令的语法，在shell命令前加`!`，将不会通过Python内核运行，而是通过系统命令运行。<jupyter_code>!ls
!pwd
!echo "数据科学"<jupyter_output>数据科学<jupyter_text>在shell中传入或传出值shell命令不仅可以从Jupyter中调用，还可以和Jupyter命名空间进行交互。例如，通过一个赋值操纵符将任何shell命令的输出保存到一个Python列表：<jupyter_code>contents = !ls  
contents
directory = !pwd 
directory<jupyter_output><empty_output><jupyter_text>这些结果并不以列表的形式返回，虽然可以像列表一样操作，但是这种类型还有其他功能，例如`grep`和`fields`方法以及`s`、`n`和`p`属性，允许你轻松地搜索、过滤和显示结果。<jupyter_code>type(directory)<jupyter_output><empty_output><jupyter_text>另一个方向的交互，即将Python变量传入shell，可以通过`{varname}`语法实现：<jupyter_code>message = "美妙的Python"
!echo {message}<jupyter_output>美妙的Python<jupyter_text>变量名包含在大括号内，在shell命令中用实际的变量替代。  与shell相关的魔法命令不能通过`!cd`来切换目录，原因是Notebook中的shell命令是在一个临时的shell中执行的。如果你希望以一种更持久的方式更改工作路径，可以使用`%cd`魔法命令：<jupyter_code>%cd ..<jupyter_output>/home/junjiet/data_science2020<jupyter_text>其实可以直接用cd实现该功能：<jupyter_code>cd ..<jupyter_output>/home/junjiet<jupyter_text>这种方式称作自动魔法（`automagic`）函数，可以通过`%automagic`魔法函数进行调整，默认开启。除了`%cd`，其他可用的类似shell的魔法函数还有`%cat`、`%cp`、`%env`、`%ls`、`%man`、`%mkdir`、`%more`、`%mv`、`%pwd`、`%rm`和`%rmdir`，默认都可以省略`%`符号。  错误和调试代码开发和数据分析经常需要一些调试，Jupyter调试代码工具：1. `%xmode`：控制打印信息进行traceback1. `%debug`：基于ipdb（pdb增强版）专用的调试器进行调试1. `%run -d`：交互式模式运行脚本，用`next`命令单步向下交互地运行代码常用调试命令如下:命令 | 描述  ---|---  `list` | 显示文件的当前路径  `h(elp)` | 显示命令列表，或查找特定命令的帮助信息 `q(uit)` | 退出调试器和程序 `c(ontinue)` | 退出调试器，继续运行程序  `n(ext)` | 跳到程序的下一步  `` | 重复前一个命令 `p(rint)` | 打印变量`s(tep)` | 步入子进程  `r(eturn)` | 从子进程跳出  在调试器中使用`help`命令，或者查看[`ipdb`的在线文档](https://github.com/gotcha/ipdb)获取更多的相关信息  代码分析与优化“过早优化是一切罪恶的根源。”——高德纳Jupyter提供了很多执行这些代码计时和分析的操作函数。1. `%time`:对单个语句的执行时间进行计时1. `%timeit`:对单个语句的重复执行进行计时，以获得更高的准确度1. `%prun`:利用分析器运行代码1. `%lprun`:利用逐行分析器运行代码，需要先安装`pip install line_profiler`，再导入`%load_ext line_profiler`1. `%memit`:测量单个语句的内存使用，需要先安装`pip install memory_profiler`，再导入`%load_ext memory_profiler`1. `%mprun`:通过逐行的内存分析器运行代码详情请参考[Python数据科学手册 第 1 章　IPython：超越 Python](https://www.ituring.com.cn/book/tupubarticle/19702)  [Python 基础](https://docs.python.org/zh-cn/3/index.html)通过“猜数字”游戏介绍Python编程基础，涉及输入输出、模块、函数、控制流、数据结构等概念。程序流程图如下：  游戏代码<jupyter_code># 猜数字游戏
import random

m, n = 30, 5  # 最大值和猜测次数
x = random.randrange(1, m)
name = input("Hello! 你是谁?")
print(f"欢迎你，{name}同学，我在1到{m}之间选了一个整数，共{n}次机会，你猜猜看？")
for i in range(n):
    guess = int(input(f"{name}同学猜是："))
    if guess == x:
        print(f"猜中啦，{name}同学！就是{x}，赶紧去买注大乐透吧！")
        break
    else:
        print("猜大了~", end="") if guess > x else print("猜小了~", end="")
        print(f"还有{n-i-1}次机会，再猜猜看：") if i + 1 < n else print("")
else:
    print(f">:<没猜中，我想的是{x}啊！")<jupyter_output>Hello! 你是谁?tutu
欢迎你，tutu同学，我在1到30之间选了一个整数，共5次机会，你猜猜看？
tutu同学猜是：15
猜中啦，tutu同学！就是15，赶紧去买注大乐透吧！<jupyter_text>import语句与random模块第1行是Python的注释，以开头，解释器运行时会忽略 后面的字符<jupyter_code># 猜数字游戏<jupyter_output><empty_output><jupyter_text>第2行是import语句，导入了一个random模块（module）<jupyter_code>import random<jupyter_output><empty_output><jupyter_text>>**语句（statement）是执行操作的若干指令，而表达式（expression）会计算并返回值**  第4行是赋值（assignment）语句，将30、5保存在变量（variable）m、n中<jupyter_code>m, n = 30, 5  # 最大值和猜测次数<jupyter_output><empty_output><jupyter_text>字面值（Literals）字面值用于表示一些内置类型的常量。  数字整型(int)、浮点数(float)和复数(complex)，与计算器一样，输入一个表达式就会有答案<jupyter_code>3 + 4 * 5 / 6
20 // 3, 20 % 3, 20 ** 3
import sys
sys.float_info<jupyter_output><empty_output><jupyter_text>字符串 字符串有多种形式，可以使用单引号（''），双引号（""），反斜杠 \ 表示转义字符:<jupyter_code>print("C:\some\name")<jupyter_output>C:\some
ame<jupyter_text>字符串跨行连续输入。用三重引号："""...""" 或 '''...'''。字符串中的回车换行会自动包含到字符串中，如果不想包含，在行尾添加一个 \ 即可。如下例:<jupyter_code>print("""\
Usage: thingy [OPTIONS]
     -h                        Display this usage message
     -H hostname               Hostname to connect to
""")<jupyter_output>Usage: thingy [OPTIONS]
     -h                        Display this usage message
     -H hostname               Hostname to connect to<jupyter_text>字符串可以用 + 进行连接（粘到一起），也可以用 * 进行重复<jupyter_code>3 * "un" + "ium"<jupyter_output><empty_output><jupyter_text>很长的字符串用括号包裹多个片段即可:<jupyter_code>text = ('很长的字符串用括号'
      '包裹多个片段即可')
text<jupyter_output><empty_output><jupyter_text>函数关键字`def`创建函数，后面函数名称和带括号的形式参数列表第5行调用了random模块的randrange()函数（function），这个函数会生成一个随机数`[1,n]`范围的整数x<jupyter_code>x = random.randrange(1, m) 
# 定义 Fibonacci数列
def fib(n):
    """Print a Fibonacci series up to n."""
    a, b = 0, 1
    while a < n:
        print(a, end=" ")
        a, b = b, a + b
    print()
# 调用函数
fib(2000)<jupyter_output>0 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597<jupyter_text>函数通过`return`语句返回值，即使没有 return 语句的函数也会返回一个值None<jupyter_code># 定义 Fibonacci数列
def fib_return(n):
    """Print a Fibonacci series up to n."""
    a, b = 0, 1
    fib = []
    while a < n:
        fib.append(a)
        a, b = b, a + b
    return fib
fib2000 = fib_return(2000)
fib2000
random.randrange??<jupyter_output><empty_output><jupyter_text>输入输出 第6行input()函数获取用户输入<jupyter_code>name = input("Hello! 你是谁?")
name<jupyter_output><empty_output><jupyter_text>第7行print()函数打印输出，f字符串可以在{ 和 } 字符之间引用变量或表达式（Python3.6新特性，另外还有C语言%字符串与.format字符串）<jupyter_code>print(f"欢迎你，{name}同学，我在1到{m}之间选了一个整数，共{n}次机会，你猜猜看？")
def mod(a,b):
    return a % b
import dis
dis.dis(mod)
mod('hello%s','world')
print(f"{1+2*3}")<jupyter_output>7<jupyter_text>更常见的还有文件读写操作：<jupyter_code>with open("myscript.py", "r") as f:
    txt = f.read()
    print(txt)
with open("myscript2.py", "w") as f:
    f.write(txt)
cat myscript2.py
with open("myscript.py", "r") as f:
    txt = f.readlines()
txt
with open("myscript2.py", "w") as f:
    f.writelines(txt)
cat myscript2.py<jupyter_output>def square(x):
    """求平方"""
    return x ** 2

for N in range(1, 4):
    print(N, "squared is", square(N))<jupyter_text>控制流1. for...[else]、break、contiue2. if...else3. while...[else]  for第8行-17行是一块for...else循环语句，对序列或其他可迭代对象的每个元素进行迭代:```pythonfor i in range(n):...else:    print(f">:<没猜中，我想的是{x}啊！")...```else子句是可选语句，当元素被迭代结束后，else 子句才会被执行<jupyter_code>for i in range(10):
    print(i ** 2)
else:
    print("game over")
for i in range(10):
    if i > 5:
        break
    print(i ** 2)
else:
    print("game over")
for i in range(10):
    if i > 5:
        continue
    print(i ** 2)
else:
    print("game over")<jupyter_output>0
1
4
9
16
25
game over<jupyter_text>if...else...第10行-13行是if...else条件语句```python    if guess == x:        print(f"猜中啦，{name}同学！就是{x}，赶紧去买注大乐透吧！")        break    else:        ...```<jupyter_code>m = 5.1
if m < 5:
    print(f"{m}小于5")
elif m % 2 == 1:
    print(f"{m}是奇数")
elif m % 2 == 0:
    print(f"{m}是偶数")
else:
    print("不是整数")
5.1 % 2<jupyter_output><empty_output><jupyter_text>浮点数精度问题，建议参考decimal标准库> 鲜为人知的Python特性 https://github.com/satwikkansal/wtfpython  while另一种循环语句，支持else可选子句<jupyter_code>i = 0
while i < 10:
    if i > 5:
        break
    print(i ** 2)
    i += 1
else:
    print("game over")<jupyter_output>0
1
4
9
16
25<jupyter_text>运算符（Operators）```+       -       *       **      /       //      %      @（Python3.5新特性）>      &       |       ^       ~       :=（Python3.8新特性）       =      ==      !=```运算符优先级： 运算符描述:=赋值表达式lambdalambda 表达式if -- else条件表达式or布尔逻辑或 ORand布尔逻辑与 ANDnot x布尔逻辑非 NOTin, not in,is, is not, &lt;,&lt;=, &gt;, &gt;=, !=, ==比较运算，包括成员检测和标识号检测|按位或 OR^按位异或 XOR&amp;按位与 AND&lt;&lt;, &gt;&gt;移位+, -加和减*, @, /, //, %乘，矩阵乘，除，整除，取余+x, -x, ~x正，负，按位非 NOT**乘方await xawait 表达式x[index], x[index:index],x(arguments...), x.attribute抽取，切片，调用，属性引用(expressions...),[expressions...],{key: value...},{expressions...}绑定或加圆括号的表达式，列表显示，字典显示，集合显示 条件表达式也称三元运算符(ternary operator):<jupyter_code>cmp = "2 > 1" if 2 > 1 else "2 < 1"
cmp<jupyter_output><empty_output><jupyter_text>第14、15行均使用了条件表达式：<jupyter_code>print("猜大了~", end="") if guess > x else print("猜小了~", end="")
print(f"还有{n-i-1}次机会，再猜猜看：") if i + 1 < n else print("")<jupyter_output>猜小了~<jupyter_text>[数据结构](https://docs.python.org/zh-cn/3/library/stdtypes.html)1. range1. list(列表)1. tuple(元组)1. set(集合)1. dict(字典)  range第8行中有一个range()函数，返回值是一个range对象，属于Python的基本序列类型，另外两个是list和tuple。range(n)包含`[0,n-1]`范围内所有整数。`range(start, stop[, step])`<jupyter_code>range(10)
type(range(10))
tuple(range(10))<jupyter_output><empty_output><jupyter_text>列表list列表是可变序列，通常用于存放同类项目的集合1. 使用一对方括号来表示空列表: []1. 使用方括号，其中的项以逗号分隔: [a], [a, b, c]1. 使用列表推导式: [x for x in iterable]1. 使用类型的构造器: list() 或 list(iterable)<jupyter_code>harry_potter = [
    "魔法石",
    "密室",
    "阿兹卡班囚徒",
    "火焰杯",
    "凤凰社",
    "混血王子",
    "死亡圣器",
    "被诅咒的孩子",
]

harry_potter
type(harry_potter)
len(harry_potter)
harry_potter.index("凤凰社")
harry_potter[4]
harry_potter.pop()
harry_potter
harry_potter.append("被诅咒的孩子")
harry_potter
for book in harry_potter:
    print(f"《哈利·波特与{book}》")
[f"《哈利·波特与{book}》" for book in harry_potter]<jupyter_output><empty_output><jupyter_text>tuple(元组)元组是不可变序列，通常用于储存异构数据集、同构数据集的不可变序列1. 使用一对圆括号来表示空元组: ()1. 使用一个后缀的逗号来表示单元组: a, 或 (a,)1. 使用以逗号分隔的多个项: a, b, c or (a, b, c)1. 使用内置的 tuple(): tuple() 或 tuple(iterable)<jupyter_code>harry_potter = (
    ("哈利·波特", "魔法石"),
    ("哈利·波特", "密室"),
    ("哈利·波特", "阿兹卡班囚徒"),
    ("哈利·波特", "火焰杯"),
    ("哈利·波特", "凤凰社"),
    ("哈利·波特", "混血王子"),
    ("哈利·波特", "死亡圣器"),
    ("哈利·波特", "被诅咒的孩子"),
)
for i, book in enumerate(harry_potter):
    print(f"第{i+1}本《{'与'.join(book)}》")<jupyter_output>第1本《哈利·波特与魔法石》
第2本《哈利·波特与密室》
第3本《哈利·波特与阿兹卡班囚徒》
第4本《哈利·波特与火焰杯》
第5本《哈利·波特与凤凰社》
第6本《哈利·波特与混血王子》
第7本《哈利·波特与死亡圣器》
第8本《哈利·波特与被诅咒的孩子》<jupyter_text>set(集合)由不重复哈希对象构成的无序容器。用途包括1. 成员检测<jupyter_code>"哈利" in {"哈利", "波特"}<jupyter_output><empty_output><jupyter_text>2. 从序列中去除重复项<jupyter_code>set(("哈利", "波特", "哈利", "波特"))<jupyter_output><empty_output><jupyter_text>3. 数学集合交、并、补等运算<jupyter_code>harry_potter_books = set(x for y in harry_potter for x in y)
harry_potter_books
magic, secret = set(harry_potter[0]), set(harry_potter[1])
magic, secret
magic & harry_potter_books
harry_potter_books - magic
magic | secret
magic ^ secret
magic.add("死亡圣器")
magic<jupyter_output><empty_output><jupyter_text>frozenset不可变对象，不支持add、remove、pop和update等操作<jupyter_code>magic2 = frozenset(magic) 

magic2
magic2.add<jupyter_output><empty_output><jupyter_text>dict(字典)映射类型，形式为若干键:值对。键不可重复，列表、字典或其他可变类型不可作为键。  Python3.6改写字典算法，保持顺序不变。<jupyter_code>harry_potter1 = dict(
    魔法石=1997, 密室=1998, 阿兹卡班囚徒=1999, 火焰杯=2000, 凤凰社=2003, 混血王子=2005, 死亡圣器=2007,
)
harry_potter2 = {
    "魔法石": 1997,
    "密室": 1998,
    "阿兹卡班囚徒": 1999,
    "火焰杯": 2000,
    "凤凰社": 2003,
    "混血王子": 2005,
    "死亡圣器": 2007,
}
harry_potter1 == harry_potter2
harry_potter3 = dict(
    zip(
        ("魔法石", "密室", "阿兹卡班囚徒", "火焰杯", "凤凰社", "混血王子", "死亡圣器",),
        (1997, 1998, 1999, 2000, 2003, 2005, 2007,),
    )
)<jupyter_output><empty_output><jupyter_text><jupyter_code>harry_potter1 == harry_potter2 == harry_potter3
harry_potter2["凤凰社"]
for book, year in harry_potter2.items():
    print(f"《哈利·波特与{book}》出版于{year}年")
harry_potter2["被诅咒的孩子"] = 2016
harry_potter2
harry_potter2.pop("被诅咒的孩子")
harry_potter2<jupyter_output><empty_output><jupyter_text>函数式编程借助Python的不可变对象和高阶函数，进行更简洁的表达，实现快速并行计算。1. 可变与不可变对象1. max、min、sum、any、all、sorted、reversed1. filter、map、reduce1. multiprocessing与concurrent.futures1. 第三方高阶函数包：cytoolz、fn与fancy  可变与不可变对象1. Python中一切皆对象，对象有两种：可变（mutable ）与不可变（immutable）2. 对象被实例化（instantiate）之后获得唯一id，可变对象可以改变状态或内容，而不可变不能修改3. 不可变对象：int、float、bool、string/unicode、tuple\*、nametuple、frozenset，读取速度快，节省内存4. 可变对象：list、dict、set、自定义类5. tuple例外<jupyter_code>tup = ([3, 4, 5], 'two')
tup[0][-1] = 1
tup<jupyter_output><empty_output><jupyter_text>collections.nametuple                   symbol      name      current      percent      pe_ttm      current_year_percent      volume                  0      SH601398      工商银行      5.17      0.39      5.855      -12.07      104063910              1      SH601939      建设银行      6.43      0.16      5.939      -11.07      51089825              2      SH600519      贵州茅台      1265.70      -0.72      36.908      6.99      2466087              3      SH601318      中国平安      74.46      0.62      10.474      -12.87      48625473              4      SH601288      农业银行      3.46      0.58      5.631      -6.23      118473509              5      SH601988      中国银行      3.48      0.58      5.420      -5.69      79563490              6      SH600036      招商银行      35.09      0.20      9.274      -6.63      71533247              7      SH601857      中国石油      4.44      1.37      42.328      -23.84      85973295              8      SH601628      中国人寿      28.54      -0.73      16.348      -18.15      16644522              9      SH600028      中国石化      4.46      0.45      23.430      -12.72      119640204<jupyter_code>data = [{"symbol": "SH601398","name": "工商银行","current": 5.17,"percent": 0.39,"pe_ttm": 5.855,"current_year_percent": -12.07,"volume": 104063910,"industry": "银行",},
{"symbol": "SH601939","name": "建设银行","current": 6.43,"percent": 0.16,"pe_ttm": 5.939,"current_year_percent": -11.07,"volume": 51089825,"industry": "银行",},
{"symbol": "SH600519","name": "贵州茅台","current": 1265.7,"percent": -0.72,"pe_ttm": 36.908,"current_year_percent": 6.99,"volume": 2466087,"industry": "白酒",},
{"symbol": "SH601318","name": "中国平安","current": 74.46,"percent": 0.62,"pe_ttm": 10.474,"current_year_percent": -12.87,"volume": 48625473,"industry": "保险",},
{"symbol": "SH601288","name": "农业银行","current": 3.46,"percent": 0.58,"pe_ttm": 5.631,"current_year_percent": -6.23,"volume": 118473509,"industry": "银行",},
{"symbol": "SH601988","name": "中国银行","current": 3.48,"percent": 0.58,"pe_ttm": 5.42,"current_year_percent": -5.69,"volume": 79563490,"industry": "银行",},
{"symbol": "SH600036","name": "招商银行","current": 35.09,"percent": 0.2,"pe_ttm": 9.274,"current_year_percent": -6.63,"volume": 71533247,"industry": "银行",},
{"symbol": "SH601857","name": "中国石油","current": 4.44,"percent": 1.37,"pe_ttm": 42.328,"current_year_percent": -23.84,"volume": 85973295,"industry": "石化",},
{"symbol": "SH601628","name": "中国人寿","current": 28.54,"percent": -0.73,"pe_ttm": 16.348,"current_year_percent": -18.15,"volume": 16644522,"industry": "保险",},
{"symbol": "SH600028","name": "中国石化","current": 4.46,"percent": 0.45,"pe_ttm": 23.43,"current_year_percent": -12.72,"volume": 119640204,"industry": "石化",}]
from collections import namedtuple
Stocks = namedtuple(
    "Stocks",
    [
        "symbol",
        "name",
        "current",
        "percent",
        "pe_ttm",
        "current_year_percent",
        "volume",
        "industry",
    ],
)
Stocks
icbc = Stocks(
    symbol="SH601398",
    name="工商银行",
    current=5.17,
    percent=0.39,
    pe_ttm=5.855,
    current_year_percent=-12.07,
    volume=104063910,
    industry="银行",
)
icbc
icbc.name
icbc.name = 'dd'
stocks = tuple(Stocks(**_) for _ in data)
stocks
stocks[0].name<jupyter_output><empty_output><jupyter_text>max、min、sum、any、all、sorted、reversed<jupyter_code>max(stocks)
max(stocks, key=lambda _: _.current)
min(stocks)
min(stocks, key=lambda _: _.current)
sum((1, 2, 3, 0))
any((1, 2, 3, 0))
all((1, 2, 3, 0))
sorted(stocks, key=lambda _: _.current)
tuple(reversed(stocks))<jupyter_output><empty_output><jupyter_text>filter、map、reduce<jupyter_code>tuple(filter(lambda s: s.current > 5, stocks))
tuple(s for s in stocks if s.current > 5)
tuple(map(lambda s: (s.name, round(s.volume / 10000, 1)), stocks))
tuple((s.name, round(s.volume / 10000, 1)) for s in stocks)
from functools import reduce
reduce?
reduce(lambda acc, v: acc + v.volume, stocks, 0)
from itertools import accumulate
tuple(accumulate(s.volume for s in stocks))<jupyter_output><empty_output><jupyter_text>groupby<jupyter_code>def box(acc, v):
    acc[v.industry].append(v.name)
    return acc
stocks_industry = reduce(box, stocks, {"银行": [], "保险": [], "白酒": [], "石化": []})
stocks_industry
from collections import defaultdict
stocks_industry = reduce(box, stocks, defaultdict(list))
stocks_industry
stocks_industry = reduce(
    lambda acc, v: {**acc, **{v.industry: acc[v.industry] + [v.name]}},
    stocks,
    {"银行": [], "白酒": [], "保险": [], "石化": []},
)
stocks_industry
from itertools import groupby
{
    k: [_.name for _ in s]
    for k, s in groupby(
        sorted(stocks, key=lambda s: s.industry), key=lambda s: s.industry
    )
}<jupyter_output><empty_output><jupyter_text>multiprocessing与concurrent.futures<jupyter_code>import time
import os
import multiprocessing
def cmpt_open(x):
    print(f"进程：{os.getpid()} 正在计算 {x.name}")
    time.sleep(1)
    rst = {"name": x.name, "open": x.current / (1 + x.percent * 0.01)}
    print(f"进程：{os.getpid()} 完成计算 {x.name}")
    return rst
%%time
result = tuple(map(cmpt_open, stocks))
result
%%time
pool = multiprocessing.Pool()
result = pool.map(cmpt_open, stocks)
result
import concurrent.futures
%%time
with concurrent.futures.ProcessPoolExecutor() as pool:
    result = pool.map(cmpt_open, stocks)
tuple(result)
%%time
with concurrent.futures.ThreadPoolExecutor() as pool:
    result = pool.map(cmpt_open, stocks)
tuple(result)<jupyter_output>进程：81395 正在计算 工商银行
进程：81395 正在计算 建设银行
进程：81395 正在计算 贵州茅台
进程：81395 正在计算 中国平安
进程：81395 正在计算 农业银行
进程：81395 正在计算 中国银行
进程：81395 正在计算 招商银行
进程：81395 正在计算 中国石油
进程：81395 正在计算 中国人寿进程：81395 正在计算 中国石化

进程：81395 完成计算 工商银行
进程：81395 完成计算 建设银行
进程：81395 完成计算 贵州茅台
进程：81395 完成计算 中国平安
进程：81395 完成计算 农业银行
进程：81395 完成计算 中国银行进程：81395 完成计算 招商银行

进程：81395 完成计算 中国石油
进程：81395 完成计算 中国人寿进程：81395 完成计算 中国石化

CPU times: user 19.2 ms, sys: 8.83 ms, total: 28 ms
Wall time: 1.01 s<jupyter_text>[cytoolz](https://github.com/pytoolz/cytoolz)、[fn](https://github.com/fnpy/fn.py) 与[fancy](https://github.com/Suor/funcy)借助第三方包实现大量高阶函数，可以增强Python函数式编程功能，包括纯函数、curry柯里化（偏函数）、lazy惰性计算、并行化...下面以cytoolz为例演示双城记词频统计：<jupyter_code>from cytoolz import map, concat, frequencies  # cytoolz 的 map 默认惰性计算
%%time
frequencies(
    concat(map(str.upper, open("tale-of-two-cities.txt", "r", encoding="utf-8-sig")))
)
from collections import Counter
%%time
Counter(
    map(str.upper, open("tale-of-two-cities.txt", "r", encoding="utf-8-sig").read())
)<jupyter_output>CPU times: user 190 ms, sys: 2.68 ms, total: 193 ms
Wall time: 190 ms
{'fraction_non_alphanumeric': 0.16467011689768737, 'fraction_numerical': 0.048460062972748, 'mean_word_length': 5.301482326111745, 'pattern_counts': {'":': 101, '<': 255, '<?xml version=': 0, '>': 270, 'https://': 10, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '66', 'dir': 'jupyter-structured-clean-dedup', 'id': '410352', 'license': 'Apache-2.0', 'n_tokens_mistral': 15997, 'n_tokens_neox': 16681, 'n_words': 1775, 'path': '_notebooks/2020-05-08-jupyter-python.ipynb', 'repo_name': 'CyberSciTech/air', 'size': '27642'}