Python
基础语法
1.跨平台 面向对象 解释型编程语言
python是解释型语言,但为了提高运行速度,使用了一种编译的方法。编译之后得到pyc文件,存储了字节码(特定于Python的表现形式,不是机器码)。
源代码 – 编译 --> 字节码 – 解释 --> 机器码
|————1次———|
1. 由源代码转变成机器码的过程分成两类:编译和解释。
2. 编译:在程序运行之前,通过编译器将源代码变成机器码,例如:C语言。
-- 优点:运行速度快
-- 缺点:开发效率低,不能跨平台。
3. 解释:在程序运行之时,通过解释器对程序逐行翻译,然后执行。例如Javascript
-- 优点:开发效率高,可以跨平台;
-- 缺点:运行速度慢。
-
模块单导入,行结尾不加分好
-
命名规范:组件小写,类骆驼,常量名大写,类的属性和方法小写1.项目名称 首字母大写+大写式驼峰, ProjectName 2.模块名和包名 全部小写+下划线驼峰 module_name package_name 3.类名称,异常 首字母大写+大写式驼峰, class ClassName: ,ExceptionName 4.全局变量、常量 全部使用大写字母+下划线驼峰 GLOBAL_VAR_NAME,CONSTANT_NAME 5.方法名,函数名,其余变量,参数,实例 全部小写+下划线驼峰 method_name,function_name,instance_var_name, function_parameter_name, local_var_name 6.处理计数器外,不使用单字母命名
-
使用下划线“_”开头的模块变量和函数是受保护的,不能变量导入,表示不能直接访问的类属性
-
使用双下划綫“__”的变量和方法是类私有的,是类的私有成员
-
以双下划綫开头和结尾是Python的专用标识
__init__()
标识构造函数
注释
#
‘’‘……’‘’
‘’‘‘’‘……’‘’’‘’
变量
变量命名
慎用小写字母l和大写字母O
允许多个变量指向同一个值(享元,节约内存,和C#中string 类似)
#在Python中,整数和短小的字符(不含空格),Python都会缓存这些对象,以便重复使用
True
a = 1
b = 1
print(a is b)
# True
a = "good"
b = "good"
print(a is b)
# False
a = "very good morning"
b = "very good morning"
print(a is b)
# False
a = []
b = []
print(a is b)
1、Python缓存了整数和短字符串,因此每个对象在内存中只存有一份,引用所指对象就是相同的,即使使用赋值语句,也只是创造新的引用,而不是对象本身;
2、python对于字符串来说,如果不包含空格的字符串,则不会重新分配对象空间,对于包含空格的字符串则会重新分配
数字类型(整数,浮点数,复数)
不可变性(和C# string 类似);当给一个数字类型修改变量时,先开辟一个内存空间,放置这个值,然后再讲这个数字变量指向这个内存地址
整数:位数无限制,数比较大时会在数字后自动加上l或L(2.x版本),十进制不能以0开头,0除外
八进制数:以 0o开头 或 0O开头(3.x),在 2.x可以以 0 开头
十六进制:0x 或 0X开头
字符串:
不可变性
用’’,"",’’’ ‘’’,""" “”"包含
转义字符 \
取消\转义 r或者R,和C# @类似
布尔
所有对象都可以进行真值测试
运算符
// 取整 7//2 为3
** 幂 2**4 为16
逻辑运算符 用and or not ,位运算和C# 一样
条件表达式(没有括号,有冒号)
if a<b:
...
else:
...
while none:
...
for a in range(1,10,2): # 和C# foreach类似,还可以遍历字符串,用来输出字符
print(i,end='-') #表示按一行输出1-3-5-7-9; #end='' 用来定义分隔符 Python 3.x;python 2.x 用 print(i, )表示
continue ,break 和c#用法一致
pass 是一个占位符,不做任何事情,可留做以后做修改
eg:
for i in range(1,10):
if i%2==0:
print(i,end=' ')
else
pass #占位符,不做任何处理,方便以后对不是偶数的数进行处理
类似三元运算符
gender = 1 if input('请输入性别\n') == '男' else 0 # 类似三元运算符
迭代器
要实现迭代,则必须要有如下方法
__iter__()
list01 = [10,20,15,76,28]
# for item in list01:
# print(item)
#原理:
#通过__iter__获取迭代器对象(10,20,15,76,28)
iterator = list01.__iter__()
while True:
#如果迭代器中没有可以继续__next__的值
#会抛出"停止迭代" 异常
try:
item = iterator.__next__()#StopIteration
print(item)
except StopIteration:
break
for item in iterator:
print(item)
#一个迭代器只能使用一次
for item in iterator:
print(item)
自定义迭代器
from collections import Iterable, Iterator
class Person:
def __init__(self, persion_list):
self.persion_list = persion_list
def __iter__(self):
return Myiterator(self.persion_list) # 调用我们重写的迭代器方法
class Myiterator(Iterator): # 继承Iterator 就不需要写__iter__,直接调用父类的.
def __init__(self, persion_list):
self.persion_list = persion_list
self.index = 0 # 由于iterator 是没有index 的,这个要我们手动添加
def __next__(self): # 这个就是迭代器的取值逻辑
while True: # 当为false 的时候结束循环
try:
word = self.persion_list[self.index] # 取值动作
except IndexError: # index 当变得超出persion_list 会报错的 ,先抓住这个异常
raise StopIteration # 迭代到没有值要用这异常,咱们把异常做个转化
self.index = self.index + 1 # 递增我们的index
return word # 返回取到的值
body = Person(["Xiuwu", "Adny", "Maggie"])
a = iter(body) # 调用我们自定义的迭代器方法
print(a) # <__main__.Myiterator object at 0x00000000022399B0>
# 从打印结果看说明我们自定义的迭代器方法生效了,已经返回一个迭代器
print(next(a)) # Xiuwu
# 从打印结果我们判断出 __next__生效,如果想循环输出,自己试试for 循环.
for item in body:
print(item)
class SkillManager:
"""
技能管理器 可迭代对象
"""
def __init__(self):
self.__skills = []
def add_skill(self,str_skill):
self.__skills.append(str_skill)
def __iter__(self):
# return SkillIterator(self.__skills)
# 执行过程:
# 1. 调用__iter__()不执行
# 2. 调用__next__()才执行当前代码
# 3. 执行到yield语句暂时离开
# 4. 再次调用__next__()继续执行
# ....
# yield作用:标记着下列代码会自动转换为迭代器代码.
# 转换大致过程:
# 1. 将yield关键字以前的代码,放到next方法中。
# 2. 将yield关键字后面的数据,作为next返回值.
# print("准备数据:")
# yield "降龙十八掌"
#
# print("准备数据:")
# yield "黑虎掏心"
#
# print("准备数据:")
# yield "六脉神剑"
for item in self.__skills:
yield item
manager = SkillManager()
manager.add_skill("降龙十八掌")
manager.add_skill("黑虎掏心")
manager.add_skill("六脉神剑")
for item in manager:
print(item)
iterator = manager.__iter__()
while True:
try:
item = iterator.__next__()
print(item)
except StopIteration:
break
迭代
可迭代对象:__iter__() 可以for
迭代器:__next__() 可以迭代(取元素)
class 迭代器:
def __next__():
pass
class 自定义类:
def __iter__():
return 迭代器()
for item in 自定义类():
pass
生成器
价值:惰性/延迟操作(省内存)
生成器函数
def 函数名():
yield 数据
yield:返回多个数据
return:返回一个数据
生成器表达式
for item in (变量 for 变量 in 可迭代对象 if 条件):
...
class 生成器本质:
def __iter__():
return self
def __next__():
pass
序列
连续内存空间,按一定顺序排列,每一元素分配一个索引
含:列表,元组,集合,字典,字符串。集合和字典不支持索引,切片,相加和相乘
序列运算
**索引 **
可以为负数,表示 从右 至左,负数起始值为 -1 ,表示最后一个元素。从左至右,起始值为0**
**切片 **
表示获取一定范围内的元素 sname[start : end : step]** #包含start,不包含end
strarr=["abc0","def1","dgd2","dsgs3","sdf4","dffas5","cgs6","ujy7","jhkf8"]
a1=strarr[2]
a2=strarr[-1]
print(a1) #dgd2
print(a2) #jhkf8
for s in strarr[1:7:2]:
print(s,end=' ') #输出def1 dsgs3 dffas5
print('\n')
for s in strarr[:8:3]:
print(s,end='-') #输出abc0-dsgs3-cgs6-
print('\n')
asfs=strarr[:] #表示复制整个序列
for s in asfs:
print(s,end=',') #输出abc0,def1,dgd2,dsgs3,sdf4,dffas5,cgs6,ujy7,jhkf8,
list01=[['A','B'],'哪吒','c']
list02 = list01[::-1] #表示按倒序赋值
print(list02) # ['c','哪吒',['A','B']]
序列相加:
将俩个 相同类型 序列合并,不会去除重复项,相同类型是指同为列表,元组,字符串,而不是指元素类型相同
arr1=arr2+arr3
strarr1=[1,2,3]
print(strarr+strarr1)
#输出abc0,def1,dgd2,dsgs3,sdf4,dffas5,cgs6,ujy7,jhkf8,['abc0', 'def1', 'dgd2', 'dsgs3', 'sdf4', 'dffas5', 'cgs6', 'ujy7', 'jhkf8', 1, 2, 3]
序列乘法:
示将其中的元素再依次重复出现多少次,可用于初始化序列长度
序列赋值
name,age = ['shibw',18]
print(name,age) #shibw 18
判断包含 in
print(“testin” in strarr1) #false
print(“testin” not in strarr1) #true
len(strarr1) 获取长度
max(strarr1) 最大值,元素要同类型
min(strarr1)最小值
sum(整数序列)求和
str(),list() 序列类型转换
sorted(strarr1),reversed(strarr1):正序和倒序
enumerate() 将序列组合为一个索引序列,多用在for循环中
arr=[“sdf”,“ewr”,“Rty”]
for index,item in enumerate(strarr1):
print(index,item) # eg: 0 sdf ……
列表[]
添加,修改,删除列表元素
strarr1.apend(“abc”) strarr1.insert(1,“abc”) strarr1.extend(arr) strarr1.pop()
添加
apend表示在末尾添加,
insert 在指定索引处添加,效率没apend高,一般不推荐使用。
extend 和序列+运算一样,将一个列表全部添加进去,.pop表示弹出最后一个元素。
修改
直接用=重新赋值
删除
del strarr1[1] 删除指定索引的元素
del strarr1删除整个列表,一般不用,自带垃圾回收
strarr1.remove(“abc”),删除值为abc的元素,如果不存在会报错,通常用strarr1.count(“abc”)>0判断是否存在
del使用场景:使用del删除指定位置元素,变量,而无法删除数据
remove使用场景:使用remove()删除指定值,如果不确定或不关心元素在列表中的位置,可以使用remove()根据指定的值来删除元素。
pop使用场景:使用pop()获取并删除指定位置元素。使用pop()同样可以获取列表中指定位置的元素,但在获取完成之后,该元素会自动被删除。如果为pop(off)指定了偏移量,它会返回偏移量对应位置的元素。如果不指定,则默认使用-1(即默认删除末位元素)。因此pop(0)将返回头元素,而pop()或pop(-1)则会返回列表的尾元素
clear() 清空所有数据 ,返回None
列表其它计算
strarr1.count(“abc”)>0 #返回true 表示abc在strarr1中
strarr1.index(“abc”) #返回首次出现的索引
sum(listname[,start]) #计算数值列表的和,start表示统计结果从哪个数开始,默认为0 ,相当于是listname的和加上start的值
sort(key=none,reverse=false) key提取的比较键,reverse表示是否倒序.中文排序不支持
arr=[1,3,4,2,45]
s1=sum(arr)
s2=sum(arr,2)
print(s1,s2) # 55 57
char=['cat','Tome','Angela','pet']
char.pop() #弹出最后一个元素
schar=char.sort() #sort 无返回,列表顺序变化
print(schar,char) #None ['Angela', 'Tome', 'cat', 'pet']
char.sort(key=str.lower,reverse=True)
print(char) #['Tome', 'pet', 'cat', 'Angela']
print(sorted(char,key=str.lower,reverse=False))
#sorted有返回一个排序后的副本,不会改变原列表 ['Angela', 'cat', 'pet', 'Tome']
print(char) #['Tome', 'pet', 'cat', 'Angela']
list01 = [0,1,2]
list01[1:2] = ['A','B']#[0,'A','B',2]
列表推导式
newlist=[expresion for var in list]
any = [random.randint(10, 100) for i in range(10)]
print(any)
newchar=[x+"test" for x in char]
print(newchar)
newfilter=[x for x in char if len(x)>3]
print(newfilter)
元组(,)
有序,不可变
和列表相比,元组是不可变序列,元素不可以单独修改;列表是可变序列,元素可随意修改;
元组用()定义,也可以省略,每个元素用,隔开;列表用[]定义,不可省略。若元组只有一个元素,则这个元素要跟一个“,”,不然会被解析成字符串
import random
char = ['cat', 'Tome', 'Angela', 'pet']
char.pop() #弹出最后一个元素
print(char)
schar = char.sort() # sort 无返回
print(schar, char) # None ['Angela', 'Tome', 'cat', 'pet']
char.sort(key=str.lower, reverse=True)
print(char)
any = (random.randint(10, 100) for i in range(10))
print(any)
newchar=(x+"test" for x in char)
print(newchar)
newfilter=(x for x in char if len(x)>3)
print(newfilter)
元组比列表的访问和处理速度快
列表不能作为字典的键,而元组可以
字典{key:name,key1:name1}
可变,键值对,键不可变,任意类型,任意嵌套,无序
dictionary = {'name': 'kcs', 'age': 30, 'sex': '男', "address": "深圳"} # 创建字典
print(dictionary['name']) #kcs
dicEmpty = {} # 空字典创建
dicEmpty1 = dict()
print(dicEmpty, dicEmpty1) #{} {}
listname = ['A', 'B', 'C']
listshow = ('s', 'd', 'e')
zipt = zip(listname, listshow)
# zip 将俩个列表或元组,组合成元组,并返回包含这些对象的zip对象,可用tuple转化为元组
print(zipt) # <zip object at 0x000000000253E800>
print(zip(listname, listshow)) # <zip object at 0x00000000024F2900> !主要这俩个对象不一样需回头了解原因
print(tuple(zipt)) (('A', 's'), ('B', 'd'), ('C', 'e'))
dic = dict(zipt) # dict和zip配合生成字典,
print(dic) # {} !需回头了解原因
print(dict(zipt)) # {}
print(dict(zip(listname, listshow))) # {'A': 's', 'B': 'd', 'C': 'e'}
print(dic)
#通过函数创建字典
dict02 = dict([['name','shibw'],['age',30]]) # 列表 元组中的只能由一对
print(dict02)## {'name': 'shibw', 'age': 30}
dict02 = dict([['name','shibw'],('age',20)])
print(dict02)#{'name': 'shibw', 'age': 20}
dictdemo = dict(a='1', b=2) # 键不用引号
print(dictdemo) # {'a': '1', 'b': 2}
lst = ['tp', 'sd']
dicnone = dict.fromkeys(lst) # dict.fromkeys创建值为none的字典
print(dicnone) #{'tp': None, 'sd': None}
tstuple = ('asf', 'asf', 'test')
listDemo = ["sf", "rte", "te"]
dicTtt = {tstuple: listDemo}
print(dicTtt) # {('asf', 'asf', 'test'): ['sf', 'rte', 'te']}
print(dicTtt[tstuple]) # ['sf', 'rte', 'te']
dictionaryDemo = {'name': 'kcs', 'age': 30, 'sex': '男', "address": "深圳"}
if 'name' in dictionaryDemo: # 键是否存在于字典
print(dictionaryDemo.get('name')) # Python推荐用get kcs
print(dictionaryDemo['name']) # kcs
a = dictionaryDemo.pop('age') # 删除指定键,并返回值
#同get 但是键不存在时 会添加键值对
print(dictionaryDemo.setdefault('address','beijing'))
print(a) # 30
dictionaryDemo.popitem() # 删除最后一项
del dictdemo
dicTtt.clear()
print(dicTtt) # {}
dictFor = {'name': 'kcs', 'age': 30, 'sex': '男', "address": "深圳"}
for item in dictFor:
print(item,end=" ") # 输出键值 name age sex address
for item in dictFor.items():
print(item,end='')
# 输出键值对('name', 'kcs') ('age', 30) ('sex', '男') ('address', '深圳')
for index, value in dictFor.items():
print(index, value,end=' ') # 输出键和value name kcs age 30 sex 男 address 深圳
dictFor["tel"]=1856566 #添加
dictFor["address"]="szlg" #修改
del dictFor['age'] #删除,不存在时报错
print(dictFor) # {'name': 'kcs', 'sex': '男', 'address': 'szlg', 'tel': 1856566}
dict01 = {'name':'shibw','age':20}
dict02 = {'name':'laowang','address':'北京'}
#使用02更新字典01 如果02中的键在01中不存在 添加
#如果02的键在01中存在 使用02的值
dict01.update(dict02)
print(dict01) #{'name': 'laowang', 'age': 20, 'address': '北京'}
dict01 = {1:"1.0",2:"2.0"}
print(dict01)
dict01 = {(1,2,3):3,(1,2,3,4):4}
print(dict01)
#可变类型无法通过哈希算法运算
#所以字典的键都是不可变类型
dict01 = {[1,2,3]:3}#TypeError
print(dict01)
字典推导式
dicRandom={i:random.randint(10,100) for i in range(1,5)}
print(dicRandom) # {1: 82, 2: 40, 3: 46, 4: 10}
name=['a','b','c']
age=[12,23,34]
dicZip={i:str(j)+"岁" for i,j in zip(name,age)}
print(dicZip) # {'a': '12岁', 'b': '23岁', 'c': '34岁'}
dicIf = {i: j for i, j in dicZip.items() if j == '23岁'}
print(dicIf) # {'b': '23岁'}
集合{a,b,c}
不重复 可变集合&不可变集合
setDemo = {1, 2, 3, 5, 1, 5, 23}
print(setDemo) # {1, 2, 3, 5, 23}
setDemo1 = set("集合测试")
print(setDemo1) #{'测', '试', '集', '合'}
print(set()) # 不能用{}表示空集合,{}表示空字典 #set()
tupleDemo=('asdf', 'fd', 'ert')
set1=set(tupleDemo)
print(set1) #{'ert', 'fd', 'asdf'}
set1.add("add")
print(set1) #{'ert', 'add', 'fd', 'asdf'}
set1.remove('asdf')
print(set1) #{'ert', 'add', 'fd'}
set1.pop()
print(set1) #{'add', 'fd'}
set1.clear()
print(set1) #set()
set1={'ert', 'add', 'fd', 'asdf'}
set2={'add','muti'}
print(set1&set2) #{'add'}
print(set1-set2) #{'ert', 'fd', 'asdf'}
print(set1|set2) #{'add', 'muti', 'ert', 'fd', 'asdf'}
print(set1^set2) #{'muti', 'ert', 'fd', 'asdf'}
s1 = {1,2,3}
s2 = {1,2}
#子集
#< 判断一个集合所有元素是否完全在另一个集合中
#如果s1中包含了s2的全部元素 我们就把s2称为s1的子集
print(s2<s1)#True
#超集(super)
#> 判断一个集合是否包含另一个集合的所有元素
#如果s1中包含了s2的全部元素 我们就把s1称为s2的超集
print(s1>s2)#True
类型总结
#任意数据类型 可变序列
#元组 tuple()
#任意数据类型 不可变序列
#序列的通用操作
# + 拼接两个序列
# * 生成重复的序列
# += *=
# 索引 / 切片
#字典 dict()
#键必须是不可变类型 值可以为任意对象
#可变散列对象
#集合 set()
#只能包含不可变类型 可变散列对象
#固定集合 frozenset() 不可变散列对象
# None
# int float bool complex str list tuple dict set
#可变 list dict set
#不可变 数字 str tuple
浅拷贝 /深拷贝
浅拷贝
list01 = ['三丰','翠山','无忌']
#把list01中储存的地址赋值给list02
list02 = list01
list01[0] = '张三丰'
print(list02[0])#张三丰
list01 = [800, 900, 1000]
list02 = list01
list03 = list01
list01[0] = '八百'
print(list02[0]) # '八百'
list03[1] = '九百'
print(list02) # ['八百', '九百', 1000]
print(id(list03))
list03 = '九九百' # 这里直接改变了类型和引用
print(id(list03))
print(list03)
print(list02) # ['八百', '九百', 1000]
#
#
list01 = [800, 900, 1000]
list02 = list01[:] # list01[:] 第一层引用是深拷贝
list01[0] = '八百'
print(list02) # [800, 900, 1000]
a = [1,2,3, [3,4,5]]
b=a[:]
a.append('a')
b.append('b')
print(a) # [1, 2, 3, [3, 4, 5], 'a']
print(b) # [1, 2, 3, [3, 4, 5], 'b']
a[3].append('aa')
b[3].append('bb')
print(a) # [1, 2, 3, [3, 4, 5, 'aa', 'bb'], 'a']
print(b) # [1, 2, 3, [3, 4, 5, 'aa', 'bb'], 'b']
深拷贝
import copy
list01 = [100, [200, 300]]
# 深拷贝 划清界限 拷贝前的对象和拷贝后的对象互不影响
# 注意 深拷贝可能会占用大量内存
list02 = copy.deepcopy(list01)
list01[1][0] = 500
print(list01) # [100, [500, 300]]
print(list02) # [100, [200, 300]]
内置高阶函数 filter map max sorted(lambda,类似委托,linq)
import ListHelper
class Enemy:
def __init__(self, name, hp,atk=None, defense=None):
self.name = name
self.hp = hp
self.atk = atk
self.defense = defense
enemy_list = [
Enemy('玄冥二老',86,80,120),
Enemy('成昆',0,0,150),
Enemy('谢逊',120,50,150),
Enemy('灭霸',0,0,999)
]
# 1. 在敌人列表中查找所有死人
for item in filter(lambda item:item.hp == 0,enemy_list):
print(item.name)
for item in ListHelper.find_all(enemy_list,lambda item:item.hp == 0):
print(item.name)
# 2.在敌人列表中查找所有敌人的名称
for item in map(lambda item:item.name,enemy_list):
print(item)
for item in ListHelper.select(enemy_list,lambda item:item.name):
print(item)
# 3. 获取攻击力最大的敌人
re = max(enemy_list,key = lambda item:item.atk )
print(re.name)
re1=ListHelper.get_max(enemy_list,lambda item:item.atk )
print(re1.name)
# 4. 对敌人列表根据攻击力升序排列
# sorted 返回排好序的数据
for item in sorted(enemy_list,key = lambda item:item.atk):
print(item.atk)
# 对敌人列表根据攻击力降序排列
# sorted 返回排好序的数据
for item in sorted(enemy_list,key = lambda item:item.atk,reverse=True):
print(item.atk)
for item in ListHelper.order_by(enemy_list,lambda item:item.atk):
print(item.atk)
ListHelper 如下,自定义扩展和上述高阶函数做对比
"""
定义项目中所有对容器的操作。
"""
class ListHelper:
"""
列表助手类
"""
@staticmethod
def find_all(iterable_target,func_condition):
"""
在可迭代对象中,查找满足条件的所有元素。
:param iterable_target:可迭代对象
:param func_condition:所有条件
:return:生成器对象
"""
for item in iterable_target:
if func_condition(item):
yield item
@staticmethod
def find_single(iterable_target,func_condition):
"""
在可迭代对象中,查找满足条件的单个元素。
:param iterable_target:可迭代对象
:param func_condition:所有条件
:return:元素
"""
for item in iterable_target:
if func_condition(item):
return item
@staticmethod
def sum(iterable_target,func_condition):
"""
在可迭代对象中,根据条件求和。
:param iterable_target:可迭代对象
:param func_condition:求和条件
:return:求和的数值
"""
sum_value = 0
for item in iterable_target:
sum_value += func_condition(item)
return sum_value
@staticmethod
def select(iterable_target,func_condition):
"""
在可迭代对象中,根据条件选择属性。
:param iterable_target:可迭代对象
:param func_condition:筛选条件
:return:生成器对象
"""
for item in iterable_target:
yield func_condition(item)
@staticmethod
def get_max(iterable_target,func_condition):
"""
在可迭代对象中,根据条件获取最大元素。
:param iterable_target:可迭代对象
:param func_condition:获取条件
:return:最大元素
"""
max_value = iterable_target[0]
for i in range(1,len(iterable_target)):
# if max_value.atk < iterable_target[i].atk:
if func_condition(max_value) < func_condition(iterable_target[i]):
max_value = iterable_target[i]
return max_value
@staticmethod
def order_by(iterable_target,func_condition):
"""
对可迭代对象, 根据任意条件进行升序排列
:param iterable_target: 可迭代对象
:param func_condition: 排序条件
"""
for r in range(len(iterable_target)-1):
for c in range(r+1,len(iterable_target)):
# if iterable_target[r].atk > iterable_target[c].atk:
if func_condition(iterable_target[r]) > func_condition(iterable_target[c]):
iterable_target[r],iterable_target[c] = iterable_target[c],iterable_target[r]
内存管理
可变类型,不可变类型
可变类型的地址不一样,但是其中的元素内存地址又是一致的
可变数据类型的意思就是说对一个变量进行操作时,其值是可变的,值的变化并不会引起新建对象,即地址是不会变的,只是地址中的内容变化了或者地址得到了扩充。
对可变数据类型的操作不能是直接进行新的赋值操作,比如说a = [1, 2, 3, 4, 5, 6, 7],这样的操作就不是改变值了,而是新建了一个新的对象
list01=[['A','B'],'哪吒','c']
list02 = list01[::-1]
print(list02) # ['c','哪吒',['A','B']]
print(list01 is list02) # False
print(id(list01)) # 41801472
print(id(list02)) # 41622848
#
list02.insert(0,5)
print(list02) # [5, 'c', '哪吒', ['A', 'B']]
print(id(list01[0])) # 41931904
print(id(list02[-1])) # 41931904
print(id(list01[1])) # 39551600
print(id(list02[-2])) # 39551600
print(list01[0] is list02[-1]) # True
# # 可变类型的地址不一样,但是其中的元素内存地址又是一致的
# # 可变数据类型的意思就是说对一个变量进行操作时,其值是可变的,值的变化并不会引起新建对象,即地址是不会变的,只是地址中的内容变化了或者地址得到了扩充。
# # 对可变数据类型的操作不能是直接进行新的赋值操作,比如说a = [1, 2, 3, 4, 5, 6, 7],这样的操作就不是改变值了,而是新建了一个新的对象
说明
**Python中的变量都是指针,这确实和之前学过的强类型语言是有不同的。**因为变量是指针,所以所有的变量无类型限制,可以指向任意对象。
指针的内存空间大小是与类型无关的,其内存空间只是保存了所指向数据的内存地址。
Python 的所有**变量其实都是指向内存中的对象的一个指针,所有的变量都是!此外,对象还分两类:一类是可修改的,一类是不可修改的。我的理解是把不可修改(mutable)的类型叫做值类型,值传递,可修改(immutable)类型叫,地址不变,按引用传递,比做引用类型。**
对象=确定内存空间+存储在这块内存空间中的值。
在Python中,数值(整型,浮点型),布尔型,字符串,元组属于值类型,本身(内存地址)不允许被修改(不可变类型),数值的修改实际上是让变量指向了一个新的对象(新创建的对象),所以不会发生共享内存问题。 这种方式同Java的不可变对象(String)实现方式相同 。
-
Python在底层做了一定的优化,对于使用过小整数以及短字符串都会被缓存起来。Python中会为匿名列表对象和匿名字典对象以及短字符串创建缓存区
-
之所以采用这种优化的方式,是因为python中数字和字符串一经创建都是不可修改的。所以不会出现,因使用了缓存的对象值造成“脏读”的问题
fd = 3.12
print(type(fd))
fd1 = 3.12
print(id(fd), id(fd1)) # 41491216 41491216
tuple1 = ('sdf', 23, 43)
tuple2 = ('sdf', 23, 43)
print(id(tuple1), id(tuple2)) # 40848768 40848768
# 不可变类型的享元,如上所示,相同值是共享同一内存地址的.
# 不可变类型的值是不可变的,是指其内存地址不可变,但如果改变不可变对象其中子对象的值,其子对象元素的地址没有变化,则是合法的,如下
tuple3= ('adf', 234, 54, ['asf', 34])
#tuple3[0]='34' # 改变了地址: 'tuple' object does not support item assignment
tuple3[3].append('test')
print(tuple3) #('adf', 234, 54, ['asf', 34, 'test'])
list01 = ['a', 'b', 'c']
list01[0] = ['A', 'B']
print(list01) # [['A','B'],'b','c']
# 将右边列表的值赋值给list01的第一个位置
print(list01[1:3])
list01[1:2] = ['哪吒']
# 序列赋值
# name,age = ['shibw',18]
# print(name,age)
print(list01) # [['A','B'],'哪吒','c']
list02 = list01[::-1]
print(list02) # ['c','哪吒',['A','B']]
print(list01 is list02) # False
print(id(list01)) # 41801472
print(id(list02)) # 41622848
list02.insert(0,5)
print(list02) # [5, 'c', '哪吒', ['A', 'B']]
print(id(list01[0])) # 41931904
print(id(list02[-1])) # 41931904
print(id(list01[1])) # 39551600
print(id(list02[-2])) # 39551600
print(list01[0] is list02[-1]) # True
# 可变类型的地址不一样,但是其中的元素内存地址又是一致的
# 可变数据类型的意思就是说对一个变量进行操作时,其值是可变的,值的变化并不会引起新建对象,即地址是不会变的,只是地址中的内容变化了或者地址得到了扩充。
# 对可变数据类型的操作不能是直接进行新的赋值操作,比如说a = [1, 2, 3, 4, 5, 6, 7],这样的操作就不是改变值了,而是新建了一个新的对象
list3=[True,2,['df','ty',45],'tr']
list4=['tr',['df','ty',45],2,True]
print(id(list3[0]),id(list4[-1])) # 8790904592208 8790904592208 # 序列中元素每一个元素指向一个地址,不可变对象值相同,指向的地址相同,可变的对象,代表不同的地址,不共享内存
print(id(list3[2]),id(list4[-3])) # 41873856 41867328
list3[2]=list4[-3]
print(id(list3[0]),id(list4[-1])) # 8790904592208 8790904592208
print(id(list3[2]),id(list4[-3])) # 41867328 41867328 # 可变对象赋值后聚有类似引用特性的性质
list3[2][0]=123
print(list4) # ['tr', [123, 'ty', 45], 2, '1']
tuple4=(True,2,['df','ty',45],'tr')
tuple5=(True,2,['df','ty',45],'tr')
print(tuple4==tuple5) #true
print(id(tuple4),id(tuple5)) #39015824 38971568 因为含有不可变对象,所以指针不同
tuple6=(True,2,('df','ty',45),'tr')
tuple7=(True,2,('df','ty',45),'tr')
print(tuple6==tuple7) #true
print(id(tuple6),id(tuple7)) #40720960 40720960 全是不可变对象,所以指针相同
变量是指针,变量赋值,实际是指针变更 。如上验证,相同值(不含可变类型)的不可变类型,在内存中只会存在一份,不会重复保存 。有这么一种机制,但是在测试的时候,也有可能出现数值大于256,id不一样的情况.
这里并没有出现长度限制,有说法是pycharm做了优化,但是有通过IDEL 执行py文件生成相同的结果,能肯定的是,python对不可变类型存在优化策略。需深入了解。。。
fd = 3.12
print(type(fd))
fd1 = 3.12
print(id(fd), id(fd1)) # 41491216 41491216
tuple1 = ('sdf', 23, 43)
tuple2 = ('sdf', 23, 43)
print(id(tuple1), id(tuple2)) # 40848768 40848768
# 不可变类型的享元,如上所示,相同值是共享同一内存地址的.
# 不可变类型的值是不可变的,是指其内存地址不可变,但如果改变不可变对象其中子对象的值,其子对象元素的地址没有变化,则是合法的,如下
tuple3= ('adf', 234, 54, ['asf', 34])
#tuple3[0]='34' # 改变了地址: 'tuple' object does not support item assignment
tuple3[3].append('test')
print(tuple3) #('adf', 234, 54, ['asf', 34, 'test'])
# 在Python中会为匿名列表对象和匿名字典对象以及短字符串创建缓存区
# 列表和字典这种可变对象当为匿名对象是会向外暴露一个内存地址,不论里面的内容怎么变,该地址不变,所以下面的地址都一样
# '''
# print(id([1, 2, 3]) == id([4, 5, 6])) # True
# print(id([1]) == id([1, 2, 3, 4, 5, 6])) # True
# print(id({1: 1, 2: 2}) == id({3: 3, 4: 4})) # True
# '''
# 但是如果存在对象的引用,即非匿名对象,上面的结果都是False,如下
# '''
# a = [1, 2, 3]
# b = [4, 5, 6]
# c = {1: 1, 2:5}
# d = {3: 3, 4: 4}
# print(id(a) == id(b) ) # Fasle
# print(id(c) == id(d) ) # Fasle
数据比较
id 比较的是对象的内存地址是否相等
== 比较的是对象的值是否相等
is 比较的是对象的内存地址和值是否相等
字符串
在Python中 常用的俩种字符串类型 str 和 bytes,通过encode()和decode()进行转换,转换时的默认编码格式为UTF-8。Python中str在内存中已Unicode表示
编码
# str.encode([encoding="UTF-8"][,errors="strict"])
# encoding 可省,编码方式
# errors 可省,异常处理方式 strict 非法字符抛异常 ignore 忽略非法字符 replace 用“?”替代非法字符 xmlcharrefreplace 使用xml字符引用
ver = "野渡无人舟自横"
verByte = ver.encode(encoding='UTF-8', errors="strict")
print(verByte) # b '\xe9\x87\x8e\xe6\xb8\xa1\xe6\x97\xa0\xe4\xba\xba\xe8\x88\x9f\xe8\x87\xaa\xe6\xa8\xaa'
verBack = verByte.decode(encoding="UTF-8", errors="strict")
print(verBack)
verGB = ver.encode("GBK")
verGBBack = verGB.decode("GBK")
print(verGB) # b '\xd2\xb0\xb6\xc9\xce\xde\xc8\xcb\xd6\xdb\xd7\xd4\xba\xe1'
print(verGBBack)
拼接,截取等
# 拼接 +
print("sfsf"+"---"+"sfas") #sfsf---sfas
# str.encode([encoding="UTF-8"][,errors="strict"])
# encoding 可省
# errors 异常处理方式 strict 非法字符抛异常 ignore 忽略非法字符 replace 用“?”替代非法字符 xmlcharrefreplace 使用xml字符引用
ver = "野渡无人舟自横"
verByte = ver.encode(encoding='UTF-8', errors="strict")
verByte1 = ver.encode()
print(verByte, verByte1) # b '\xe9\x87\x8e\xe6\xb8\xa1\xe6\x97\xa0\xe4\xba\xba\xe8\x88\x9f\xe8\x87\xaa\xe6\xa8\xaa'
verBack = verByte.decode(encoding="UTF-8", errors="strict")
print(verBack)
verGB = ver.encode("GBK")
verGBBack = verGB.decode("GBK")
print(verGB) # b '\xd2\xb0\xb6\xc9\xce\xde\xc8\xcb\xd6\xdb\xd7\xd4\xba\xe1'
print(verGBBack)
print("sfsf" + "---" + "sfas")
str = "野渡无人舟自横。test"
print(len(str)) # 12
print(len(str.encode())) # 28 UTF-8中文3个字节
print(len(str.encode("gbk"))) # 20 GBK中文俩个字节
# 截取 string[start:end:step] 截取字符串,start:end:step 都可省 ,string表示要处理的字符串
# 分割 str.split(sep,maxsplit) sep表示分隔符,可省,默认为None(包含空格,换行'\n' 制表符 ‘\t’ 等 );maxsplit表示分割的次数,分割的后的集合元素个数为maxsplit+1,-1或省略,则全部分割
print("渡无_人_舟自_横".split('_', 2)) # ['渡无', '人', '舟自_横']
# 合并 strnew= stringchar.join(iterable) stringchar表示用来合并连接iterable元素的字符,iterable表示可迭代对象,如列表,元组
lst = ["test", "测试", "kcs"]
print("".join(lst)) # test测试kcs
print("@" + "@".join(lst)) # @test@测试@kcs
# 计数 substr在str中出现的个数,start和end描述检索范围,可省略
str.count(substr[start[,end]])
# 首次 substr在str中首次出现的索引,不存在为-1。rfind表示从右边开始查找
str.find(substr[start[,end]])
# 首次索引 substr在str中首次出现的索引, 不存在时抛异常。rindex表示从右边开始查找
str.index(substr[start[,end]])
# 开始 str检索范围是否以str开头,start和end描述检索范围,可省略
str.startswith(substr[start[,end]])
# 结尾 str检索范围是否以str开头,start和end描述检索范围,可省略
str.endswith(substr[start[,end]])
# 小大写转换
str.lower() str.upper() str1.swapcase() #小写转大写 大写转小写
# 去除特殊字符 str.strip([chars]) 表示去除字符串str左右俩边的字符 lstrip表示去掉左边的,rstrip表示去掉右边的
#判断字符串中是不是全都为空白
print(str1.isspace())
#判断字符串中是不是全都为数字类型
num_str = '123456'
print(num_str.isdigit())
#修改
#替换 将旧的字符串替换成新的字符串
str1 = 'this is a test string'
print(str1.replace('is','are'))
#替换 最多换1次
print(str1.replace('is','are',1))
#如果指定的旧的字符串不存在 就什么都不做
print(str1.replace('there','that'))
str = " @ssfa . "
print(str.strip('@.')) # " @ssfa . "
print(str.strip('@. ')) # "ssfa" 去掉左右俩边的“@"和”.“和空字符
print(str.strip()) # "@ssfa ." 默认去掉左右俩边的空字符和 制表符\t 换行\n 回车符\r等特殊字符等
字符串格式化
# 格式化
# 格式化 方式1: ’%[-][+][0][m][.n]格式化字符‘%exp 说明: -|+|0 可选参数 分别表示左、右、右对齐,并为无符号数添加符号 m表示占有的宽度 n表示小数点位 exp 要转换的项,多个用元组表示,不能用列表
# 常用 格式化字符 : %s 字符串str %c 单个字符 %d %i 十进制整数 %f %F 浮点数 %r字符串repr %% 字符% %x 十六进制 %o 八进制 %e %E指数
strMoban = "姓名:%s 年龄:%03d 身高:%02.2f 米 "
pinfo = ('kcs', 30, 1.65)
strAll = strMoban % pinfo
print(strAll) # 姓名:kcs 年龄:030 身高:1.65 米
# 方式2 使用format 推荐方式 str.format(args) str:模板 args 要转换并替换占位的字符,多个用,隔开 模板定义 {[index][:[[fill]align][sign][#][
# width][.precision][type]]} index 表示占位符在参数列表的起始位置,默认0 , fill表示空白处填充字符 align 对齐方式用< > = ^分别表示左 右 右 居中对齐
# sign 用于给无符号数添加符号 用 + - 空格 表示 # 表示是否给数据加上进制标识 width占用宽度 precision 小数位 type 指定类型 S:字符串 D:十进制数 C:将十进制自动转换为Unicode字符
# b:将十进制转换为2进制 o:十转八 x X:十转十六 f F 浮点数 %显示百分比 e E 转为科学计数 g G:自动在e f ,E F中切换
strMb = "姓名:{:-<9s} 年龄:{:0<+#3d} 身高:{:0< 3.2f}米 鞋码:{:0<o} newage:{:0>b}"
strRet = strMb.format(" k c s", 30, 1.65, 39, 30)
print(strRet, "test") # 姓名: k c s--- 年龄:+30 身高: 1.65米 鞋码:47 newage:11110 test
strMb = "姓名:{0:-<9s} 年龄:{2:0<+#3d} 身高:{1:0< 3.2f}米 鞋码:{3:0<o} newage:{4:0>b}"
strRet = strMb.format(" k c s",1.65, 30, 39, 30)
print(strRet, "test") # 姓名: k c s--- 年龄:+30 身高: 1.65米 鞋码:47 newage:11110 test
时间函数
import time
#获取当前时间戳 从1970年1月1日到现在的总秒数
print(time.time()) #1592551864.3892887
#获取当前的时间元组
#(年 月 日 时 分 秒 一周的第几天(0 1 2...) 一年的第几天 夏令时)
print(time.localtime()) #time.struct_time(tm_year=2020, tm_mon=6, tm_mday=19, tm_hour=15, tm_min=31, tm_sec=4, tm_wday=4, tm_yday=171, tm_isdst=0)
#获取指定时间戳的时间元组
print(time.localtime(12345678)) #time.struct_time(tm_year=1970, tm_mon=5, tm_mday=24, tm_hour=5, tm_min=21, tm_sec=18, tm_wday=6, tm_yday=144, tm_isdst=0)
time_tuple = time.localtime()
#将时间元组变成时间戳
print(time.mktime(time_tuple)) #1592551864.0
print(time.struct_time.tm_year) #<member 'tm_year' of 'time.struct_time' objects> 取不到其值
for item in time_tuple:
print(item,end=';') # 2020;7;8;14;21;35;2;190;0;
print('\n')
#将时间元组转换成时间字符串
print(time.strftime('%y/%m/%d %H:%M:%S',time_tuple)) #20/06/19 15:31:04
print(time.strftime('%Y-%m-%d %H:%M:%S',time_tuple)) #2020-06-19 15:31:04
#将日期字符串转换为时间元组
print(time.strptime("19/08/21 11:31:30", '%y/%m/%d %H:%M:%S')) #time.struct_time(tm_year=2019, tm_mon=8, tm_mday=21, tm_hour=11, tm_min=31, tm_sec=30, tm_wday=2, tm_yday=233, tm_isdst=-1)
print(time.strptime("2019/08/21 11:31:30", '%Y/%m/%d %H:%M:%S')) #time.struct_time(tm_year=2019, tm_mon=8, tm_mday=21, tm_hour=11, tm_min=31, tm_sec=30, tm_wday=2, tm_yday=233, tm_isdst=-1)
print('开始运行')
time.sleep(5) #等待5秒
print('程序结束')
正则表达式
规则和C#几乎一致 用r R替代@取消\转义
. \w \s \d \b ^ $
? + * {n} {n,} {n,m}
模块名re ,需引入 import re
re.match(pattern,string[,flags])
re.match(pattern,string[,flag]) 返回match 对象: flag : A或ASCII 只进行ASCII匹配 ; I 或 IGNORECASE 忽略大小写 ; M或MUTILINE ; S 或 DOTAIL 表示.匹配任意字符,包含换行符 ; X或VERBOSE 忽略模式字符字符串中未转义的空格和注释
re.search(pattern,string[,flags])
re.findall(pattern,string[,flags])
re.sub(pattern,repl,string,count,flags)
re.split(pattern,string,[maxsplit],[flags])
!和C#不同是从头开始匹配,如果开头不匹配,则直接返回none,没有迭代过程
import re
strDemo = "阿sdf_sflweSDF344#SFasfasf%sf7e"
strPt = r"_sf\w+?#"
strPts=r"\w+?_sf\w+?#"
matchRet = re.match(strPt, strDemo, re.I)
matchRet1 = re.match(strPts, strDemo, re.I) #从头匹配的第一个
print(matchRet,matchRet1) # None !和C#不同是从头开始匹配,如果开头不匹配,则直接返回none,没有迭代过程 <re.Match object; span=(0, 18), match='阿sdf_sflweSDF344#'>
print("起始位置:{:d} 结束位置:{:d} 匹配位置的元组:{:s} 源字符:{:s} 匹配数据:{:s}".format(matchRet1.start(),matchRet1.end(),str(matchRet1.span()),matchRet1.string,matchRet1.group()))
# 起始位置:0 结束位置:18 匹配位置的元组:(0, 18) 源字符:阿sdf_sflweSDF344#SF 匹配数据:阿思sdf_sflweSDF344#
ret=re.search(strPt,strDemo,re.I) #匹配的第一个
print("起始位置:{:d} 结束位置:{:d} 匹配位置的元组:{:s} 源字符:{:s} 匹配数据:{:s}".format(ret.start(),ret.end(),str(ret.span()),ret.string,ret.group()))
# 起始位置:5 结束位置:18 匹配位置的元组:(5, 18) 源字符:阿sdf_sflweSDF344#SF 匹配数据:_sflweSDF344#
strPt = r"sf"
allRet=re.findall(strPt,strDemo,re.I) #查找全部匹配的字符串,并以列表返回
print(allRet) # ['sf', 'SF']
strRp=re.sub(strPt,'1111',strDemo,1,re.I) #阿sdf_1111lweSDF344#SFasfasf%sf7e 1表示只替换一次
print(strRp) # 阿sdf_1111lweSDF344#SF
strSplit=re.split(strPt,strDemo,2,re.I) #['阿sdf_', 'lweSDF344#', 'asfasf%sf7e'] 2表示分割俩次
print(strSplit)
函数
# def 定义,注意冒号: 多个参数用, 当传递的实参是不可变对象时,进行值传递,为可变对象时,进行引用传递
def functionname([pramlst]) : #命名规范:全小写
[’’‘comment’’’] # 和C#的///给函数添加注释类似
[funcbody] # 函数体,如果有返回值,则用return
def functionDemo(name,age)
print(“姓名:{😒} 年龄:{:d}”.format(name,age))
函数传参
'''
函数传参
实参传递方式
'''
def function_demo(name, age, des="说明"): # 有默认值,可以不传
print("姓名:{:s} 年龄:{:d} des:{:s}".format(name, age + 1, des))
def fun01(a,b,c):#形参 a b c
print(a)
print(b)
print(c)
#位置实参 实参是根据位置与形参对应的
#如果实参位置发生改变 会影响函数结果
fun01(10,20,30)
fun01(30,10,20)
#序列实参 用*将序列中的元素拆开然后与形参依次对应
#序列 字符串 列表 元组
list01 = [10,20,30]
fun01(*list01)
str01 = 'abcd'
fun01(*str01)#报错
#关键字实参
#实参的值与形参的名称对应
fun01(a=10,b=20,c=30)
#使用关键字实参 传参的顺序可以不固定
fun01(c=30,a=10,b=20)
fun01(a=10,b=20,d=40)#错误
#字典实参 使用**将字典拆开,字典中的键值对以关键字的形式进行对应,传递值
dict01 = {'a':10,'b':20,'c':30}
#a = 10 , b = 20 ,c = 30
fun01(**dict01)
#字典中的键的名字要与形参名对应
dict01 = {'a':10,'e':20,'d':30}
fun01(**dict01)#报错
# 混合使用
# 语法规定 先写位置参数 再写关键字参数
fun01(10,20,c=30)
# fun01(c=30,b=20,10)#报错
fun01(10,c=30,b=20)
#星号元组形参
#接受不定数量的实参 让实参的数量可以无限多
def fun02(p1,p2,*args):
print(p1) #1
print(p2) #2
print(*args) #3 4 5 6 7 8 9 10
fun02(1,2,3,4,5,6,7,8,9,10)
fun02(1,2)
#在星号元组形参以后的形参叫做命名关键字形参
#传递实参的过程中 必须指定形参的名字
def fun03(*args,p1 = '',p2):
print(*args) # 1 2 3 4 5 6
print(p1) #
print(p2) # 20
fun03(1,2,3,4,5,6,p2=20)
#双星号字典形参 让关键字实参的数量无限
#将关键字实参的变量名作为字典的键 值做为字典中对应键的值保存
def fun05(**kwargs):
print(kwargs)
fun05(a=10) #{'a': 10}
fun05(a=10,b=20,c=30) #{'a': 10, 'b': 20, 'c': 30}
#定义一个函数 函数中包含位置形参、星号元组形参、默认形参、命名关键字形参和双星号字典形参
def fun07(a,*args,b='bbb',c,**kwargs):
print(a) #1 #5
print(args) #() #(6,7)
print(b) #bbb #bbb
print(c) #ccc #abc
print(kwargs) #{} {'d':'798'}
fun07(1,c='ccc')
fun07(5,6,7,c='abc',d='789')
#定义一个函数 接受任意的参数 并输出结果
def fun08(*args,**kwargs):
for item in args:
print(item)
for k,v in kwargs.items():
print(k,v)
fun08()
fun08(5,6,7,a=123,b='789')
# 5
# 6
# 7
# a 123
# b 789
#全局变量,局部变量 定义在函数外,函数内,函数内的局部变量通过 global 修饰后,就成为全局变量
locals() 收集当前的局部变量 保存到字典
def user_info():
uesr_name = 'shibw'
user_age = 20
user_email = 'shibw@tedu.cn'
gender = '男'
address = '北京市东城区'
# return (uesr_name,user_age,uest_email,gender,address)
# return user_age
# return {'uesr_name':uesr_name,'user_age':user_age,'user_email':uesr_email,'gender':gender,'address':address}
#locals() 收集当前的局部变量 保存到字典
return locals()
#获取到返回的元组
print(user_info()) #{'uesr_name': 'shibw', 'user_age': 20, 'user_email': 'shibw@tedu.cn', 'gender': '男', 'address': '北京市东城区'}
inofs = user_info()
#输出详细信息
print('姓名:%s,年龄:%s,邮箱:%s,性别:%s,地址:%s' % (inofs['uesr_name'],inofs['user_age'],inofs['user_email'],inofs['gender'],inofs['address']) )
#姓名:shibw,年龄:20,邮箱:shibw@tedu.cn,性别:男,地址:北京市东城区
匿名函数
result=lambda[arg1[,arg2…]]:expression
"""
lambda : 匿名函数
作用:充当实参
"""
def fun01():
print("fun01")
fun01()
# 无参数 无返回值
a = lambda :print("fun01")
a()
def fun02(func):
print("fun02")
func()
# 将函数作为参数,建议使用lambda.
fun02(lambda :print("fun01"))
def fun03(a,b,c):
print("fun03")
# 有参数lambda
b = lambda a,b,c:print("fun03")
def fun04():
print("fun04")
print("fun04又执行喽")
# lambda 函数体只能有一句话
# fun02(lambda :print("fun04++");print("fun04又执行喽"))
# 不支持赋值语句
# fun02(lambda a:a.name = "zs")
面向对象
类,方法,属性
class ClassName: # 命名规范 驼峰命名
‘’‘类注释信息’’’ # 类说明
statement #类主体
创建类实例 cn=ClassName(paramlst)
paramlst是参数列表 ,可省
__init__(self) 构造函数,不手动创建时除self是无参的,手动创建必须包含一个self参数,指向实例本身的引用,且必须放在第一个
class ClassDemo:
name = "kcsdemo" # 类属性 定义在类中,实例方法之外,用法和C#的静态变量类似 各类实例共用
age = 60
address = "shenzhen"
__pinfo="private 信息"
_ptinfo="protected 信息"
def __init__(self):
self.des='实例属性,由实例定义,也只能由实例访问'
def instaceFunc(self, str): # 实例方法 定义在类中 第一参数必须是self
print("name:{:s} age {:d} address:{:s} other:{:s}".format(ClassDemo.name,ClassDemo.age,ClassDemo.address,str+"——"+ClassDemo.company+":"+self.des)) # 通过类名.类属性访问
ClassDemo.company='kingdee' # 自由的添加类属性
ins1=ClassDemo()
ins2=ClassDemo()
ins2.des="实例属性自由添加修改"
ins1.instaceFunc('test') # name:kcsdemo age 60 address:shenzhen other:test——kingdee:实例属性,由实例定义,也只能由实例访问
ins2.instaceFunc('test') # name:kcsdemo age 60 address:shenzhen other:test——kingdee:实例属性自由添加修改
访问权限
方法 属性的访问权限设置 _name 单下划线加名称 protected __name 双下滑线加名称表示private __name__前后双下划线一般是系统内置命名
属性
class Wife:
def __init__(self, name,age):
self.name = name
#私有成员:以双下划线开头
# self.__age = age
# self.set_age(age)
self.age = age
def get_age(self):
return self.__age
def set_agetest(self,value):
if 25<=value<=30:
self.__age = value
else:
#抛出异常
raise ValueError("我不要!")
#property 拦截对age的读写操作
age = property(get_age,set_agetest) #函数名可自定义,不需要带属性名
w01 = Wife('甄宓',26)
#不能访问私有变量
# print(w01.__age)
w01.set_age(25)
print(w01.get_age()) #25
print(w01.age) #25
# 通过@property 将方法转换为属性,调用时,不在需要()
# @property
# def methodname(self): # 表示类的实例
# block # 方法体
class ProClass:
def __init__(self, width, height):
self.width = width
self.height = height
self.__area=0.00
@property
def area(self): #只读属性 area 可访问,不可修改,返回私有字段的属性为只读属性
self.__area=self.width * self.height
return self.__area
rect = ProClass(29, 31.5)
print("面积为:{:.2f}".format(rect.area)) # 面积为:913.50
#rect.area=100 #can't set attribute 只读不可修改
class Wife:
def __init__(self, name, age):
self.__name = name #不会触发name属性
self.name='test' #会触发name属性
# 私有成员:以双下划线开头
# self.__age = age
# self.set_age(age)
self.age = age
@property # 拦截读取 age = property(get_age,None)
def age(self):
return self.__age
# 设置写入方法age.setter(写入方法)
@age.setter # 拦截写入
def age(self, value):
if 25 <= value <= 30:
self.__age = value
else:
# 抛出异常
raise ValueError("我不要!")
# property 拦截对age的读写操作
# age = property(get_age,set_age)
@property
def name(self):#name名称要与self相同
return self.__name
@name.setter
def name(self, value):
if len(value) > 5:
self.__name = value
w01 = Wife('甄宓', 25)
# 不能访问私有变量
# print(w01.__age)
# w01.set_age(25)
# print(w01.get_age())
print(w01.age) # 25
print(w01.name) # 甄宓
print(w01.__dict__) #{'_Wife__name': '甄宓', '_Wife__age': 25}
print(w01._Wife__age) #25
#类的静态函数、类函数、普通函数、全局变量以及一些内置的属性都是放在类__dict__里的
#对象的__dict__中存储了一些self.xxx的一些东西
#读写age
class Wife:
def __init__(self, name,age):
self.name = name
self.age = age
@property
def age(self):
return self.__age
@age.setter
def age(self,value):
if 25<=value<=30:
self.__age = value
else:
raise ValueError("我不要!")
#只读(只能获取)age
class Wife01:
def __init__(self, name):
self.name = name
self.__age = 25
@property
def age(self):
return self.__age
# w01 = Wife01('小乔')
# w01.age = 18 异常
#只写age
class Wife02:
def __init__(self, name,age):
self.name = name
self.age = age
def set_age(self,value):
if 25<=value<=30:
self.__age = value
else:
raise ValueError("我不要!")
age = property(None,set_age)
# w03 = Wife02('大桥',28)
# print(w03.age) 异常
继承
class ClassName(baseclasslist): #baseclasslist 要继承的基类,多个用,隔开,和C#类单继承不一样
‘’‘类注释’’’
statement #类体
#isinstance()判断对象是否属于一个类
rint(isinstance(t01,Student))
# issubclass()判断一个类是不是另一个类的子类
print(issubclass(Teacher,Person))
方法重写
直接在子类中写一个同名,同参数的方法就重写了
调用基类的构造函数
super().__init__()
str repr eval、 exec
str()函数:将值转化为适于人阅读的字符串的形式
repr()函数:将值转化为供解释器读取的字符串形式
eval() 将字符串当做一句代码去执行 ,只能执行一行表达式
exec() 将字符串当做一块代码去运行用换行符或分号 分隔每一个语句
class StudentModel:
def __init__(self, name='', age=0, score=0,id=0):
self.name = name
self.age = age
self.score = score
self.id = id
#对象-->字符串(格式随意)
def __str__(self):
return '编号:%d,姓名:%s' % (self.id,self.name)
#对象-->字符串(解释器可识别的,有格式要求,也就是说返回的字符串要能通过eval等函数执行)
def __repr__(self):
return "StudentModel('%s',%d,%d,%d)" % (self.name,self.age,self.score,self.id)
s01 = StudentModel('哪吒',3000,59,1)
print(s01)
str01 = str(s01)
print(str01)
#将字符串当做一句代码去执行
#eval()只能执行一行表达式
print(eval("1+2*5"))
#eval('del s01') #报错 不能识别语句
#将字符串当做一块代码去运行
#用换行符或分号 分隔每一个语句
str02 = '''a = 10
b = 20
print(a+b)
'''
exec(str02)
exec("a=11\nb=20;print(a+b)")
#克隆对象
print(repr(s01))
#repr 返回Python格式的字符串
#eval 执行字符串
s02 = eval(repr(s01)) # 将repr形成的字符串当做代码与去执行
#s03 = eval(str(s01)) # 异常
print(s02) #编号:1,姓名:小李
# print(s02)
s02.name = "小李"
print(s01) #编号:1,姓名:小李
print(s02) #编号:1,姓名:小李
st="asdbd"
print(str(st)) #asdbd
print(repr(st)) #'asdbd'
print(eval(repr(st))) #asdbd
print(eval(str(st))) #异常:name 'asdbd' is not defined str转换出来的字符串,没带‘’会被解释器认为是变量,所以异常
a=1
# str()函数:将值转化为适于人阅读的字符串的形式
# repr()函数:将值转化为供解释器读取的字符串形式
# 1.除了字符串类型外,使用str还是repr转换没有什么区别,字符串类型的话,外层会多一对引号,这一特性有时候在eval操作时特别有用;
# 2.命令行下直接输出对象调用的是对象的repr方法,print输出调用的是str方法
eval() 案例
class SkillImpactEffect:
'''
技能影响效果
'''
def impact(self):
pass
#伤害效果
class DamageEffect(SkillImpactEffect):
def __init__(self,value):
self.value = value
def impact(self):
print('扣你%d血' % self.value)
#消耗法力
class CostSPEffect(SkillImpactEffect):
def __init__(self,value):
self.value = value
def impact(self):
print('消耗%d法力' % self.value)
#降低防御力
class LowerDeffenseEffect(SkillImpactEffect):
def __init__(self, ratio, time):
self.ratio = ratio
self.time = time
def impact(self):
print('降低防御%.1f防御力,持续%.1f秒' % (self.ratio,self.time))
#技能释放器
class SkillDeployer:
'''
技能释放器
'''
def __init__(self,name):
self.name = name
#保存配置文件内容
self.__dict_skill_config = self.__loacd_config_file()
#保存创建好的效果对象
self.__list_effect_object = self.__create_effect_object()
#读配置文件
def __loacd_config_file(self):
return {
'韦陀杵':["LowerDeffenseEffect(0.3,2.5)","CostSPEffect(20)","DamageEffect(200)"],
'亢龙有悔':["DamageEffect(500)","CostSPEffect(100)"]
}
#创建对象
def __create_effect_object(self):
list_effect_name = self.__dict_skill_config[self.name]
return [eval(item) for item in list_effect_name] #通过eval执行生成对象
#调用方法
def genernate_skill(self):
print('看招',self.name)
for item in self.__list_effect_object:
item.impact()
skill01 = SkillDeployer("亢龙有悔")
skill01.genernate_skill()
# 看招 亢龙有悔
# 扣你500血
# 消耗100法力
闭包
-
三要素:
– 必须有一个内嵌函数。
– 内嵌函数必须引用外部函数中变量。
– 外部函数返回值必须是内嵌函数。
-
语法
– 定义:
def 外部函数名(参数):
外部变量
def 内部函数名(参数):
使用外部变量
return 内部函数名
– 调用:
变量 = 外部函数名(参数)
变量(参数)
-
定义:在一个函数内部的函数,同时内部函数又引用了外部函数的变量。
-
本质:闭包是将内部函数和外部函数的执行环境绑定在一起的对象。
-
优点:内部函数可以使用外部变量。
-
缺点:外部变量一直存在于内存中,不会在调用结束后释放,占用内存。
-
作用:实现python装饰器。一个函数可以被多个装饰器修饰,执行顺序为从近到远。
"""
闭包:外部函数执行完毕后,不立即释放内存.
而是等着内部函数使用外部嵌套变量.
"""
def fun01():
a = 10
def fun02():
print(a)
return fun02# 返回内部函数(没有执行)
# re 存储的是fun02
re = fun01()
re()# 调用fun02 10
设计模式
1.装饰器模式
def print_func_name(func):
# *args 原函数参数可以无限制
def wrapper(*args,**kwargs):
print(func.__name__)# 打印函数名称
# return 原函数返回值
return func(*args,**kwargs)# 调用函数,
return wrapper
@print_func_name #@语法糖
def say_hello():
a=1
print('test')
print("hello")
return 1
@print_func_name
def say_goodbye(name):
print(name,"---goodbye")
return 2
print(say_hello())#1 相当于调用 print_func_name(say_hello),执行返回的wapper()
print(say_goodbye("qtx"))#2
模块
一个 .py文件 就是一个模块
每个python模块(python文件,也就是此处的test.py和import_test.py)都包含内置的变量__name__,当运行模块被执行的时候,__name__等于文件名(包含了后缀.py);如果import到其他模块中,则__name__等于模块名称(不包含后缀.py)。而“main”等于当前执行文件的名称(包含了后缀.py)。进而当模块被直接执行时,name == 'main’结果为真。
导入模块
import modulename
from modulename import member #member 可以是一个也可以是多个,多个用,隔开,也可用*通配符代表所有member
import ootest
ootest.ClassDemo().instaceFunc("testModule")
print(ootest.ClassDemo.company) #模块名.相应对象 会把引入模块执行一次
if __name__=='__main__':
print("no module",__name__,'__main__')
else:
print(__name__,'__main__')
from funcDemo import *
print(circlearea(20)) # from import 不用再写模块名 全部引入,会把引入模块执行一次 譬如funcDemo中的 func_demo('tet', 23, 'rt') 会执行输出 ('tet', 23, 'rt')
from funcDemo import circlearea
print(circlearea(30)) # 指引入需要的
import funcDemo
print(funcDemo.circlearea(20))
以下为引入的模块
ootest
class ClassDemo:
name = "kcsdemo" # 类属性 定义在类中,实例方法之外,用法和C#的静态变量类似 各类实例共用
age = 60
address = "shenzhen"
__pinfo="private 信息"
_ptinfo="protected 信息"
def __init__(self):
self.des='实例属性,由实例定义,也只能由实例访问'
def instaceFunc(self, str): # 实例方法 定义在类中 第一参数必须是self
print("name:{:s} age {:d} address:{:s} other:{:s}".format(ClassDemo.name,ClassDemo.age,ClassDemo.address,str+"——"+ClassDemo.company+":"+self.des)) # 通过类名.类属性访问
ClassDemo.company='kingdee' # 自由的添加类属性
ins1=ClassDemo()
ins2=ClassDemo()
ins2.des="实例属性自由添加修改"
ins1.instaceFunc('test') # name:kcsdemo age 60 address:shenzhen other:test——kingdee:实例属性,由实例定义,也只能由实例访问
ins2.instaceFunc('test') # name:kcsdemo age 60 address:shenzhen other:test——kingdee:实例属性自由添加修改
# 方法 属性的访问权限设置 _name 单下划线加名称 protected __name 双下滑线加名称表示private __name__前后双下划线一般是系统内置命名
funcDemo
def function_demo(name, age, des="说明"): # 有默认值,可以不传
print("姓名:{:s} 年龄:{:d} des:{:s}".format(name, age + 1, des))
function_demo("kcs", 29, "kingdee") # 位置参数 传参时,按位置顺序指定 姓名:kcs 年龄:30 des:kingdee 姓名:kcs 年龄:30 des:说明
function_demo(age=29, name="kcs") # 关键字参数,传参时,按名称传值,无关顺序
def func_demo(*prams):
print(prams) # ('tet', 23, 'rt') *可变参数,会处理放到一个元组中
for item in prams:
print(item)
func_demo('tet', 23, 'rt')
def func_dicDemo(**params):
print(params) # {'tes': 1, 'dte': 'te'}
for key, value in params.items():
print(key, value, end='_')
#func_dicDemo(tes=1, dte='te') # ** 表示输入的参数,类似于键值对形式,并会转换成字典
#全局变量,局部变量 定义在函数外,函数内,函数内的局部变量通过global修饰后,就成为全局变量
# 匿名函数
import math
def circlearea(r):
result=math.pi*r*r
return result
r=10
print(circlearea(r)) #314.1592653589793
retlamda=lambda r:math.pi*r*r
print(retlamda(r)) #314.1592653589793
模块检索
模块文件检索优先顺序:
1.在当前目录查找
2.在环境变量 PYTHONPATH下的每个目录进行查找
3.在Python的默认安装目录查找
以上路径都保存在模块sys的属性path中
import sys
print(sys.path)
#['E:\\PythonDemo', 'E:\\PythonDemo', 'C:\\Program Files\\JetBrains\\PyCharm 2020.1.2\\plugins\\python\\helpers\\pycharm_display', 'C:\\Program Files\\python38.zip', 'C:\\Program Files\\DLLs', 'C:\\Program Files\\lib', 'C:\\Program Files', 'C:\\Program Files\\lib\\site-packages', 'C:\\Program Files\\JetBrains\\PyCharm 2020.1.2\\plugins\\python\\helpers\\pycharm_matplotlib_backend']
添加检索路径
egpath:“E:\program\demo”
1.临时添加(只在执行当前文件的窗口中有效,窗口关闭即失效)
sys.path.apend(“E:\program\demo”)
2.在Python的安装目录下的 Lib\site-packages子目录中添加*.ph 文件 将路径添加到其中
E:\program\demo
3. 在环境变量 PYTHONPATH添加
模块变量
__all__变量:定义可导出成员,仅对from xx import *语句有效。
__doc__变量:文档字符串。
__file__变量:模块对应的文件路径名。
__name__变量:模块自身名字,可以判断是否为主模块。
当此模块作为主模块(第一个运行的模块)运行时,__name__绑定'__main__',不是主模块,而是被其它模块导入时,存储模块名。
包
包名 小写
# 创建包就是创建一个文件夹,并在改该文件夹下创建一个__init__.py的文件,该文件可为空,也可添加代码,在导入包的时候会自动执行
print("包名:{:s}".format(__name__))
异常处理
try:
block1
except [(ExceptionName1,ExceptionName2)[as alias]] :
block2 # 发生异常执行 eg:print(alias)
else:
block3 # 不发生异常执行
finally:
block4 # 始终执行
抛出异常
raise [ExceptionName[(异常描述信息)]]
try
pass
except Exception as exp
print(str(exp.args))
数据结构
线性结构:1-1
其存储方式分为顺序存储,链式存储
-
顺序结构 优点 : 查找遍历方便 缺点 : 数据量大的时候不利于存储 不利于从中间插入数据
若将线性表L=(a0,a1, ……,an-1)中的各元素依次存储于计算机一片连续的存储空间,这种机制表示为线性表的顺序存储结构。
特点
- 逻辑上相邻的元素 ai, ai+1,其存储位置也是相邻的;
- 存储密度高,方便对数据的遍历查找。
- 对表的插入和删除等运算的效率较差。
-
链式结构 优点: 数据存储分散,便于数据的插入删除操作 缺点: 结构设计相对复杂,遍历速度慢
将线性表L=(a0,a1,……,an-1)中各元素分布在存储器的不同存储块,称为结点,每个结点(尾节点除外)中都持有一个指向下一个节点的引用,这样所得到的存储结构为链表结构。
特点
- 逻辑上相邻的元素 ai, ai+1,其存储位置也不一定相邻;
- 存储稀疏,不必开辟整块存储空间。
- 对表的插入和删除等运算的效率较高。
- 逻辑结构复杂,不利于遍历。
自定义链表
# 创建节点类
class Node:
"""
包含一个简单的数字作为数据
next 构建关系
"""
def __init__(self,val,next=None):
self.val = val # 有用数据
self.next = next # 节点关系
"""
1. 构建节点间关系
2. 在节点中存储数据
3. 对单链表进行节点操作
"""
# 单链表的类
class LinkList:
"""
思路: 生成对象即表示一个单链表对象
对象调用方法可以完成对单链表的各种操作
"""
def __init__(self):
"""
初始化时 创建一个无用的节点,让对象拥有该节点,以表达链表的开端
"""
self.head = Node(None)
# 初始化
def init_list(self,iter):# 链表初始化,将含迭代器的对象转换为链表
p = self.head
for i in iter:
p.next = Node(i)
p = p.next
# 遍历打印
def show(self):
p = self.head.next # 第一个有效节点
while p is not None:
print(p.val)
p = p.next # p向后移动
# 判断链表是否为空
def is_empty(self):
return self.head.next is None
# 清空链表
def clear(self):
self.head.next = None
# 尾部插入
def append(self,val):
p = self.head
# p 移动到最后一个节点
while p.next is not None:
p = p.next
p.next = Node(val)
# 头部插入
def head_insert(self,val):
node = Node(val)
node.next = self.head.next
self.head.next = node
# 指定位置插入
def insert(self,index,val):
p = self.head
# 将p移动到待插入位置的前一个
for i in range(index):
# 超出最大范围
if p.next is None:
break
p = p.next
node = Node(val)
node.next = p.next
p.next = node
# 删除节点(删除第一个val值)
def delete(self,val):
p = self.head
# 确定p的位置(停留在待删除节点的前一个)
while p.next and p.next.val != val:
p = p.next
# 分情况讨论
if p.next is None:
raise ValueError("x not in link")
else:
p.next = p.next.next
# 获取节点值
def get_value(self,index):
if index < 0:
raise IndexError('link index out of range')
p = self.head.next
for i in range(index):
if p.next is None:
raise IndexError('link index out of range')
p = p.next
return p.val
栈
- 定义
栈是限制在一端进行插入操作和删除操作的线性表(俗称堆栈),允许进行操作的一端称为“栈顶”,另一固定端称为“栈底”,当栈中没有元素时称为“空栈”。
- 特点:
- 栈只能在一端进行数据操作
- 栈模型具有先进后出或者叫做后进先出的规律
栈的操作有入栈(压栈),出栈(弹栈),判断栈的空满等操作。
"""
sstack.py 栈模型的顺序存储
思路:
1. 利用列表完成顺序存储,但是列表功能多,不符合栈模型特点
2. 使用类将列表封装,提供符合栈特点的接口方法
"""
# 自定义异常
class StackError(Exception):
pass
# 顺序栈模型
class SStack:
def __init__(self):
# 开辟一个顺序存储的模型空间
# 列表的尾部表示栈顶
self._elems = []
# 判断栈是否为空
def is_empty(self):
return self._elems == []
# 入栈
def push(self,val):
self._elems.append(val)
# 出栈
def pop(self):
if self.is_empty():
raise StackError("Stack is empty")
# 弹出一个值并返回
return self._elems.pop()
# 查看栈顶元素
def top(self):
if self.is_empty():
raise StackError("Stack is empty")
return self._elems[-1]
"""
lstack.py 栈的链式结构
重点代码
思路:
1. 源于节点存储数据,建立节点关联
2. 封装方法 入栈 出栈 栈空 栈顶元素
3. 链表的开头作为栈顶(不需要每次遍历)
"""
# 自定义异常
class StackError(Exception):
pass
# 创建节点类
class Node:
def __init__(self,val,next=None):
self.val = val # 有用数据
self.next = next # 节点关系
# 链式栈
class LStack:
def __init__(self):
# 标记顶位置
self._top = None
def is_empty(self):
return self._top is None
def push(self,val):
self._top = Node(val,self._top)
def pop(self):
if self._top is None:
raise StackError("Stack is empty")
value = self._top.val
self._top = self._top.next
return value
def top(self):
if self._top is None:
raise StackError("Stack is empty")
return self._top.val
队列
- 定义
队列是限制在两端进行插入操作和删除操作的线性表,允许进行存入操作的一端称为“队尾”,允许进行删除操作的一端称为“队头”。
- 特点:
- 队列只能在队头和队尾进行数据操作
- 队列模型具有先进先出或者叫做后进后出的规律
"""
squeue.py 队列的顺序存储
思路分析:
1. 基于列表完成数据的存储
2. 通过封装功能完成队列的基本行为
3. 无论那边做对头/队尾 都会在操作中有内存移动
"""
# 自定义异常
class QueueError(Exception):
pass
# 队列操作
class SQueue:
def __init__(self):
self._elems = []
# 判断队列是否为空
def is_empty(self):
return self._elems == []
# 入队
def enqueue(self,val):
self._elems.append(val)
# 出队
def dequeue(self):
if not self._elems:
raise QueueError("Queue is empty")
return self._elems.pop(0) # 弹出第一个数据
"""
lqueue.py 链式队列
重点代码
思路分析:
1. 基于链表构建队列模型
2. 链表的头作为队头,尾作为队尾
3. 定义两个标记标记队头和队尾
4. 头和尾代表同一个无用节点时队列为空
"""
# 自定义异常
class QueueError(Exception):
pass
# 创建节点类
class Node:
def __init__(self,val,next=None):
self.val = val # 有用数据
self.next = next # 节点关系
# 队列操作
class LQueue:
def __init__(self):
# front 队头 rear 队尾
self.front = self.rear = Node(None)
def is_empty(self):
return self.front == self.rear
def enqueue(self,val):
self.rear.next = Node(val)
self.rear = self.rear.next
def dequeue(self):
if self.is_empty():
raise QueueError("Queue is empty")
# front 是指向队头节点的前一个
self.front = self.front.next
return self.front.val
*树形结构 1-n
树(Tree)是n(n≥0)个节点的有限集合T,它满足两个条件:有且仅有一个特定的称为根(Root)的节点;其余的节点可以分为m(m≥0)个互不相交的有限集合T1、T2、……、Tm,其中每一个集合又是一棵树,并称为其根的子树(Subtree)。
- 一个节点的子树的个数称为该节点的度数,一棵树的度数是指该树中节点的最大度数。
- 度数为零的节点称为树叶或终端节点,度数不为零的节点称为分支节点。
- 一个节点的子树之根节点称为该节点的子节点,该节点称为它们的父节点,同一节点的各个子节点之间称为兄弟节点。一棵树的根节点没有父节点,叶节点没有子节点。
- 节点的层数等于父节点的层数加一,根节点的层数定义为一。树中节点层数的最大值称为该树的高度或深度。
二叉树
定义
二叉树(Binary Tree)是n(n≥0)个节点的有限集合,它或者是空集(n=0),或者是由一个根节点以及两棵互不相交的、分别称为左子树和右子树的二叉树组成。二叉树与普通有序树不同,二叉树严格区分左孩子和右孩子,即使只有一个子节点也要区分左右。
"""
bitree.py 二叉树的实现
思路分析:
1. 使用链式存储, Node表达一个节点(值,左链接,右链接)
2. 分析遍历过程
3. 递归实现遍历
"""
# 二叉树节点
class Node:
def __init__(self, val, left=None, right=None):
self.val = val
self.left = left
self.right = right
# 二叉树遍历
class Bitree:
# 传入树根
def __init__(self, root):
self.root = root
# 先序遍历
def preOrder(self, node):
if node is None:
return
print(node.val, end=' ')
self.preOrder(node.left)
self.preOrder(node.right)
# 中序遍历
def inOrder(self, node):
if node is None:
return
self.inOrder(node.left)
print(node.val, end=' ')
self.inOrder(node.right)
# 后序遍历
def postOrder(self, node):
if node is None:
return
self.postOrder(node.left)
self.postOrder(node.right)
print(node.val, end=' ')
# 层次遍历
def levelOrder(self, node):
lq = LQueue() # 引用上述链式对列
# 初始节点先入队,循环判断,队列不为空则出队
# 出队元素的左右孩子分别入队
lq.enqueue(node)
while not lq.is_empty():
# 出队,打印表示遍历
node = lq.dequeue()
print(node.val, end=' ')
if node.left:
lq.enqueue(node.left)
if node.right:
lq.enqueue(node.right)
if __name__ == '__main__':
# B F G D H I E C A
# 构建起一个二叉树
b = Node('B')
f = Node('F')
g = Node("G")
d = Node('D', f, g)
h = Node('H')
i = Node('I')
e = Node('E', h, i)
c = Node('C', d, e)
a = Node('A', b, c) # 树根
bt = Bitree(a)
bt.preOrder(bt.root)
print()
bt.inOrder(bt.root)
print()
bt.levelOrder(bt.root)
常见排序算法
"""
sort.py 排序训练
"""
# 冒泡
def bubble(l):
n = len(l)
# 循环n - 1 次,每次确定一个最大值
for i in range(n - 1):
# 两两比较交换
for j in range(n - 1 - i):
if l[j] > l[j + 1]:
l[j],l[j + 1] = l[j + 1],l[j] # 直接一起交换值,不需要中间项过度
l = [4,5,7,1,2,9,6,8]
bubble(l)
print(l)
"""
*快速排序算法
"""
def sub_sort(l,low,high):
x = l[low] # 基准
while low < high:
# 如果后边的数比x大high向前走
while l[high] > x and high > low:
high -= 1
l[low] = l[high] # 比x小的往前甩
# 如果前面的数比x小则low往后走
while l[low] <= x and low < high:
low += 1
l[high] = l[low] # 比x大的往后甩
l[low] = x # 将x插入最终位置
return low # 每一轮最终基准数确定的位置
# low : 第一个元素索引号,high: 最后一个元素索引号
"""
该写法表示希望low传一个整形,high传一个整形,返回值为None类型
但是没有强制要求必须这样做
"""
def quick(l:list,low:int,high:int)->None:#-> 3.x语法,常常出现在python函数定义的函数名后面,为函数添加元数据,描述函数的返回类型
if low < high:
key = sub_sort(l,low,high)
quick(l,low,key-1)
quick(l,key+1,high)
l = [8,1,12,7,6,10,3,4,8,5,9,2,11,5,13]
quick(l,0,len(l)-1)
print(l)
IO / 文件及目录操作
字节串(bytes)
在python3中引入了字节串的概念,与str不同,字节串以字节序列值表达数据,更方便用来处理二进程数据。因此在python3中字节串是常见的二进制数据展现方式。
- 普通的ascii编码字符串可以在前面加b转换为字节串,例如:b’hello’
- 字符串转换为字节串方法 :str.encode()
- 字节串转换为字符串方法 : bytes.decode()
文件
-
打开文件
Python 内置了File对象 通过open(),close() 要及时关闭文件
file=open(filename[,mode[,buffering[,encoding]]])
filename:如果与py文件在同一目录下,写文件名就可以,不在同一目录要全路径或相对路径
file_object = open(file_name, access_mode='r', buffering=-1)
功能:打开一个文件,返回一个文件对象。
参数:file_name 文件名;
access_mode 打开文件的方式,如果不写默认为‘r’
文件模式 操作
r 以读方式打开 文件必须存在
w 以写方式打开
文件不存在则创建,存在清空原有内容
a 以追加模式打开
r+ 以读写模式打开 文件必须存在
w+ 以读写模式打开文件
不存在则创建,存在清空原有内容
a+ 以读写模式打开 追加模式
rb 以二进制读模式打开 同r
wb 以二进制写模式打开 同w
ab 以二进制追加模式打开 同a
rb+ 以二进制读写模式打开 同r+
wb+ 以二进制读写模式打开 同w+
ab+ 以二进制读写模式打开 同a+
buffering 1表示有行缓冲,默认则表示使用系统默认提供的缓冲机制。
返回值:成功返回文件操作对象。
- 读取文件
read([size])
功能: 来直接读取文件中字符。
参数: 如果没有给定size参数(默认值为-1)或者size值为负,文件将被读取直至末尾,给定size最多读取给定数目个字符(字节)。
返回值: 返回读取到的内容
- 注意:文件过大时候不建议直接读取到文件结尾,读到文件结尾会返回空字符串。
readline([size])
功能: 用来读取文件中一行
参数: 如果没有给定size参数(默认值为-1)或者size值为负,表示读取一行,给定size表示最多读取制定的字符(字节)。
返回值: 返回读取到的内容
readlines([sizeint])
功能: 读取文件中的每一行作为列表中的一项
参数: 如果没有给定size参数(默认值为-1)或者size值为负,文件将被读取直至末尾,给定size表示读取到size字符所在行为止。
返回值: 返回读取到的内容列表
文件对象本身也是一个可迭代对象,在for循环中可以迭代文件的每一行。
for line in f:
print(line)
"""
从终端输入一个单词,从单词本中刚找到该单词,打印这一行内容,如果没有找到则打印"找不到"
"""
word = input("单词:")
# 打开文件
f = open('dict.txt')
for line in f:
w = line.split(' ')[0]
# 遍历的单词已经大于目标,说明找不到了
if w > word:
print("没有找到该单词")
break
elif w == word:
print(line)
break
else:
print("没有找到该单词")
f.close()
- 写入文件
write(string)
功能: 把文本数据或二进制数据块的字符串写入到文件中去
参数:要写入的内容
返回值:写入的字符个数
- 如果需要换行要自己在写入内容中添加\n
writelines(str_list)
功能:接受一个字符串列表作为参数,将它们写入文件。
参数: 要写入的内容列表
"""
编写一个文件拷贝程序,将一个文件拷贝一份,重新取另外一个名字(自定).
文件可能是文本,也可能是二进制
"""
filename = input("文件:")
fr = open(filename,'rb')
fw = open("备份-"+filename,'wb')
while True:
# 循环读取
data = fr.read(1024)
if not data: # 文件结束
break
fw.write(data)
fr.close()
fw.close()
- 关闭文件
打开一个文件后我们就可以通过文件对象对文件进行操作了,当操作结束后使用close()关闭这个对象可以防止一些误操作,也可以节省资源。
file_object.close()
- with操作
python中的with语句使用于对资源进行访问的场合,保证不管处理过程中是否发生错误或者异常都会执行规定的“清理”操作,释放被访问的资源,比如有文件读写后自动关闭、线程中锁的自动获取和释放等。
with语句的语法格式如下:
with context_expression [as obj]:
with-body
通过with方法可以不用close(),因为with生成的对象在语句块结束后会自动处理,所以也就不需要close了,但是这个文件对象只能在with语句块内使用。
with open('file','r+') as f:
f.read()
注意
- 加b的打开方式读写要求必须都是字节串
- 无论什么文件都可以使用二进制方式打开,但是二进制文件使用文本方式打开读写会出错
filetxt= open(r"C:\Users\Administrator.-20171206IUEUWS\Desktop\pythonNode\Test\tets.txt","r",1,encoding="utf-8")
print(filetxt)
str=filetxt.read() #测试python file open
print(str)
filetxt.close()
filetxt1= open(r"C:\Users\Administrator.-20171206IUEUWS\Desktop\pythonNode\Test\tetsRW.txt","a+",1,encoding="utf-8")
filetxt1.write("safasf")
filetxt1.close()
filetxt2=open(r"..\testfile\te1.txt")
print(filetxt2)
filetxt2.close()
if os.path.exists(r'..\testfile\te1.txt'):
os.rename(r'..\testfile\te1.txt', r'..\testfile\test.txt', ) # 删除文件 如果不存在,则异常
print(os.stat(r'..\testfile\test.txt')) #os.stat_result(st_mode=33060, st_ino=1407374883553465, st_dev=1710438242, st_nlink=1, st_uid=0, st_gid=0, st_size=86, st_atime=1591954640, st_mtime=1592186618, st_ctime=1591954640)
os.startfile(r'..\testfile\test.txt') # 用默认关联的程序打开文件
print(os.access(r'..\testfile\test.txt', os.W_OK)) # 获取对文件的操作权限 os.R_OK:读取 os.W_OK:写入 os.OK:执行 os.F_OK:存在
import stat #引入状态包
os.chmod(r'..\testfile\test.txt',stat.S_IREAD)
print(os.access(r'..\testfile\test.txt', os.W_OK))
# if os.path.exists(r'..\testfile\te1.txt'):
# os.remove(r'..\testfile\test.txt') #删除文件 如果不存在,则异常
with 和C# using 功能类似,控制自动释放
with expression as target
with body
with open(r"..\testfile\te1.txt","r+",1,encoding="utf-8") as filearw:
str =filearw.read()
print('111'+str) #111testa
刷新缓冲区
缓冲:系统自动的在内存中为每一个正在使用的文件开辟一个缓冲区,从内存向磁盘输出数据必须先送到内存缓冲区,再由缓冲区送到磁盘中去。从磁盘中读数据,则一次从磁盘文件将一批数据读入到内存缓冲区中,然后再从缓冲区将数据送到程序的数据区。
刷新缓冲区条件:
- 缓冲区被写满
- 程序执行结束或者文件对象被关闭
- 行缓冲遇到换行
- 程序中调用flush()函数
"""
buffer.py
缓冲区演示
"""
# f = open('test','w',1) # 行缓冲
f = open('test','w')
while True:
data = input(">>")
if not data:
break
f.write(data + '\n')
f.flush() # 主动刷新缓冲,如果不刷新,每次以w模式打开就会清除掉之前写入的内容
f.close()
文件偏移量
- 定义
打开一个文件进行操作时系统会自动生成一个记录,记录中描述了我们对文件的一系列操作。其中包括每次操作到的文件位置。文件的读写操作都是从这个位置开始进行的。
- 基本操作
tell()
功能:获取文件偏移量大小
seek(offset[,whence])
功能:移动文件偏移量位置
参数:offset 代表相对于某个位置移动的字节数。负数表示向前移动,正数表示向后移动。
whence是基准位置的默认值为 0,代表从文件开头算起,1代表从当前位置算起,2 代表从文件末尾算起。
- 必须以二进制方式打开文件时基准位置才能是1或者2
"""
seek.py 文件偏移量
注意: 1. open打开文件会重置文件偏移量
2. 读写操作使用的是一个偏移量值
3. seek操作文件偏移量一般是以二进制打开
"""
f = open("test",'wb+')
f.write(b"Hello world")
# f.close()
#
# f = open('test','r')
print("偏移量:",f.tell()) # 获取文件偏移量 # 偏移量: 11
f.seek(-5,2) # 将文件偏移量定位到开头
data = f.read()
print(data) #b'world'
f.close()
文件描述符
- 定义
系统中每一个IO操作都会分配一个整数作为编号,该整数即这个IO操作的文件描述符。
- 获取文件描述符
fileno()
通过IO对象获取对应的文件描述符
f = open("test",'wb+')
print(f.fileno()) # 3
文件管理函数
- 获取文件大小
os.path.getsize(file)
- 查看文件列表
os.listdir(dir)
- 查看文件是否存在
os.path.exists(file)
- 判断文件类型
os.path.isfile(file)
- 删除文件
os.remove(file)
目录操作
os 是内置的与操作系统功能和文件系统相关的模块,不同的系统表现可能表现不同
import os
print(os.name) # nt 用于获取操作系统类型 nt:windows posix:Linux,unix,Mac os
print(os.linesep) # 当前操作系统的换行符
print(os.sep) # \ 当前操作系统的操作分隔符
print(os.getcwd()) # E:\PythonDemo\ostest 获取当前路径
print(os.path.abspath(r'..\testfile\demo1.py')) # E:\PythonDemo\testfile\demo1.py 获取相对路径的绝对路径
print(os.path.join(os.getcwd(),'test')) # E:\PythonDemo\ostest\test 拼接路径,不需要/
print(os.path.exists(r'..\testfile')) # True
print(os.path.isdir(r'..\testfile')) # True
# print(os.listdir('E:\PythonDemo'))
if not os.path.exists('DirDemo'):
print(os.mkdir('DirDemo',)) # None
if not os.path.exists('E:\PythonDemo\DirTest'):
print(os.mkdir('E:\PythonDemo\DirTest')) # None
# print(os.mkdir(r'E:\PythonDemo\Dirtest\1\2')) #系统找不到指定的路径。: 'E:\\PythonDemo\\Dirtest\\1\\2'
if not os.path.exists(r'E:\PythonDemo\Dirtest\1\2'):
print(os.makedirs(r'E:\PythonDemo\Dirtest\1\2')) # None
os.rmdir(r'E:\PythonDemo\Dirtest\1\2') # 只能删除空目录
import shutil #标准模块
shutil.rmtree(r'E:\PythonDemo\ostest\DirDemo') # 可以删除非空目录
tuples=os.walk(r'E:\PythonDemo') #遍历目录,返回元组生成器对象
for tp in tuples:
print(tp)
# ('E:\\PythonDemo', ['.idea', 'DirTest', 'FileDemo', 'ostest', 'pkgdemo1', 'testfile', '__pycache__'], ['demo1.py', 'DicTest.py', 'funcDemo.py', 'moduletest.py', 'modulpath.py', 'ootest.py', 'pdemo1.py', 'pkgtest.py', 'propertyDemo.py', 'retestdemo.py', 'strtest.py', 'TestDemo.py', 'tupletest.py', 'ZetTest.py', '人哇若.py'])
# ('E:\\PythonDemo\\.idea', ['dictionaries', 'inspectionProfiles'], ['.gitignore', 'misc.xml', 'modules.xml', 'PythonDemo.iml', 'workspace.xml'])
# ('E:\\PythonDemo\\.idea\\dictionaries', [], ['Administrator.xml'])
# ...
数据库链接
sqlite:
内置
#1.创建链接对象 connect() 链接参数以具体模块定义为准 相当于sqlConnection
#2.游标对象 cursor() 用于指示抓取数据操作的上下文,用于执行sql ,proc等 #相当于sqlCommand+sqlAdapter
import sqlite3
conn=sqlite3.connect('iteTest.db')
cursor=conn.cursor()
cursor.execute('drop table User')
cursor.execute('create table User(id int(10) primary key,name nvarchar(10),age int(3))')
cursor.close()
conn.commit()
conn.close()
mssql:
需要先安装包,在终端 pip install pymssql;因为网络等原因,通常需要离线下载后,在本地安装,如:
pip install E:\PythonDemo\pymssql-2.1.4-cp38-cp38-win_amd64.whl
import pymssql
try:
msConn = pymssql.connect('127.0.0.1', 'sa', 'kangzi20119', 'ReportServer')
if msConn:
print("链接成功")
cursor = msConn.cursor()
cursor.execute("SELECT * FROM Roles") # 执行查询
data = cursor.fetchall()
print(type(data))
print(data)
msConn.close()
except Exception as ei:
print("链接失败:" + str(ei))
常用内置函数
testfield=125
type(testfield) 返回变量的类型
id(testfield) 返回变量所指的内存地址
str(testfield) 将数值转换为字符串
range(start,end,step):返回一系列连续的整数 range(7) 返回 0~6
枚举函数enumerate
- 语法:
for 变量 in enumerate(可迭代对象):
语句
for 索引, 元素in enumerate(可迭代对象):
语句
- 作用:遍历可迭代对象时,可以将索引与元素组合为一个元组。
zip
- 语法:
for item in zip(可迭代对象1, 可迭代对象2….):
语句
作用:将多个可迭代对象中对应的元素组合成一个个元组,生成的元组个数由最小的可迭代对象决定
range
range() 函数可创建一个整数列表
range(10) # 从 0 开始到 10
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
range(1, 11) # 从 1 开始到 11
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
range(0, 30, 5) # 步长为 5
[0, 5, 10, 15, 20, 25]
range(0, 10, 3) # 步长为 3
[0, 3, 6, 9]
range(0, -10, -1) # 负数
[0, -1, -2, -3, -4, -5, -6, -7, -8, -9]
range(0)
[]
range(1, 0)
[]