(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。
eles[0:3]
# 输出:[1, 2, 3]
eles[1:5]
# 输出:[2, 3, 4, 5]
eles[-5:-1]
# 输出:[5, 6, 7, 8]
eles[3:]
# 输出:[4, 5, 6, 7, 8, 9]
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]
list = [i * 2 for i in range(0, 10)]
print(list)
# 输出:[0, 2, 4, 6, 8, 10, 12, 14, 16, 18]
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]
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 生成。
生成器是一种一边循环一边计算的机制。
相比于列表生成式,只需将最外层的[ ]换成( )即可。随着变换,返回的类型也随着变动。
print(type([x * 2 for x in range(1, 11)]))
# 输出:
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
(1)什么是函数
函数是组织好的,可重复使用的,用来实现单一,或相关联功能的代码段。
函数能提高应用的模块性,和代码的重复利用率。你已经知道Python提供了许多内建函数,比如print()。但你也可以自己创建函数,这被叫做用户自定义函数。
(2)如何定义一个函数
语法:
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)函数返回多个值
def my_fun():
return [1, 2, 3]
def my_fun():
return {"x": 1, "y": 2, "z": 3}
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。
混合参数使用:混合参数使用时,参数定义的顺序必须是:必选参数,默认参数,可变参数,命名关键字参数,关键字参数。
(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])。
result = map(lambda x: x + 1, [1, 2, 3, 4])
print(list(result))
# 输出:[2, 3, 4, 5]
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)
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()
# 输出:
# 打印前置增强
# 打印函数
# 打印后置增强
(1)什么是模块
(2)什么是包
在pycharm中,我们右键可以创建一个目录,也可以创建一个包,两者看起来差不多,唯一的区别在于,创建包的时候,包下面会有一个__init__.py的文件,这也是python为了区分目录跟包所作出的界定。
(3)什么是命名空间
命名空间是变量到对象的映射集合。一般都是通过字典来实现的。主要可以分为三类:
比如在B模块中要引用A中的变量a,在导入A模块之后,可以使用A.a访问A中的变量。
(4)命名空间的查找顺序
当一行代码要使用变量 x 的值时,Python 会到所有可用的名字空间去查找变量,按照如下顺序:
如果 Python 在这些名字空间找不到 x,它将放弃查找并引发一个 NameError 异常,如,NameError: name ‘xxx’ is not defined
当函数嵌套时的查找规则:
def fun():
name = "zhangsan"
def fun_son():
name = "lixiang" # 此处的name变量,覆盖了父函数的name变量
print(name)
# 调用内部函数
fun_son()
print(name)
fuc()
# 输出:
# lixiang
# zhangsan
(5)命名空间的生命周期
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导入时整个流程:
当一个模块首次被导入时,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
(1)异常的捕获与处理
错误:在编辑器语法解析的时候,就发现语法存在问题,这个时候就是错误。
print("lixiang"
假如我们写代码打印少写了一个括号,这个时候只能的编辑器已经告诉我们这句语法有问题,如果一意孤行,硬要运行,就会报相应的错误。
异常:代码没有语法错误,但是在运行的时候发生的了错误。
print(10/0)
警告:代码运行会抛出警告。
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
(1)文件IO输入输出
Python 提供了input()内置函数从标准输入读入一行文本,默认的标准输入是键盘。
print("输入的内容:", input())
# 输入:lixiang
# 输出:输入的内容: lixiang
print("请输入内容,按回车结束:")
str = input()
print("用户输入的内容:", str)
# 请输入内容,按回车结束:lixiang
# 用户输入的内容: lixiang
(2)IO文件的读取
open(filename,mode)
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()
# 输出:我喜欢写代码我喜欢写代码
with open("/lixiang.txt", "r", encoding="utf-8") as file:
fileContent = file.readlines()
for line in fileContent:
print(line)
# 输出:我喜欢写代码我喜欢写代码
(3)IO文件的写入
同样的写入文件内容时,需要使用open打开文件,相应mode自定为可写入,之后可以使用write函数进行文件的写入。
try:
file = open("/lixiang.txt", "w", encoding="utf-8")
file.write("我喜欢写代码")
finally:
file.close()
try:
file = open("/lixiang.txt", "a", encoding="utf-8")
file.write("我喜欢写代码")
finally:
file.close()
(4)IO操作文件件
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())
多线程类似于同时执行多个不同程序,多线程运行有如下优点:
Python通过两个标准库thread和threading提供对线程的支持。thread提供了低级别的、原始的线程以及一个简单的锁。
threading 模块提供的其他方法:
除了使用方法外,线程模块同样提供了Thread类来处理线程,Thread类提供了以下方法:
使用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()