零基础学Python之高级特性

1.Python高级语法

(1)切片

对于一个列表eles = [1, 2, 3, 4, 5, 6, 7, 8, 9],如果我们想获取前三个元素,我们可以使用数组的下标去获取。

print(eles[0], eles[1], eles[2])

# 输出:1 2 3

但是这样操作,如果元素很多,那我们的代码会非常冗余。所以我们使用切片来获取特定元素。

语法:eles[left_index:right_index]数组下标从0开始,切片是左闭右开的区间,也就是包含左边的index,不包含右边的index。

  • 获取第1个到第3个
eles[0:3]

# 输出:[1, 2, 3]
  • 获取第2个到第5个
eles[1:5]

# 输出:[2, 3, 4, 5]
  • 取倒数第5个到倒数第2个
eles[-5:-1]

# 输出:[5, 6, 7, 8]
  • 取第2个到最后一个
eles[3:]

# 输出:[4, 5, 6, 7, 8, 9]
  • 前5个数,每2个取一个
eles[:5:2]

# 输出:[1, 3, 5]
  • 所有数,每个两个取一个
eles[::2]

# 输出:[1, 3, 5, 7, 9]

能使用切片的类型:字符串、列表、元组

(2)列表生成式

语法:

# 首先迭代iterable里所有内容,每一次迭代,都把 iterable 里相应内容放到iter_var 中,再在表达式中应用该 iter_var 的内容,最后用表达式的计算值生成一个列表。
[expr for iter_var in iterable]
# 加入了判断语句,只有满足条件的内容才把 iterable 里相应内容放到 iter_var 中,再在表达式中应用该 iter_var 的内容,最后用表达式的计算值生成一个列表。
[expr for iter_var in iterable if cond_expr]
  • 生成一个10个元素的数据,每个分别对应 1-10的两倍
list = [i * 2 for i in range(0, 10)]
print(list)

# 输出:[0, 2, 4, 6, 8, 10, 12, 14, 16, 18]
  • 生成一个50以内所有偶数的列表
list = [i for i in range(0, 50) if i % 2 == 0]
print(list)

# 输出:[0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46, 48]
  • 使用列表生成式生成[[1, 0], [1, 1], [1, 2], [1, 3], [1, 4], [1, 5], [2, 0], [2, 1], [2, 2], [2, 3], [2, 4], [2, 5]] 这样的列表
list = [[i, j] for i in range(1, 3) for j in range(0, 6)]
print(list)

# 输出:[[1, 0], [1, 1], [1, 2], [1, 3], [1, 4], [1, 5], [2, 0], [2, 1], [2, 2], [2, 3], [2, 4], [2, 5]]

(3)迭代对象

  • 如何判断一个对象是不是可迭代的
# isinstance 方法 判断当前data是不是Iterable类型
from collections.abc import Iterable
print(isinstance('aaa', Iterable))
  • 如果可迭代对象里的数据成对出现,想一次性遍历出两个对象
for x, y in [(1, 2), (4, 5)]:
    print("x+y=", x + y)
    
# 输出:
# x+y= 3
# x+y= 9

(4)生成器

通过列表生成式,可以直接创建一个列表,但是,受到内存的限制,列表容量是有限的,当列表元素很大的时候,会很浪费内存空间。所以可以通过生成器 Generator 生成。

生成器是一种一边循环一边计算的机制。

相比于列表生成式,只需将最外层的[ ]换成( )即可。随着变换,返回的类型也随着变动。

  • 列表生成式,返回的是list
print(type([x * 2 for x in range(1, 11)]))

# 输出:
  • 生成器,返回的是generator类型
print(type((x * 2 for x in range(1, 11))))

# 输出:
  • 生成器的使用
range_ = (x * 2 for x in range(1, 11))
for i in range_:
	print(i)

# 输出:2 4 6 8 10 12 14 16 18 20 

2.Python函数使用

(1)什么是函数

函数是组织好的,可重复使用的,用来实现单一,或相关联功能的代码段。

函数能提高应用的模块性,和代码的重复利用率。你已经知道Python提供了许多内建函数,比如print()。但你也可以自己创建函数,这被叫做用户自定义函数。

(2)如何定义一个函数

  • 函数代码块以 def 关键词开头,后接函数标识符名称和圆括号**()**。
  • 任何传入参数和自变量必须放在圆括号中间。圆括号之间可以用于定义参数。
  • 函数的第一行语句可以选择性地使用文档字符串—用于存放函数说明。
  • 函数内容以冒号起始,并且缩进。
  • return [表达式] 结束函数,选择性地返回一个值给调用方。不带表达式的return相当于返回 None。

语法:

def functionname( parameters ):
   "函数_文档字符串"
   function_suite
   return [expression]
  • 定义一个两数相加的函数
def add_nums(x, y):
    return x + y

a = add_nums(1, 4)
print(a)

# 输出:5
  • 定义一个空函数,无实现
def fun():
	pass

print(fun())

(3)函数返回多个值

  • 直接在函数里把多个返回值按一定顺序放到list、tuple里
def my_fun():
	return [1, 2, 3]

def my_fun():
	return {"x": 1, "y": 2, "z": 3}
  • 在return 的时候直接返回多个逗号分隔的值,在返回的时候,也可以直接用多个变量接收
def my_fun():
	return val1, val2, val3
x, y, z = my_fun()

注意:返回多个参数时,如果想一次性用多个变量接收,那么有多少个返回值就得有多少个变量接收。

其实返回多个值 ,本质上返回的是一个元组。

(4)函数的递归

递归说到底,就是自己调用自己。需要特别注意的点:当自己写递归函数时,首要步骤是要先写函数的最终结束条件。

  • 使用递归,做阶乘运算
def factorial(n):
	if n == 1:
		return 1
	else:
		return n * factorial(n-1)

print(factorial(5))

# 输出:120

(5)形参、实参和位置参数

形参:即形式参数,函数定义时指定的可以接受的参数即为形参,比如定义函数时的max(a,b)函数中的a和b就是形参。

实参:即实际参数,调用函数时传递给函数参数的实际值即为实参,比如调用上面函数用max(1, 9)函数中的1和9就是实参。

位置参数:我们定义函数max(a,b)后,在调用时,我们无需指定参数名,只需max(1,9),这个时候实参入参的的位置进行匹配,也就是在函数中,a=1,b=9。当然,如果我们不想基于位置顺序,也可以直接指定对应的参数名,比如max(b=9,a=1),这个时候调用后,不会按入参顺序赋值,而是直接按指定的参数名赋值。

(6)默认参数

默认参数,顾名思义就是在调用函数时,如果不传这个参数,怎会采用默认的值。

def power(x, n=2):
	return x ** n

print(power(2))

当有多个参数时,可以显式指定传入某个参数的值,在调用函数时,入参使用参数名=参数值的形式即可。

def test(a=1, b=2, c=3):
	print("a=%d b=%d c=%d" % (a, b, c))
test(c=2)

(7)可变参数

可变参数:顾名思义就是调用函数时,传递参数的个数的可变的。

可变参数的基本格式:在定义函数的时候,入参前加*号,表示可变参数。

可变参数,其实是将入参封装成元组。

def sum(*numbers):
    total = 0
    print(type(numbers))
    for i in numbers:
        total += i
    return total
print(sum(1, 2, 3))

# 输出:
# 
# 6

(8)命名关键字参数

普通入参方式,例如def sum(x, y),这种方式无需指定入参的名字,只要位置相对应,就可以。

与位置参数相对的另一种方式,是每次调用的时候,都必需指定参数的名字,也就是命名关键字。

限制调用者不能按位置传递,需要放在形参的后面,并在前面使用星号*(独占一个参数位)与普通形参分隔。

命名关键字形参名具有十分明显的含义,显式写出有利于可读性。

def person_info(name, age, *, sex,address):
    print(name,age,sex,address)

person_info("lixiang",18,sex="男",address="天津")

# 输出:lixiang 18 男 天津

命名关键字使用做分隔,之前的参数,基于位置参数,后面的参数,在调用的时候必需指定其参数名。

一旦使用命名关机字之后,如果调用时,没指定参数名,会报相应的错误:TypeError: person() takes 2 positional arguments but 3 were given.

* 后面的参数,同样也可以使用默认参数进行设置。

def person_info(name, age, *, sex,address = "北京"):
    print(name,age,sex,address)

person_info("lixiang",18,sex="男")

# 输出:lixiang 18 男 北京

(9)关键字参数

关键字参数允许你传入0个或任意个含参数名的参数,这些关键字参数在函数内部自动组装为一个dict。

def my_fun(name, **info):
    print("name=", name)
    print("info=", info)


my_fun("李祥", sex="男", age=18, address="天津")

# 输出:
# name=李祥
# info= {'sex': '男', 'age': 18, 'address': '天津'}

也可以使用下面的方式传参:

info = {"sex": "男", "age": 18, "address": "天津"}
my_fun("李祥", email="[email protected]", **info)

# 输出:
# name= 李祥
# info= {'email': '[email protected]', 'sex': '男', 'age': 18, 'address': '天津'}

info表示把info这个dict的所有key-value用关键字参数传入到函数的**info参数,**info将获得一个dict。

注意**info获得的dict是info的一份拷贝,对**info的改动不会影响到函数外的info。

混合参数使用:混合参数使用时,参数定义的顺序必须是:必选参数,默认参数,可变参数,命名关键字参数,关键字参数。

3.Python函数式编程

(1)匿名函数lambda表达式

匿名函数,顾名思义就是没有名字的函数,在程序中不用使用def进行定义,可以直接使用lambda关键字编写简单的代码逻辑。lambda本质上是一个函数对象,可以将其赋值给另一个变量,再由该变量来调用函数,也可以直接使用。

# 假如我们定义一个相加的函数
def sum(x,y):
	return x + y
# 如果换成lambda,可以这样操作
sum = lambda x,y:x+y
print(sum(1,4))
# 也可以这样写
print((lambda x,y:x+y)(1,4))

lambda表达式的使用场景:

一般适用于创建一些临时性的,小巧的函数。比如上面的 sum函数,我们当然可以使用 def 来定义,但使用 lambda 来创建会显得很简洁,尤其是在高阶函数的使用中。

如何将一个函数作为方法的参数进行调用。

def add(func,l = []):
	return [func(x) for x in l]
print(add(lambda x:x+1,[1,2,3]))
print(add(lambda x:x+2,[1,2,3]))

(2)map函数

*map的基本格式:map(func, iterables)

map()函数接收两个以上的参数,开头一个是函数,剩下的是序列,将传入的函数依次作用到序列的每个元素,并把结果作为新的序列返回。也就是类似map(func,[1,2,3])。

  • 将list每个元素的值加1
result = map(lambda x: x + 1, [1, 2, 3, 4])
print(list(result))

# 输出:[2, 3, 4, 5]
  • 使用map函数,将两个序列的数据对应位置求和,之后返回,也就是对[1,2,3],[4,5,6]两个序列进行操作之后,返回结果[5,7,9]
result = map(lambda x, y: x + y, [1, 2, 3, 4], [6, 7, 8, 9])
print(list(result))

# 输出:[7, 9, 11, 13]
  • 当两个序列元素一样时,一一对应,但是如果元素不一样的时候是结果以个数少的为准
[7, 9, 11]

(3)reduce函数

reduce函数的基本格式:reduce(function, sequence, initial=None)

reduce把一个函数作用到一个序列上,这个函数必须接受两个参数,reduce函数把结果继续和序列的下一个元素做聚合运算。

reduce函数需要引入,from functools import reduce。

print(reduce(lambda x, y: x + y, [1, 2, 3, 4, 5]))

# 输出:15

(4)filter函数

filter顾名思义就是过滤。

filter函数的格式:filter(function,iterable)

filter() 接受一个函数和一个序列,把传入的函数依次作用于每个元素,然后根据返回值是True还是False决定该元素是去是留。

print(list(filter(lambda x: x % 2 == 0, [1, 2, 3, 4, 5, 6])))

# 输出:[2, 4, 6]

(5)sorted函数

sorted用来排序的函数,sorted可以对所有可迭代的对象进行排序操作。

sorted格式:sorted(iterable,key=none,reverse=false)

  • iterable:可迭代的对象
  • key:用来进行比较的元素,只有一个参数,具体的函数的参数就是取自于可迭代对象中,指定可迭代对象中的一个元素来进行排序。
  • reverse:排序规则 True为降序,False为升序(默认)
print(sorted([1, 5, 2, 3, 5, 8]))
print(sorted([1, 5, 2, 3, 5, 8], reverse=True))

# 输出:
# [1, 2, 3, 5, 5, 8]
# [8, 5, 5, 3, 2, 1]
# 列表字典排序
data = [{"name": "李祥", "age": 18}, {"name": "李四", "age": 23}, {"name": "张三", "age": 19}]
print(sorted(data, key=lambda item: item['age'], reverse=True))

# 输出:[{'name': '李四', 'age': 23}, {'name': '张三', 'age': 19}, {'name': '李祥', 'age': 18}]

(6)Python闭包

python中闭包主要包括:函数闭包和装饰器闭包。

函数闭包:函数闭包是指在一个函数内部返回另一个函数,并且返回的函数能访问到其内部的变量。这样的返回函数就称为函数闭包。

函数闭包在程序中可以反复使用,因此可以实现一些功能上的封装。

def outer():
    x = 10
    def inner():
        return x ** 2
    return inner()

print(outer())

# 输出:100

装饰器闭包:装饰器闭包是指在使用装饰器时,被装饰的函数并没有直接被调用,而是被包装在一个函数内部,并返回一个新的函数。

这个新的函数就是一个装饰器闭包。装饰器闭包通常用于实现功能增强,日志记录等。

def print_cost(func):
    def wrapper():
        print("打印前置增强")
        func()
        print("打印后置增强")
    return wrapper()

@print_cost
def my_func():
    print("打印函数")
my_func()

# 输出:
# 打印前置增强
# 打印函数
# 打印后置增强

4.Python包、模块

(1)什么是模块

  • 当我们新建一个python file,这个时候形成一个.py后缀的文件,这个文件就称之为模块。
  • 模块让你能够有逻辑地组织你的 Python 代码段。
  • 把相关的代码分配到一个模块里能让你的代码更好用,更易懂。
  • 模块能定义函数,类和变量,模块里也能包含可执行的代码。

(2)什么是包

在pycharm中,我们右键可以创建一个目录,也可以创建一个包,两者看起来差不多,唯一的区别在于,创建包的时候,包下面会有一个__init__.py的文件,这也是python为了区分目录跟包所作出的界定。

零基础学Python之高级特性_第1张图片

(3)什么是命名空间

命名空间是变量到对象的映射集合。一般都是通过字典来实现的。主要可以分为三类:

  • 每个函数都有着自已的命名空间,叫做局部命名空间,它记录了函数的变量,包括函数的参数和局部定义的变量。
  • 每个模块拥有它自已的命名空间,叫做全局命名空间,它记录了模块的变量,包括函数、类、其它导入的模块、模块级的变量和常量。
  • 还有就是内置命名空间,任何模块均可访问它,它存放着内置的函数和异常。

比如在B模块中要引用A中的变量a,在导入A模块之后,可以使用A.a访问A中的变量。

(4)命名空间的查找顺序

当一行代码要使用变量 x 的值时,Python 会到所有可用的名字空间去查找变量,按照如下顺序:

  • **局部命名空间:**特指当前函数或类的方法。如果函数定义了一个局部变量 x,或一个参数x,Python 将使用它,然后停止搜索。
  • **全局命名空间:**特指当前的模块。如果模块定义了一个名为 x 的变量,函数或类,Python将使用它然后停止搜索。
  • **内置命名空间:**对每个模块都是全局的。作为最后的尝试,Python 将假设 x 是内置函数或变量。

如果 Python 在这些名字空间找不到 x,它将放弃查找并引发一个 NameError 异常,如,NameError: name ‘xxx’ is not defined

当函数嵌套时的查找规则:

  • 先在当前 (嵌套的或 lambda) 函数的命名空间中搜索
  • 然后是在父函数的命名空间中搜索
  • 接着是模块命名空间中搜索
  • 最后在内置命名空间中搜索
def fun():
	name = "zhangsan"
	def fun_son():
		name = "lixiang" # 此处的name变量,覆盖了父函数的name变量
		print(name)
	# 调用内部函数
	fun_son()
	print(name)
fuc()

# 输出:
# lixiang
# zhangsan

(5)命名空间的生命周期

  • 内置命名空间在 Python解释器启动时创建,会一直保留,不被删除。
  • 模块的全局命名空间在模块定义被读入时创建,通常模块命名空间也会一直保存到解释器退出。
  • 当函数被调用时创建一个局部命名空间,当函数返回结果 或 抛出异常时,被删除。每一个递归调用的函数都拥有自己的命名空间。
a = 1
def fun():
    if a == 1:
        a = 2

fun()

上面的程序会在报错:UnboundLocalError: local variable ‘a’ referenced before assignment.

在python的函数中和全局同名的变量,如果你有修改变量的值就会变成局部变量,在修改之前对该变量的引用自然就会出现没定义这样的错误了,如果确定要引用全局变量,并且要对它修改,必须加上global关键字。

对上面的错误进行修改,如下:

def fun():
    global a
    if a == 1:
        a = 24
fun()
print(a)

# 输出:24

(6)命名空间的访问

局部命名空间可以 locals() 来访问。

def fun():
    a = 1
    b = 2
    print(locals())
fun()

# 输出:{'a': 1, 'b': 2}

locals 返回一个名字/值对的 dictionary。这个 dictionary 的键是字符串形式的变量名字,dictionary 的值是变量的实际值。

全局命名空间可以globals() 来访问。

a = 1
b = 2
print(globals())
输出:
{
    '__name__': '__main__', 
    '__doc__': None, 
    '__package__': None, 
    '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x00000251D8AE5308>,
    '__spec__': None,
    '__annotations__': {},
    '__builtins__': <module 'builtins' (built-in)>, 
    '__file__': 'D:/pythonworkspace/test1.py', 
    '__cached__': None,
    'a': 1,
    'b': 2
}

locals 是只读的,但globals是可读写的。

def fun():
    x = 123
    locals()["x"] = 456
    print("x=", x)
y = 123
fun()
globals()["y"] = 111
print("y=", y)

# 输出:
# x= 123
# y= 111

(7)工程中导入模块

在平时的开发中,一般会有多模块,为了软件的复用性,我们通常在模块之间相互引用,已达到复用的目的。

在python中,可以使用import 关键字进行模块的导入,语法如下:import module_name

例如,在模块b中要引用同目录下a中的变量x,此时可以如下:

import a
print(a.x)

这个时候,需要使用命名空间来访问相应的变量。

这样导入似乎很轻松,但是如果模块名长了,代码写起来就有点别扭了,比如:

import 

print(python_module1.name)

这样的代码看起来非常臃肿,这个时候我们想让代码看起来简短些,我们可以使用别名,具体语法如下:import module_name as alias

# 导入模块并重命名为a
import python_module1 as a
print(xd.name)

import导入时整个流程:

  • 查找一个模块,如果有必要还会加载并初始化模块。
  • 在局部命名空间中为 import 语句发生位置所处的作用域定义一个或多个名称。

当一个模块首次被导入时,Python 会搜索该模块,如果找到就创建一个 module 对象并初始化它。 如果指定名称的模块未找到,则会引发 ModuleNotFoundError。 当发起调用导入机制时,Python 会实现多种策略来搜索指定名称的模块。

使用import关键字导入模块是最常用的方式,还可以使用importlib模块进行模块的导入。

import importlib

importlib.import_module("module_name")

如果想导入另一个包中的模块,可以使用如下语法:from package import module

如果想导入多层包中的模块,可以使用如下语法:from package.son import module

(8)工程中导入变量

工程中导入变量的语法:from module import variable

from module1 import a
print(a)

如果要导入多个变量,可以使用逗号分隔

from module2 import a, b
print(a)
print(b)

当要导入的变量非常多的时候,可以使用*进行导入

from class3 import *
print(a)
print(b)
print(c)

虽然支持*通配符进行导入,但是不建议过多使用,因为使用*导入,阅读代码这难以理清其语义。

(9)导包机制

导入期间,会在 sys.modules 查找模块名称,如存在则其关联的值就是需要导入的模块,导入过程完成。 然而,如果值为 None ,则会引发ModuleNotFoundError。 如果找不到指定模块名称,Python 将继续搜索该模块。

如果指定名称的模块在 sys.modules找不到,则将发起调用 Python 的导入协议以查找和加载该模块。 此协议由两个概念性模块构成,即 查找器和 加载器。 查找器的任务是确定是否能使用其所知的策略找到该名称的模块。 同时实现这两种接口的对象称为 导入器—— 它们在确定能加载所需的模块时会返回其自身。

(10)_init_.py的作用及用法

_init_.py的作用

  • 标志所在目录是一个模块包
  • 本身也是一个模块
  • 可用于定义模糊导入时要导入的内容

如果使用 from package import * 会报错误,如果想使用该语法不报错,可以在__init__.py中定义要导入的模块。

我们可以在__init__.py文件中,使用__all__ = ['module_name1','module_name2']定义*号匹配时要导入的模块,之后再导入的时候,就可以使用 *通配符进行模糊导入。

  • 导入一个包的时候,包下的__init__.py中的代码会自动执行
  • 用于批量导入模块

当我们的许多模块中,都需要导入某些公共的模块,此时,可以在__init__.py中进行导入,之后直接导入该包即可。

(11)_all_、 __name__的作用及其用法

__all__的作用及其用法:

  • 在普通模块中使用时,表示一个模块中允许哪些属性可以被导入到别的模块中
  • 在包下的__init__.py中,可用于标识模糊导入时的模块

__name__的作用及其用法:

  • __name__这个系统变量显示了当前模块执行过程中的名称,如果当前程序运行在这个模块中,__name__ 的名称就是__main__如果不是,则为这个模块的名称。
  • __main__一般作为函数的入口,类似于C语言,尤其在大型工程中,常常有 if __name__ =="__main__":来表明整个工程开始运行的入口。
def fun():
    if __name__ == "__main__":
        print("this is main")
fun()

# 输出:this is main

5.Python错误处理

(1)异常的捕获与处理

错误:在编辑器语法解析的时候,就发现语法存在问题,这个时候就是错误。

print("lixiang"

假如我们写代码打印少写了一个括号,这个时候只能的编辑器已经告诉我们这句语法有问题,如果一意孤行,硬要运行,就会报相应的错误。

零基础学Python之高级特性_第2张图片

异常:代码没有语法错误,但是在运行的时候发生的了错误。

print(10/0)

零基础学Python之高级特性_第3张图片

警告:代码运行会抛出警告。

import warnings
def fun():
    warnings.warn("deprecated", DeprecationWarning)
    print("自定义函数")
fun()

# 输出:NameError: name 'warnings' is not defined. Did you mean: 'Warning'? Or did you forget to import 'warnings'?

异常的定义:

try:
  业务逻辑
except Exception1:
  业务逻辑
except Exception2:
  业务逻辑
finally:
	最终要做的事
try:
   print("测试代码")
   print(10 / 0)
except ZeroDivisionError:
   print("发生除0异常")
finally:
   print("最终执行")

# 输出:
# 测试代码
# 发生除0异常
# 最终执行

(2)自定义的异常

Python自定义异常继承Exception类。

class BizException(Exception):
    def __init__(self, errMsg):
        Exception.__init__(self, errMsg)
        self.errMsg = errMsg
def num(x):
   if x == 2:
       raise BizException("当前数值不能为2")

num(2)

# 输出:当前数值不能为2

(3)异常的抛出,直接用关键字raise即可

try:
   print("测试代码")
   print(10 / 0)
except ZeroDivisionError:
   print("发生除0异常")
   # 捕获这个异常然后在抛出这个异常
   raise
finally:
   print("最终执行")

# 输出:ZeroDivisionError: division by zero

6.Python文件IO操作

(1)文件IO输入输出

Python 提供了input()内置函数从标准输入读入一行文本,默认的标准输入是键盘。

print("输入的内容:", input())
# 输入:lixiang
# 输出:输入的内容: lixiang
print("请输入内容,按回车结束:")
str = input()
print("用户输入的内容:", str)
# 请输入内容,按回车结束:lixiang
# 用户输入的内容: lixiang

(2)IO文件的读取

open(filename,mode)

  • filename:文件名,一般包括该文件所在的路径。
  • mode:模式,如果读取时读取中文文本,需要在打开文件的时候使用encoding指定字符串编码为utf-8
t:文本模式(默认)
x:写模式,新建一个文件,如果该文件已存在则会报错
b:二进制模式
+:打开一个文件进行更新(可读可写)
r:以只读方式打开文件,文件的指针将会放在文件的开头,默认模式。
rb:以二进制格式打开一个文件用于读写。文件指针将会放在文件的开头,一般用于非文本文件如图片等
r+:打开一个文件用于读写,文件指针将会放在文件的头部
rb+:以二进制格式打开一个文件用于读写。文件指针将会放在文件的开头,一般用于非文本文件如图片等。
w:打开一个文件只用于写入,如果该文件已存在则打开文件,并从开头开始编辑,即原有内容会被删除。如果该文件不存在,创建新文件
wb:以二进制格式打开一个文件只用于写入。如果该文件已存在则打开文件,并从开头开始编辑,即原有内容会被删除。如果该文件不存在,创建新文件。一般用于非文本文件如图片等。
w+:打开以一个文件用于读写。如果该文件已存在则打开文件,并从开头开始编辑,济源有内容会被删除。如果文件不存在,创建新文件
wb+:以二进制格式打开一个文件用于读写。如果该文件已存在则打开文件,并从开头开始编辑,即原有内容会被删除。如果该文件不存在,创建新文件。一般用于非文本文件如图片等。
a:打开一个文件用于追加。如果该文件已存在,文件指针将会放在文件的结尾。也就是说,新的内容将会被写入到已有内容之后。如果该文件不存在,创建新文件进行写入。
ab:以二进制格式打开一个文件用于追加。如果该文件已存在,文件指针将会放在文件的结尾。也就是说,新的内容将会被写入到已有内容之后。如果该文件不存在,创建新文件进行写入
a+:打开一个文件用于读写。如果该文件已存在,文件指针将会放在文件的结尾。文件打开时会是追加模式。如果该文件不存在,创建新文件用于读写。
ab+:以二进制格式打开一个文件用于追加。如果该文件已存在,文件指针将会放在文件的结尾。如果该文件不存在,创建新文件用于读写。
读取文件的内容,使用read相关方法
使用read方法,读取文件的全部内容(如果文件较大,一次性读取可能会导致内存不足),此时需要指定
使用readline方法,读取文件的一行
readlines():一次读取所有内容并返回按行返回list
注意每次打开文件都要关闭该文件,且因为文件在读写过程中可能出现IOError而导致文件不能正常关闭,所以每次读写文件时,必须使用try finally语法包裹。
try:
    file = open("/lixiang.txt", "r", encoding="utf-8")
    fileContent = file.readlines()
    for line in fileContent:
        print(line)
finally:
    file.close()

# 输出:我喜欢写代码我喜欢写代码 

零基础学Python之高级特性_第4张图片

with open("/lixiang.txt", "r", encoding="utf-8") as file:
    fileContent = file.readlines()
    for line in fileContent:
        print(line)

# 输出:我喜欢写代码我喜欢写代码 

(3)IO文件的写入

同样的写入文件内容时,需要使用open打开文件,相应mode自定为可写入,之后可以使用write函数进行文件的写入。

  • w:覆盖
try:
    file = open("/lixiang.txt", "w", encoding="utf-8")
    file.write("我喜欢写代码")
finally:
    file.close()
  • a:追加
try:
    file = open("/lixiang.txt", "a", encoding="utf-8")
    file.write("我喜欢写代码")
finally:
    file.close()

(4)IO操作文件件

  • 引入文件操作模块os
import os
  • 创建目录
os.mkdir("test")
  • 创建多级文件夹
os.makedirs("test/py")
  • 获取当前所在目录
print(os.getcwd())
  • 改变当前的工作目录
os.chdir("test")
  • 删除空文件夹,注意只能是单层目录
os.rmdir("test")
  • 删除多层空文件夹
os.removedirs("test/py")

(5)IO核心StringIO与BytesIO

实际开发中我们只需要缓存这些文本,并不需要往新建文件写入。这会我们就有了StringIO和 ByteIO。

StringIO和 ByteIO两者操作类似,只是StringIO写入的是字符串,ByteIO写入的是二进制。

from io import StringIO
str_io = StringIO("hello lixiang")
for line in str_io.readlines():
    print(line)
str_io.close()

with StringIO() as str_io:
    str_io.write("hello")
    str_io.write("zhangsna")
    print(str_io.getvalue())

7.Python多线程

多线程类似于同时执行多个不同程序,多线程运行有如下优点:

  • 使用线程可以把占据长时间的程序中的任务放到后台去处理。
  • 用户界面可以更加吸引人,这样比如用户点击了一个按钮去触发某些事件的处理,可以弹出一个进度条来显示处理的进度
  • 程序的运行速度可能加快
  • 在一些等待的任务实现上如用户输入、文件读写和网络收发数据等,线程就比较有用了。在这种情况下我们可以释放一些珍贵的资源如内存占用等等。

Python通过两个标准库thread和threading提供对线程的支持。thread提供了低级别的、原始的线程以及一个简单的锁。

threading 模块提供的其他方法:

  • threading.currentThread(): 返回当前的线程变量。
  • threading.enumerate(): 返回一个包含正在运行的线程的list。正在运行指线程启动后、结束前,不包括启动前和终止后的线程。
  • threading.activeCount(): 返回正在运行的线程数量,与len(threading.enumerate())有相同的结果。

除了使用方法外,线程模块同样提供了Thread类来处理线程,Thread类提供了以下方法:

  • run(): 用以表示线程活动的方法。
  • **start()*启动线程活动。
  • join([time]): 等待至线程中止。这阻塞调用线程直至线程的join() 方法被调用中止-正常退出或者抛出未处理的异常-或者是可选的超时发生。
  • isAlive(): 返回线程是否活动的。
  • getName(): 返回线程名。
  • setName(): 设置线程名。

使用Threading模块创建线程,直接从threading.Thread继承,然后重写__init__方法和run方法:

def print_time(threadName, delay):
    count = 0
    while count < 5:
        time.sleep(delay)
        count += 1
        print("%s: %s" % (threadName, time.ctime(time.time())))

import threading
class MyThread(threading.Thread):

    def __init__(self, threadName, delay):
        threading.Thread.__init__(self)
        self.threadName = threadName
        self.delay = delay

    def run(self):
        print ("Starting " + self.threadName)
        print_time(self.threadName, self.delay)
        print ("Exiting " + self.name)

thread1 = MyThread("thread1",2)
thread2 = MyThread("thread2",2)

thread1.start()
thread2.start()

零基础学Python之高级特性_第5张图片

你可能感兴趣的:(#,Python基础专栏,python,开发语言)