人生苦短我用Python,本文助你快速入门

友情提示:本文针对的是非编程零基础的朋友,可以帮助我们快速了解Python语法,接着就可以快乐的投入到实战环节了。如果是零基础,还是老老实实看书最为稳妥。

前言

​ 偶然在知乎上看到了一些好玩的Python项目(学 Python 都用来干嘛的?),让我对Python产生了些许兴趣。距离北漂实习还有两个月时间,正好可以在这段空闲时间里学一学。如果能做出些小工具,说不定对工作还有帮助,何乐而不为呢?

​ 关于环境的安装和IDE就不多说了,网上有很多教程。这里贴出一篇博客,大家按里面的步骤安装就行:VSCode搭建Python开发环境。使用VSCode主要是因为免费,而且有大量插件可以下载,大家可以尽情的定制自己的IDE。如果曾经没有使用过VSCode,最好多了解下哪些必须的插件,优化自己的Coding体验。比如:Python插件推荐

​ 环境搭建好后,就可以愉快地敲代码了。VSCode需要自己创建Python文件,以.py为后缀。Ctrl+F5运行程序,F5调试程序。

Python基础

注释

​ 单行注释:#

​ 多行注释:”’ (三个英文单引号开头,三个英文单引号结尾)

# 这是单行注释

'''
这是多行注释
'''

变量

​ Python的变量定义不需要显式指明数据类型,直接【变量名=值】即可。注意变量名分大小写,如Name和name不是同一个变量。

name = "小王"
print(name) # 输出 小王

数据类型

​ Python提供6种基础的数据类型:数字类型(number)、字符串类型(string)、列表(list)、元组(tuple)、字典(dictionary)、集合(set)。其中数字类型还包括三种数值类型:整型(int)、浮点型(float)、复数类型(complex)。

​ 列表、元组那些我们留在容器那一节里面讲,先看看数字类型。

浮点型

​ 浮点型表示小数,我们创建一个浮点型变量,再通过type函数看一看它的类型:

pi = 3.1415926
print(type(pi)) # 输出<class 'float'>

int整数型就不说了,其为Integer的缩写。

复数类型

​ 复数类型,所谓复数就是我们中学学的,实数+虚数,比如:

x = 10+1.2j # 虚数以j或J结尾
print(type(x)) # 输出<class 'complex'>

​ 刚开始接触复数时,很纳闷为啥会有这种类型,到底有啥实际作用,遂百度了一番:

mzy0324:微电子方面的运算基本全部都是复数运算。

hilevel:至少复数用来计算向量的旋转要比矩阵方便多了。科学计算和物理应该会用得到吧。PS:我经常把Python当带编程功能的计算器用,用来调试纯粹的数学算法挺方便的。

morris88:Python 的一大应用领域,主要是科学计算,主要用于太空宇航、银行等。

​ 联想到Python平时在算法、科学研究等领域应用颇多,所以也就明白了,只是自己没使用的需求而已。

字符串

​ 字符串类型的变量定义用一对双引号或者单引号括起来。如:

x = "Hello Python"
y = 'Hello Python'
print(x,y) # 输出Hello Python Hello Python

​ 字符串内置函数:

函数 作用
find(str[,start,end]) 在字符串中查找子串str,可选参数start和end可以限定范围
count(str[,start,end]) 在字符串中统计子串str的个数,可选参数start和end可以限定范围
replace(old,new[,count]) 在字符串中用new子串替换old子串,可选参数count代表替换个数,默认全部替换
split(sep[,maxsplit]) 用指定分隔符sep分割字符,返回一个列表,可选参数maxsplit代表分割几次,默认全部
upper()、lower() 转换大小写
join(序列) 把序列中的元素用指定字符隔开并生成一个字符串。
startwith(prefix[,start,end]) 判断字符串中是否以prefix开头,返回bool类型。还有一个endwith,判断结尾的。
strip([,str]) 去掉字符串开头和结尾的空白字符(包括\n、\t这些),可选参数代表可以去掉指定字符

布尔类型

​ 顺便再说一下布尔类型,不过与Java不同的是,布尔类型的True和False,首字母必须大写:

x = True
print(type(x)) # 输出<class 'bool'>

类型转换

​ 说完几个基本的数据类型,不免要提到类型转换。Python内置一些类型转换的函数:

函数名 作用
int(x) 将x转换为整型(小数转整型会去掉小数部分)
float(x) 将x转换为浮点型
str(x) 将x转换为字符串
tuple(x) 将x转换为元组
list(x) 将x转换为列表
set(x) 将x转换为集合,并去重

输入与输出

​ 输入函数为input。input函数返回用户输入的信息为字符串类型。所以如果你输入的是数字类型,记得类型转换

x = input("请输入数字")
print(type(x),x) # 输出<class 'str'> 10

​ 输出前面已经演示了很多次了,函数为print,可以直接输出变量与值。一次输出多个变量可以用逗号隔开,就想上面的演示一样,既要输出类型,也要输出值。不换行输出,可以在print函数里加上end=””这个参数,因为print默认end=”\n”,\n就是换行的意思。如果想输出特殊字符,可能需要用到转义字符:\。

x = 10
y = 20
print(x,y,end="") # 输出10 20 加上end="" 不换行
print("Hello \\n Python") # 输出 Hello \n Python

​ 在输出时,还可以格式化输出内容:%s代表字符串格式、%d代表整型、%f代表浮点型

z = 1.2
print("%f"%z) # 输出 1.200000

​ 除了格式化,%d等还可以当作占位符:

name = "小明"
age = 18
print("姓名:%s,年龄:%d"%(name,age)) # 姓名:小明,年龄:18

​ 如果你闲这个占位符麻烦,还可以使用format函数,占位符只用写一对{}:

print("姓名:{},年龄:{}".format(name,age)) # 姓名:小明,年龄:18

运算符

算术运算符

​ 除了加减乘除,还有幂(**)、取模(%)、取整(//)

x = 3 ** 2 # x=9 即3的2次方 
y = 5 % 3 # y=2 即5除以3余2
z = 5 // 2 # z=2 即5除以2,整数部分为2

比较运算符

​ 和其他常用编程语言基本一模一样,不等于(!=)、大于等于(>=)、等于(==)。

赋值运算符

​ Python也支持+=、*=等形式的赋值运算。除此之外,当然也支持前面说到的幂、取模等算术运算符,如取整并赋值(//=)、取模并赋值(%=)。

x = 10
x %= 3
print(x) # 输出1 ,x%=3 意为 x = x%3

逻辑运算符

​ 非(not)、与(and)、或(or)

x = True
print(not x) # 输出 False

if、while、for

​ 这三个和其他编程语言基本没差,就是写法上有点区别。首先没了大括号,条件语句后以冒号开头;代码快有严格的缩进要求,因为没了大括号,缩进就是条件语句判断自己代码快范围的依据。其他的基本一样,比如continue跳过当次循环,break跳出整个循环体。下面看三个简单的例子就明白了:

a = 10
# if或else后面是冒号,代码块还需要缩进
if a >= 10:
    print("你好啊老大")
else:
    print("滚蛋")
    
# 同样的while后面也需要冒号,代码块必须缩进。(Python没有num++,得写成num+=1)
# print想不换行打印,最后得加个end="",因为默认有一个end="\n"
# " "*(j-i),代表j-i个空格
i = 1
j = 4
while i <= j:
    print(" "*(j-i), end="")
    n = 1
    while n <= 2*i-1:
        print("*", end="")
        n += 1
    print("")
    i += 1

# 语法:for 变量 in 序列 ,还没讲序列,暂时用range表示,代表1-21的序列
# continue略过当次循环,break跳出整个循环
for i in range(1, 21):
    if i % 2 == 0:
        if(i % 10 == 0):
            continue
        if(i >= 15):
            break
        print(i)

容器

列表

​ 列表使用一对[]定义,每个元素用逗号隔开,元素类型不强求相同,通过索引获取列表元素。具体的我们看下面的代码:

info_list = ["小红", 18, "男"] #可以不是同一类型
info_list[2] = "女" # 修改指定索引位置的元素
del info_list[1] # 删除指定索引位置的元素
info_list.remove("女") # 删除列表中指定的值
for att in info_list:   # 遍历元素
    print(att)

​ 上面的示例代码演示了部分列表的用法,下面再列出一些其他的常用函数或语法:

函数或语法 作用
list.append(element) 向列表list结尾添加元素(这个元素也可以是个列表)
list.insert(index,element) 向列表指定位置添加元素
list.extend(new_list) 向列表list添加new_list的所有元素
list.pop([,index]) 弹出最后一个元素,可选参数index,弹出指定位置元素
list.sort([,reverse=True]) 对列表排序,可选参数reverse=True表示降序
list[start:end] 对列表分片,start和end代表起始结束索引
list1+list2 拼接两个列表

元组

​ 元组用一对()定义。元组也是有序的,它和列表的区别就是,列表可以修改元素,元组不行。正是因为这个特点,元组占用的内存也比列表小。

name_list=("小红","小王")

字典

​ 字典使用一对{}定义,元素是键值对。用法示例如下:

user_info_dict = {"name": "小王", "age": "18", "gender": "男"}
name = user_info_dict["name"] # 直接用key获取value
age = user_info_dict.get("age") # 也可以用get(key)获取value
user_info_dict["tel"] = "13866663333" # 当key不存在,就是往字典添加键值对,如果存在就是修改value
del user_info_dict["tel"] # 删除指定键值对

​ 以上就是常用语法和函数。字典也可以遍历,只是遍历时,需要指定遍历的是key还是value,比如:

for k in dict.keys(): # 遍历所有key
for v in dict.values(): # 遍历所有value
for item in dict.items(): # 也可以直接遍历键值对

集合

​ 集合是无序的,也用一对{}定义,但不是键值对了,是单独且不重复的元素。部分用法如下:

user_id_set = {"1111","22222","3333"} # 元素不重复
print(type(user_id_set)) # 输出<class 'set'>
# 除了直接用{}定义,还可以用set函数传入一个序列,其会为list去重,并返回一个集合(如果是字符串,字符串会被拆成字符)
new_user_id_set = set(list) 

​ 上面演示了部分用法,下面我们用一个表格展示一些常用的函数或语法:

函数或语法 作用
element in set 判断元素是否在集合中,返回布尔类型
element not in set 判断元素是否不在集合中
set.add(element) 向集合添加元素
set.update(list,…..) 将序列中的每个元素去重并添加到集合中,如果有多个序列,用逗号隔开
set.remove(element) 删除指定元素,如果元素不存在就会报错
set.discard(element) 删除指定元素,如果元素不存在也不会报错
set.pop() 随机删除集合中的元素,并返回被删除的元素
set1 & set2 或set1 intersection set2 求两个集合的交集,两种用法结果一样
set1 | set2 或set1 union set2 求两个集合的并集
set1 – set2 或set1.difference(set2) 求两个集合的差集,注意顺序。set1-set2代表set1有set2没有的元素

函数

函数的定义

​ Python中函数用def定义,格式为:

def function_name(参数列表): # 参数可为空,多个参数用逗号隔开
	函数体
	return 返回值 #可选

# 函数的调用
function_name(参数列表)

缺省参数

​ 和循环体一样的,因为没有了大括号,所以缩进是严格要求的。除了上面那种比较常见的格式,Python函数的参数中,还有一种缺省参数,即带有默认值的参数。调用带有缺省参数的函数时,可以不用传入缺省参数的值,如果传入了缺省参数的值,则会使用传入的值。

def num_add(x,y=10): # y为缺省函数,如果调用这个函数只传入了x的值,那么y默认为10

命名参数

​ 一般情况下,调用函数传入实参时,都会遵循参数列表的顺序。而命名参数的意思就是,调用函数时,通过参数名传入实参,这样可以不用按照参数定义的顺序传入实参。

def num_add(x, y):
    print("x:{},y:{}".format(x, y))
    return x+y
# 输出:
# x:10,y:5
# 15
print(num_add(y=5, x=10))

不定长参数

​ 不定长参数可以接收任意多个参数,Python中有两种方法接收:1.在参数前加一个*,传入的参数会放到元组里;2.在参数前加两个**,代表接收的是键值对形式的参数。

# 一个*
def eachNum(*args):
    print(type(args))
    for num in args:
        print(num)
# 输出:
# <class 'tuple'>‘
# (1, 2, 3, 4, 5)
eachNum(1,2,3,4,5)

## 两个**。这个other是想告诉你,在使用不定长参数时,也可以搭配普通的参数
def user_info(other,**info):
    print(type(info))
    print("其他信息:{}".format(other))
    for key in info.keys():
        print("{} : {}".format(key,info[key]))
# 传入参数时,不用像定义字典一样,加个大括号再添加键值对,直接当命名参数传入即可
# 输出:
# <class 'dict'>
# 其他信息:管理员
# 略...
user_info("管理员",name="赵四",age=18,gender="男")

​ 上面示例代码中的注释说到了,当使用不定长参数时,不用像字典或者元组的定义那样,直接传入参数即可。但有时候,可能会遇到想把字典、元组等容器中的元素传入到不定长参数的函数中,这个时候就需要用到拆包了。

​ 所谓拆包,其实就是在传入参数时,在容器前面加上一个或两个*。还是以上面的user_info函数为例:

user_info_dict={"name":"赵四","age":18,"gender":"男"}
user_info("管理员",**user_info_dict) # 效果和上面一样

​ 注意,如果接收方的不定长参数只用了一个 * 定义,那么传入实参时,也只能用一个 *。

匿名函数

​ 匿名函数,即没有名字的函数。在定义匿名函数时,既不需要名称,也不需要def关键字。语法如下:

lambda 参数列表: 表达式

​ 多个参数用逗号隔开,匿名函数会自动把表达式的结果return。在使用时,一般会用一个变量接收匿名函数,或者直接把匿名函数当参数传入。

sum = lambda x,y : x+y
print(sum(1,2)) # 输出3

闭包和装饰器

​ 在Python中,函数内还可以定义函数,外面这个函数我们就称为外部函数,里面的函数我们就称为内部函数。而外部函数的返回值是内部函数的引用,这种表达方式就是闭包。内部函数可以调用外部函数的变量,我们看一个示例:

# 外部函数
def sum_closure(x):
    # 内部函数
    def sum_inner(y):
        return x+y
    return sum_inner # 返回内部函数
# 获取了内部函数
var1 = sum_closure(1)
print(var1) # 输出<function sum_closure.<locals>.sum_inner at 0x000001D82900E0D0>,是个函数类型
print(var1(2)) # 输出3

​ 说完闭包的用法,接着了解一下装饰器。不知道大家了解过AOP没,即面向切面编程。说人话就是在目标函数前后加上一些公共函数,比如记录日志、权限判断等。Python中当然也提供了实现切面编程的方法,那就是装饰器。装饰器和闭包一起,可以很灵活的实现类似功能,下面看示例:

import datetime #如果没有这个包,在终端里输入pip3 install datetime
# 外部函数,其参数是目标函数
def log(func):
    #内部函数,参数得和目标函数一致。也可以使用不定长参数,进一步提升程序灵活性
    def do(x, y):
        # 假装记录日志,执行切面函数。(第一次datetime是模块、第二个是类、now是方法。在下一节讲到模块)
        print("时间:{}".format(datetime.datetime.now()))
        print("记录日志")
        # 执行目标函数
        func(x, y)
    return do

# @就是装饰器的语法糖,log外部函数
@ log
def something(x, y):
    print(x+y)
    
# 调用目标函数
# 输出:
# 时间:2021-01-06 16:17:00.677198
# 记录日志
# 30
something(10, 20)

​ 函数相关的就说到这里了,其实还有一些知识没说到,比如变量的作用域、返回值等。这部分内容和其他语言几乎无异,一点区别无非就是返回值不用在乎类型了,毕竟定义函数时也没指定函数返回值类型,这一点各位老司机应该也会想到。

包和模块

​ Python中包与普通文件夹的区别就是,包内要创建一个__init__.py文件,来标识它是一个包。这个文件可以是空白的,也可以定义一些初始化操作。当其他包下的模块调用本包下的模块时,会自动的执行__init__.py文件的内容

模块

​ 一个Python文件就是一个模块,不同包下的模块可以重名,在使用的时候以“包名.模块名”区别。导入其他模块用import关键字,前面的示例代码中也演示过一次。导入多个模块可以用逗号隔开,也可以直接分开写。除了导入整个模块,还可以导入模块中指定的函数或类:

from model_name import func_name(or class_name)

​ 导入函数或类后,就不要使用模块名了,直接调用导入的类或函数即可

面向对象

类和对象

​ Python是一种面向对象的解释型编程语言。面向对象的关键就在于类和对象。Python中类的定义用class关键字,如下:

class 类名:
	def 方法名(self[,参数列表])
	...

​ 定义在类里面的函数叫做方法,只是与类外部的函数做个区分,不用在意叫法。类里面的方法,参数列表中会有一个默认的参数,表示当前对象,你可以当作Java中的this。因为一个类可以创建多个对象,有了self,Python就知道自己在操作哪个对象了。我们在调用这个方法时,不需要手动传入self。示例代码:

class Demo:
    def do(self):
        print(self)
# 创建两个Demmo类型的对象
demo1=Demo()
demo1.do() # 输出<__main__.Demo object at 0x0000019C78106FA0>
demo2=Demo() 
demo2.do() # 输出<__main__.Demo object at 0x0000019C77FE8640>
print(type(demo1)) # <class '__main__.Demo'>

构造方法

​ 构造方法的作用是在创建一个类的对象时,对对象进行初始化操作。Python中类的构造方法的名称是__init__(两边分别两个下划线)。在创建对象时,__init__方法自动执行。和普通方法一样的,如果你想自定义构造方法,也要接收self参数。示例代码:

class Demo:
    # 构造方法,还可以传入其他参数化
    def __init__(self,var1,var2):
        # 把参数设置到当前对象上,即使类中没有属性也可以设置
        self.var1=var1
        self.var2=var2
        print("初始化完成")
    def do(self):
        print("Working...")
# 通过构造方法传入实参
demo1=Demo(66,77)
demo1.do()
# 通过当前对象,获取刚刚设置的参数
print(demo1.var1)
print(demo1.var2)

访问权限

​ Java或C#中有好几种访问权限,在Python中,属性和方法前添加两个下划线即为私有,反之就是共公有。具有私有访问权限的属性和方法,只能在类的内部方法,外部无法访问。和其他语言一样,私有的目的是为了保证属性的准确性和安全性,示例代码如下:

class Demo:
    # 为了方便理解,我们显示的设置一个私有属性
    __num = 10
    # 公有的操作方法,里面加上判断,保证数据的准确性
    def do(self, temp):
        if temp > 10:
            self.__set(temp)
	# 私有的设置方法,不让外部直接设置属性
    def __set(self, temp):
        self.__num = temp
	# 公有的get方法
    def get(self):
        print(self.__num)

demo1 = Demo()
demo1.do(11)
demo1.get() # 输出 11

​ 一堆self.刚开始看时还有点晕乎,把它当作this就好。

继承

​ 继承是面向对象编程里另一大利器,好处之一就是代码重用。子类只能继承父类的公有属性和方法,Python的语法如下:

class SonClass(FatherClass):

​ 当我们创建一个SonClass对象时,直接可以用该对象调用FatherClass的公有方法。Python还支持多继承,如果是多继承就在小括号里把父类用逗号隔开。

​ 如果想在子类里面调用父类的方法,一般有两种方式:1.父类名.方法名(self[,参数列表])。此时的self是子类的self,且需要显示传入;2.super().方法名()。第二种方式因为没有指定父类,所以在多继承的情况下,如果调用了这些父类中同名的方法,Python实际会执行小括号里写在前面的父类中的方法。

​ 如果子类定义了与父类同名的方法,子类的方法就会覆盖父类的方法,这就是重写

异常处理

捕获异常

​ 捕获异常的语法如下:

try: 
    代码快 # 可能发生异常的代码
except (异常类型,...) as err: # 多个异常类型用逗号隔开,如果只有一个异常类型可以不要小括号。err是取的别名
    异常处理 
finally:
    代码快 # 无论如何都会执行

​ 在try代码块中,错误代码之后的代码是不会执行的,但不会影响到try … except之外的代码。看个示例代码:

try:
    open("123.txt") #打开不存在的文件,发生异常
    print("hi") # 这行代码不会执行
except FileNotFoundError as err:
    print("发生异常:{}".format(err)) # 异常处理

print("我是try except之外的代码") #正常执行

​ 虽然上面的内容和其他语言相差不大,但是刚刚接触Python鬼知道有哪些异常类型,有没有类似Java的Exception异常类型呢?肯定是有的。Python同样提供了Exception异常类型来捕获全部异常。

​ 那如果发生异常的代码没有用try except捕获呢?这种情况要么直接报错,程序停止运行。要么会被外部的try except捕获到,也就是说异常是可以传递的。比如func1发生异常没有捕获,func2调用了func1并用了try except,那么func1的异常会被传递到func2这里。是不是和Java的throws差不多?

抛出异常

​ Python中抛出异常的关键字是raise,其作用和Java的throw new差不多。示例代码如下:

def do(x):
    if(x>3): # 如果大于3就抛出异常
        raise Exception("不能大于3") # 抛出异常,如果你知道具体的异常最好,后面的小括号可以写上异常信息
    else:
        print(x)

try:
    do(4)
except Exception as err:
    print("发生异常:{}".format(err)) # 输出 发生异常:不能大于3

文件操作

读写文件

​ 想要操作一个文件,首先得打开它。Python中有个内置的函数:open。使用open打开文件可以有三种模式,分别为:只读(默认的模式,只能读取文件内容,r表示)、只写(会覆盖原文本内容,w表示)、追加(新内容追加到末尾,a表示)。示例如下:

f = open("text.txt","a") # 用追加的方式获取文件对象

​ 因为text.txt和代码在同一目录所以只写了文件名,如果不在同一目录需要写好相对路径或绝对路径。

​ 获取到文件对象后,接下来就可以操作了,反正就是些API,直接看示例:

f = open("text.txt","a",encoding="utf-8") # 以追加的方式打开文件,并设置编码方式,因为接下来要写入中文
f.write("234567\n") # 写入数据,最后的\n是换行符,实现换行
f.writelines(["张三\n","赵四\n","王五\n"]) # write只能写一个字符串,writelines可以写入一列表的字符串
f.close() # 操作完记得关闭

​ 以上是写文件的两个方法。最后记得关闭文件,因为操作系统会把写入的内容缓存起来,万一系统崩溃,写入的数据就会丢失。虽然程序执行完文件会自动关闭,但是实际项目中,肯定不止这点代码。Python也很贴心,防止我们忘了close,提供了一种安全打开文件的方式,语法是 with open() as 别名:,示例如下

with open("test.txt","w") as f: # 安全打开文件,不需要close。
    f.write("123")

​ 写完了,该读一读了。示例如下:

f = open("text.txt","r",encoding="utf-8")
data = f.read() # read会一次性读出所有内容
print(data)
f.close()

​ 除了一次性读取完,还可以按行的方式返回全部内容,并用一个列表装起来,这样我们就可以进行遍历了。方法是readlines,示例如下:

f = open("text.txt","r",encoding="utf-8")
lines = f.readlines() # lines是个列表
for line in lines:
    print(line)
f.close()

文件管理

​ 在操作文件的时候,肯定不止读写这么简单,可能还会涉及文件的删除、重命名、创建等等。在用Python的函数操作文件之前,需要导入os模式:import os 。下面简单的演示一下重命名的函数,其他的函数我们以表格的形式展现。

import os
os.rename("text.txt","123.txt") # 把text.txt改名为123.txt
函数 作用
os.remove(path) 删除指定文件
os.mkdir(path) 在指定路径下创建新文件
os.getcwd() 获取程序运行的绝对路径
os.listdir(path) 获取指定路径下的文件列表,包含文件和文件夹
os.redir(path) 删除指定路径下的空文件夹(如果不是空文件夹就会报错)

操作JSON

​ 学了前面的容器,会发现JSON的格式和Python的字典有点像,都是键值对形式的。虽然格式很像,但还是有点小区别,比如:Python的元组和列表在JSON中都是列表、Python的True和Flase会被转换成小写、空类型None会被转换成null。下面我们来看一些具体的函数把。

​ 在Python中操作JSON格式的数据需要导入json模块。同样的,我这里只演示一个函数,其他常用的用表格列出来。

import json
user_info={"name":"张三","age":18,"gender":"男","hobby":("唱歌","跳舞","打篮球"),"other":None} # 创建一个字典
json_str=json.dumps(user_info,ensure_ascii=False) # dumps函数会把字典转换为json字符串
# 输出 {"name": "张三", "age": 18, "gender": "男", "hobby": ["唱歌", "跳舞", "打篮球"], "other": null}
print(json_str)

​ 需要注意如果数据存在中文,需要在dumps函数加上ensure_ascii=False

函数 作用
json.loads(json_str) 把json字符串转换为Python数据结构
json.dump(user_info,file) 把Python数据写入到json文件,要先获取文件,那个file就是文件对象
json.load(file) 把json文件中的数据转为成Python数据结构,同样需要获取文件

​ 关于JSON的操作就说这些。通用的数据格式不止JSON一种,比如还有xml、csv等。为了节约篇幅,就不再赘述了,大家可以根据自己的需求查对应的API即可。

正则表达式

​ 最后一节讲正则表达式,一是因为这也算个基础知识,在很多地方都有可能用到。二是因为后面的爬虫实战,肯定会用到正则表达式来解析各种数据。

​ Python中内置了re模块来处理正常表达式,有了这个模块我们就可以很方便的对字符串进行各种规则匹配检查。不过正则表达式真正难的是表达式的书写,函数主要就一个:re.match(pattern,string),其中pattren就是正则表达式,stirng就是待匹配字符串。如果匹配成功就会返回一个Match对象,否则就返回None。匹配是从左往右,如果不匹配就直接返回None,不会接着匹配下去。示例如下:

import re
res=re.match("asd","asdabcqwe") # 匹配字符串中是否有asd(如果asd不在开头就会返回None)
print(res) # 输出 <re.Match object; span=(0, 3), match='asd'>
print(res.group()) # 输出 asd 如果想获取匹配的子字符就用这个函数

​ 秉着帮人帮到底的精神,下面就简单的介绍下正则表达式的一些规则。

单字符匹配

​ 单字符匹配,顾名思义就是匹配一个字符。除了直接使用某个具体的字符,还可以使用以下符号来进行匹配:

符号 作用
. 匹配除”\n“以外的任意单个字符
\d 匹配0-9之间的一个数字,等价于[0-9]
\D 匹配一个非数字字符,等价于[^0-9]
\s 匹配任意空白字符,如空格、\t、\n等
\S 匹配任意非空白字符
\w 匹配单词字符,包括字母、数字、下划线
\W 匹配非单词字符
[] 匹配[]中列举的字符,比如[abc],只要出现这三个字母中的一个即可匹配

​ 以防有的朋友从未接触过正则表达式,不知道怎么用,下面我来做个简答的演示。假如我想匹配三个字符:第一个是数字、第二个是空格、第三个是字母,一起来看看怎么写这个正则表达式吧:

import re
pattern = "\d\s\w" # \d匹配数字、\s匹配空格、\w匹配字母(切记是从左往右依次匹配的,只要有一个字符匹配不上就直接返回None)
string = "2 z你好"
res=re.match(pattern,string)
print(res.group()) # 输出:2 z

​ 看到这你可能会想,非得一个个字符匹配,那多麻烦啊,有没有更灵活的规则?当然有了,接着看。

数量表示

​ 如果我们只想匹配字母,但不限制有多少个,该怎么写呢?看下面的表格就知道了:

符号 作用
* 匹配一个字符出现0次或多次
+ 匹配一个字符至少出现一次,等价于{,1}
? 匹配一个字符出现0次或1次,等价于{1,2}
{m} 匹配一个字符出现m次
{m,} 匹配一个字符至少出现m次
{m,n} 匹配一个字符出现m到n次

​ 数量匹配的符号后面如果加上?就会尽可能少的去匹配字符,在Python里面叫非贪婪模式,反之默认的就是贪婪模式。比如{m,}会尽可能多的去匹配字符,而{m,}?在满足至少有m个的情况下尽可能少的去匹配字符。其他的同理。

​ 来看一个例子,我想匹配开头是任意个小写字母,接着是1到5个2-6的数字,最后是至少一个空格:

import re
pat = r"[a-z]*[2-6]{1,5}\s+"
str = "abc423  你好"
res=re.match(pat,str) 
print(res) #输出 abc423  

​ 我们来解析下这个正则表达式,pat字符串开头的r是告诉Python这是个正则表达式,不要转义里面的\,建议写表达式时都加上。[a-z]代表任意小写字母,不用\w的原因是,\w还包括数字、下划线,没有严格符合我们的要求。加上个*就代表任意数量。这里强调一下单字符匹配和数量表示之间的逻辑关系,以[a-z]*为例,其表达的是任意个[a-z],而不是某个字母有任意个。明白了这个逻辑后,其他的也好理解了。

​ 前面的例子都是我随意编的,其实学了这些,已经可以写出一个有实际作用的表达式了,比如我们来匹配一个手机号。首先手机号只有11位,第一个数字必须是1,第二个是3、5、7、8中的一个。知道了这三个个规律,我们来写一下表达式:1[3578]\d{9}。看上去好像可以,但是仔细一想,前面不是说了正则表达式是从左往右匹配,只要符合了就会返回结果,也不会管字符串匹配完全没有。如果最后有10个数字,这个表达式也会匹配成功。关于这个问题我们接着看。

边界表示

​ 边界表示符有两个:开头^和结尾$。使用起来也很简单,还是以上面的手机号为例,我们再来完善一下:^1[3578]\d{9}$。其中^1表示以1开头,\d{9}$表示以9个数字结尾。其实这个^1可有可无,毕竟是从左往右的,字符串不是1开头的话直接就会返回None,但是这个结尾符是必须的。

转义字符

​ 假如我们想匹配的字符与正则表达式规定的这些字符一样该怎么办?比如我们想单纯的匹配.这个字符,但是这个字符在正则表达式中表示的是任意字符。这时候就要用到转义字符\了。其实这个转义字符在很多语言里都是一样的。那么前面的例子就可以写出\.。我们再演示个匹配邮箱的例子:

import re
pat = r"^\w{4,10}@qq\.com" # 如果.前面不加\,就代表任意字符了
str = "[email protected]"
res=re.match(pat,str)
print(res)

匹配分组

​ 看到上面的匹配邮箱例子,是不是有个疑问,如果我想不止匹配QQ邮箱该怎么办呢。那就要用到分组了,其可以实现匹配多种情况。分组符号如下:

符号 作用
() 将括号里的内容当作一个分组,每个分组会有一个编号,从1开始
| 连接多个表达式,表达式之间是“或”的关系,可与()一起使用
\num 引用分组,num代表分组编号
(?P…) 给分组取别名,别名写在表达式前面,name不用打引号
(?P=name) 根据别名使用分组中的正则表达式

​ 那么我们把上面的例子稍微修改下:^\w{4,10}@(qq|163|outlook|gmail)\.com。这样就可以匹配多种邮箱了。

​ 简单的演示了下|的用法,大家可能对其他的分组符号还有点疑惑,下面我们再来演示一下这些符号:

import re
pat = r"<(.+)><(.+)>.*<(/\2)><(/\1)>" 
str = "<body><div></div></body>"
res=re.match(pat,str)
print(res)

​ 这个表达式匹配的是由两个标签组成的html字符串。第一眼看上去有点麻烦,实际很简单。再次强调一下,普通字符也可以当表达式来匹配的,比如上面的< >就是普通字符而已。

​ 我们来分析一下这个表达式,首先一对小括号表示一个分组,里面的.+表示只有一个非\n字符。中间的.*用来匹配标签内的内容。/\2中,第一个斜杠与前面的html标签组成一对,/2表示引用第二个分组的内容。这里为什么要使用分组呢?因为我们还要保证html标签正确匹配。如果后面也使用.+,大家可以试着把/div/body交换位置,表达式依旧匹配成功,但这显然不符合html的语法。

操作函数

​ 正则表达式的一些规则符号终于讲完了,最后再列举几个Python中操作正则表达式的函数:(re为导入的模块)

函数 作用
re.compile(patt) 封装正则表达式,并返回一个表达式对象
re.search(patt,str) 从左往右搜索第一个配正则表达式匹配的子字符串
re.findall(patt,str) 在字符串中查找正则表达式匹配到的所有子字符串,并返回一个列表
re.finditer(patt,str) 在字符串中查找正则表达式匹配到的所有子字符串,并返回一个Iterator对象
re.sub(patt,newstr,str) 将字符串中被正则表达式匹配到的子字符串替换成newstr,并返回新的字符串,原字符串不变

​ Python的第一篇文章就到这里了。接下来会边学边写,做一些好玩的Python项目,再一起分享出来。如有错误,感谢指出!

参考资料:《Python 3快速入门与实战》

Tags: