01.基本理论
一、对象的概念
1.万物皆对象
2.对象是一个具体的物体
拥有的属性、拥有的行为、零散的封装成一个整体。例如:王小二(属性:姓名、年龄、身高...行为:走路、吃饭、放羊...)
3.对象在python中的体现
python是一门特别彻底的面向对象编程(OOP)的语言( 其它语言包括基本数据类型与对象类型)
二、面向过程与面向对象的区别
1.面向过程与面向对象的编程思想
他们都是属于一种解决问题的思路。
面向过程:在解决问题的时候,关注的是解决问题的每一个过程(步骤)
面向对象:在解决问题的时候,关注的是解决问题所需要的对象
例如:吃完饭之后洗碗(面向过程:放水、放碗、倒洗洁精、刷、擦干;面向对象:你的技能、你对象的技能...)
2.面向过程与面向对象的对比
1)面向对象和面向过程都是解决问题的一种方式(思想),面向对象是面向过程的一种封装。
2)面向过程编程最重要的是:把一个任务分解成具体的步骤。
3)面向对象编程最重要的是:按照对象的功能进行划分,找到对象确定对象的属性和行为。
3.从面向过程编程的思想过渡到面向对象编程的步骤
1)列举出一个任务具体的实现步骤;
2)试图分离这些实现步骤中的功能代码;
3)将这些功能代码块,划分都某一个对象中;
4)根据这个对象以及对应的行为,抽象出对应的类;
三、类的简介
1.什么是类
某一个具体对象特征的抽象
2.类的作用
根据抽象的类,生产出具体的对象
例如:(类:不良青年 属性:年龄、身高、体重... 行为:吃喝嫖赌...对象:张三、李四、王五...都满足以上的属性和行为)
3.类的组成
名称、属性(静态的特征值)、方法(动态的动作)
注意:1)以上属性和方法,都是抽象的概念
2)在产生对象之后,对象才拥有具体的属性值和方法实现
4.生活中常用的类
1)类:钱 对象:具体的一块、两块、一百块...
2)类:汽车 对象:奥迪、宝马、奔驰...
5.对象和类的区别
对象——>抽象——>类——>实例化——>对象
02.面向对象编程在python中的实践
一、定义一个类(经典类、新式类)
class Money: #class类名:(类名的首字母需要大写,不要加小括号)
pass
class renminbi(money): #括号内部表示继承关系
二、根据类创建一个对象
one =Money() #one就是一个对象(通过*类名+一个括号*即可创建一个对象)
print(one)
三、创建对象时的底层运作
ProcessOn在线绘图(可以绘制流程图)
四、属性相关
1.属性和变量的区别及判定依据
1)区别:变量是“可以改变的量值”,根据不同的位置存在不同的访问权限(全局变量、局部变量)
属性是“属于某个对象的特性”,只能通过一个对象进行访问
2)判定依据:是否存在宿主
2.对象属性
1)添加操作:直接通过对象动态添加——>对象.属性=值
通过类的初始化方法(构造方法)——>int方法
2)查询操作
3)修改操作
4)删除操作
#定义一个类
class Person:
pass
#根据这个类创建一个对象
p=Person()
#给对象p增加属性
p.age=18
p.height=180
#查找当前对象所有的属性(返回一个字典)
p.__dict__
#删除一个属性
del p.age
5)注意事项:不同对象之间不能访问其唯一的属性。
3.类属性
1)添加操作:类名.属性=值 ;在类里面写变量名=值
2)查询属性:既可以通过类进行查询,也可以通过对象进行查询
(python对象查找机制:优先到对象自身去查找属性,如果没有找到,则根据class找到对应的类,再在这个类里面进行查找)
3)修改属性:只能通过类名进行修改,不能通过对象进行修改
4)删除操作:只能通过类名进行删除,不能通过对象进行删除
5) 注意事项:类属性的内存存储问题;类属性被各个对象所共享
#在类里面添加属性
class Money:
age=18
count=1
pass
class Text:
sex=male
#更改对象所属的类
one=Money()
one.__class__=Text
#对类的属性的修改
Money.age=22
#删除类的属性
del Money.age
五、方法相关
1.方法的概念
1)方法的概念:描述一个目标的行为动作(比如描述一个人怎么吃、怎么喝...)和函数非常的类似
——>都封装了一系列行为动作
——>都可以被调用之后执行一系列行为动作
——>最主要的区别就是调用的方式不同
#函数
def eat():
print(1)
print(2)
print(3)
eat()
#方法
class Person:
def eat2(self):
print(1)
print(2)
p=Person()
p.eat2()
2.方法的划分
1)类、对象、实例、实例对象、实例化
2)方法的划分依据
实例方法:默认第一个参数需要接收到一个实例;
类方法:默认第一个参数需要接收到一个类;
静态方法:静静的看着前面两个在装逼,第一个参数啥也不默认接收;
注意:
—>划分的依据是第一个参数必须接收到的数据类型
—>不管是哪一种类型的方法,都是存储在类当中,没有在实例当中的
—>不同类型的调用方式不同
—>重点关注方法的使用层面:语法、不同类型方法的规则、不同类型的方法调用、根据不同的问题设计不同的方法解决问题
3)方法的存储问题
3.实例方法
1)标准形式
2)标准调用:使用实例调用实例方法(不用手动传,解释器会自动被调用对象本身传递过来)
注意:如果实例方法没有接收到任何参数,则会报错。
#创建一个类+实例方法
class Person:
def eat(self,food):
print("在吃饭",food)
#标准调用
p=Person()
p.eat("土豆")
#————>输出:在吃饭,土豆
4.类方法
1)标准形式
2)标准调用:既可以通过类调用,也可以通过实例调用
#创建一个类+类方法
class Person:
@classmethod #类方法装饰器
def leifangfa(cls,a):
print("这是一个类方法",cls,a)
#标准调用(既可以通过类调用,也可以通过实例调用)
Person.leifangfa(123)
#—————>输出:这是一个类方法,<class'__main__.Person>123
5.静态方法
1)标准形式
2)标准调用:既可以通过类调用,也可以通过实例调用
#创建一个类+静态方法
class Person:
@staticmethod
def jingtai():
print("这是一个静态方法")
#标准调用(既可以通过类调用,也可以通过实例调用)
6.不同类型的方法中访问不同类型属性的权限问题
class person:
age=0
def shilifangfa(self):
print(self.age)
print(self.num)
@classmethod
def leifangfa(cls):
print(cls.age)
print(cls.num)
p=Person()
p.num=10
p.shilifangfa() #age和num属性全部能够输出
p.leifangfa() #age属性能够输出,num属性会报错
六、补充
1.类相关补充
1)元类:创建类对象的类
2)类的描述:(用于规范编程)
-
目的:方便自己理清逻辑思路、方便多人合作开发时的沟通、方便生成项目文档
-
描述方式:在定义类、属性、方法下面一行写注释,使用双引号对(标明效果、 参数、参数含义、参数类型[动态语言参数不确定],是否有默认值, 返回值)
-
生成项目文档:(不需要把源码全部给别人,先使用help()生成项目)
方式一,使用python内置模块pydoc(参考课程,现学现用)
方式二,使用第三方模块Sphinx、epydoc、doxygen(参考课程,现学现用)
2.属性相关补充
1)私有化属性(对访问范围较大的属性设置一个范围,使其访问范围较小)
可用于数据保护以及数据过滤
-
注意:python中并没有真正的私有化支持(只是通过名字重整规则加密了),但是可以使用下划线完成伪私有效果(使用其他的手段进行访问); 类属性(方法)和实例属性(方法)遵循相同的规则
-
公有属性(x):类内部访问——>可以
子类内部访问——>可以
模块内其他位置访问 类访问——>警告
实例访问——>警告
跨模块访问 import——>可以
from 模块 import *——>可以
- 受保护的属性(_y):类内部访问——>可以
子类内部访问——>可以
模块内其他位置访问 类访问——>可以
实例访问——>可以
跨模块访问 import——>报错
from 模块 import *——>报错
#file1
_a=6
#file2
from file1 import *
print(_a) #受保护会报错
###########################################################################################
#file1
_a=6
__all__=['_a'] #内置属性__all__可用来传输受保护参数
#file2
from file1 import *
print(_a) #可以正常访问
- 私有属性(__z): 类内部访问——>可以
子类内部访问——>报错
模块内其他位置访问 类访问——>报错
实例访问——>报错
跨模块访问 import——>报错
from 模块 import *——>报错
注意:也可以使用__all__=['']使其他模块进行访问
- 补充: x_ 与系统内置的关键字做区分
_ _x__ 系统内置的特殊属性(例如:__dict__、__count__)
2)只读属性(一个属性【一般指实例属性】只能读取不能写入)
有些属性只限于内部根据不同场景进行修改,而对外界来说不能修改,只能读取。
例如:电脑类的网速属性、网络状态属性。
- 设置只读属性(方案一):全部隐藏,部分公开【这里就会用到@property修饰器】
首先通过“属性前置双下划线”实现,再通过公开的方法进行部分公开。
class Person(object):
def __init__(self):
#全部隐藏
self.__age=18
#部分公开
@property #主要作用就是可以使用属性的方式来使用这个方法
def age(self):
return self.__age
p=Person()
print(p.age)
'''
经典类与新式类(建议使用新式类)
经典类:没有继承(object)
新式类:有继承(object)
可以通过'__bases__'查询继承类
python2.x 如果定义一个类,没有显示继承自object,那么这个类就是一个经典类。
python3.x 无论有没有显示继承,默认都是一个新式类
'''
'''
@property 装饰器讲解
1.主要作用:将一个些“属性的相关方法(删、改、查)”关联到某一个属性中。常用于定义或管理__X
2.property在新式类中的使用:(代码1)
3.property在经典类中的使用:(代码2)(一般不使用,随用随听)
'''
#代码1
class Person(object):
def __init_(self):
self.__age=18
@property ##########装饰器1:(查)
def age(self):
return self.__age
@age.setter #########装饰器2:(改)
def age(self,value)
self.__age=value
@age.deleter #########装饰器3:(删)
def x(self):
del self.__age
p=Person()
print(p.age)
print(p.age=90)
- 设置只读属性(方案二)
class Person:
def __setattr__(self,key,value):
#当我们通过 实例.属性=值 时,给一个实例增加一个属性或者说修改一个属性值的时候,都会调用这个方法
#在这个方法内部,才会真正把这个属性以及对应的数据存储到__dict__字典里面。
print(key,value)
#第一步:判定key是否是我们要设定的只读属性的名称。
if key=='age' and key in self.__dict__.keys():
print('这个属性是只读属性,不能设置数据')
#第二部:如果不是只读属性的名称,则真正的给它添加到这个实例里面去。
else:
self.__dict__[key]=value
3)内置特殊属性(当定义好一个类或通过这个类创造了一个实例,系统自动的分配了一些可以直接使用的属性,以完成一些小的测试操作或某些应用场景)
- 类属性:__dict__:类的属性
__bases__:类的所有父类元组(python是多继承,java是单继承)
__doc__:类的文档字符串
__name__:类名
__module__:类定义所在的模块
- 实例属性:__dict__:实例的属性
__class__:实例对应的类
class Person:
age=19 #类属性
def __init__(self):
self.name='sz' #利用初始化方法定义一个实例属性
def run(self):
print("run")
print(Person.__dict__)
print(Person.__bases__)
print(Person.__doc__)
print(Person.__name__)
print(Person.__module__)
P=Person()
print(P.__dict__)
print(P.__class__)
2.方法相关补充
1)私有化方法(同于私有化属性:名称重整机制)
当类的内部才使用的方法,且不暴露给外界使用。
2)内置特殊方法
- 生命周期方法(下一节单独介绍)
- 信息格式化操作:__str__方法:面向用户
__repr__方法:面向开发人员、python解释器
class Person:
def __init__(self,n,a):
self.name=n
self.age=a
def __str__(self):
return "这个人的姓名是:%s,这个人的年龄是:%s"%(self.name,self.age)
p1=Person("sz",18)
p2=Person("zs",19)
print(p1) #此时打印的就不再是模块名+类名+内存地址,而是__str__方法,return的返回值
print(p2)
print(repr(p1)) #当有__str__方法时,用此方法可打印模块名+类名+内存地址
print(repr(p2))
- 调用操作:__call__方法 使得实例化的对象具有被调用的能力
class Person:
def __call__(self,*args,**kwargs):
'''
使用用__call__方法,使得实例化的对象具有被调用的能力。
*args 为元组的形式
**kwargs 为字典形式
这两个参数保证了可以传输任何形式
'''
print("xxx",args,kwarge)
pass
p=Person
p(123,456,name='sz') #若类中没有__call__方法,则会报错;若有__call__方法,则会调用__call__的方法。
- 索引操作
- 切片操作
- 迭代器
- 描述器
03.面向对象编程——综合案例
#任务:做一个计算器,实现一些基本操作,加减乘除以及打印结果
#面向过程编程,对于连续运算极其复杂,不安全
#设计以及优化思路:包装——>安全性——>能否满足多个同时运算——>是否容错(健壮性)
class Cacular:
def __int__(self,num): #用于初始化一个类,num是初始化的属性值
if not isinstance(num,int): #Python中的 isinstance() 函数,是Python中的一个内置函数,用来判断一个函数是否是一个已知的类型,类似 type()。
raise TypeError("当前这个数据类型有问题,应该是一个整型数据。")
#当程序出现错误,python会自动引发异常,也可以通过raise显示引发异常,并通过TypeError()给出提示语。一旦执行了raise语句,raise后面的语句将不能执行。
self.__result=num #“__”是私密化处理,实例只能调用、查询,不能修改
def jia(self,n):
if not isinstance(num,int):
raise TypeError("当前这个数据类型有问题,应该是一个整型数据。")
self.__result+=n
def jian(self,n):
if not isinstance(num,int):
raise TypeError("当前这个数据类型有问题,应该是一个整型数据。")
self.__result-=n
def cheng(self,n)
if not isinstance(num,int):
raise TypeError("当前这个数据类型有问题,应该是一个整型数据。")
self.__result*=n
def show(self):
print("计算的结果是:%d"%self.__result)
c1=Cacular(2)
c1.jia(6)
c1.jian(4)
c1.cheng(5)
c1.show()
—————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————
#进一步优化思路:以上代码太过冗余,需要简化————>装饰器(不改变原来的代码基础)————>装饰器私有方法
class Cacular:
def __check_num_zsq(func): #私有方法的装饰器
def inner(self,n):
if not isinstance(num,int):
raise TypeError("当前这个数据类型有问题,应该是一个整型数据。")
return func(self,n) ######################################################不太明白!!!
return inner
@__check_num_zsq #装饰器
def __int__(self,num):
self.__result=num
@__check_num_zsq
def jia(self,n):
self.__result+=n
@__check_num_zsq
def jian(self,n):
self.__result-=n
@__check_num_zsq
def cheng(self,n)
self.__result*=n
def show(self):
print("计算的结果是:%d"%self.__result)
—————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————
#增加一个新任务:每一步都实现语音播报功能(类似与开启声音的计算器)
#既可以通过安装第三方库实现,也可以通过调用Windows操作系统的接口
#设计以及优化思路:在哪里插入代码——>简化代码————>采用装饰器,不破坏原有的结构(嵌套装饰)
import win32com.client
class Cacular:
def __check_num_zsq(func): #私有方法的装饰器
def inner(self,n):
if not isinstance(num,int):
raise TypeError("当前这个数据类型有问题,应该是一个整型数据。")
return func(self,n) ######################################################不太明白!!!
return inner
def __say(self,word):
spearker=win32com.client.Dispatch("SAPI.ApVoice") #创建一个播报对象
speaker.Speak(word) #通过这个播报器对象,直接播放对应的语音字符串即可
def __creat_say_zsq(word=""):
def __say_zsq(func):
def inner(self,n):
self.__say(word+str(n))
return func(self,n)
return inner
return __say__zsq
@__check_num_zsq #装饰器
@__creat_say_zsq()
def __int__(self,num):
self.__result=num
@__check_num_zsq
@__creat_say_zsq("加")
def jia(self,n):
self.__result+=n
@__check_num_zsq
@__creat_say_zsq("减去")
def jian(self,n):
self.__result-=n
@__check_num_zsq
@__creat_say_zsq("乘以")
def cheng(self,n)
self.__result*=n
def show(self):
self.__say("计算的结果是:%d"%self.__result)
print("计算的结果是:%d"%self.__result)
c1=Caculator(10)
c1.jia(6)
c1.jian(4)
c1.cheng(5)
c1.show