第 8 章 Python基础
你总是给自己设置障碍,因为你不敢。—邪不压正
8.1 Python简介
本章我们介绍Python
的基础知识。在这之前,读者可能会好奇,为什么又多了一个语言?我们已经学习了R语言
和SQL语言
,接触了bash
,在未来还要学习html语言
和正则表达式
。之所以学习这么多语言是因为数据工程中涵盖了多种多样的任务,针对不同的任务往往有不同的最佳工具。与其追求在一个语言中费劲完成
正如在第三章介绍的,尽管R语言
非常强大,但是它毕竟不是一个通用语言,在很多任务上,效率并不高。例如,绝大多数的API
都使用的是Python
语法,这就极大限制了R语言
的应用场景。因此,我们需要学习一门通用语言,
Python
也是一门解释性语言,对于编译型语言如C/C++
它更容易调试。Python
的语法如R语言
一般简洁易懂。Python
是一个通用语言可,在生活中的方方面面都可以使用。
Python
的软件库丰富,可以完成非常多其他的功能。Python
可以将跨语言的程序粘合在一起,是天生的“胶水语言”,非常适合跨语言协作。
由于以上优点,Python
成为当前最流行的编程语言。Python
的出现大大降低了编程的门槛,程序员供给数量指数增多。
更重要的是,学习编程语言在总收益不变的情况下,边际成本是断崖式下降的,假设大家在学习R语言
时候用了100%的努力,在学习Python
时只用付出25%的努力,就可以快速掌握Python
,但是编程能力和视野会翻倍。在未来深入学习bash
时,只用付出10%的努力即可。不同的编程语言更是代表了不同的世界观,在增加技能的同时,让我们的用于更多看待世界的角度。
8.2 Python安装与Pycharm
8.2.1 安装
Python
存在两个版本主要版本,Python2
和Python3
。Python3
是针对Python2
的一次大规模改进,以至于很多Python2
的程序无法在Python3
中运行,因此可以将两者看做是不同的语言,以避免混淆出错。
Windows用户在WSL环境中使用apt
命令安装python3
。
apt install python3
R用户可以通过brew
或者在Gentoo prefix
中的merge
命令安装。
brew install python3
8.2.2 编辑器
Python
有一个增强式编译环境,Ipython
可用于交互式编程。还提供了Jupyter Notebook
,是IPython
发展而来的可以在网页上进行交互式编程的环境。它的交互性更强,可以规避掉命令行,上手容易,适合写教程。
但是,网页并不能替代编辑器,不适合写大段程序,也没有批量处理能力。如果是只是学会了Jupyter
就以为自己学会了编程,到处招摇过市,会暴露自己的无知与傲慢。因此,不推荐大家将其作为编辑器使用。
8.2.3 Pycharm的安装与教育许可证
我们推荐Pycharm Professional
作为Python
的编辑器(下载地址),这是一款不逊色于Rstudio
的编辑器,且为教育机构用户提供免费的权限。Pycharm
不仅继承了语法高亮、自动补全以及版本控制等基本功能,更是集合了远程服务器编译功能,让远程调试非常便捷,这是一个Rstudio
都没有的功能。同时,Pycharm
也支持跨语言编辑。
安装之后请在https://www.jetbrains.com/shop/eform/students申请教育许可证。建议使用“官方文件”认证的方式。
打开Pycharm
的第一步是新建项目,新建项目是要确定项目路径与编译代码的Python
路径。Windows系统读者注意要使用WSL环境中的python
,具体配置方法见视频。
这里对应的习惯是一个研究项目对应一个路径,其实这样的习惯在R
中也应该学习。
Pycharm
的窗口环境如下:
此外Rstudio
也是可以兼容python的,使用reticulate::repl_python()
即可将Console
的编译环境变成python
环境。在做一些小命令尝试的时候,可以直接在Rtudio中编程,而不需要特别转换工作环境。毕竟,任何环境的转换都是需要成本的。
8.4 数据类型
Python
有五种数据类型:整型(精度无限的整数)、浮点型(64位高精度小数)、字符串和None
。字符串引号(单引号或双引号)标注、布尔型(Ture
和False
)。
None
是Python
中一个特别的值,即不是整数也不是浮点数,属于一种独有的类型。可以代表很多含义:“空”或者“没有”,或者“无法表达”,或者“出错了”、“非法”。转换为布尔类型时,None 的赋值为“假”。
type
函数可以查看的数据类型。
## <class 'int'>
## <class 'float'>
## <class 'str'>
## <class 'bool'>
## <class 'NoneType'>
8.4.2 算术运算
+-*/
用于四则运算,幂运算使用两个星号,//
表示整除,%
为取余数。
## 6
## 0
## 9
## 1.5
## 729
## 1
## 0
更复杂的运算,可以导入math
模块,例如阶乘。
## 3628800
注意,在Python
里面整数是高精度的,具体多少精度,取决于计算机的硬件水平。Python
会自行做出判断,用计算效率损失来换取人类在编程时候的方便。这样的恰恰适合经济学家,如果能够节省人类的时间,不惜浪费计算机的时间;通过升级硬件来节约计算机的时间。
8.4.3 布尔运算
and
,or
,not
是Python
中的“与或非”运算。
## False
## True
## False
<
、<=
、>
、>=
、==
,!=
用于数值比较,生成布尔型。
## True
## False
在Python
内部,布尔型被储存成整数型的0和1,因此可进行算术运算。
## 3
## True
8.4.4 字符串
Python
有非常强大的字符串工具库,针对单个字符串,Python
开发了非常强大的属性与操作。很多操作让R用户眼睛瞪得像铜铃。例如,用加法和乘法,定义字符串的拼接与重复。
## '3.1415926'
## '重要的话说三次!重要的话说三次!重要的话说三次!'
len
函数可以得到字符串的长度。
## 6
8.4.5 字符串的替换
.format
函数可以实现灵活的字符串替换,例如,
"我感觉{}还需要{},你们毕竟还是{},你明白这意思吧?我告诉你们我是{}了,见得多了,西方哪一个{}我没有去过?你们要知道,美国的{},比你们不知道要{}到哪里去了,我跟他{}。".format("你们新闻界","学习","too young","身经百战","国家","华莱士","高","谈笑风生")
## '我感觉你们新闻界还需要学习,你们毕竟还是too young,你明白这意思吧?我告诉你们我是身经百战了,见得多了,西方哪一个国家我没有去过?你们要知道,美国的华莱士,比你们不知道要高到哪里去了,我跟他谈笑风生。'
其中,变的部分用{}
表示,.format
中的参数与前面的{}
一一对应。
f-string
可以实现同样的功能,在数据输出时经常使用。
## '3乘以5等于15'
8.5 标准输入输出
8.6 数据结构
把基本数据类型组合起来可以构成复杂的数据结构。Python
数据结构包括列表、元组和字典。
8.6.1 列表
列表用[]
表达,其中的元素用,
分割。
8.6.2 元组
元组是一类特殊的列表,不同之处在于元组的元素不能修改。使用()
生成。+
与+=
可以用于拼接元组,*
用于重复,[]
用于切片。
由于元组是不能修改的,因此只能全部删除元组。
## (1, 23, 4)
注意一个细节,("x")
一个元素时,会被认为是字符串而不是元组。但是["x"]
却是一个列表。
## <class 'str'>
## <class 'list'>
8.6.3 字典
字典是Python
的经典数据结构,与JSON
数据结构类似。字典用{}
定义,以key:value
这样的键值对定义一组词,词与词之间用,
分隔。
可以通过.keys()
和.values()
取出字典的键和值,.items()
在则可以构建迭代器。
字典的索引是通过keys
实现的,是无序的,但仍可以通过对keys的索引进行元素的删除,只是不支持切片操作。
## 25
## True
dict
和enumerate
函数可以把任何序列直接生成字典。这在后续的循环迭代器构建中有妙用。
## {0: 'a', 1: 'b', 2: 'c', 3: 'd'}
8.7 流程控制
8.7.1 条件判断
Python
中条件判断的语法如下:
## 10是偶数
上述代码构建了一个奇偶数判断器。特别要注意,Python
强制要求代码缩进来表达程序的结构。如果缩进错误会直接报错。这么做的好处是可以省略掉其他语言中用于显示结构关系的标记符,例如{}
。其哲学是,与其希望用户自觉缩进,不如要求强制缩进。
对于有良好编程习惯的程序员,这个约定不会带来额外的负担。
当有三重选择时,可以使用else if(或elif)
,搬出我们的泰山代码。
m = 100
n = 50
if m > n:
print(m,"is lager than", n)
elif m ==n:
print(m,"is equal to", n)
else:
print(m,"is smaller than", n)
## 100 is lager than 50
8.7.2 循环
Python的循环结构有两种,for
语句和while
语句。例如,求和代码可以写成。
## 5050
## 5050
此处的range
函数是生成了一个从0到100的迭代器(iterator)。在每次 for 循环时,都从迭代器中取出一个值。迭代器是一般概念,Python 中的多数多个元素组成的数据结构都可以看作迭代器。
字符串可以当成天然的迭代器,下面的写法就比R
里面方便的多。
## h
## e
## l
## l
## o
##
## w
## o
## r
## l
## d
enumerate
函数生成索引和迭代器,
## 第0个字符是h
## 第1个字符是e
## 第2个字符是l
## 第3个字符是l
## 第4个字符是o
## 第5个字符是
## 第6个字符是w
## 第7个字符是o
## 第8个字符是r
## 第9个字符是l
## 第10个字符是d
字典也是天生的迭代器,
## 班上有25个女生
## 班上有18个男生
由此看来,构造迭代器是for
循环的关键。
在循环里执行continue
,可以跳过本次循环进入下一步。执行break
则终止循环,直接跳出循环体。这与R的用法类似。
8.8 函数与模块
Python
定义函数的方式如下:
def add(x,y):
print(f"x is {x} and y is {y}")
return x + y # Return values with a return statement
add(2,3)
## x is 2 and y is 3
## 5
8.8.3 函数文档
为了更好地解释函数的使用方法,可以在函数定义时,输入一段字符串,这样的字符串可以用help
函数读出。
## Help on function squared in module __main__:
##
## squared(x)
## 计算平方的函数
如果一大段的说明,可以使用Python
多行字符串。多行字符串以三个引号开始,三个引号结束,单引号双引号皆可。
8.8.4 模块
模块是把函数等聚集起来的名字空间,由目录或者文件划定。使用import
方法可以导入模块,模块都具有详实的在线帮助,可以使用help
函数查看。
## Help on module math:
##
## NAME
## math
##
## DESCRIPTION
## This module provides access to the mathematical functions
## defined by the C standard.
##
## FUNCTIONS
## acos(x, /)
## Return the arc cosine (measured in radians) of x.
##
## The result is between 0 and pi.
##
## acosh(x, /)
## Return the inverse hyperbolic cosine of x.
##
## asin(x, /)
## Return the arc sine (measured in radians) of x.
##
## The result is between -pi/2 and pi/2.
##
## asinh(x, /)
## Return the inverse hyperbolic sine of x.
##
## atan(x, /)
## Return the arc tangent (measured in radians) of x.
##
## The result is between -pi/2 and pi/2.
##
## atan2(y, x, /)
## Return the arc tangent (measured in radians) of y/x.
##
## Unlike atan(y/x), the signs of both x and y are considered.
##
## atanh(x, /)
## Return the inverse hyperbolic tangent of x.
##
## cbrt(x, /)
## Return the cube root of x.
##
## ceil(x, /)
## Return the ceiling of x as an Integral.
##
## This is the smallest integer >= x.
##
## comb(n, k, /)
## Number of ways to choose k items from n items without repetition and without order.
##
## Evaluates to n! / (k! * (n - k)!) when k <= n and evaluates
## to zero when k > n.
##
## Also called the binomial coefficient because it is equivalent
## to the coefficient of k-th term in polynomial expansion of the
## expression (1 + x)**n.
##
## Raises TypeError if either of the arguments are not integers.
## Raises ValueError if either of the arguments are negative.
##
## copysign(x, y, /)
## Return a float with the magnitude (absolute value) of x but the sign of y.
##
## On platforms that support signed zeros, copysign(1.0, -0.0)
## returns -1.0.
##
## cos(x, /)
## Return the cosine of x (measured in radians).
##
## cosh(x, /)
## Return the hyperbolic cosine of x.
##
## degrees(x, /)
## Convert angle x from radians to degrees.
##
## dist(p, q, /)
## Return the Euclidean distance between two points p and q.
##
## The points should be specified as sequences (or iterables) of
## coordinates. Both inputs must have the same dimension.
##
## Roughly equivalent to:
## sqrt(sum((px - qx) ** 2.0 for px, qx in zip(p, q)))
##
## erf(x, /)
## Error function at x.
##
## erfc(x, /)
## Complementary error function at x.
##
## exp(x, /)
## Return e raised to the power of x.
##
## exp2(x, /)
## Return 2 raised to the power of x.
##
## expm1(x, /)
## Return exp(x)-1.
##
## This function avoids the loss of precision involved in the direct evaluation of exp(x)-1 for small x.
##
## fabs(x, /)
## Return the absolute value of the float x.
##
## factorial(n, /)
## Find n!.
##
## Raise a ValueError if x is negative or non-integral.
##
## floor(x, /)
## Return the floor of x as an Integral.
##
## This is the largest integer <= x.
##
## fmod(x, y, /)
## Return fmod(x, y), according to platform C.
##
## x % y may differ.
##
## frexp(x, /)
## Return the mantissa and exponent of x, as pair (m, e).
##
## m is a float and e is an int, such that x = m * 2.**e.
## If x is 0, m and e are both 0. Else 0.5 <= abs(m) < 1.0.
##
## fsum(seq, /)
## Return an accurate floating point sum of values in the iterable seq.
##
## Assumes IEEE-754 floating point arithmetic.
##
## gamma(x, /)
## Gamma function at x.
##
## gcd(*integers)
## Greatest Common Divisor.
##
## hypot(...)
## hypot(*coordinates) -> value
##
## Multidimensional Euclidean distance from the origin to a point.
##
## Roughly equivalent to:
## sqrt(sum(x**2 for x in coordinates))
##
## For a two dimensional point (x, y), gives the hypotenuse
## using the Pythagorean theorem: sqrt(x*x + y*y).
##
## For example, the hypotenuse of a 3/4/5 right triangle is:
##
## >>> hypot(3.0, 4.0)
## 5.0
##
## isclose(a, b, *, rel_tol=1e-09, abs_tol=0.0)
## Determine whether two floating point numbers are close in value.
##
## rel_tol
## maximum difference for being considered "close", relative to the
## magnitude of the input values
## abs_tol
## maximum difference for being considered "close", regardless of the
## magnitude of the input values
##
## Return True if a is close in value to b, and False otherwise.
##
## For the values to be considered close, the difference between them
## must be smaller than at least one of the tolerances.
##
## -inf, inf and NaN behave similarly to the IEEE 754 Standard. That
## is, NaN is not close to anything, even itself. inf and -inf are
## only close to themselves.
##
## isfinite(x, /)
## Return True if x is neither an infinity nor a NaN, and False otherwise.
##
## isinf(x, /)
## Return True if x is a positive or negative infinity, and False otherwise.
##
## isnan(x, /)
## Return True if x is a NaN (not a number), and False otherwise.
##
## isqrt(n, /)
## Return the integer part of the square root of the input.
##
## lcm(*integers)
## Least Common Multiple.
##
## ldexp(x, i, /)
## Return x * (2**i).
##
## This is essentially the inverse of frexp().
##
## lgamma(x, /)
## Natural logarithm of absolute value of Gamma function at x.
##
## log(...)
## log(x, [base=math.e])
## Return the logarithm of x to the given base.
##
## If the base is not specified, returns the natural logarithm (base e) of x.
##
## log10(x, /)
## Return the base 10 logarithm of x.
##
## log1p(x, /)
## Return the natural logarithm of 1+x (base e).
##
## The result is computed in a way which is accurate for x near zero.
##
## log2(x, /)
## Return the base 2 logarithm of x.
##
## modf(x, /)
## Return the fractional and integer parts of x.
##
## Both results carry the sign of x and are floats.
##
## nextafter(x, y, /, *, steps=None)
## Return the floating-point value the given number of steps after x towards y.
##
## If steps is not specified or is None, it defaults to 1.
##
## Raises a TypeError, if x or y is not a double, or if steps is not an integer.
## Raises ValueError if steps is negative.
##
## perm(n, k=None, /)
## Number of ways to choose k items from n items without repetition and with order.
##
## Evaluates to n! / (n - k)! when k <= n and evaluates
## to zero when k > n.
##
## If k is not specified or is None, then k defaults to n
## and the function returns n!.
##
## Raises TypeError if either of the arguments are not integers.
## Raises ValueError if either of the arguments are negative.
##
## pow(x, y, /)
## Return x**y (x to the power of y).
##
## prod(iterable, /, *, start=1)
## Calculate the product of all the elements in the input iterable.
##
## The default start value for the product is 1.
##
## When the iterable is empty, return the start value. This function is
## intended specifically for use with numeric values and may reject
## non-numeric types.
##
## radians(x, /)
## Convert angle x from degrees to radians.
##
## remainder(x, y, /)
## Difference between x and the closest integer multiple of y.
##
## Return x - n*y where n*y is the closest integer multiple of y.
## In the case where x is exactly halfway between two multiples of
## y, the nearest even value of n is used. The result is always exact.
##
## sin(x, /)
## Return the sine of x (measured in radians).
##
## sinh(x, /)
## Return the hyperbolic sine of x.
##
## sqrt(x, /)
## Return the square root of x.
##
## sumprod(p, q, /)
## Return the sum of products of values from two iterables p and q.
##
## Roughly equivalent to:
##
## sum(itertools.starmap(operator.mul, zip(p, q, strict=True)))
##
## For float and mixed int/float inputs, the intermediate products
## and sums are computed with extended precision.
##
## tan(x, /)
## Return the tangent of x (measured in radians).
##
## tanh(x, /)
## Return the hyperbolic tangent of x.
##
## trunc(x, /)
## Truncates the Real x to the nearest Integral toward 0.
##
## Uses the __trunc__ magic method.
##
## ulp(x, /)
## Return the value of the least significant bit of the float x.
##
## DATA
## e = 2.718281828459045
## inf = inf
## nan = nan
## pi = 3.141592653589793
## tau = 6.283185307179586
##
## FILE
## /usr/local/Cellar/python@3.12/3.12.1/Frameworks/Python.framework/Versions/3.12/lib/python3.12/lib-dynload/math.cpython-312-darwin.so
当模块中的内容很多时,会被安排在不同层次的名字空间中,可以使用from
来简化调用,as
可以指定模块的别名。
8.9 数据读写与文件操作
之前我们介绍了通过input
从键盘输入和print
输出到屏幕。除此之外,还可以直接从文件读入和输出到文件。Python
内置了file
类,来完成文件操作。
8.9.1 读入文件
open
函数用于打开文件,创建一个file
对象,第一个参数是文件名,第二参数为打开模式,默认值r
表示只读,w
表示写出,a
表示添加。
open
打开文件之后,用迭代器逐行读取。
## This is a record of my progress in learning Python
## Day 1: Introduction
8.10 os模块
os
提供了帮你执行文件处理操作的方法,比如重命名和删除文件。
方法 | 作用 |
---|---|
remove |
删除文件 |
rename |
重名命文件 |
mkdir |
创建新目录 |
chdir |
改变当前目录 |
getcwd |
查看当前目录 |
rmdir |
删除目录 |
8.11 Python的环境配置
笼统地说,Python环境指的是“解释器(即python本身)与其使用模块的整体”。如下图所示,有两种方案来管理python的环境,一种是只针对python内部的pip管理器方案,另一种是跨语言的环境整合方案。注意,此时的环境管理指的是拥有管理员权限的管理。
pip是python自带的语言管理工具,只适合早期试验个别最新的python软件,长期维护性差,而且只适用于python。pip会和系统的库发生冲突,出现玄学问题。
更严重的是,pip没有经过第三方检测,意味着无条件相信上游软件作者,没有办法保障安全,不确定是否会被恶意植入病毒代码,在不知不觉中隐私信息被窃取,或者电脑被他人操纵。这样的重大安全事故,历史上已经发生过多次。
环境整合方案则是需要经过跨语言整合测试的方案,对应多种软件的稳定版本。会有负责安全、兼容和性能的团队,以及大量使用相同环境的用户反馈问题。环境整合方案意味着我们要信任环境整合的第三方。
环境整合方案有两种模式:Debian/Arch/Gentoo志愿者模式与Ubuntu/Conda公司免费模式。前者的开发团队由没有利益关系的、兴趣一致的人们通过git共同开发,没有人能够强制整个项目的走向,集体决策,是一种民主模式。后者则是利用免费服务吸引用户,构建潜在的客户池或者暗藏广告来改造用户,产品总是服务于公司的盈利或扩大影响的战略,代码是有雇员生产的,雇员的总体规模肯定远小于志愿者模式。
我们不推荐使用公司模式,Ubuntu的代码95%直接来自Debian,且公司曾经定制广告;Conda的依赖关系的计算效率极低,但在公司层面花费大量的外宣经费进行推广,所以互联网上可以看到大量的技术博主推广conda,看上去仿佛conda是被所有人喜欢的,这是NGO的一种宣传策略。所以,非常不推荐使用conda。
上面的场景对应的是自己拥有root权限,可以维护自己的主机或者一个服务器的整体环境。还有一种情形是自己只有普通用户权限,此时推荐使用Gentoo Prefix进行用户态管理。Gentoo Prefix由续本达召集管理,有问题可以直接物理上找到召集人,还是更放心的。
值得一提的是,对于macOS用户,Homebrew提供了志愿者模式的环境整合方案,本书目前推荐Homebrew模式,但是brew中的依赖经常滞后,存在一定的局限性;Gentoo Prefix也为macOS开发了一套环境整合方案,更适用于科学计算,但是Gentoo Prefix的安装与使用,会遇到一些玄学问题,作者自己也没有完全搞定,如果全部搞定之后,会将教材替换成Gentoo Prefix模式。
总结起来就是,自己的机器用Debian,工具安装用apt;他人的机器用Gentoo Prefix,工具安装用emerge。