目录
前言
写这篇文章的目的是记录、复习一下自己在暑假学习python语法的注意事项,不过主要还是防止自己以后不用python遗忘哈哈^ _ ^,所以会在一些地方有所侧重(像条件语句和循环语句讲的会比较简洁,这些都和其他语言是相通的)
plus:以代码展示为主,但基本每行代码我都有写注释,以防看不懂
注意本文中对函数和方法并没有严格区分!(虽然说两者几乎完全等价,只不过概念上有所不同)
函数与方法的简要区别(可以略过)
函数是一段代码,通过名字来进行调用。它能将一些数据(参数)传递进去进行处理,然后返回一些数据(返回值),也可以没有返回值。
所有传递给函数的数据都是显式传递的。
方法也是一段代码,也通过名字来进行调用,但它跟一个对象相关联。方法和函数大致上是相同的,但有两个主要的不同之处:
1.方法中的数据是隐式传递的;
2.方法可以操作类内部的数据(请记住,对象是类的实例化–类定义了一个数据类型,而对象是该数据类型的一个实例化)
以上转载自函数与方法的区别
Python使用环境配置+插件下载(这块比较简略因为CSDN教程很多)
我用的python编辑器是vscode,可以根据这篇博客完成vscode的安装和python环境的配置:安装python+opencv+vscode与配置环境
vscode最大的优点就是免费,开源,轻量,跨平台,它的众多功能都通过种类繁多的插件来实现,能让你的代码更美观,编写过程更流畅,比方说不同括号自动高亮显示、错误标签显示、中文页面等等,这些都是不同插件带来的效果,下面是个人推荐vscode插件集合
插件名称 | 功能 |
---|---|
Chinese (Simplified) Language Pack for Visual Studio Code | 显示中文的vscode页面(英文大佬略过) |
Python | 写python代码的基础插件,提供了代码高亮、规范化等基本功能 |
jupyter | 用来打开.ipynb后缀的文件,可以一段一段运行python代码,墙裂推荐! |
Auto Close Tag | 自动添加标签闭合 |
vscode-icons | 这个插件提供各种漂亮的文件图标,推荐给各位颜控 |
Auto Rename Tag | 自动修改闭合标签名 |
Path Autocomplete | 路径自动闭合 |
Open file from path | 直接打开引入的文件 |
正文
内置对象类型+变量
整数(int)、浮点数(float)、字符串(str)、列表(list)、元组(tuple)、字典(dict)、集合(set)
# 基本赋值:python中的变量不像c需要显示说明它的类型,python会自动根据你赋值的内容来确定类型
# 并且这个变量类型是可以根据赋值类型改变的
a=3 # int
b=3.14 # float
c='123' # str 用双引号或者单引号来标志字符串
c="123" # str
d=[1,2,4,4,'123',[1,2,34]] # list 中括号标记,列表中的数据类型同样是不固定的
e=(1,2,4) # tuple 小括号标记,与列表类似,具体内容之后细讲
f={'name':'大聪明','age':1234,3:4} # dict 大括号标记,基本形式{key:value}
g=set([1,2,3,4,1,2,3,4]) # set 与数学上的集合意义相同,g中内容(1,2,3,4)
可以通过type
内置函数查看变量类型
a=1
print(type(a)) # <class 'int'>
强制类型转化
非常简单,直接上代码
a=14
b=3.14
print(float(a),int(b)) # 14.0 3
变量对调
python在变量赋值上非常简便的一个特点,如果要将a,b变量交换内容,直接a,b=b,a
即可(多变量时同样成立)
# 实例:斐波那契数列
a=0
b=1
while(a<10000):
print(a)
a,b=b,a+b # 异常简洁
查看变量地址
内置函数id
,可以用来验证某个拷贝是浅拷贝还是深拷贝
基本数学运算
# 加减乘除+整除+求余+幂次方+四舍五入
a+b
a-b
a*b
a/b # 浮点数除法,无论a,b,a/b的类型都是float
a//b # 整数除法,根据a,b类型,a//b的类型为float、int,比方说10//5=2,10.0//5=2.0
a**b # 幂次方
pow(a,b) # 也是幂次方,a是底数,b是指数
round(a,b) # a是浮点数,b是保留小数位数(这个函数因为二进制的转化问题存在bug,不过日常使用没太大问题)
1.2e4 # 科学记数法表示 12000.0
plus:python整数运算无上限,浮点数运算有上限
库的了解与使用流程(以math库为例)
先介绍一下库的概念:库是具有相关功能模块的集合。这也是Python的一大特色之一,即具有强大的标准库、第三方库以及自定义模块。
而math库,就是python内置的一个有关浮点数运算的基本库(为浮点运算提供了对底层C函数库的访问),它提供了许多数学运算方法
Plus:通过math库的介绍学会help
和dir
函数的使用
了解并使用库的基本流程
- 导入库
import math
- 了解一个库,可以先通过
dir
函数查看模块内定义的所有名称,并以一个字符串列表的形式返回(如下图,其中没有双下滑线的代表的都是math库中具有的基本方法) - 使用
help
函数查看具体方法的使用语法(下面以divmod
为例展示)
但是pow函数过于简单,无法展示help语法显示的基本格式,下面以str.index
为例展示
help(str.index)
Help on method_descriptor:
index(…)
S.index(sub[, start[, end]]) -> int
Return the lowest index in S where substring sub is found,
such that sub is contained within S[start:end]. Optional
arguments start and end are interpreted as in slice notation.
Raises ValueError when the substring is not found.
str.index(sub[,start[,end]])->int
中括号内表示可选参数,sub代表子字符串,start和end分别代表起始结束索引,->int
方法返回值是int类型
math库中比较常用的几个函数:pow、pi、sin、ceil
math.pow(1,2) # 1
math.pi # π
math.sin(math.pi/3) # 注意参数形式为弧度制
math.sqrt(3) # 根号三
math.ceil(1.414) # 向上取整函数
解决二进制运算误差问题(decimal库)
print(0.1+0.2) # 输出:0.30000000000000004
import decimal
a=decimal.Decimal(0.1) # 类的实例化(之后会讲)
b=decimal.Decimal(0.2)
print(a+b) # 0.3
常用方法(杂)
进制转换
- 转十进制
int(x,base=n)
,x为数字对应字符串,n为x的对应进制 - 转二进制
bin(number)
,number为数字 - 转八进制
oct(number)
- 转十六进制
hex(number)
查看自己电脑编码方式
import sys
sys.getdefaultencoding() # 我的电脑是utf-8
字符串执行方法eval
eval('print(1)') # 等价于执行print(1)
eval('[1,2,3,4,5]') # 将字符串转换为列表[1,2,3,4,5]
单行代码书写、单行代码多行书写以及多行字符串书写
print(1);print(2);print('hello world') # 多个语句之间添加‘;’即可,注意末尾无‘;’
total=item_one+\
item_two+\
item_three # 使用反斜杠'\'将一行的语句分为多行显示
s="""i
love
python
""" # 多行语句的书写,三引号
格式化输出
print方法
print(value, …, sep=’ ‘, end=’\n’, file=sys.stdout, flush=False)# print函数原型 sep是value之间的分隔,默认为space,end是两个print之间的分隔,默认是换行符,各个参数全看需要选择
# 限定格式输出
print("%4.3s长度为%+d" %(string,len(string))) # %.3s输出字符串前三位且靠右对齐(默认靠右,-靠左) %+d 带符号输出,基本格式化和c语法一样
x=12345789
print("Hex:%x Dec:%d Oct:%o Bin:%s" %(x,x,x,bin(x))) # 分别是16、10、8、2进制
format方法
利用占位符实现格式化输出
"I like {0} and {1}!".format("python","c") # format方法,按照占位符的拼接方法
"I like {0:<10} and {1:<15}!".format("python","c") # {0:10} 占十位,左对齐,{0:>10} 占十位,右对齐 {0:^10} 占十位,居中显示
print("she is {0:4d} years old and {1:10.4f} meters tall".format(4,1.23)) # format类型约束
字符串
s='i am a string'
,字符串性质与列表极其类似,下面介绍一下字符串的常用属性和方法
index方法:查找子字符串
x1="12345"
x2="45"
# str.index(sub,start,end) # 规定范围内字符串查找方法,在母字符串中查找子字符串出现的索引
x1.index(x2,2,6) # start,end规定查找范围
split方法:将字符串分割为列表
a="i love python"
temp_str=a.split() # 格式str.split(sep=' ',maxsplit=-1) sep为分隔符,默认为none,表示舍弃任何空格符,maxsplit为分割次数
print(temp_str) # 按照空格分割,得到一个列表['i','love','python']
join方法:字符串拼接方法
lis=['2021','8','20','14','5'] # lis为可迭代对象
'-'.join(lis) # 格式 sep.join(list) 注意lis内容必须是字符串
单字符串
s='a'
print(ord(s)) # ascii转化为对应整数
print(chr(97)) # 反向转化
s.isupper()
s.islower() # 判断大小写
s.isdigit()
s.isalpha() # 和c库中的基本一致
lower(s)
upper(s) # 大小写转化
列表、元组、字典、集合
方法很多很多,可以根据自己所需在官方文档中挑选,以下仅展示常用的几个
列表
# 列表、元组基本操作
lis=[1,2,3,5.34,"str"]
# 列表中基本操作都和字符串相同
lis.append(3) # lis,append(),将所选择元素添加在末尾
print(lis) # [1, 2, 3, 5.34, 'str', 3]
lis.extend([2,3,4,5])
print(lis) # [1, 2, 3, 5.34, 'str', 3, 2, 3, 4, 5]
lis.insert(3,[4,45,6]) # insert和extend的区别,extend以扩展的方式插入可迭代对象,insert则是完整插入
print(lis) # [1, 2, 3, [4, 45, 6], 5.34, 'str', 3, 2, 3, 4, 5]
print(lis.pop(0)) # 1 取出index处的值并返回该取出值
print(lis) # [2, 3, [4, 45, 6], 5.34, 'str', 3, 2, 3, 4, 5]
lis.remove(5.34) # 移除第一个出现的value
print(lis) # [2, 3, [4, 45, 6], 'str', 3, 2, 3, 4, 5]
# lis.clear()清除列表中所有元素
lis=[2,3,4,52,3,54,1,2333,1.2333]
print(lis) # [2,3,4,52,3,54,1,2333,1.2333]
lis.sort(reverse=-1) # 在原有lis上排序,可以改变reverse参数改变排序顺序
print(lis)
sorted(lis) # 与lis.sort()区别是产生了一个新的列表
del(lis[1:]) # 删除索引1后的所有元素(包括)
#----------------------------------------------#分割线
lis=[1,2,3,4,5,6]
lis_copy=lis.copy() # 列表拷贝(浅拷贝)
print(lis_copy)
lis.count(1) # 1 返回某指定元素的出现次数
lis.reverse() # 列表反序
reversed(lis) # 产生了一个迭代性对象:<list_reverseiterator at 0x21f7647e520>,想知道是啥,再使用list即可
list(reversed(lis))
plus:列表和字符串的互相转化
s="python"
x=list(s)
print(x) # ['p', 'y', 't', 'h', 'o', 'n']
"".join(x) # 'python'
元组
# 元组介绍
t=(1,2,34,2.234,[1,2,4])
print(type(t))
tuple()
t_plus=()# 创建空元组
t_plus=('a',) # 创建只有一个元素的元组,记住必须加‘,’
# dir(tuple) 常用方法只有count和index(使用同list),因为元组值不可修改
# 如果要修改元组的值,先转换为list在转换回来
# 元组相较与列表的区别就是元组的运算速度要比列表快
字典
# 字典{} 字典非序列,但和列表元组都是容器类对象(对元素操作,地址不变)
d={} # 空字典dict={key:value}
# 三种创建方法
d=dict(a=1,b=2,c=3,d="132")
d=dict([('a',1),('name','ababa')])
d={'name':'wwt','age':100,'sex':'male'}
print(d['name']) # 'wwt'
# plus:字典中的key必须是不可变对象(整型、浮点型、字符串、元组)
d.get('name','abc') # 获取'name'关键字对应值,若不存在则返回'abc'(设置的默认返回值)
d.setdefault('name') # 如果不存在,则自动创建一个不存在的键值对
d.update([(1,123),(3,"134")]) # 添加键值对
d.pop('name','default') # 与get方法类似,可以设置默认返回值
d.popitem() # 在末尾删除,无参数
del d['name'] # 删除key=name的键值对
d.keys() # dict_keys(['name', 'age', 'sex']) 获取关键字
d.values() # dict_values(['wwt', 100, 'male']) 获取值
d.items() # dict_items([('name', 'wwt'), ('age', 100), ('sex', 'male')])
集合
s=set([1,2,3,4,5,5,5])
print(s) # 显然,集合元素必须是不可变对象
s.add('python') # 添加元素
print(s) # {1, 2, 3, 4, 5, 'python'}
s.pop() # 随机删除
print(s) # {2, 3, 4, 5, 'python'}
s.remove(4)
print(s) # {2, 3, 5, 'python'}
s.discard(3) # 被删除元素不存在时不会报错
print(s) # {2, 5, 'python'}
f_set=frozenset('pythonoo') # 生成不可变集合
print(f_set)
#--------------------------------#
# 判断是否是子集或超集
a=set([1,2,3,5,6,7,8])
b=set([1,2,4])
print(a.issuperset(b)) # a是b的超集?
print(a.issubset(b)) # a是b的子集
a&b # 交集{1, 2} 等价于方法 a.union(b)
a|b # 并集{1, 2, 3, 4, 5, 6, 7, 8} 等价于方法 a.intersection(b)
a-b # 差集{3, 5, 6, 7, 8} 等价于方法 a.difference(b)
浅拷贝和深拷贝
浅拷贝:第一层拷贝,更深层地址共用
b1=[1,2,3,[11,22,33,[0,0,0]]]
b2=b1.copy() # 浅拷贝
b1[3][0]=22
b1[3][3][0]=11111
print(id(b1),id(b2)) # 2334168502912 2334163790976 地址不同
print(b1,b2,sep='\n') # 内容均为[1, 2, 3, [22, 22, 33, [11111, 0, 0]]],仅修改其中一个两者值均改变,说明深层地址是共用的
深拷贝:完全拷贝(需要调用copy内置库)
import copy
b1=[1,2,3,[11,22,33,[0,0,0]]]
b2=copy.deepcopy(b1) # 真正的深拷贝
b1[3][0]=22
b1[3][3][0]=11111
print(id(b1),id(b2)) # 地址不同
print(b1,b2,sep='\n') # [1, 2, 3, [22, 22, 33, [11111, 0, 0]]] [1, 2, 3, [11, 22, 33, [0, 0, 0]]] 两者元素也不同,深层地址不共用
布尔类型(bool)
过于简单,就是True
和False
,注意一下首字母大写即可
条件语句
同样非常简单,顺带介绍一下python中的三元运算符
# 简单条件语句
if:
.....
elif:
.....
elif:
.....
else:
.....
a='python' if x>2 else 'java' # 三元运算符(与c中的"?:;"类似)
循环
循环(面向的对象是可迭代对象,字符串、列表、元组等都是),可迭代对象特征:dir()查看方法均有"iter" iterable:可迭代的
# 键值对循环,因为字典不是可迭代对象所以要借用items方法
dic={"aba":1324,"python":1334,"java":2222}
for i,j in dic.items():
print(i,j)
for循环和while循环的基础语法就不介绍了,非常简单
for循环常用方法(range\zip\enumerate)
r=range(start,end,step) 范围(start,end-step),而且创建时不会占用内存空间,只有被循环引用时才占用内存(使用几个读入几个)
zip函数,将传入的可迭代对象按顺序配对成元组(配对截止至最短处)
枚举函数enumerate(iterable,start=0) 1.用于for循环中的计数 2.用于统计文件行数
for i in range(0,10,2):
print(i) # 0 2 4 6 8
lic=list(zip(range(10),[1,2,34,4]))
print(lic) # [(0, 1), (1, 2), (2, 34), (3, 4)]
x=[2,3,543,456,7]
print(enumerate(x)) # <enumerate object at 0x00000164B0308240> 可迭代对象
for index,value in enumerate(x): # 自动匹配index
print(index,value)
*列表解析
列表解析的最大优点就是使代码更加简洁优美+优化代码运行速度
# 非列表解析
lst=[]
for i in range(10):
lst.append(i**2)
# 列表解析
lst=[i**2 for i range(10)]
# 两种形式
# 筛选[i for i in range(10) if i%2 == 0] [0,2,4,6,8]
# 赋值[i if i == 0 else 100 for i in range(10)] [0,100,100,100,......]
# 示例:筛选出100以内能被2或3整除的数
lst=[i for i in range(100) if(i%2==0 or i%3==0)] # 注意别把&、|和and、or搞混了
小测试:求解s中每个单词长度
s='life is short you need python'
lst=s.split()
count=[len(i) for i in lst]
result=list(zip(lst,count))
print(result)
# [(4, 'life'), (2, 'is'), (5, 'short'), (3, 'you'), (4, 'need'), (6, 'python')]
函数(重点!)
函数基本内容
函数基本格式:
def function(x,y,z):
do something
return object
# 示例:加法函数
def ADD(x,y):
return x+y
a=2;b=3;print(ADD(a,b)) # 3
# 按照位置调用:ADD(2,3)
# 按照值调用:ADD(y=3,x=2)
# 函数可以设置默认参数,但是默认参数必须排在位置参数后面,例如def function(x,y=2):
# 可以以元组的形式返回多个值,或者用多个变量接收返回的多个值
def my_func():
return 1,2,3
x,y,z=my_func();print(x,y,z) # 1 2 3
t=my_fun();print(t) # (1,2,3)
有关可变参数在函数中的使用
# *和**在函数参数中的作用
def my_func(x,*args):
print('x={0}'.format(x))
print('args={0}'.format(args))
my_func(123,2,3,4)
def bar(x,**kargs): # **传入参数名和参数值(传入对应关系)
print('x={0}'.format(x))
print('kargs={0}'.format(kargs))
bar(1,a=2,y=3,z=4) # 且不能传入之前出现过的位置参数
有关拆分参数和打包参数
# 拆分参数和打包参数
# *args\**kwargs
def foo(*args):
print(args)
foo(1,2,3,4,5,6)
# 输出(1,2,3,4,5,6) *args将传入的1,2,3,4,5,6打包成(1,2,3,4,5,6)所以称之为打包参数
def foo_1(a,b,c):
print(a,b,c)
foo(*[1,2,3])
print(*[1,2,3]) # 等效
# 输出1 2 3 *[1,2,3]将列表拆分成了a,b,c三个元素,所以称之为拆分参数
# 而**kwargs和*args的区别就是将传入的参数打包成字典
def foo_2(**kwargs):
print(kwargs)
foo_2(**{'a':1,'b':2,'c':3}) # **将字典拆分成对应关系
# 拆分参数注意点,key值和函数参数名要相同
嵌套函数
注意:函数本身是一个对象,函数+()代表调用这个函数/执行函数,而函数的参数是对象,也就是说函数之间可以互相调用
函数嵌套的定义:函数里面再写一个函数
参数传入函数
# 示例1
def bar():
print('abab')
def foo(f):
f()
foo(bar) # 'abab'
# 示例2
def opt_seq(func,lis):
r=[func(i) for i in lis]
return r
print(opt_seq(abs,range(-10,9)))
print(opt_seq(ord,'python'))
print(opt_seq(str,[1,2,3]))
*基础示例
# 示例1
def foo():
def bar():
print('i am a bar')
print('i am a foo')
bar()
# 示例2
def foo():
def bar():
print('i am a bar')
return bar # 返回的是bar对象
r=foo()
r() # 相当于调用了bar函数
变量作用域问题:全局变量以及内部变量(global关键字和nonlocal关键字)
a=1
def f1():
print(a+1)
f1()
def f2():
a=a+1 # 这里的a是函数内部的变量
print(a)
# f2() 报错
def f3():
global a # 声明a是全局变量的a
a=a+1
print(a)
f3()
# nonlocal关键字用来在函数或其他作用域中使用并修改外层(非全局)变量(逐层到外面作用域找,直到全局作用域之前的局部作用域)。
# 意义:nonlocal使用能够弥补global和闭包的两个问题。
# 对于global,只能使用全局变量,对于嵌套函数中的内层函数而言,无法通过global使用外层函数,通过nonlocal就可以,当然直接读取也可以(闭包)。
# 对于闭包,内层函数可以读取外层函数的变量,但是如果在内部函数中尝试进行修改外部变量,且外部变量为不可变类型,则需要在变量前加nonlocal,如果变量为可变类型,则不需要添加nonlocal。
def f4():
a=1
def bar():
nonlocal a
a=a+1
print(a)
bar()
f4()
哇,泥居然看到这里了,看只猫猫休息一下吧哇,好大一坨猫!
闭包(重点)
基本概念:(个人理解,内部函数与外部函数的临时变量的绑定称为闭包)
一个函数结束的时候,会把自己的临时变量都释放给内存,之后变量都不存在了。
一般情况下,确实是这样的。但是闭包却是一个特别的情况。
外部函数发现,自己的临时变量会在将来的内部函数中用到,于是在自己结束的时候,返回内函数的同时,会把外函数的临时变量和内函数绑定在一起。
所以哪怕外函数已经结束了,调用内函数的时候仍然能够使用外函数的临时变量。
要对闭包中的临时变量进行修改 1.使用nonlocal关键字声明变量 2.将变量转化为可修改类型,比如列表
plus:使用闭包的过程中,一旦外函数被调用一次返回了内函数的引用,虽然每次调用内函数,是开启一个函数执行过后消亡,但是闭包变量实际上只有一份,每次开启内函数都在使用同一份闭包变量(如果内函数在操作过程中对闭包变量进行了修改,则每次返回值将会不一样)
# 一个典型的闭包
def f1(x):
def f2(y):
nonlocal x
x+=y
print(x)
return f2 # 返回一个由x和内部函数f2组成的闭包
a=f1(10)
print(a(1));print(a(1)) # 11 12 两者结果不一样,因为闭包外层函数已经消亡,而闭包变量只有一份
装饰器(重点):需要用到闭包相关的知识
装饰器功能:(在不需要更改原函数代码的前提下)拓展原来函数的功能
装饰器函数:特点就是返回值也是一个函数
格式 @func
下面让偶们来康康如何将一个原始函数优化到能利用装饰器添加记录运行时间的功能(语病就别管了orz)
首先引入time
库
import time
原始代码
def func():
print('hello')
time.sleep(1)
print('world')
func()
蠢方法:直接在原函数上修改(代码复用率低)
def func():
starttime=time.time()
print('hello')
time.sleep(1)
print('world')
endtime=time.time()
print("%.4f s" %(endtime-starttime))
func()
但如果原来的func是核心代码不允许修改咋办?
利用伪装饰器:
def deco(func):
startTime = time.time()
func()
endTime = time.time()
msecs = (endTime - startTime)*1000
print("time is %d ms" %msecs)
def func():
print("hello")
time.sleep(1)
print("world")
deco(func)
但如果有千千万万个func都需要测试时间呢,难道每个都像‘伪装饰器’那样修改吗
plus:装饰器是通过闭包的方式实现的,外函数接收一个函数作为外函数的临时变量,然后在内函数中执行这个函数。
装饰器
def deco(func):
def wrapper():
startTime = time.time()
func()
endTime = time.time()
msecs = (endTime - startTime)*1000
print(msecs)
return wrapper
@deco # 如果有多个装饰器装饰函数,运行顺序,由下至上(且一个装饰器只修饰一个临近函数)
def func():
print('hello')
time.sleep(1)
print('world')
func()
# hello
# world
# 1011.3747119903564
示例:一个简单的通用装饰器
# 一个简单的通用装饰器(测试函数运行时间)
import time
from functools import wraps
def timing_func(func):
@wraps(func) # 也是一个装饰器,作用是保留func函数的原信息
def wrapper(*args,**kwargs): # 可以传入任意形式的装饰器
starttime=time.time()
func(*args,**kwargs)
endtime=time.time()
print("函数{0}运行时间为{1:.6f} s".format(func.__name__,endtime-starttime)) # func.__name__获取函数名称
return wrapper
@timing_func
def append_test():
r=[]
for i in range(1000000):
r.append(i)
append_test() # 函数append_test运行时间为0.128657 s
@timing_func
def analyse_test():
r=[i for i in range(1000000)]
analyse_test() # 函数analyse_test运行时间为0.078789 s
# 由此也可看出列表解析运行时间要远优于append等创建列表的方法
特殊函数(lambda、map、filter)
注意:这三个特殊函数不是非用不可,它们的主要作用还是简化程序代码,优化可读性(当然是可以使用其他基础代码代替的)
lambda函数
lambda [x1[,x2[,x3…]]]:expression
lambda语句中,冒号前是参数,可以有多个,用逗号隔开,冒号右边的返回值。lambda语句构建的是一个函数对象。
def add(x):
x+=3
return x
print(add(2))
lam=lambda x:x+3
print(lam(2),add(2)) # 两者等价
用途:列表指定值排序
items = [{'name': 'Homer', 'age': 39},{'name': 'Bart', 'age': 10},{"name": 'cater', 'age': 20}]
items.sort(key=lambda item:item.get("age"),reverse=True) # 按照年龄降序排序
map函数
原型:map(func,*iterables)->map object 将func作用于iterables,返回map对象,所以查看返回值还需要额外转化一下
# 例1
m1=map(lambda x:x+3,range(10))
print(list(m1)) # [3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
# 例子2(三个列表按照index求和)
lst1=[1,2,34,45,5]
lst2=[74,5,6,3,54]
lst3=[9,8,38,9,7]
# 方法1
m2=map(lambda x,y,z:x+y+z,lis1,lis2,lis3)
print(list(m2))
# 方法2
m2=[x+y+z for x,y,z in lis1,lis2,lis3]
print(m2) # [84, 15, 78, 57, 66]
filter函数
filter过滤函数:filter(func or none,iterable)->filter object 传入的是函数对象(按照函数过滤)!!!
def test(x):
return x-1
print(list(filter(test,[1,2,3,4,5,56,0,0]))) # [2, 3, 4, 5, 56, 0, 0]
异常捕获
try…except…(这块内容由于时间原因就少说点,以后再来补充)
类似于c语言中的while(~scanf(…)){}
while True:
try:
year=int(input('please input your age:'))
break
except ValueError: # 如果try内部的expression爆出了'ValueError'异常,则进入except语句
print('please input a correct number')
print('mission completed!')
对象
万物皆对象,对象包括类(class)和实例,类是一种面向对象计算机编程语言的构造,是创建对象的蓝图,
类描述了所创建实例共同的属性和方法
可以说,面向对象的编程思想是Python语言一切的基础
下面来创建一个简单音乐人类
class Musician:# 类名首字母必须大写
lovemusic=True # 类属性
name=input('请输入姓名:')
def sing(self): # 类方法(这里的self代表实例,不理解没事,慢慢来)
print(self.name+'会唱歌') # self参数详解:在类的实例化中接收传入的数据,在代码中运行,也就是说self就代表了实例
wwt=Musician() # wwt实例化,也就是说wwt是音乐家
print(wwt.lovemusic) # wwt的爱唱歌属性为True
wwt.sing() # wwt会唱歌
*初始化方法__init__
特殊方法,创建类的实例时会自动调用这个方法
class Musician:
'''
A class of musician
''' # 类的文档,或者说类的介绍
def __init__(self,name,age,gender): # 初始化方法
self.name=name
self.age=age
self.gender=gender
print('hello')
def sing(self): # 普通方法
print('abab')
singer=Musician('123',123,'male') # 初始化传入参数
小实例
编写一个程序,用于判断学生是否完成作业。如果完成,教师给出表扬,否则进行批评
使用面向对象的思想,面向过程虽然简便,但是在复杂程序时比面向对象更繁琐
class Teacher:
def __init__(self,name,subject):
self.name=name
self.subject=subject
def evaluate(self,result=True):
if result:
return 'you are great'
else:
return 'you should work hard'
class Student:
def __init__(self,name,age,grade,subject):
self.name=name
self.age=age
self.grade=grade
self.subject=subject
def do_work(self,time):
if self.grade>3 and time>2:
return True
elif self.grade<3 and time<0.5:
return True
else:
return False
Anny=Student('zhang',5,3,'math')
tech_wang=Teacher('wang','math')
tech_said=tech_wang.evaluate(Anny.do_work(1))
print("Teacher {0} said {1}".format(tech_wang.name,tech_said))
类属性与实例属性
类属性,又称静态属性,只有通过类才能修改,实例具有类属性,但是不能修改类属性
非常生动的例子,类是工厂,实例就是工厂生产的一辆小汽车,车主给小汽车加装小配件,当然不会对工厂产生影响
通过对属性的理解,理解self的作用(引用当前实例)
class Foo:
lang='python' # 类属性,但类属性不局限于此
# 类属性:只能通过类修改的属性
# 实例属性:通过实例修改的属性
def __init__(self,name):
self.name=name
you=Foo('abab')
you.group='123' # 通过实例修改,dir(Foo)中不会增加group属性,但是dir(you)会
print(set(dir(you)).difference(set(dir(Foo)))) # 两者的差集{'name', 'group'}
print(you.__dict__,Foo.__dict__) # __dict__ 对类而言,是显示当前所有的类属性,对实例而言则是显示实例属性
# you.__dict__ {'name': 'abab', 'group': '123'}
# Foo.__dict__ {'__module__': '__main__', 'lang': 'python', '__init__': <function Foo.__init__ at 0x00000164B03345E0>, '__dict__': <attribute '__dict__' of 'Foo' objects>, '__weakref__': <attribute '__weakref__' of 'Foo' objects>, '__doc__': None}
Foo.group='123' # 这样修改,group才是类属性
# plus:类属性和实例属性会互相覆盖,这个稍微试一下就知道了,当然可以通过del Foo.lang避免覆盖
类方法、静态方法与实例方法
只需充分理解类与实例的区别即可
class Foo:
def method(self,x): # 实例方法,注意,这里传入的可是两个参数而不是一个参数,self参数是在调用__init__的时候就传入的!
return x**2
f=Foo() # 类的实例化,相当于传入了self参数
f.method(2) # 4
Foo.method(2) # 报错:missing 1 required positional argument
class Foo:
@classmethod
def method(x): # 类方法,与实例无关
return x**2
Foo.method(2) # 4
class Foo:
def __init__(self,name):
self.name=name
@classmethod # 类方法:是用于模拟java定义多个实例构造函数的
def method(cls,name_plus): # cls代表的是类(不是实例)
print(cls) # <class '__main__.Foo'>
data=cls(' '.split(name_plus)[1]) # 实例化
return data
f=Foo.method("W wt")
实例:datatime库的模拟
class Data:
def __init__(self,year=0,month=0,day=0):
self.year=year
self.month=month
self.day=day
@classmethod # 类方法
def form_string(cls,data_as_string):
year,month,day=map(int,data_as_string.split('-'))
data1=cls(year,month,day)
return data1
@staticmethod # 静态方法
def is_data_valid(data_as_string):
year,month,day=map(int,data_as_string.split('-'))
return day<=31 and month<= 12 and year<=2038
d=Data.form_string('2019-11-11')
is_data=Data.is_data_valid('2019-11-11')
print(is_data)
print(d)
有关类方法的使用用途(@classmethod)
用于模拟java定义多个构造函数的情况(实例如上)
静态方法也可实现,只不过每次都要写上类名,不太方便
静态方法里可以利用self调用其他静态方法和类方法,类方法也是一样
类的继承
继承是对象的特性,也是面向对象编程的一个重要概念
单继承
class P:
def __init__(self,name):
self.name=name
def eat(self):
print("fish")
@classmethod
def hh(cls):
pass
@staticmethod
def yy():
pass
class C(P): # C类是父类P的子类
pass # 啥也不做
c=C('name')
dir(c) # 现在实现的是单继承
继承的覆盖
# 类P是上面代码块出现的那个
class E(P):# 子类的同名方法会实现覆盖(子盖父),其余继承
def __init__(self,age):
self.age=age
def eat_plus(self):
pass
@classmethod
def hh1(cls):
pass
@staticmethod
def yy1():
pass
e=E('age')
print(set(dir(e)).difference(set(dir(c)))) # {'age', 'eat_plus', 'hh1', 'yy1'} age覆盖了name
多继承
很少用,一笔带过
class K1:
def foo(self):
print("k1-foo")
class K2:
def foo(self):
print("k2-foo")
def bar(self):
print("k2-bar")
class J1(K1,K2):
pass
class J2(K1,K2):
def bar(self):
print("J2-bar")
class C(J1,J2):
pass
# 实际编程中多使用单继承
print(C.__mro__) # (同名函数被哪个类覆盖,看这个查找顺序即可)查看C这个类的查找顺序
j=J1()
j.foo()
J1.__mro__
对象性质:多态与封装
多态
不是啥神秘的东西,python语言具有天然的多态性
def add(x,y):
return x+y # x,y的类型多样,这就是多态的特点
print(add(1,3))
print(add("32",'dfs'))
封装
封装是对象的特性之一,python通过私有化的方式实现封装
class Foo:
__name='wwt' # 属性前加双下划线,表示属性被私有化,封装到作用域里面,作用域外不能访问
book='python'
def printf(self): # 如果方法要私有化,同样是在方法名前+'__',具体情况和属性相同
print(self.book)
print(self.__name)
print(Foo.book)
try:
print(Foo.__name) # 作用域外无法访问,实例化也是一样
except:
print("Error")
f=Foo()
f.printf() # 只能通过类内部的方法简介访问
定制类
暂时放弃,以后有时间在来补充
控制属性访问
1.优化内存__slots__
2.与属性相关的特殊方法__getattr__、setattr
其余同样有时间再来补充
迭代器和生成器
迭代器(iterator)
迭代器是访问可迭代对象的工具
可迭代对象,具有__iter__方法(使用dir或者hasattr查看)
迭代器是指用iter(obj)函数返回的对象(实例)
迭代器是指用next(it)函数获取可迭代对象的数据
iter(iterable)从可迭代对象中返回一个迭代器,iterable必须是能提供一个迭代器的对象
next(iterator) 从迭代器iterator中获取下一了记录,如果无法获取下一条记录,则触发stoptrerator异常
而且迭代器和range函数有相同的特点,那就是创建时不读入内存,所以哪怕创建无限大的迭代器对象都没事
基本性质展示
lst=[1,2,3,4] # 列表元素直接读入内存
iter_lst=iter(lst) # 迭代器对象还未读入内存
print(iter_lst) # <list_iterator object at 0x0000023CA398C970>迭代器对象
print(hasattr(iter_lst,"__iter__")) # True 查看iter_lst 是否具备__iter__方法
print(hasattr(iter_lst,"__next__")) # True 这是和迭代器的独特方法iterator.__next__与next(iterator)等价
print(hasattr(lst,"__next__")) # False
iter_lst=iter(lst) # 相当于初始化指针到头
iter_lst.__next__() # 相当于是指针移动,移动一次将一个元素读入内存,移动到末尾,指针指向空所以报错
for i in iter_lst:
print(i)
用类的方法定义迭代器
这是迭代器应用的关键,可以自定义__iter__和__next__,借此能很方便的获得斐波那契数列等
下面以获取斐波那契数列举例
class Fib:
def __init__(self,n):
self.n=n
self.a=0
self.b=1
def __iter__(self): # 表示对象可迭代,只在迭代开始是自动运行一次(也可以用于初始化),返回实例对象
print("iter start")
return self
def __next__(self):
fib=self.a
self.a,self.b=self.b,self.a+self.b
if self.a>self.n:
raise StopIteration()
return fib
print("Fib:",[i for i in Fib(100)]) # 100以内的斐波那契数列
# 输出
# iter start
# Fib: [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55]
# 也可以这样创建
x=Fib(1000000000000) # 因为创建不会读入内存
print("Fib:",[x.__next__() for i in range(10)])
plus:python中还有专门用于创建迭代器的模块itertools
,感兴趣的可以额外学习一下
生成器
生成器本质是一个使用了yield返回值的函数,支持使用next()函数不断返回下一个值,同时支持使用send函数向生成器发送消息
可以解决无限个变量和优先内存之间矛盾的问题,可以优化内存使用效率
因为比如一个包含1万个变量的列表,和一个包含推导算法的生成器,其内存占用空间,可能前者是后者的几个数量级倍数,比如下面的函数举例
a=[i for i in range(10000)]
print(sys.getsizeof(a)) # 87616
a=(i for i in range(10000)) # 列表解析生成器,下面会讲
print(sys.getsizeof(a)) # 112
生成器创建方法
- 使用函数创建
函数内部需要实现一个循环体,并实现返回值推导算法,并由yield返回每次推导出来的值
yield关键词(如果返回多个值则以元组的形式返回)
类似return,将指定值或多个值返回给调用方
记录此次返回或遍历的位置,返回数值之后,挂起,直到下一次执行next函数,再重新从挂起点接着运行(类似断点的作用)
yield语句可以接受通过生成器send方法传入的参数并赋值给一个变量,以动态调整生成器的行为表现
'''
利用生成器开发的斐波那契数列
'''
def gen():
a=0
b=1
while True:
c=a
yield c
a,b=b,a+b
g=gen()
print([g.__next__() for i in range(20)])
- 使用列表解析创建(很简单)
g=(i*3 if i<10 else i*5 for i in range(100))
生成器中的send和return
在一个生成器函数中,如果没有 return,则默认执行至函数完毕,如果在执行过程中 return,则直接抛出 StopIteration 终止迭代
yield接受通过send传入的参数并动态调整生成器行为
def gene(maxcount):
a,b=0,1
count=1
whlie True:
if count>maxcount:
# 直接退出函数体
return
else:
yield a
# yield返回值后,函数在该处挂起,下次再从这里恢复运行
a,b=b,a+b
#------------------------------------#
def gene(maxcount):
a,b=0,1
count=1
while True:
if count>maxcount:
return
else:
msg=yield a+b # msg获取传入参数
if msg=='stop':
return
a,b=b,a+b
count+=1
g=gene(10)
for i in g:
print(i) # 1 2 3 5 8 13 21 34 55
print(g.send('2333'))
可迭代对象的深入理解(__iter__与__getitem__)
以后有时间再来介绍(暑假结束之前会完善)
简单的介绍几个库
以后有时间再来介绍(暑假结束之前会完善)