PYTHON编程基础回顾
函数-进阶
根据前文《【Python】python基础编程回顾(1)》最后我们探讨的有关函数参数和函数返回值,可以将函数分成以下四个类型:
p.s. 在命名定义函数时,不会刻意对函数的参数和返回值做出要求
全局/局部变量
- 局部变量
- 局部变量就是在函数体内部定义的变量,为了临时保存数据
- 局部变量只作用在定义它的函数体内部,一旦函数执行完,该变量就会被释放
- 由第二点可知,不同的函数可以定义相同名称的变量
- 全局变量
- 全局变量是在函数体外部定义的变量
- 全局变量与局部变量的区别仅在于其作用域范围的不同
- 如果全局变量和局部变量有相同的变量名,在函数体内部优先访问局部变量(就近原则)
- 如果要在函数体内部对局部变量进行修改,需要使用关键字
global
对变量的作用域进行声明
'''
全局变量与局部变量示例:
'''
info='全局变量'
def func1():
name = '局部变量1'
print(name,info)
def func2():
name='局部变量2'
print(name,info)
def func3():
global info
info ='修改全局变量'
func1()
func2()
func3()
print(info)
'''
运行结果:
局部变量1 全局变量
局部变量2 全局变量
修改全局变量
'''
引用
- 对象、变量及引用
- 在python中,万物即对象,不论是数字、字符串,还是列表、元组,在python中都是作为对象存在的;
- 数据对象存储的数据就称为值,值是靠引用来传递的;
- 变量是python中对于对象的引用,对象的操作实质上是通过引用来完成的;
- 可以使用
id()
查看一个对象的引用,其结果就是值保存在内存中的那块内存地址的标识。
- 在python中执行的赋值操作,实质上就是把一个变量名和一块内存地址空间的标识进行绑定,而赋的值就存放在相应的内存地址空间中。
- 而如果对同一个变量重新赋值,实质上是让原来那个变量指向了内存中新的地址空间。
- 函数的参数引用传值
- 在python的函数调用中,传入的参数是引用传递,而非值传递,即传入参数后,实参和形参指的都是内存中统一对象
'''
函数的参数引用传值
'''
#-----------①传递给函数的参数是引用而非值-------------------
a = 1
def func(x):
print("x的地址为: ",id(x))
print("变量a的内存地址为: ",id(a))
func(a)#通过将变量a传入函数来输出相应内存地址
'''
运行结果:
变量a的内存地址为: 140713837368128
x的地址为: 140713837368128
'''
- 在python中虽然传入的是变量的引用,但如果在函数内部要对不可变类型的对象进行修改或对任意对象进行重新赋值,则只会在内存中开辟一段新的内存空间用于存储新的值,然后将原变量指向这段新的内存空间。
- 也正因为修改不可变类型的对象时会开辟新的内存空间,所以在函数内部的修改不会影响到函数外部的数据。
'''
函数的参数引用传值----对不可变对象:数字的修改
'''
#--②在函数内部对不可变对象进行修改时,会重新开辟一段内存空间保存新的值----
a = 1
def func(x):
print('x的值为: ',x)
print("x的地址为: ",id(x))
x = 2#对传进来的参数进行修改
print('修改后的x的值为: ',x)
print('修改后的x的地址为: ',id(x))
print('变量a的值为: ',a)
print("变量a的内存地址为: ",id(a))
func(a)#通过将变量a传入函数来输出相应内存地址
print('修改了形参过后的a的值为: ',a)
print('修改了形参过后的a的地址为: ',id(a))
'''
运行结果:
变量a的值为: 1
变量a的内存地址为: 140713837368128
x的值为: 1
x的地址为: 140713837368128
修改后的x的值为: 2
修改后的x的地址为: 140713837368160
修改了形参过后的a的值为: 1
修改了形参过后的a的地址为: 140713837368128
'''
'''
对可变对象:列表的重新赋值
'''
def func(para):
print(id(para))
para = ['new']
print("修改后的对象为{},对象地址为{}".format(para,id(para)))
li = ['old']
print(li,id(li))
func(li)
print("函数外部的对象为{},对象地址为{}".format(li,id(li)))
'''
运行结果:
['old'] 1400386445896
1400386445896
修改后的对象为['new'],对象地址为1400386445960
函数外部的对象为['old'],对象地址为1400386445896
'''
匿名函数
- 定义
匿名函数就是指用
lamda
关键字创建的,没有名字的函数
- 参数的个数不定,根据需求变化
- 匿名函数中冒号后的表达式有且仅有一个
- 匿名函数自带
return
,return的结果就是表达式计算的结果,不需要显式返回- 但是匿名函数语法只能接单个表达式,仅适用于简单函数场景,用于封装有限的逻辑
- 创建语法
'''
匿名函数示例
'''
#匿名函数创建语法
#lamda para1,para2,para3:代码表达式
test = lambda x,y:x+y
print("使用匿名函数计算的结果: ",test(1,2))#3
#不定义相应对象指向匿名函数,也可以对其直接调用
print("直接调用匿名函数计算结果: ",(lambda x,y:x+y)(1,2))#3
#等价的函数定义
def sum(a,b):
return a+b
print('使用标准函数计算的结果: ',sum(1,2))#3
- 匿名函数与三目表达式
age = 18
#双分支选择
if age >= 18:
print('已成年')
else:
print('未成年')
#三目表达式
msg = '已成年' if age > 18 else '未成年'
print(msg)
#匿名函数使用三目表达式,实现选择逻辑
bigger = lambda x,y:x if x>y else y
print(bigger(10,12))
递归函数
递归函数:即一个函数在内部对自己本身进行调用
- 递归实现的逻辑简单且清晰
- 在实现递归函数的时候,一定要有结束条件
'''
文件树递归调用-示例
'''
import os
def findFile(file_path):
listRs = os.listdir(file_path)#得到指定路径下的所有文件夹
for fileitem in listRs:
fullpath = os.path.join(file_path,fileitem)#获取当前目录下每一个文件(夹)的完整路径
if os.path.isdir(fullpath):
findFile(fullpath)#如果是文件夹,则继续递归执行
else:
print(fileitem)#如果是文件,则把文件名打印出来
else:
return
findFile('D:\\typora\\blog')
内置函数
所谓内置函数就是python安装后就自带的函数
数据运算
函数名称 | 作用 |
---|---|
abs(x) | 返回数字的绝对值 |
round(x[,n]) | 按照一定精度返回浮点数的近似值 |
pow(x,y[,z]) | 返回xy的值 |
divmod(x,y) | 返回商和余数(a//b,a%b) |
max(x,y,z,…) | 返回给定参数的最大值 |
min(x,y,z,…) | 返回给定参数的最小值 |
sum(iterable[,start]) | 返回运算结果 |
eval() | 执行一个字符串表达式 |
备注:
①round()
函数对浮点数的近似运算不完全按照四舍五入,与python的版本和浮点数的精度有关;
②iterbale可迭代对象,包括列表、元组、集合;
'''
内置函数-数学运算的应用示例
'''
#求数字的绝对值
print('-3的绝对值为:',abs(-3))
#求浮点数的近似值
print('2.647的两位近似值为:',round(2.647,2))
print('2.647的一位近似值为:',round(2.647,1))
#求幂运算的结果
print('3的2次方结果为:',pow(3,2))
print('3的2次方为:',3**2)#乘方运算的简洁表示
#求给定参数(可以是序列)的最值
print('[1,2,3,4,5]的最大值为:',max([1,2,3,4,5]))
print('[1,2,3,4,5]的最小值为:',min([1,2,3,4,5]))
#使用sum()进行运算
print('0-9这10个数字总和的结果为:',sum(range(10)))
print('0-9这10个数字总和并额外加10的结果为:',sum(range(10),10))
#使用eval()执行字符串表达式
a,b,c = 1,2,3
print('使用eval计算三数相加的结果:',eval('a+b+c'))
def test():
print("test函数被执行了")
eval('test()')
类型转换
'''
强制类型转换使用示例
'''
#进制转换
print('10的二进制为:',bin(10))
print('10的十六进制为:',hex(10))
#元组、列表之间的转换
tu = (1,2,3)
print('tu的类型为:',type(tu))
tu2li = list(tu)
print('将tu强制转换后类型为:',type(tu2li))
li2tu = tuple(tu2li)
print('再将列表转换回元组,类型为:',type(li2tu))
序列操作
'''
序列操作使用示例
'''
#all()——判断的可迭代对象的元素是否均为True,相当于AND逻辑
print('空列表的all判断结果为: ',all([]))
print('列表[1,2,3,False]的all判断结果为:',all([1,2,3,False]))
#可迭代对象中只要有一个元素是0,空或者False则all()判断的结果就是False
#any()——判断可迭代对象的元素是否均为False,相当于OR逻辑
print('空列表的any判断结果为:',any([]))
print('列表[0,1,2,3]的any判断结果为:',any([0,1,2,3]))
print('元组('',False,0)的any判断结果为:',any((('',False,0))))
#sorted()返回重新排序后的可迭代对象
li = [1,2,3,6,4,9,8]
print('按照降序进行排列:',sorted(li,reverse=True))#reverse控制排序的升序和降序
#zip()将可迭代对象打包成一个个元组,并返回这些元组组成的列表
l1 = ['a','b','c']
l2 = ['x','y','z']
l3 = [1,2]
print('对l1和l2使用zip操作的结果为:',zip(l1,l2))
print('将zip对象转换成列表',list(zip(l1,l2)))#zip压缩操作是按照可迭代对象的索引来进行的
print('如果两个对象的长度不一致,则按照最短长度进行压缩:',list(zip(l1,l3)))
'''
使用zip方法,可以类似于数据库操作一样处理多个数据项
'''
def printBookInfo():
books = []#存储所有的图书信息
id = input('请输入编号:(每个项以空格隔开)')
bookName = input('请输入书名:(每个项以空格隔开)')
bookPos = input('请输入位置:(每个项以空格分隔)')
idList = id.split(',')
bookList = bookName.split(',')
posList = bookPos.split(',')
bookInfo = zip(idList,bookList,posList)#进行打包
for bookitem in bookInfo:
'''
遍历当前存储的所有图书信息
'''
dictInfo = {'编号':bookitem[0],'书名':bookitem[1],'位置':bookitem[2]}
books.append(dictInfo)
for item in books:
print(item)
printBookInfo()
#enumerate()用于将可遍历的数据对象组成为一个索引序列,同时列出数据和数据下标
listl = ['a','b','c']
print('对listl进行enumerate操作后的结果为:',enumerate(listl))
'''
enumerate操作通常会和for循环结合使用
'''
for index,item in enumerate(listl):#enumerate中第二个参数可以传入数值,自由选择下标的起始值
print(index,item)
Set集合
- 集合对象
集合是python的一种数据类型,是一个无序且不重复的元素集合
- 因为set是无序的,所以它不支持索引和切片操作
- set对象和之前的列表与元组一样,并不要求内部的所有元素类型是一致的
- 常用方法
'''
集合对象及常用方法示例
'''
#集合的创建:①使用花括号 ②使用强制类型转换
set1 = {1,2,3}
li = [1,2,3]
set2 = set(li)
print(type(set1),set1)
print(type(set2),set2)
#对集合的增删改查操作
set1.clear()
print('对集合set1进行清空后:',set1)
set1.add(4)
print('对集合set1添加一个元素4之后:',set1)
set3 = {1.3,5}
set1.update(set3)#用update更新集合中的元素
print('集合set1中的数据用set3更新后为:',set1)#可以理解为udate是直接在原有集合基础上进行的并集操作
print('随机移除集合的元素为:',set1.pop())#pop操作随机移除某个元素并且获取那个参数
print('将集合set1中的一个元素弹出后:',set1)
set2.discard(1)
print('指定移除集合set2的元素1:',set2)
#对集合进行交并补差等数学运算
print('集合set1和set2的并集为:',set1.union(set2))
print('还可以使用运算符‘|’进行集合的并集运算:',set1|set2)
print('集合set1和Set2的交集为:',set1.intersection(set2))
print('还可以使用运算符‘&’进行集合的交集运算:',set1&set2)
print('集合set1相对于集合set2的差集为:',set1.difference(set2))#即存在于集合set1之中,但是不在集合set2中的元素
print('还可以使用减号来进行集合之间的差集运算:',set1-set2)
'''
运行结果:
<class 'set'> {1, 2, 3}
<class 'set'> {1, 2, 3}
对集合set1进行清空后: set()
对集合set1添加一个元素4之后: {4}
集合set1中的数据用set3更新后为: {1.3, 4, 5}
随机移除集合的元素为: 1.3
将集合set1中的一个元素弹出后: {4, 5}
指定移除集合set2的元素1: {2, 3}
集合set1和set2的并集为: {2, 3, 4, 5}
还可以使用运算符‘|’进行集合的并集运算: {2, 3, 4, 5}
集合set1和Set2的交集为: set()
还可以使用运算符‘&’进行集合的交集运算: set()
集合set1相对于集合set2的差集为: {4, 5}
还可以使用减号来进行集合之间的差集运算: {4, 5}
'''
面向对象
编程思想类型:
①面向过程:根据业务逻辑从上到下进行代码编写
②函数式:将某种功能代码封装到函数中,日后无需重复编写,仅仅调用函数即可
③面向对象:将数据和函数绑定在一起进行封装,更快速地开发程序,减少了重复代码的重写过程
面向对象与面向对象编程
- 面向对象:按人们认识客观世界的系统思维方式,采用基于对象(实体)的概念建立模型,模拟客观世界分析、设计、实现软件的办法;
- 面向对象编程:(OOP)是一种解决软件复用的设计和编程方法。这种方法把软件系统中相近相似的操作逻辑和操作应用数据、状态,以类的形式描述出来,以对象实例的形式在软件系统中复用,以达到提高软件开发效率的作用。
类&对象,方法和属性
- 类和对象
- 基本概念
类:是一个模板,其中包含很多函数,函数里面实现一些功能;包括三部分——类的名称、类的属性和类的方法
对象:根据模板创建的实例,通过实例对象可以执行类中的函数;对象是类的一个具象化
- 类的定义
def ClassName(object):
#定义属性
attribute_name = attribute_value
#定义方法
def method_name(self):
#方法体
'''
①类型建议采用大驼峰方式进行命名
②关于定义中的object可以省略,这是新式类和旧式类之间的区别
'''
- 对象的创建
类的定义是不在内存中开辟任何空间的,只有当基于该类的对象创建了之后才会在内存中给对象分配相应的空间;
在创建对象之后,就可以调用类中的方法和类中的属性。
objectName = ClassName()#进行类的实例化
- 实例方法和属性
- 基本概念
实例方法:就是在类的内部使用
def
定义的,第一个参数必须为诸如self
这样的名字标识的类方法;实例方法自然是归实例所有,实例化后的对象可以进行调用。
类属性:在类的内部定义的变量,称为类属性。
实例属性:定义在方法里面使用self
引用的属性称为实例属性;自然而然,实例属性也是归属于实例所有的
- 基本用法
①通过给实例化后的对象追加属性,添加的属性是实例属性
'''
类和对象的代码示例
'''
#python中对象的属性可以通过实例化后再定义来追加
class People(object):
def eat(self):
print('喜欢美食')
xw = People()#将类进行实例化后得到对象
xw.name = '小王'#以下三条语句都是通过追加的形式给xw这个对象添加了属性
xw.age = 20
xw.sex = '男生'
print('{}是一个{},今年{}岁'.format(xw.name,xw.sex,xw.age))
self和魔术方法
__init__()
方法
它是初始化方法,在实例化对象的时候自动调用,完成一些初始化设置。
__init__(self)
中默认有一个参数名字为self
,为了让构建出来的类更通用,我们考虑将属性当做参数在实例化对象的时候传进去,这个时候就写成__init__(self,x,y)
# __init__()方法,实例化对象的时候自动调用,完成一些初始化设置
class Animal():
color = '白色'#类属性
def __init__(self):#在方法内部定义并使用self引用的是实例属性
self.name = '小狗'
self.sex = '公'
self.age = '8个月'
dog = Animal()
print(dog.name,dog.sex,dog.age,dog.color)
# __init__()传参,将属性值作为参数在实例化对象的时候传进去
class dog(object):
def __init__(self,kind,color,sex,age):
self.kind = kind
self.color = color
self.sex = sex
self.age = age
shiyi = dog('哈士奇','棕色','公','两岁')
print(shiyi.kind,shiyi.color,shiyi.sex,shiyi.age)
self
参数
①self在定义实例方法的时候需要
②self关键字是可以修改的
③self和对象指向同一个内存地址,可以认为self就是对象的引用
④self传参:所谓的self可以简单理解就是对象本身,某个对象调用其方法时,python解释器会把这个对象作为第一个参数传递给self,所以开发者只需要传递后面的参数即可。
# self看做是对象的引用
class Car(object):
#创建一个实例方法打印self的id值
def __init__(self,name):
self.name = name
def getself(self):
print('self = %s'%(id(self)))
def run(something,person,road):#这里可以把self关键字进行修改,注意前后一致且放在第一个位置即可
print('%s在%s上开着%s。'%(person,road,something.name))
#实例化一个对象
bmw = Car('BMW')
#打印对象的id值
print('bmw = %s'%id(bmw))
#通过调用实例方法打印self的id值
bmw.getself()
#self传参——python解释器会自动把这个对象作为第一个参数传递给self
bmw.run('小王','和平路')#故虽然run方法中有三个形参,实际上只需要传入两个实参
'''
运行结果:
bmw = 1693895132496
self = 1693895132496
小王在和平路上开着BMW。
'''
- 魔术方法
①定义:在python中有一些内置好的特定的方法,方法名形如“
__xxx___
”,在进行特定的操作时会被自动调用,这些方法统称为魔术方法;
②一些常用的魔术方法:
p.s. 如果没有显式地对这些函数进行实现,则在定义类的时候系统会按照默认形式自动生成这些函数。
__str__()
方法
#__str__方法,将对象转换成字符串对象,方便输出打印
class Animal(object):
def __init__(self,name,color):
self.name = name
self.color = color
dog = Animal('旺财','white')
#未使用该方法进行直接打印时
print(dog)
'''
运行结果:
<__main__.Animal object at 0x0000028D291AEA90>
'''
在定义了__str__()
方法后,在打印对象(print(xxx)
)时会执行该方法,该方法只能return一个字符串。
#__str__方法,将对象转换成字符串对象,方便输出打印
class Animal(object):
def __init__(self,name,color):
self.name = name
self.color = color
def __str__(self):
return '我叫%s,我的颜色是%s'%(self.name,self.color)
dog = Animal('旺财','white')
#使用该方法进行打印时
print(dog)
'''
运行结果:
我叫旺财,我的颜色是white
'''
__new__()
方法
①在对一个类进行实例化时,首先执行的方法就是__new__()
方法,只有实例化出一个对象之后,才能对该对象进行初始化;
②可以控制创建对象的一些属性限定,经常在单例模式时使用
__init__()
v.s.__new__()
①__new__()
方法就是类的实例化方法,需要实现创建并实例化对象的逻辑,并且用return
关键字返回创建好的对象;必须有cls
参数——代表要实例化的类;
②__init__()
方法就是对数据属性的初始化,可以理解为是实例的构造方法,接受创建好的实例对象self
,并对其的属性进一步构造,必须有self
参数——代表要初始化的对象/实例。
p.s. 不管是cls
还是self
参数,在方法执行时都由python解释器自动传入;__new__()
方法的执行要早于__init__()
方法。
#__new__方法,创建并返回一个实例对象,每调用一次就会得到一个对象
class Animal(object):
def __init__(self,name,color):
#其实每次实例化对象时都要先等new方法执行成功,再根据相应参数对对象进行初始化
print('---------init方法被执行-----------')
self.name = name
self.color = color
def __str__(self):
return '我叫%s,我的颜色是%s'%(self.name,self.color)
def __new__(cls, *args, **kwargs):
print('---------new方法被执行-----------')
return object.__new__(cls)#objeect是最底层的对象,cls表示当前类
#这就是默认的new函数的执行逻辑,把当前类作为参数传进,生成一个对象
dog = Animal('旺财','white')
#使用该方法进行打印时
print(dog)
'''
运行结果:
---------new方法被执行-----------
---------init方法被执行-----------
我叫旺财,我的颜色是white
'''
案例实现与课后作业
- 决战紫荆之巅
- 问题需求
- 问题实现
- 实现了人物角色类,并且对具体两个NPC进行了实例化
- 增加了人物状态判断和游戏状态判断
- 通过随机数生成,结合状态实现游戏过程的自动模拟
import random
class Person(object):
def __init__(self,name,blood):
self.name = name
self.blood = blood
self.alive = True
def tong(self,enemy):
print('【%s】捅了【%s】一刀......'%(self.name,enemy.name))
enemy.blood -= 10
print('【%s】掉血10滴'%(enemy.name))
def kanren(self,enemy):
print('【%s】砍了【%s】一刀......' % (self.name, enemy.name))
enemy.blood -= 15
print('【%s】掉血15滴' % (enemy.name))
def chiyao(self):
print('【%s】吃了一颗药,补血10滴'%(self.name))
self.blood += 10
def state_check(self):
if self.blood < 0:
self.blood = 0
self.alive = False
if self.blood > 100:
self.blood = 100
# if not self.alive:
# print('【%s】已阵亡'%(self.name))
def __str__(self):
return '【%s】当前血量%d滴'%(self.name,self.blood)
game_start = True
def operation(role,number):
if number == 0:
role[0].tong(role[1])
role[1].state_check()
elif number == 1:
role[0].kanren(role[1])
role[1].state_check()
elif number == 2:
role[0].chiyao()
role[0].state_check()
print(role[0])
print(role[1])
if not role[1].alive:
global game_start
game_start = False
print('【%s】已阵亡' % (role[1].name))
xmcx = Person('西门吹雪',100)
ygc = Person('叶孤城',100)
while(game_start):
r_xmcx = random.randint(0, 3)
operation([xmcx,ygc],r_xmcx)
if not game_start:
break
print('------------------------------------')
r_ygc = random.randint(0,3)
operation([ygc,xmcx],r_ygc)
print()
- 定义一个水果类,通过水果类,创建苹果、橘子、西瓜对象并分别添加上颜色属性
class Fruit(object):
def __init__(self,kind,color):
self.kind = kind
self.color = color
def __str__(self):
return '%s是%s的'%(self.kind,self.color)
apple = Fruit('苹果','红色')
orange = Fruit('橘子','橙色')
watermelon = Fruit('西瓜','绿色')
print(apple)
print(orange)
print(watermelon)
- 定义一个Animal类
- 问题需求
- 问题实现
class Animal(object):
def __init__(self,color,name,age):
self.name = name
self.age = age
self.color = color
def eat(self,food):
print('%s在吃%s'%(self.name,food))
def run(self):
print('%s在跑.....'%self.name)
def __str__(self):
return '%s是%s的,现在%s了'%(self.name,self.color,self.age)
dog = Animal('黑白色','小狗','8个月')
cat = Animal('橘色','小猫','1岁')
rabbit = Animal('白色','小兔子','2岁')
for item in [dog,cat,rabbit]:
print(item)