Overture
Brief Introduction
Python是一种动态的(变量类型, 函数定义可以在代码中改变)、强类型的(不同类型之间的运算有严格的限制)脚本语言(也叫胶水语言)(用来拼接一个项目中不同语言的成分)。
其由荷兰数学和计算机科学研究学会的Guido van Rossum 于1990 年代初设计。名字来源为英国20世纪70年代首播的电视喜剧《蒙提·派森的飞行马戏团》(Monty Python’s Flying Circus)。Python 2和Python 3有很大区别,现在Python 2已经停止更新。我们一般情况下介绍的都是Python 3。
Python涵盖了许多有用的库,可以用于数学计算、数据可视化、机器学习、信息安全等各个领域。
Python如今在广大非计算机专业也是一门常见的入门语言。
在Python中, Indent(缩进)是一个非常重要的概念。Python程序员应该养成良好的缩进习惯。
Python Zen
在Python Shell里输入
import this
终端会给出如下的输出:
1 | The Zen of Python, by Tim Peters |
算是个小彩蛋。Coding是浪漫的,不是吗?
Insert User Snippets in
vscode
文件->首选项->用户片段
向对话框中输入python,
在跳出的
python.json
文件里加入下列片段
1
2
3
4
5
6
7
8
9
10
11
12
13
14 {
"Print to console": {
"prefix": "main", //用户片段名
"body": [
"def main():",
" $0", //光标起始位置
"", //空行
"if __name__ == \"__main__\":",
" main()",
""
],
"description": "Log output to console"
}
}保存。
然后在后缀为py的文件中键入main,按tab(或手动/enter也行),便会自动跳出:
1
2
3
4
5 def main():
if __name__ == "__main__":
main()你可以往用户片段中加入各种实用的片段,大大提高编程的效率。
其他语言的配置方法都是类似的。这里不多赘述。
Python Syntaxes & Syntatic Sugar
Identifier & condition statement
在Python中,变量名可以是中文。
a <= x <= b
这样的条件语句,在Python中可以得到C中a <= x && b >= x
的效果Python中的逻辑运算相关的关键字:
and
,or
,true
,false
注意: Python中不存在自带的
switch
语句.
Python的三目运算符
x if condition else y
: 若condition成立,返回x,否则y。实例详见string process - 一道简单的OJ题
string process
字符串切片
s[index]
: 首字符从0开始计,返回第index位的字符。若index为负,则以最后一个字符为-1,以此类推。
s[pos_1 = 0: pos_2 = -1]
: 从pos_1开始,到pos_2前一位结束,0和-1为缺省值。s[:]代表整个字符串。
s[pos_1 = 0: pos_2 = -1: pace = 1]
: pace为步长。每隔pace截取一个字符。若pace为负值,则得到的结果是将s逆向,以终止位置开始,起始位置结束,取pace的绝对值所得到的结果。s[::-1]得到逆向的字符串。
find()
查找子串在原串中的位置,若没有,返回-1。
strip()
去除字符串两边的空格。
lower(), upper()
字面意思,大小写转换。
split(), join()
1 | # 一行高精度 |
三引号
允许定义多行字符串。如:
1 | s = ''' |
打印s,将会保留原格式。
RegExp
1 | # 导入re模块 |
group(), groups() 方法
我们可以使用group(num) 或 groups() 匹配对象函数来获取匹配表达式。
1 | import re |
另一个实例:
1 | import re |
结果:
matchObj.group() : Cats are smarter than dogs
matchObj.group(1) : Cats
matchObj.group(2) : smarterstart(), end(), span() 方法
start() 返回匹配字符串的起始位置;
end() 返回终止位置;
span() 返回一个元组,分别为起始和终止位置。
一道简单OJ题实战
输入格式:”三个字母一个数字”,字母可能是”USD”或”RMB”。输出USD和RMB互换的结果。
输出格式”三个字母一个数字”,数字保留到小数点后两位。
USD和RMB汇率6.78。
两种写法: (本题中所涉及的格式化输出请见本文以下的格式化输出章节。)
1 | # 用正则表达式,仅仅为了熟悉Python的正则 |
x for x in... if...
语句
1 | # 这种写法非常简洁美观 |
输出:
[18, 9]
Overload in Python
Python中不存在原生支持严格意义的函数重载,对函数的每一次重定义都将会覆盖上一个函数。参数类型不同的函数重载只能用逻辑判断来实现。
pass
什么也不做。用作占位语句。等价于C语言的
;
。
Python
generator
&iterable
带有
yield
的函数在Python中为generator
(生成器)。generator
在python中生成一个可迭代(iterable)的对象。
- 对可迭代对象可以进行range-based for (类定义了
__iter__()
方法),和__getitem__()
(不必要)。- 常见的可迭代对象:(1) list、tuple、dict、set、str; (2) generator;
- 判断对象是否可迭代:isinstance(e, collections.Iterable)
链接:
class
& OOPPython的一个基本的类定义如下:
1 | class className(): |
类的attribute利用
.
运算符访问。类似C的结构体。Snippet:
1 | def main(): |
同时我们发现:
p1调用自我介绍函数的时候的三次输出分别为:
1 | My name is LG, age is 10 and I am Female |
可以看出,对
private
类型成员变量修改后不会改变变量的值,也不会有报错信息。对
public
类型成员变量修改后确确实实发生了改变。(原因未知)
格式化输出
在上文片段中,如果将:
1 | # C风格格式化输出 |
替换为:
1 | print(f"My name is {self.name}, age is {self._age} and I am {self.__gender}") |
效果是一样的。Python的格式化输出功能要比原生C, C++要完善不少。
Python中还可以对字符串类型调用format()方法来达到格式化输出的效果。
global
Python中若要使函数体内对变量的改变作用于函数体外,可以在变量前加上
global
关键字。
zip()
1 | def zip(iter1: Iterable[_T1]) |
eval()
将字符串作为一个Python表达式执行。和Php的eval()类似。
1 | a = 1 |
输出:
2
Container
tuple
不可变的序列。
注意:单值元组在定义时要在元素后加一个
,
,如tp = (12,)
,防止语义冲突。
tuple的连接
类似
tp3 = tp1 + tp2
tuple的删除
del tp
more details see: python中del的用法
elem in tp
判断某元素是否在tuple中,返回布尔值。
切片
参见string process一节。规则是类似的。
tuple的相关函数
min(tp)
,max(tp)
,len(tp)
,tuple(seq)
字面意思,很好理解。
tuple是最基本的序列,对tuple可以做的所有操作几乎都可以用在其他序列中。故下文将尽量避免冗余。
list
可以增删查改的序列。用
[]
定义。
list可以用
+
连接,同样,可以用del
删除某一元素。也可以运用in
,find()
等。
方法 作用 append(obj) 向列表最后添加一个元素。 count(obj) 返回obj在列表中出现的次数。 extend(seq) 将seq追加到原列表中。 index(obj) 获取第一个出现的obj的索引。如要获取最后一个, len(list) - list[::-1].index(obj) - 1
是一种方法。insert(index, obj) 将obj插入列表索引index的位置。 pop(index = -1) 删除列表一个元素。默认索引-1。即删除最后一个。并返回该元素的值。 remove(obj) 移除列表中第一个obj。 reverse() 列表反向。 sort(key = None, reverse = False) 排序。
key为函数,指定用元素的哪一项进行比较。可以为lambda函数。
reverse = False代表升序。
sort的原型:sort(*, key: Optional[Callable[[_T], Any]]=…, reverse: bool=…) -> None
set, dict
没什么好说的,跳过了。
more details see:
Inside Python Mechanism
One
1 | c = {} |
输出:
1
2
3 {'a': 1, 'b': 2, 'c': 3, 'd': 4, 'e': 5, 'f': 6}
{'a': 1, 'b': 2, 'c': 3, 'd': 4, 'e': 5, 'f': 6}
{'a': 1, 'b': 2, 'c': 3, 'd': 4, 'e': 5, 'f': 6}
mutable & immutable object
python中的变量间赋值本质上为传引用。而用字面值对变量赋值,根据对象的不同,会产生不同的行为。
我们依据这一性质将对象分为两类:
- 不可变。常见的不可变对象为int, float, str, complex, tuple;
- 可变。常见的可变对象为list和dict.
我们将从下面的代码理解Python的这一特性。
1 | def main(): |
两次的输出结果:
addr of a is: 2347657685584, b is: 2347657685584
addr of a is: 2347657685584, b is: 2347657685424
可见,Python对不可变对象的字面值赋值处理方式是:
开辟一块新的内存,让被改变的对象指向这块内存。其他的对象的内存指向不改变。
而对于可变对象,处理的方式便不同了。
对可变对象的修改不会开辟新的内存空间。但也有特例,如果赋予可变对象一个不可变对象字面值,那么可变对象的仍会开辟新的内存地址。
下面给出一个规律性的总结:
- Python的所有赋值均为传引用。Python优雅的
a, b = b, a
就得益于Python的传引用方式。用C++的方式来理解a, b = b, a的原理:
1
2
3
4 auto pb = &b;
auto pa = &a;
&a = pb;
&b = pa;对于a, b, c = c, a, b的情形,其原理也是一样的。
但不同的是,C++不可以随意修改变量的地址,上述代码无法通过编译,所以Python的底层做法应该不是这样的。
- 不可变对象被初始化的时候,编译器分配一个随机的地址给这个对象。值相同的对象指向同一个地址。当对象值改变,其地址也随之改变。
- 可变对象被初始化的时候,编译器同样会给其分配一个随机地址。值相同的对象未必指向同一个地址。即使给同一对象赋前后相同的值,其地址也会改变。但如果仅对对象的某个属性,或者存储的元素进行修改,对象本身的地址不会发生改变。与C不同的是,Python中任何的容器都不会开辟一段连续的空间,尽管容器存放的元素地址可能改变,但容器本身地址并不会随之改变。
编写一个不可变的类
1 | # 这里直接拷贝上述博文的代码 |
可变与可哈希
Python中,可变对象是不可哈希的,而不可变对象是可哈希的。
体现在字典上:{keys: vals}
键值对的键必须是不可变对象,因为不可变对象可以产生哈希值,而Python的字典底层是用哈希表实现的。(C++中的std::map底层则是红黑树,而std::unordered_map底层是哈希表)。
而对于值则没有要求。
不可变对象的哈希值通过调用
__hash__()
方法来获得,故用户自定义的不可变类必须给出合理的哈希函数重写。