这篇博文主要介绍 Python3 的一些常用语法,因为大部分基础语法在廖雪峰大神的 Python 教程 都有详细的介绍,此文主要介绍一些常用的入门知识,并提供详细知识点的学习链接。

输入&输出

输出

print() 在括号中加上字符串,就可以向屏幕上输出指定的文字,print() 方法支持可变参数,支持同时打印多个值,用逗号隔开,遇到逗号时会输出一个空格。

>>> print('The quick brown fox', 'jumps over', 'the lazy dog')
The quick brown fox jumps over the lazy dog

print() 还可以直接打印表达式的运算结果

>>> print('100 + 200 =', 100 + 200)
100 + 200 = 300

输入

之所以把输入放到输出的后面,是因为就爬虫而言,输入实在很少用,即使有定制的需求,通常也是通过读取配置文件实现。笔者目前唯一用到输入的功能,就是在程序运行结束后等待用户输入确认再退出程序,避免程序运行结束后自动退出,因为自动退出会让用户误以为是闪退。

有需要了解输入部分的请看这里,把下面的代码加到程序最后就可以等待用户确认后再退出程序:

input('enter anything to exit: ')

当然也可以通过线程睡眠达到这个效果,但是笔者认为它即麻烦又不够友好,并不建议使用。

数据类型和变量

Python 基础数据类型包括 int、float、str、bool、None,分别是整形、浮点型、字符串、布尔值、空值。

其中整形和浮点型不多介绍,跟其他语言差别不大。

字符串表示

字符串可以用单引号或双引号括起来,具体使用哪个看个人习惯或团队约定,建议是单个项目使用统一的约定。如果字符串内部既包含 ' 又包含 ",Python 的处理和大部分语言一样,可以在引号的前面加上转义字符 \ 来标识。

如果字符串里面有很多字符都需要转义,就需要加很多 \,为了简化,Python还允许用 r'' 表示 '' 内部的字符串默认不转义:

>>> print('\\\t\\')
\ \
>>> print(r'\\\t\\')
\\\t\\

如果字符串内部有很多换行,用\n写在一行里不好阅读,为了简化,Python允许用 '''...''' 的格式表示多行内容:

print('''line1
line2
line3''')

多行字符串 '''...''' 还可以在前面加上 r 使用。

布尔值

布尔值只有 TrueFalse 两种值,可以用 andornot 对布尔值进行运算操作,这相对于其他语言来说比较奇葩,不过也不是很难接受:

>>> True or True
True
>>> True or False
True
>>> False or False
False
>>> 5 > 3 or 1 > 3
True

布尔值经常用于条件判断:

if age >= 18:
print('adult')
else:
print('teenager')

空值

空值是 Python 里一个特殊的值,用 None 表示。None 不能理解为0,因为0是有意义的,而 None 是一个特殊的空值。

变量

变量名必须是大小写英文、数字和 _ 的组合,且不能用数字开头。

可以把任意数据类型赋值给变量,同一个变量可以反复赋值,而且可以是不同类型的变量。

a = 123 # a是整数
print(a)
a = 'ABC' # a变为字符串
print(a)

这种变量本身类型不固定的语言称之为动态语言,与之对应的是静态语言。静态语言在定义变量时必须指定变量类型,如果赋值的时候类型不匹配,就会报错。

对于动态语言的这种特性是好是坏,笔者无法做出断言,不过建议不要随意修改变量的类型,这会导致出错从概率变大,而且不易维护。

常量

所谓常量就是不能变的变量,比如常用的数学常数π就是一个常量。在Python中,通常用全部大写的变量名表示常量:

PI = 3.14159265359

但事实上PI仍然是一个变量,Python根本没有任何机制保证PI不会被改变,所以,用全部大写的变量名表示常量只是一个习惯上的用法,如果你一定要改变变量PI的值,也没人能拦住你。

字符串操作

编码转换

前文已经提过,在最新的 Python 3 版本中,字符串 str 是以 Unicode 编码的,通过 encode() 方法可以将 str 编码为指定的 bytes,例如:

>>> 'ABC'.encode('ascii')
b'ABC'
>>> '中文'.encode('utf-8')
b'\xe4\xb8\xad\xe6\x96\x87'
>>> '中文'.encode('ascii')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-1: ordinal not in range(128)

反过来,要把 bytes 变为 str,就需要用 decode() 方法,例如:

>>> b'ABC'.decode('ascii')
'ABC'
>>> b'\xe4\xb8\xad\xe6\x96\x87'.decode('utf-8')
'中文'

由于Python源代码也是一个文本文件,所以,当你的源代码中包含中文的时候,在保存源代码时,就需要务必指定保存为UTF-8编码。当Python解释器读取源代码时,为了让它按UTF-8编码读取,我们通常在文件开头写上这两行:

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

第一行注释是为了告诉Linux/OS X系统,这是一个Python可执行程序,Windows系统会忽略这个注释;

第二行注释是为了告诉Python解释器,按照UTF-8编码读取源代码,否则,你在源代码中写的中文输出可能会有乱码。

申明了UTF-8编码并不意味着你的.py文件就是UTF-8编码的,必须并且要确保文本编辑器正在使用 UTF-8 without BOM 编码

长度计算

要计算str包含多少个字符,可以用len()函数,len()函数计算的是str的字符数,如果换成bytes,len()函数就计算字节数:

>>> len(b'ABC')
3
>>> len(b'\xe4\xb8\xad\xe6\x96\x87')
6
>>> len('中文'.encode('utf-8'))
6

格式化

在Python中,采用的格式化方式和C语言是一致的,用 % 实现

>>> 'Hello, %s' % 'world'
'Hello, world'
>>> 'Hi, %s, you have $%d.' % ('Michael', 1000000)
'Hi, Michael, you have $1000000.'

常见的占位符有:

占位符 说明
%d 整数
%f 浮点数
%s 字符串
%x 十六进制整数

其中,格式化整数和浮点数还可以指定是否补0和整数与小数的位数:

>>> '%2d-%02d' % (3, 1)
' 3-01'
>>> '%.2f' % 3.1415926
'3.14'

如果你不太确定应该用什么,%s 永远起作用,它会把任何数据类型转换为字符串:

>>> 'Age: %s. Gender: %s' % (25, True)
'Age: 25. Gender: True'

如果要在字符串里显示 % ,可以用 %% 表示:

>>> 'growth rate: %d %%' % 7
'growth rate: 7 %'

匹配和搜索文本

>>> text = 'yeah, but no, but yeah, but no, but yeah'
>>> # Exact match
... text == 'yeah'
False
>>> # Match at start or end
... text.startswith('yeah')
True
>>> text.endswith('no')
False
>>> # Search for the location of the first occurrence
... text.find('no')
10

字符串拼凑

str 支持 +、+= 运算符:

>>> s1 = 'a'
>>> s2 = 'b'
>>> s1 + s2
'ab'
>>> s1 += s2
>>> s1
'ab'

字符串分割、合并

>>> s = 'my name is python'
>>> arr = s.split(' ')
['my', 'name', 'is', 'python']
>>> '-'.join(arr)
'my-name-is-python'

字符串替换

>>> text = 'yeah, but no, but yeah, but no, but yeah'
>>> text.replace('yeah', 'yep')
'yep, but no, but yep, but no, but yep'

去除两侧字符

>>> s = ' test '
>>> s.strip(' ')
'test'
>>> s.lstrip(' ')
'test '
>>> s.rstrip(' ')
' test'
>>> s.rstrip('t ')
' tes'

正则表达式

正则表达式的使用见这个教程,需要注意的是正则规则使用 r'' 格式的字符串,可以减少大量的转义字符。

这边只介绍简单的字符提取,提取结果是 None or 字符串数组:

result = re.findall(r'(?<=uploads)/.*', url)
if result and len(result) > 0:
result = result[0]

list & tuple

list

list是一种有序的集合,可以随时添加和删除其中的元素。

初始化和检索

>>> classmates = ['Michael', 'Bob', 'Tracy']
>>> classmates
['Michael', 'Bob', 'Tracy']
>>> len(classmates)
3
>>> classmates[0]
'Michael'
>>> classmates[-1]
'Tracy'

下标为负数时表示逆向检索,下标越界会导致崩溃

插入、删除、替换元素

>>> classmates.append('Adam')
>>> classmates
['Michael', 'Bob', 'Tracy', 'Adam']

>>> classmates.insert(1, 'Jack')
>>> classmates
['Michael', 'Jack', 'Bob', 'Tracy', 'Adam']

>>> classmates.pop()
'Adam'
>>> classmates
['Michael', 'Jack', 'Bob', 'Tracy']

>>> classmates.pop(1)
'Jack'
>>> classmates
['Michael', 'Bob', 'Tracy']

>>> classmates[1] = 'Sarah'
>>> classmates
['Michael', 'Sarah', 'Tracy']

tuple

tuple和list非常类似,但是tuple一旦初始化就不能修改。

>>> classmates = ('Michael', 'Bob', 'Tracy')

不可变的tuple有什么意义?因为tuple不可变,所以代码更安全。如果可能,能用tuple代替list就尽量用tuple。

一个空的tuple:

>>> t = ()
>>> t
()

一个只有1个元素的tuple:

>>> t = (1,)
>>> t
(1,)

Why?

这是因为括号 () 既可以表示 tuple,又可以表示数学公式中的小括号,这会导致 (1) 产生歧义,因此,Python 规定,(1) 按小括号进行计算,表示只有一个元素的 tuple 时,加上逗号来消除歧义。

dict & set

dict

Python内置了字典 dict 的支持,dict 全称 dictionary,在其他语言中也称为 map,使用键-值(key-value)存储,具有极快的查找速度。

dict的key必须是不可变对象,这是因为dict根据key来计算value的存储位置,如果每次计算相同的key得出的结果不同,那dict内部就完全混乱了。这个通过key计算位置的算法称为哈希算法(Hash)。要保证hash的正确性,作为key的对象就不能变。在Python中,字符串、整数等都是不可变的,因此,可以放心地作为key。

初始化和检索

>>> d = {'Michael': 95, 'Bob': 75, 'Tracy': 85}
>>> d['Michael']
95

检索时如果 key 不存在就会报错:

>>> d['Thomas']
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
KeyError: 'Thomas'

有两个方案可以解决这个问题,一个是取值之前使用 in 判断 key 是否存在,另一个是用 get() 方法取值,其中 get() 方法在查找失败时,默认返回 None, 也可以指定 Value。

>>> 'Thomas' in d
False

>>> d.get('Thomas')
>>> d.get('Thomas', -1)
-1

set

set和dict类似,也是一组key的集合,但不存储value。由于key不能重复,所以,在set中,没有重复的key,重复元素在set中自动被过滤。

初始化、检索set

>>> s = set([1, 1, 2, 2, 3, 3])
>>> s
{1, 2, 3}

添加、删除元素

>>> s.add(4)
>>> s
{1, 2, 3, 4}
>>> s.add(4)
>>> s
{1, 2, 3, 4}

>>> s.remove(4)
>>> s
{1, 2, 3}

交集、并集

>>> s1 = set([1, 2, 3])
>>> s2 = set([2, 3, 4])
>>> s1 & s2
{2, 3}
>>> s1 | s2
{1, 2, 3, 4}

注意:返回None的时候Python的交互式命令行不显示结果。

插入、删除、替换元素

>>> d['Adam'] = 67
>>> d['Adam']
67

>>> d.pop('Bob')
75
>>> d
{'Michael': 95, 'Tracy': 85}

>>> d['Jack'] = 90
>>> d['Jack']
90
>>> d['Jack'] = 88
>>> d['Jack']
88

条件判断

if 语句的形式:

if <x1>:
<执行1>
elif <x2>:
<执行2>
elif <x3>:
<执行3>
else:
<执行4>

只要x是非零数值、非空字符串、非空list、非空tuple、非空dict、非空set、非None等,就判断为True,否则为False。

>>> if []:
... print(True)
... else:
... print(False)
False
>>> if None:
... print('None is True')
... else:
... print('None is False')
...
None is False
>>> if 0:
... print('0 is True')
... else:
... print('0 is False')
...
0 is False

循环

for x in ... 循环

sum = 0
for x in [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]:
sum = sum + x
print(sum)

range() 生成序列整数

arr = []
for x in range(2, 10):
arr.append(x)
print('arr =', arr)
# arr = [2, 3, 4, 5, 6, 7, 8, 9]

range() 生成 list

arr2 = list(range(1, 10, 2))
print('arr2 =', arr2)
# arr2 = [1, 3, 5, 7, 9]

while 循环

sum = 0
n = 99
while n > 0:
sum = sum + n
n = n - 2
print(sum)

高级特性

切片

>>> L = ['Michael', 'Sarah', 'Tracy', 'Bob', 'Jack']
# 从0到2
>>> L[0:3]
['Michael', 'Sarah', 'Tracy']
# 从2到3
>>> L[2:4]
['Tracy', 'Bob']
# 从0到2
>>> L[:3]
['Michael', 'Sarah', 'Tracy']
# 从2到结尾
>>> L[2:]
['Tracy', 'Bob', 'Jack']
# 下标-1表示最后一个索引值
>>> L[0:-1]
['Michael', 'Sarah', 'Tracy', 'Bob']
>>> L = list(range(100))
>>> L
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99]
# 前10个数,每两个取一个
>>> L[:10:2]
[0, 2, 4, 6, 8]
# 所有数,每5个取一个
>>> L[::5]
[0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65, 70, 75, 80, 85, 90, 95]

tuple 和 str 也可以进行切片操作,操作结果分别是 tuple 和 str。

迭代

list

迭代 value

>>> L = ['Michael', 'Sarah', 'Tracy']
>>> for x in L:
... print(x)
...
Michael
Sarah
Tracy

同时迭代 index, value

>>> for i, value in enumerate(['A', 'B', 'C']):
... print(i, value)
...
0 A
1 B
2 C

dict

迭代 key

>>> d = {'a': 1, 'b': 2, 'c': 3}
>>> for key in d:
... print(key)
a
c
b

迭代 value

>>> d = {'a': 1, 'b': 2, 'c': 3}
>>> for v in d.values():
... print(v)
2
1
3

同时迭代 key, value

>>> d = {'a': 1, 'b': 2, 'c': 3}
>>> for k, v in d.items():
... print(k, v)
b 2
a 1
c 3

当我们使用for循环时,只要作用于一个可迭代对象,for循环就可以正常运行,我们不太关心该对象究竟是list还是其他数据类型。

那么,如何判断一个对象是可迭代对象呢?方法是通过collections模块的Iterable类型判断:

>>> from collections import Iterable
>>> isinstance('abc', Iterable) # str是否可迭代
True
>>> isinstance([1,2,3], Iterable) # list是否可迭代
True
>>> isinstance(123, Iterable) # 整数是否可迭代
False

所以 str 也是可迭代的。

结尾

最初列大纲时,是没有进阶知识这一文的,因为差不多都是基础知识,但是写着写着发现 demo 太多,导致篇幅有点大,决定分出去,又不太想写个一二三,就标题党一回,叫进阶知识了,虽然不是多高深,但相对此文的基础语法,还是有一定提高的。