day03 python的函数
函数的创建和调用
函数的作用: 执行特定任务,和完成特定功能的一段代码。1复用代码2隐藏实现细节3提高可维护性4提高可读性便于调试
函数的创建: *对齐使用
def 函数名 ([输入参数])
****函数体
****[return xxx]
函数的调用参数传递:
- 位置实参,根据形参对应形参的位置进行参数传递
- 想要将序列中的每一个元素都转换为位置实参,需要再调用时列表前加*
fun(*lis)
- 关键字实参,根据形参名称进行实参传递
调用时fun(a=10,b=20)
- 想要将字典中的每一个键值对都转换为关键字实参,需要在调用时字典前加**
fun(**dict)
- 如果一个函数的参数有想利用位置参数的也有想利用关键字参数的,需要在分隔处添加* 定义时:
fun(a,b,*,c,d)
则a,b需要使用位置传参,c,d使用关键字传参。调用时:fun(10,20,c=10,d=20)
在函数调用时中进行参数传递
-
如果时不可变对象,在函数体的修改不会影响实参的值。
-
如果是可变对象,在函数体的修改会影响到实参的值。
def fun(age1,age2):
print('age1',age1)
print('age2',age2)
print('age1type', type(age1))
print('age2type',type(age2))
age1=100
age2.append(10)
print('age1',age1)
print('age2',age2)
n1=11
n2=[11,22,33]
print('n1',n1)
print('n2',n2)
fun(n1,n2)
print('n1',n1)
print('n2',n2)
"""
n1 11
n2 [11, 22, 33]
age1 11
age2 [11, 22, 33]
age1type <class 'int'>
age2type <class 'list'>
age1 100
age2 [11, 22, 33, 10]
n1 11
n2 [11, 22, 33, 10]
"""
函数的返回值
- 如果函数的返回值函数执行完毕后不需要调用处理数据时,return不需要书写
- 函数的返回值如果只有一个,则直接返回该类型变量值
- 函数的返回值如果有多个,则返回的结果为一个元组(’‘,[],10)
def fun(num):
odd=[]
even=[]
for i in num:
if i%2:
odd.append(i)
else:
even.append(i)
return odd,even
print(fun([1,2,3,4,5,6,7,8,9,0]))
"""
([1, 3, 5, 7, 9], [2, 4, 6, 8, 0])
"""
函数的参数类型
默认参数值类型
def fun(a,b=10):
print(a,b)
fun(10)
fun(100,200)#不指定默认10,指定默认
个数可变的位置形参
定义函数时,无法事先确认传递的位置实参的个数时,使用可变的位置参数。使用*定义个数可变的位置形参,结果为一个元组
可变参数的参数类型会被定义为元组。使用过程不能改变引用!!!可变的位置参数只能有一个
def fun(*args):
print(args)
print(args[0])
print('argstype',type(args))
fun(10)
fun(10,20)
fun(10,20,30)
"""
(10,)
10
argstype <class 'tuple'>
(10, 20)
10
argstype <class 'tuple'>
(10, 20, 30)
10
argstype <class 'tuple'>
"""
个数可变的关键字形参
定义函数时,无法事先确定传递的关键字实参的个数时,使用可变的关键字形参,是用定义个数可变的关键字形参,结果为一个字典**
个数可变的关键字参数只能有一个
def fun(**kwargs):
print(kwargs)
fun(a=10)
fun(a=10,b=20,c=30)
"""
{'a': 10}
{'a': 10, 'b': 20, 'c': 30}
"""
在一个函数的定义过程当中,既有个数可变的关键字形参,也有个数可变的位置形参,要求个数可变的位置形参,放在个数可变的关键字形参之前!
函数定义的形参的顺序问题
def fun1(a,b,*,c,d,**kwargs):
pass
def fun2(*args,**kwargs):
pass
def fun3(a,b=10,*args,**kwargs):
pass
"""
"""
变量的作用域
程序代码能够访问变量的区域,根据变量的有效范围可分为:
- 局部变量 函数内定义并使用的变量,只在函数内部有效,局部变量使用**global声明,**这个变量就会成全局变量。
- 全局变量 **函数体外定义的变量。**可用于函数的内外。
name='hello'
def fun():
global a
a=10
print(a,name)
fun()
print(a,name)
"""
10 hello
10 hello
"""
递归函数
如果一个函数的函数体内调用了该函数本身,该函数就称为递归函数
**组成部分:**递归调用和递归终止条件
调用过程:每次递归调用一次函数,都会在栈内存分配一个栈帧,每次执行完一次函数,都会释放相应的空间。
递归的优缺点:缺点:占用的内存多,效率低下。优点:思路和代码简单
书写阶乘函数
def fun(a):
if a==1:
return 1
else:
return a*fun(a-1)
print(fun(3))
斐波那契数列
def fun(a):# 第几个位置上的数
if (a==1):
return 1
elif a==2:
return 1
else:
return fun(a - 1) + fun(a - 2)
def fun1(a):
f1=1
f2=1
while a>0:
print(f1,f2,end=' ')
f1=f1+f2
f2=f1+f2
a-=1
print(fun(3))
print(fun1(3))
day04 bug的调试
bug
bug叫做马克2号
Debug调试。基础语法错误:
- 漏掉:
- 缩进错误
- 英文字符中文字符
- 字符串和数字拼接在一起
- 没有定义变量就使用
- ==与=的使用
函数知识点使用错误:
- 索引越界,从0-n-1!!
- 错误使用类函数与内置函数,类函数需要利用对象调用
ste.append(100)
内置函数可以直接使用str=input('hello')
不知错误在哪利用print来观看内容 Python中遍历使用for-in很好使用,判断是否序列,元组,集合,字典中是否存在某项可以直接用in not in来判断
lst=[{'rating':[9.7,2062398],'id':'1292050','type':['犯罪','剧情'],'title':'肖申克的救赎','actors':['蒂姆·罗宾斯','摩根·弗里曼']},
{'rating':[9.6,1528760],'id':'1291540','type':['爱情','剧情','同性'],'title':'霸王别姬','actors':['张国荣','张丰毅','巩俐','葛优']}]
name=input('请输入你要查询的演员:')
for item in lst:
if name in item['actors']:
print(name+'出演了:'+item['title'])
"""
请输入你要查询的演员:葛优
葛优出演了:霸王别姬
"""
异常处理机制try-except
被动掉坑,就是情况考虑无法使用代码来过分限制,像除法中两个数必须为整数,且除数不得为0.情况时我们需要异常情况处理机制,try一下,多except结构捕获异常的顺序需要按照先子类后父类的顺序,为了避免遗漏可能出现的异常,可以在最后增加BaseException
try:
可能出现异常的正常代码
except ZeroDivisionError:###什么类型的错误时!执行异常情况之后继续走!
print('除数不得为零')
except ValueError:
print('不能将字符串转化为数字')
except BaseException as e:
print(e)
复制当前文件新增加一个新的文件代码,按住F5
try-except-else
如果try块中没有抛出异常,则执行else块,如果try中抛出异常,则执行except块
try-except-else-finally
finally块无论是否发生异常都会被执行,能常用来释放try块中申请资源!
常见的异常类型:
描述 | 异常类型 |
---|---|
数学计算的异常除0的异常 | ZeroDivisionError |
列表中没有此索引! | IndexError 下标越界情况 |
映射中没有这个键 | KeyError |
未声明或未初始化对象 | NameError |
Python语法错误 | SyntaxError |
传入无效的参数 | ValueError |
traceback模块打印异常信息
import traceback
try:
print('-----------------------------------------')
print(1/0)
except:
traceback.print_exc()
"""
Traceback (most recent call last):
File "D:/pycharmProject/fist.py", line 4, in <module>
print(1/0)
ZeroDivisionError: division by zero
-----------------------------------------
"""
pycharm的debug自行阅读
day05 面向对象编程
类多个类似的事物组成的群体的统称,能够帮助我们快速理解和判断事物的性质!
不同额数据类型就是不同的类
type就可以查看数据的类型
对象类的实例具体的一个东西!
**一切皆对象!!!**10,‘name’,dict,list,tuple,str.
类的创建
class 类名:
pass
类是由:类属性,实例方法,静态方法,类方法组成!!!Student就是类对象,因为Python一切皆对象
class Student:##大驼峰的命名,每个单词的首字母大写,其余要小写
pass
#一切皆对象,这是和其他面向对象的语言的不同
print(id(Student))
print(type(Student))
print(Student)
"""
2294471727072///开辟了内存空间
<class 'type'>///有类型
<class '__main__.Student'>///有值
"""
class Student:##大驼峰的命名,每个单词的首字母大写,其余要小写
advice='济南' #类属性!!因为类是有空间的所以里面的变量可以赋值,且python中的变量是指向型的
#赋值函数,相当于java中的构造函数
def __init__(self,name,age):
self.name=name #self.name叫实例属性,将局部变量赋值给实体属性
self.age=age
##实例方法,类内的叫方法参数要有self
def eat(self):
print('这是实例方法,普通的方法')
#静态方法 无参数
@staticmethod
def method():
print('此处为静态方法是在staticmethod模块中')
#类方法,参数叫cls为一个类
@classmethod
def cm(cls):
print('此处为类方法是在classmethod模块中')
"""
他声明的这个类其实叫类对象!
"""
对象的创建
类的实例化 语法是实例名=类名() stu=Student()
有了实例就可以调用类中的内容。
class Student:
advice='济南'
def __init__(self,name,age):
self.name=name
self.age=age
def eat(self):
print('这是实例方法,普通的方法')
@staticmethod
def method():
print('此处为静态方法是在staticmethod模块中')
@classmethod
def cm(cls):
print('此处为类方法是在classmethod模块中')
stu=Student('张三',20)
print(id(stu))
print(type(stu))
print(stu) ##输出对象值时就是对象的内存地址和id(stu)的值是一样的一个二进制一个十六进制
"""
3092414309040
<class '__main__.Student'>
<__main__.Student object at 0x000002D00241A2B0>
"""
调用方法
对象名.方法名
类名.方法名(对象)
stu.eat()
print(stu.name)
print(stu.age)
Student.eat(stu)
"""
这是实例方法,普通的方法
张三
20
这是实例方法,普通的方法
"""
类属性,类方法,静态方法
类属性:类中方法外的变量称为类属性,被该类的所有对象所共享
类方法:使用@classmetho修饰的方法,使用类名直接访问的方法
静态方法:使用@staticmethod修饰的方法,使用类名直接访问的方法
print(Student.advice)#访问类属性
Student.method()#调用静态方法
Student.cm()#调用类方法
"""
济南
此处为静态方法是在staticmethod模块中
此处为类方法是在classmethod模块中
"""
动态绑定属性和方法
一个类可以创建N个Student类的实例对象,每个实体对象的属性值不同
stu1=Student('张三',20)
stu1.gender='女' ##只给张三加性别,其他对象没有无法使用哦!。像是字典中添加键值对一样,有则修改值,没有则添加
print(stu1.name,stu1.age,stu1.gender)
def show():
print('我是一个动态的函数')
stu1.show=show##像动态的变量一样,不需要加括号,保持前后名字相同
stu1.show()
"""
张三 20 女
我是一个动态的函数
"""
_init_()中的变量是所有对象都拥有的内容。
面向对象的三大特征
-
封装 提高程序的安全性
-
将数据和行为包装到类对象中,在方法内部对属性进行操作,在类对象的外部调用方法。这样,无需关心方法内部的具体实现细节。从而隔离了复杂度。
-
在python中没有专门的修饰符用于属性的私有,如果该属性不希望再类对象外部被访问,前面使用两个“_”表示私有
-
def __init__(self,name,age): self.name=name #self.name叫实例属性,将局部变量赋值给实体属性 self.__age=age ##age将不能在类外使用,只能被类内方法所调用,属性名相应的也变成来了__age def show(self): print(self.name,self.__age) #类外部: stu=Studen('张三',20) stu.show() #要想强制使用被隐藏的变量属性使用dir(stu)观察到隐藏的属性名: print(dir(stu)) print(stu._Student__age) """ 张三 20 ['_Student__age', '__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'advice', 'cm', 'eat', 'gender', 'method', 'name', 'show'] 20 """
-
-
继承 提高代码的复用性
-
class 子类名字(父类1,父类2): pass class Person: def __init__(self, name, age): self.name = name self.age = age def info(self): print('姓名:{0},年龄:{1}'.format(self.name, self.age)) class Student(Person): def __init__(self,name,age,stu_no): super().__init__(name,age) self.stu_no=stu_no stu=Student('李四',20,2308097) stu.info() """ 姓名:李四,年龄:20 """
-
如果一个类没有继承任何类,则默认继承object
-
python支持多继承
class A:
pass
class B:
pass
class C(A,B):
pass -
-
定义子类时,必须在其构造函数中调用父类的构造函数构造函数就是_init_(self)…子类需要利用super().__init__(name,age)
-
方法的重写,重写继承父类的方法!必须函数名相同!子对象调用时调用子类重写的方法
-
class Person: def __init__(self, name, age): self.name = name self.age = age def info(self): print('姓名:{0},年龄:{1}'.format(self.name, self.age)) class Student(Person): def __init__(self,name,age,stu_no): super().__init__(name,age) self.stu_no=stu_no def info(self): super().info() print(self.stu_no) stu=Student('李四',20,2308097) stu.info() """ 姓名:李四,年龄:20 2308097 """
-
-
object类:是所有类的父类,因此所有类都有object类的属性和方法。内置函数dir()可以查看指定对象所有的属性,_str_()方法用于返回一个对于“对象的描述”,对应于内置函数str()经常用于print()方法,帮助我们查看对象的信息,所以经常会对__str__()进行重写,这个函数使得我们在使用print()打印对象时不在是它所占用的内存地址了
-
def __str__(self): return f'我的名字是{self.name},我的年龄是{self.age}' stu=Student('张三',20) print(stu) #我的名字是张三,我的年龄是20
-
-
-
多态 提高程序的可扩张性和可维护性
-
具有多种形态,即便不知道一个变量所引用的对象到底是什么类型,仍然可以通过这个变量调用方法,在运行过程中根据变量所引用对象的类型,动态决定调用哪个对象中的方法。这个对象具备所要调用的方法就会调用该对象的方法
-
class Animal: def eat(self): print('吃饭了') class 猫(Animal): def eat(self): print('猫吃🐟') class 狗(Animal): def eat(self): print('狗吃骨头') class Person: def eat(self): print('人啥都吃') def fun(a): a.eat() fun(Animal()) fun(猫()) fun(狗()) fun(Person()) """ 吃饭了 猫吃🐟 狗吃骨头 人啥都吃 """
-
静态语言和动态语言
静态语言实现多态的三要素:继承,方法重写,父类引用指向子类对象(C++就是)
动态语言的多态崇尚鸭子类型当看到一个鸟走起来像鸭子、游泳起来像鸭子、收起来也想鸭子,那么这只鸟就可以称为鸭子。在鸭子类型中,不需要关心对象是什么类型,到底是不是鸭子,只关心对象的行为。只要它具有eat()这个方法就可以被多态使用
特殊的属性和方法
–|--|–|
|名称|描述
特殊属性|_dict_|获得类对象或实例对象所绑定的所有属性和字典
名称 | 描述 | |
---|---|---|
特殊属性 | _dict_ | 获得类对象或实例对象所绑定的所有属性字典 |
特殊方法 | _len_ | 通过重写_len_()方法,让内置函数len()的参数可以是自定义类型 |
_add_ | 通过重写__add()__方法,可使自定义对象具有”+“功能 | |
_new_ | 用于创建对象 | |
_init_ | 对创建的对象进行初始化 |
常用属性像:dict,class,bases,base,mro
class A:
pass
class B:
pass
class C(A,B):
advice='zhaozhuang'
def __init__(self,name,age):
self.name=name
self.age=age
x=C('张三',20)
print(x.__dict__) #实例对象的属性字典
print(C.__dict__) #类对象的属性字典
print(x.__class__) #对象所属的类
print(C.__bases__) #C类的父类类型的元组
print(C.__base__) #C类离的最近的父类
print(C.__mro__) #类的层次结构
print(A.__subclasses__()) #子类的列表
"""
{'name': '张三', 'age': 20}
{'__module__': '__main__', 'advice': 'zhaozhuang', '__init__': <function C.__init__ at 0x000001752DD77940>, '__doc__': None}
<class '__main__.C'>
(<class '__main__.A'>, <class '__main__.B'>)
<class '__main__.A'>
(<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class 'object'>)
[<class '__main__.C'>]
"""
add和len函数的使用
class Student:
def __init__(self,name):
self.name=name
def __add__(self, other):
return self.name+other.name
def __len__(self):##重写长度函数
return len(self.name)
stu1=Student('张三三')
stu2=Student('李四')
s=stu1+stu2 #实现了两个对象的加法运算(重写了__add__()函数)
print(s)
lst=[11,22,33,44]
print('lst内置函数计算长度:',len(lst))
print('lst的类函数计算长度:',lst.__len__())
##—__len__()重写了内置函数len()
#当我们要获得某一对象的长度时,我们可以重写类的__len__()的函数
print('stu1的长度:',len(stu1))
print('stu2的长度:',len(stu2))
"""
张三三李四
lst内置函数计算长度: 4
lst的类函数计算长度: 4
stu1的长度: 3
stu2的长度: 2
"""
new和init
class Person:
##new在前,先创建对象
def __new__(cls, *args, **kwargs):
print(f'new已执行,cls的id值为{id(cls)}')
obj=super().__new__(cls)
print(f'创建的对象的id为{id(obj)}')
return obj
##初始化对象的
def __init__(self, name, age):
print(f'init被调用了,self的id值为:{id(self)}')
self.name = name
self.age = age
print(f'object这个类对象的id为{id(object)}')
print(f'Person这个类对象的id为{id(Person)}')
p1=Person('张三',20)
print(f'p1这个Person这个实例对象的id为{id(p1)}')
"""
object这个类对象的id为140720163240784
Person这个类对象的id为2267325734208
new已执行,cls的id值为2267325734208
创建的对象的id为2267332315744
init被调用了,self的id值为:2267332315744
p1这个Person这个实例对象的id为2267332315744
"""
类的赋值和浅拷贝
变量的赋值操作,只是形成两个变量,实际上还是指向同一个对象
浅拷贝 python拷贝一般丢失浅拷贝,拷贝时,对象包含的子对象内容不拷贝,因此,源对象与拷贝对象会引用同一个子对像
-
class Cpu: pass class Disk: pass class Computer: def __init__(self,cpu,disk): self.cpu=cpu self.disk=disk cpu=Cpu() disk=Disk() computer=Computer(cpu,disk) import copy computer1=copy.copy(computer) print(computer,computer.cpu,computer.disk) print(computer1,computer1.cpu,computer1.disk) """ <__main__.Computer object at 0x000002D0B07291C0> <__main__.Cpu object at 0x000002D0B0756A90> <__main__.Disk object at 0x000002D0B0729700> <__main__.Computer object at 0x000002D0B079B070> <__main__.Cpu object at 0x000002D0B0756A90> <__main__.Disk object at 0x000002D0B0729700> """
深拷贝 使用copy模块的deepcopy函数,递归拷贝对象中包含的子对象,源对象和拷贝对象所有的子对象也不相同。
-
import copy computer1=copy.deepcopy(computer) print(computer,computer.cpu,computer.disk) print(computer1,computer1.cpu,computer1.disk) """ <__main__.Computer object at 0x0000027849F491C0> <__main__.Cpu object at 0x0000027849F76A90> <__main__.Disk object at 0x0000027849F49700> <__main__.Computer object at 0x0000027849FBB1F0> <__main__.Cpu object at 0x000002784A0C6CA0> <__main__.Disk object at 0x000002784A0C6D60> """