Python面向对象
1.面向对象
#OOA\OOD\OOP
#面向对象三大特性
- 继承
- 封装
- 多态
#面向过程和面向对象
- 面向过程
- 事件为中心,分析出问题所需的步骤,然后用函数实现这些步骤,按顺序调用
- 维护、复用、扩展性较差
- 面向对象
- 与面向过程相辅相成,在开发过程中,设计许多对象,每个对象都能可以接受其他对象发过来的消息,并且能够调用其他对象的方法,程序的执行就是一个个类之间数据的互换以及方法的调用
- 性能低于面向过程
#类和对象
- 类:对象的抽象,一类事物的总称
- 对象:类的具象,一个具体的事务
- 先有类,通过类创建对象
#什么是对象
- 对象是从类中出来的,只要是类名加上(),这就是一个实例化过程,这个就会实例化一个对象
- 实例化对象一共发生三件事
- 1.在内存中开辟一个对象空间
- 2.自动执行类中的__init__方法,并将这个对象空间(内存地址)传给了__init__方法的第一个位置参数self。
- 3.在__init__ 方法中通过self给对象空间添加属性。
#类和对象的创建
元类创建类
类由元类创建
#类的创建
class 类名():
属性(是什么样的)
方法(可以做什么)
#对象的创建
对象名 = 类名()
对象名.属性名
对象名.方法名()
# 创建一个类
class Person: #命名时,首字母大写,多个单词组成,驼峰体命名
#属性分类:类属性和实例属性
eyes = 2 #类属性 属于当前类
def __init__(self,name,sex): #实例属性 动态属性 属于当前对象
self.name = name
self.sex = sex
print("asdasd")
def run(self,age): #方法
print("{}是{}".format(self.name,age))
chen = Person("爸爸",18) #>>>asdasd #实例化对象就是创建对象
chen2 = Person("爷爷",180)
chen.eyes = 3 #创建一个实例属性并不是修改类属性的值
del Person.eyes #删除类属性 删除类属性不能通过对象删除 只能del 类.属性
del chen.eyes #删除实例属性
########################对象操作对象空间属性
chen.job = "it" #添加新的实例属性
chen.sex = 20 #改
print(chen.name) #查
del chen.sex #删除方法中的sex
#######################对象查看类中的属性
print(obj.eyes)
#######################对象操作类中的方法
chen.run(20) #>>>爸爸是20
- 创建对象的时候会先找【def new】 再找【def init】
- 对象的创建是在类的内存空间中开辟了一块内存,类属性和实例属性互不影响
#类添加新方法
- types
import types
class Person():
def run(self):
print("刘炳良在跑")
def jump(self):
print("冯俊在飞")
p1 = Person()
p1.jump = types.MethodType(jump,p1)
p1.jump()
#类属性和实例属性
-
类属性
- 属于类
- 当前类的所有对象该属性值都一样
- 类变量推荐直接用类名访问,但也可以使用对象名访问
- 改变类变量的值会作用于该类所有的实例化对象
- 可以通过类名在类外添加新的类属性
-
实例属性
def __init__(self,name): self.name = name ###self在类内使用,类外无效
- 属于对象
- 当前类的对象可以拥有各自的属性值
- 实例变量只能通过对象名访问,无法通过类名访问
- 实例属性不仅可以在--init--里面添加,也可以在类的其他方法中添加
- 查询对象中的所有属性:对象.--dict--
-
注意
- 允许通过对象访问类变量,但无法通过对象修改类变量的值
- 对象名和类名可以同名,但此时,使用对象名无法调用类属性
- 修改一个对象的实例变量,既不会影响类变量的值,也不会影响其他对象的实例变量
2.类直接的关系(面向对象)
- 依赖(关联)关系
- 组合(聚合)关系
- 继承(实现)关系
#依赖关系
-
执行某个动作时,需要其他类的对象来帮助完成
- 将一个类的对象传到另一个类的方法使用
class Person: def play(self,tools): tools.run() print("我要开始了") class Computer: def run(self): print("电脑已就位") class Phone: def run(self): print("手机以就位") con = Computer() pho = Phone() chen = Person() chen.play(con) chen.play(pho)
#组合(关联)关系
- 在对象里面包含对象
- 将一个类的对象封装到另一个类的对象的属性中,就叫组合
- 一对一
- 一对多
- 组合是指在新类里面创建原有类的对象,重复利用已有类的功能“has-a”关系
#一对一关系
class Chen:
def __init__(self,name,girlfriend = None):
self.name = name
self.girlfriend = girlfriend
def eat(self):
if self.girlfriend:
print("{}带着{}".format(self.name,self.girlfriend.name))
#此处的self.girlfriend传进来的是yo对象,所以得加.name实际就是对象.name
else:
print("我是儿子")
class Yi:
def __init__(self,name):
self.name = name
chen = Chen("晨浩")
yi = Yi("刘亦菲")
chen.girlfriend = yi
chen.eat()
#一对多关系
# class Teacher():
# def __init__(self,):
# self.stud = []
# def add_stu(self,name):
# self.stud.append(name)
# def show_stu(self):
# for i in self.stud:
# print(i.num,i.name,i.tea)
# class Stu():
# def __init__(self,num,name,tea):
# self.name = name
# self.num = num
# self.tea = tea
# li = Stu(1,"李","尚")
# wang = Stu(2,"王","尚")
# sha = Stu(3,"傻","宝")
# tea = Teacher()
# tea.add_stu(li)
# print(tea.stud[0]) #是将对象append进去
# tea.add_stu(wang)
# print(tea.stud[1])
# tea.add_stu(sha)
# tea.show_stu()
#继承关系
- 面向对象编程(oop)语言的一个主要功能就是“继承”
- 他可以使用现有类的所有功能,并在无需重新编写原来的类的情况下对这些功能进行扩展
- 通过继承创建的新类称为“子类”或“派生类”,被继承的类称为“基类”、“父类”或“超类”
- 在Python中,同时支持单继承与多继承
- 而继承允许设计人员根据其它类的实现来定义一个类的实现“is-a”关系
#继承关系
- 分为单继承(只有一个父类)和多继承(有多个直接父类)
- 实现继承之后,子类将继承父类的属性和方法,可以直接调用
- 增加了类的耦合性(耦合性不宜多,宜精)
- 减少了重复代码
- 使得代码更加规范化,合理化
#单继承
- 子类可以继承父类的属性和方法,可直接调用。
- 修改父类,所有的子类都会被影响
class Ano:
def __init__(self,name):
self.name = name
def run(self):
print(f"{self.name}再拍")
class Dog(Ano):
def eat(self):
print("吃吃")
chen2 = Ano("asdasd")
chen2.run() #asdasd再拍
chen = Dog("爸爸")
chen.eat() #吃吃
chen.run() #爸爸再拍
chen.name = "妈啊"
chen.run() #妈啊再拍
chen2.name = "123123"
print(chen.name) #"妈啊"
#判断继承的函数
-
isinstance(obj,cls):检查obj是不是cls的对象(传两个参数,一个是对象,一个是类)
class Boy(): def __init__(self,name,): self.name = name self.girl_f = [] boy = Boy("晨") print(isinstance(boy,Boy)) >>>>Ture
-
issubclass(sub,super):检查sub是不是super的子类(传两个参数,一个是子类,一个是父类)
class Ye: def __init__(self): print("我是爷爷") class Ez(Ye): def __init__(self): super().__init__() print("我是儿子") print(issubclass(Ez,Ye)) >>>>Ture
#多继承
-
多重继承:包含多个间接父类
-
多继承
- 有多个直接父类
- 方法的查找顺序是,先查找括号中最左边的类
- 大部分面向对象的编程语言(除C++)都支持单继承,而不支持多继承,
- 多继承不仅会增加编程的复杂度,也会出现莫名的报错
- Python虽在语法上明确支持多继承,但通常如果不是很必要,尽量不要使用多继承,而是使用单继承
- 这样可以保证编程思路更清晰,可以避免很多麻烦
- 如果多个直接父类中包含了同名的方法
- 此时排在前面的父类中的方法会“遮蔽”排在后面的父类中的同名方法
-
实例
#####继承关系#多继承 class Ye(): def run(self): print("我是爷爷") class Ba(): def __init__(self): print("我是爸爸1") Ye.run(self) print("我是爸爸2") class Die(): def __init__(self): print("我是爹爹1") print("我是爹爹2") class Ez(Die,Ba): def __init__(self): print("我是儿子1") Die.__init__(self) Ba.__init__(self) print("我是儿子2") b = Ez() >> 我是儿子1 我是爹爹1 我是爹爹2 我是爸爸1 我是爷爷 我是爸爸2 我是儿子2
![super()继承]()
#方法重写
- 在子类中可以定义和父类中同名的方法,方法的实现不同,重写后,会覆盖父类的同名方法(涉及到mro查找顺序)
#super()
-
什么是super()
- 用于调用父类(超类)的一个方法。
- 是用来解决多重继承问题的,直接用类名调用父类方法在使用单继承的时候没问题,但是如果使用多继承,会涉及到查找顺序(MRO)、重复调用(钻石继承)等种种问题。
- MRO 就是类的方法解析顺序表, 其实也就是继承父类方法时的顺序表。
-
super()关键字
- 子类如果编写了自己的构造方法,但没有显式调用父类的构造方法,而父类构造函数初始化了一些属性,就会出现问题
- 如果子类和父类都有构造函数,子类其实是重写了父类的构造函数,如果不显式调用父类构造函数,父类的构造函数就不会被执行
- 解决方式:调用超类(父类)构造方法,或者使用super函数 super(当前类名, self).init()
-
super()关键字详解
-
Python3中可以直接使用super().xxx 代替super(Class, self).xxx
-
使用多继承,会涉及到查找顺序(MRO)、钻石继承等问题
-
钻石继承
-
单继承时,类名.__init__()的方式和super().__init__()方式没啥区别 但是使用 类名.__init__()的方式在钻石继承时会出现问题
-
class Ye: def __init__(self): print("我是爷爷") class Ba(Ye): def __init__(self): print("进入爸爸") super().__init__() print("我是爸爸") def j(self): print("我是姥姥") class Die: def __init__(self): print("进入爹爹") super().__init__() # Ye.__init__(self) print("我是干爹") class Ez(Die,Ba): def __init__(self): print("我是儿子") super().__init__() ##到此步,因为存在super 所以调用父类 # super(Die,self).__init__() #当不想调用Die类的时候,super()括号里加Die,就会执行Die后面的父类Ba # super(Die,self).j() #执行Die后面的类Ba的J的方法 # Ba.__init__(self) # Die.__init__(self) print("进入儿子") B = Ez() print(Ez.mro()) >>> 我是儿子 进入爹爹 进入爸爸 我是爸爸 我是干爹 进入儿子
-
-
#mro顺序
- 深度优先 先找左侧找到底,再找右侧
class Ye:
def __init__(self):
print("我是爷爷")
class Ba(Ye):
def __init__(self):
print("进入爸爸")
super().__init__()
print("我是爸爸")
def j(self):
print("我是姥姥")
class Die(Ye):
def __init__(self):
print("进入爹爹")
super().__init__()
# Ye.__init__(self)
print("我是干爹")
class Ez(Die,Ba):
def __init__(self):
print("我是儿子")
super().__init__()
a = Ez()
print(Ez.mro())
>>>[<class '__main__.Ez'>, <class '__main__.Die'>, <class '__main__.Ba'>, <class '__main__.Ye'>, <class 'object'>]
#组合和继承区别
- 组合是指在新类里面创建原有类的对象,重复利用已有类的功能
- 继承是允许设计人员根据其他类的实现来定义一个类的实现
#继承注意
-
不要轻易使用继承
- 过多的使用继承会破坏代码的可维护性,父类被修改,影响到继承他的子类,增加维护难度和成本
-
组装的时候用组合,扩展的时候用继承
-
子类可是使用父类所有的属性和方法
-
若子类有自己的方法,先找自己的,若子类没有自己的方法,就会找父类的,若都没有,就会报错
如果父类的方法子类也有,那么就叫做方法的重写,就不执行父类了,去执行子类
-
如果子类中实现了调用父类的方法
- 在类内:super(子类,self).方法名() supper().init(参数)
- 在类外:super(子类名,对象名).方法名()
#C3算法
-
什么是C3算法
判断mro要先确定一个线性序列,然后查找路径由序列中类的顺序决定,所以C3算法就是生成线性序列
若继承至一个类 class B(A),那么此时B的mro序列为[B,A]
-
为什么采样C3算法?
是为了解决原来基于深度搜索算法不满足本地优先级和单调性的问题
本地优先级:C(A,B),访问C类对象属性时,优先查找A类再查找B类
单调性:C的解析顺序中,A排在B的前面,那么C在所有子类中,也必须满足这个条件
-
在多重继承中,解决对对资源的索引顺序
-
mro(Child(Base-1,Base-2,Base-n)) = [ Child ] + merge( mro(Base-1), mro(Base-2), mro(Base-n), [ Base1, Base2] )
-
表头和表尾
- 表头:列中中的第一个元素
- 表尾:列表中除表头外的所有元素集合(可以为空)
[A,V,C,D] 表头是A 表尾是[V,C,D]
-
merge操作
-
merge操作实例
计算merge([E,O],[C,E,F,O],[C]) 三个列表: 1 2 3 1、判断merge不为空,取出第一个列表,列表1的表头E,进行判断E是存在merge各个列表的表尾的集合中,所以跳过当前列表。 2、再取出列表2的表头C,进行判断C不在各个列表的表尾的集合中,所以将C拿出到merge外,并将C从所以的表头中删除。 此时merge([E,O],[C,E,F,O],[C])=[C]+merge([E,O],[E,F,O]) 3、再进行下一次的merge判断
3.#封装(面向对象)
#封装
- 封装就是讲抽象得到的数据和行为相结合,形成一个有机的整体
- 元组、列表、字典等:数据的封装,通过引用去使用数据
- 函数:算法的封装
- 没有函数,功能靠每一行代码去直接执行
- 耦合度太高,复用性太差,开发效率低
#封装目的
- 封装目的是简化编程和增强安全性
- 使用者不必关系该类具体的实现细节
- 通过接口(万能的点)
- 给予特定的访问权限来使用类的成员
- 明确区分类内外
- 类的实现者可以修改内部封装的东西而不影响外部调用者
- 外部调用者只需要知道自己可以使用该类对象的哪些功能
#私有属性、私有方法
- “双下划线”开始的是私有成员,在类外部不可以直接用属性或方法名调用,子类中也不能访问到这个数据
- 可以提供外界访问的接口
- 可以将对外部不需要的内容都隐藏起来
- 把属性都隐藏,提供公共方法对其访问
- 双下滑线开头的属性在继承给子类时,子类时无法覆盖的
#破解私有属性和方法
-
在名称前加上类名,即 _类名__名称(例如a._A__N)
-
class Person(): def __init__(self,name,age): self.__name = name self.age = age ####私有方法 def __get_name(self): ###方法名前加上__就算是私有成员, return (f"我是{self.__name}") def get_name2(self): return self.__get_name() def __set_name(self,name): return self.__name = name def set_name2(self,name): return self.__set_name(name) chen = Person("爸爸",19) print(chen.get_name2()) chen.set_name2("MAMA") print(chen.get_name2()) # 破解私有属性 对象._类__名称 print(chen._Person__get_name())
-
-
加双下划线仅仅是一种变形操作
-
类中所有双下划线开头的名称如__x都会自动变形成:_类名__x的形式
-
4.多态、多态性(面向对象)
#多态
什么是多态:就是不同的子类调用相同的父类方法,产生不同的结果
#多态提现1
-
Python是一种多态语言,不关心对象的类型
- 对于弱类型(Python)的语言来说,变量并没有声明类型,因此同一个变量完全可以在不同的时间引用不同的对象
- 在Python中对象是一块内存,内存中出来包含属性、方法之外,还包含了对象类型,我们通过引用来访问对象,比如a=A(),首先Python创建一个对象A,然后声明一个变量a,再将变量a与对象A联系起来。变量a是没有类型的,他的类型取决于其关联的对象
class Dog: def run(self,name): print(f"{name}是狗在跑") class Cat: def run(self,name): print(f"{name}是猫在跑") x = Dog() x.run("金毛") #金毛是狗在跑 x = Cat() x.run("大局") #大局是猫在跑
- 同一个变量x在执行同一个run方法时,由于x指向的对象不同,因此呈现出不同的行为特征,这就是多态。
#多态提现2
-
一类事物有多种形态
- 一个抽象类有多个子类,但方法实现不同
- 例如:动物类有多个子类,每个子类都重新实现了父亲的某个方法,但方法的实现不同,
-
此时需要有继承,需要有方法重写。
-
class Girl(object): #object可写可不写 def __init__(self,name): self.name = name def game(self): print(f"{self.name}Girl玩") class Norse(Girl): def game(self): print(f"{self.name}打针") class Boy(object): def __init__(self,name): self.name = name def play(self,girl): print(f"{self.name}and{girl.name}玩") girl = Girl("小红") boy = Boy("小米") girl.game() boy.play(girl) nor = Norse(girl.name) nor.game() ####### 在girl类定义一个方法game,普通girl类只能玩; 定义了一个norse类,重写了game方法,norse类可以打针 定义一个boy类,写了一个和girl玩的方法play,在方法内部,直接让girl对象调用play方法
#多态性
-
多态性polymorphism:在多态基础上,定义统一的接口(类外定
义单独的函数)- 不同类的对象作为函数的参数时,得到的结果不同
class Car: def __init__(self,color): self.color = color def run(self): print(f"{self.color}小车在跑") class Cat: def __init__(self,name): self.name = name def run(self): print(f"{self.name}猫咪在跑") def running(tools): tools.run() car = Car("白色") cat = Cat("老黑") running(car) #白色小车在跑 running(cat) #老黑猫咪在跑
#鸭子类型
-
Python崇尚鸭子类型
- 不关心类型,不需要继承,只关注方法实现,这种情况被称为鸭子类型
- 当看到一只鸟走起来像鸭子、游泳起来像鸭子、叫起来也像鸭子,那么这只鸟就可以被称为鸭子。
- 在鸭子类型中,关注的不是对象的类型本身,而是它是如何使用
class Fish: def swin(self): print("1111") class Cat: def swin(self): print("2222") def swing(tools): tools.swin() fish = Fish() cat = Cat() swing(fish) swing(cat)
#总结
- Python本身就是支持多态性的
- 增加了程序的灵活性(通用型),以不变应万变,不论对象千变万化,使用者都是用一种形式去调用
- 增加了程序的可扩展性,通过继承某个类创建了一个新的类,接口使用者无需改变自己的代码,还是用原方法调用
- 对比:
- 多态强调的是:一类事物 不同的形态
- 多态性强调的是:同一操作,作用对象不同,表现出不同实现方式(只关心行为结构)
#设计模式
- 就是开发人员长时间总结出的最佳可重用解决方案
#单例模式
-
有的时候通过单一对象方便集中管理资源
- 单例模式是保证一个类仅有一个对象的设计模式
- 保证了在程序的不同位置都可以且仅可以取到同一个对象实例:如果实例不存在,会创建一个实例,若存在会返回这个实例
class Sing(object): _ins = None def __new__(cls, *args, **kwargs): #new方法 固定格式 if cls._ins == None: cls._ins = object.__new__(cls) return cls._ins class Mon(Sing): def __init__(self,name): self.name = name def run(self): print(f"{self.name}跑") m1 = Mon("爱丽丝") m2 = Mon("友塔斯") m1.run() m2.run()
#工厂模式
-
工厂模式是不直接暴露对象创建细节,而是通过一个共用类创建对象的设计模式。
class Gril1: def xiyi(self): print("我奶子大") class Gril2: def xiyi(self): print("我奶子大,腿长") class Gril3: def xiyi(self): print("我奶子大,腿长,有钱") class Moin: def get_gril(self,chose): if chose == "奶子": return Gril1() if chose == "腿长": return Gril2() if chose == "有钱": return Gril3() fa = Moin() mygirl = fa.get_gril("奶子") mygirl.xiyi()
5.#约束(面向对象)
- 指的就是对类的约束
- 在一些重要的逻辑,与用户数据相关等的核心部分,要建立一种约束,避免发生此类错误
- 类的约束有两种解决方式:
- 方式1:在父类建立一种约束(通过抛出异常)
- 方式2:引入抽象类的概念
#类的约束方式一:
class Apay():
def pay(self):
raise Exception("子类必须实现pay方法")
####raise Exception 报错
class QQpay(Apay):
def pay(self):
print("QQ支付")
class Alipay(Apay):
def pay(self):
print("支付宝支付")
class WXpay(Apay):
def zhifu(self):
print("微信支付")
qq = QQpay()
zfb = Alipay()
wx = WXpay()
qq.pay()
wx.pay() #会报错#提示必须重写pay方法
######通过继承调用父类方法来实现报错约束
#类的约束方式二:
-
用抽象类(制定一种规范)的概念,建立一种约束
-
基类如下设置,子类如果没有定义pay方法,在实例化对象时就会报错
from abc import ABCMeta,abstractmethod class Eoor(metaclass=ABCMeta): @abstractmethod def run(self): pass
- 设置一个类的metaclass(元类)是ABCMeta
- 那么这个类就变成了一个抽象类(接口类)
- 这个类的功能就是建立一个规范
-
from abc import ABCMeta,abstractmethod
class Eoor(metaclass=ABCMeta):
@abstractmethod
def run(self):
pass
class running1(Eoor):
def run(self):
print("run1")
class running2(Eoor):
def run(self):
print("run2")
class running3(Eoor):
def run1(self):
print("run3")
r1 = running1()
r2 = running2()
r3 = running3()
r1.run()
r2.run()
r3.run()
#类的方式二详解:
- Python本身不提供抽象类和接口机制,要想实现抽象类,可以借助abc模块,ABC是Abstract Base Class(抽象父类)的缩写
- abc.ABCMeta是用来生成抽象基础类的元类,由他生成的类可以被直接继承,但是抽象类不能直接创建对象(只能被继承)
- @abstractmethod表明抽象方法,如果子类不具备@abstractmethod的方法,那么就会抛出异常。
6.#类方法(面向对象)
- 是指一个类通过@classmethod修饰的方法
- 第一个参数必须是是当前类对象,该参数名一般约定为“cls”,通过它来传递类的属性和方法(不能传实例的属性和方法)
- 调用:实例对象和类都可以调用
- 类方法是将类本身作为对象进行操作的方法
class Person():
@classmethod
def abc(cls): ##类方法 是为当前类Person服务的方法
括号里必须是cls
class Class():
__num = 0
@classmethod
def add_stu(cls):
cls.__num += 1
@classmethod
def get_stu(cls):
return cls.__num
class Student(Class):
def __init__(self,name):
self.name = name
Class.add_stu()
def show(self):
return self.__num
stu = Student("李晨浩")
stu1 = Student("baba")
stu2 = Student("baba")
stu3 = Student("baba")
print(stu.get_stu())
7.静态方法(面向对象)
- 类方法:有个默认参数cls,并且可以直接用类名去调用,可以与类属性交互(也就是可以使用类属性)
- 静态方法:让类里的方法直接被类调用,就像正常调用函数一样
- 类方法和静态方法的相同点:都可以直接被类调用,不需要实例化
- 类方法和静态方法的不同点:
- 类方法必须有一个cls参数表示这个类,可以使用类属性
- 静态方法不需要参数
#property
-
property:是一种特殊的属性,访问它时会执行一段功能(方法)然后返回值
class People: def __init__(self,name,weight,height): self.name=name self.weight=weight self.height=height @property def bmi(self): return self.weight / (self.height**2) p1=People('egon',75,1.85) print(p1.bmi) >>>
#为啥用property
-
将一个类的方法定义成属性以后,对象再去使用的时候obj.name,根本无法察觉自己的name是执行了一个函数然后计算出来的,这种特性的使用方式遵循了统一访问的原则
-
属性一般具有三种访问方式:
- 获取、property
- 修改、setter
- 删除、deleter
-
第一种方式
class Foo: def __init__(self,name): self.__name = name @property def AAA(self): return (f'get的时候运行我,{self.__name}') @AAA.setter def AAA(self, value): self.__name = value #####修改的时候运行我 @AAA.deleter def AAA(self): del self.__name #####('delete的时候运行我') a = Foo("3333") print(a.AAA) a.AAA = "19" print(a.AAA) del a.AAA print(a.AAA)
-
第二种方式
class Foo: def get_AAA(self): return ('get的时候运行我啊') def set_AAA(self,value): return ('set的时候运行我啊') def delete_AAA(self): return ('delete的时候运行我啊') AAA=property(get_AAA,set_AAA,delete_AAA) ###内置property三个参数与get,set,delete一一对应 a = Foo() print(a.get_AAA()) print(a.set_AAA("asdasd")) print(a.delete_AAA())
#总结
- 只有@property定义只读,加上@setter定义可读可写,加上@deleter定义可读可写可删除
class Stu(object):
def __init__(self,name,age):
self.__name = name
self.__age = age
@property
def source(self):
return self.__age
@source.setter
def source(self,age):
self.__age = age
@source.deleter
def source(self):
del self.__age
@property
def sname(self):
return self.__name
@sname.setter
def sname(self,name):
self.__name = name
@sname.deleter
def sname(self):
del self.__name
a = Stu("爸爸",19)
print(a.source) #19 查
print(a.sname) #爸爸
a.source = 200 #改类属性self.__age的值为200
print(a.source) #200
a.sname = "alezx"
print(a.sname) #alezx
print(a.source,a.sname) #200 alezx
del a.source #删除
#绑定方法:
- 分为普通方法和类方法
- 普通方法:默认有一个self对象传进来,并且只能被对象调用-------绑定到对象
- 类方法:默认有一个cls对象传进来,并且可以被类和对象(不推荐)调用-----绑定到类
- 非绑定方法:静态方法:没有设置默认参数,并且可以被类和对象(不推荐)调用-----非绑定
8.反射(面向对象)
-
反射:可以用字符串的方式去访问对象的属性,调用对象的方法(但是不能去访问方法),python中一切皆对象,都可以使用反射。
-
反射有四种方法:
-
hasattr:hasattr(object,name)判断一个对象是否有name属性或者name方法。有就返回True,没有就返回False
-
getattr:获取对象的属性或者方法,如果存在则打印出来。hasattr和getattr配套使用。
需要注意的是,如果返回的是对象的方法,返回出来的是对象的内存地址,如果需要运行这个方法,可以在后面添加一对()
-
setattr:给对象的属性赋值,若属性不存在,先创建后赋值
-
delattr:删除该对象指定的一个属性
-
class Foo:
def init(self):
self.name = 'egon'
self.age = 51
def func(self):
print('hello')
egg = Foo()
setattr(egg,'sex','男') #给Foo类添加sex = 男的属性 若不存在就添加
print(egg.name) >>>egon
print(egg.sex) >>>男
delattr(egg,'name') #删除
print(egg.name) >>>报错
#对象应用反射
class Foo:
def init(self):
self.name = 'egon'
self.age = 51
def func(self):
print('hello')
egg = Foo()
print(hasattr(egg,'name')) >>> True #先判断name在egg里面存在不存在
print(getattr(egg,'name')) >>> egon #如果为True它才去得到
setattr(egg,"sex","男") >>>添加新的类属性
print(getattr(egg,'sex')) >>>"男"
print(hasattr(egg,'func')) >>> True
print(getattr(egg,'func')) #得到的是地址
getattr(egg,'func')() >>> hello #在这里加括号才能得到,因为func是方法
if hasattr(egg,'func'):
getattr(egg,'func')()
else:
print('没找到')
#类应用反射
class Foo:
f = 123
@classmethod #类方法
def class_method_dome(cls):
print('class_method_dome')
@staticmethod #静态方法
def static_method_dome():
print('static_method_dome')
print(hasattr(Foo,'class_method_dome')) >>>True #判断Foo类里面有没class_method_dome方法
method = getattr(Foo,'class_method_dome')
method() >>>class_method_dome
print(hasattr(Foo,'static_method_dome')) >>>True
method1 = getattr(Foo,'static_method_dome')
method1()>>>static_method_dome
#模块应用反射
import sys
def s1():
print("s1")
def s2():
print("s2")
this_mod = sys.modules[__name__]
# 每当程序员导入新的模块,sys.modules(是一个字典)都将记录这些模块
print(hasattr(this_mod,"s1"))
a = getattr(this_mod,"s2")
a() >>>"s2"
#应用举例
-
博客园登录举例
#########################学习反射之前的方法 while True: choose = input('>>>').strip() if choose == 'login': obj = User() obj.login() elif choose == 'register': obj = User() obj.register() elif choose == 'save': obj = User()obj.save() ###########################学习反射之后的方法 user = User() while True: choose = input('>>>').strip() if hasattr(user,choose): func = getattr(user,choose) func() else: print('输入错误。。。。')
9.#内置方法
#魔法方法
- 被双下划线(两个下划线)包围的就是魔法方法
- 如果你的对象重写了这些方法中的某一个,那个这个方法就会在特殊的情况下被Python所调用,你可以定义自己想要的行为,而这一切都是自动发生的
#内置方法
-
__new__(cls[, ...]) __new__ 是09法 ####单例模式 class Singleton: def __new__(cls, *args, **kw): if not hasattr(cls, '_instance'): orig = super(Singleton, cls) cls._instance = orig.__new__(cls, *args, **kw) return cls._instance one = Singleton() two = Singleton() print(one,two) #他们两个的地址一样 one.name = 'alex' print(two.name) >>>alex
-
__init__(self[, ...]) • 构造器,当一个实例被创建的时候调用的初始化方法 class Person(): def __init__(self,name,age): self.name = name self.age = age print("我是{},{}岁".format(self.name,self.age)) chen = Person("Bay",18) >>>我是Bay,18岁 print(chen.name) >>>Bay
-
__del__(self) 析构器,当一个实例被销毁的时候调用的方法(建议不要重写) 析构方法,当对象在内存中被释放时,自动触发执行。 注:此方法一般无须定义,因为Python是一门高级语言,程序员在使用时无需关心内存的分配和释放,因为此工作都是交给Python解释器来执行,所以,析构函数的调用是由解释器在进行垃圾回收时自动触发执行的。
-
__len__ 通过len(obj)调用,返回值必须设置为int类型 class Person(): def __init__(self,name,age): self.name = name self.age = age def __len__(self): return len(self.name) chen = Person("Bay",18) print(len(chen)) >>>3
-
__hash__ 通过hash(obj)调用(一种消息摘要算法) hash() 用于获取取一个对象(非可变数据类型)的哈希值 class Foo: def hash(self): print('aaaaaaaaaa') return hash(self.name) # print('aaas') f = Foo() f.name = 'egon' print(hash(f)) #hash方法是可以重写的
-
__str__ 打印对象时调用 class Person(): def __init__(self,name,age): self.name = name self.age = age def __str__(self): return "{}1qqqqqq2{}".format(self.name,self.age) chen = Person("Bay",18) print(chen) #>>>Bay1qqqqqq218
-
__eq__ 比较两个对象def __eq__(self,obj): class A: def __eq__(self, other): return True a = A() b = A() print(a==b) #不加方法的时候返回的是False,加了个__eq__方法就返回了个True # '=='内部就调用了__eq__方法 print(a is b)
10.#异常处理
- 异常(Exception)是一个事件,该事件可能会在程序执行过程中发生,影响了程序的正常执行
- raise语句显式的抛出异常
- Python解释器自己检测到异常并引发它
- 在Python无法正常处理程序时就会发生异常
- 异常时Python对象,表示一个错误
- 当Python程序发生异常时我们需要捕获处理它,否则程序会终止执行
#常见异常
SyntaxError | 语法错误 |
---|---|
IndentationEorror | 多打一个空格 |
NameError | 使用一个还未被赋予对象的变量 |
TypeError | 传入对象类型与要求的不符合 (int + str) |
IoError | 输入/输出异常;基本上是无法打开文件 |
ImportError | 无法引入模块或包;基本上是路径问题或名称错误 |
IndexError | 下标索引超出序列边界 |
KeyError | 试图访问字典里不存在的键 |
#异常处理
- 异常是由程序的错误引起的,语法上的错误跟异常处理无关,必须在程序运行前就修正
#什么是异常处理
- python解释器检测到错误,触发异常(也允许程序员自己触发异常)
- 程序员编写特定的代码,专门用来捕捉这个异常(这段代码与程序逻辑无关,与异常处理有关)
- 如果捕捉成功则进入另外一个处理分支,执行你为其定制的逻辑,使程序不会崩溃,这就是异常处理
#为什么要进行异常处理
- 解释器检测到异常时,异常没有被处理的情况下,就会终止,后续的代码不会执行,没有用户去执行这样的程序,因此遇到异常就得有异常处理机制来增强你程序的健壮性与容错性
#如何进行异常处理
异常是由程序的错误引起的,语法上的错误跟异常处理无关,必须在程序运行前就修正
#方式1使用if语句判断
num1=input('>>: ') #输入一个字符串试试
if num1.isdigit():
int(num1) #我们的正统程序放到了这里,其余的都属于异常处理范畴
elif num1.isspace():
print('输入的是空格,就执行我这里的逻辑')
elif len(num1) == 0:
print('输入的是空,就执行我这里的逻辑')
else:
print('其他情情况,执行我这里的逻辑')
问题一:
使用if的方式我们只为第一段代码加上了异常处理,但这些if,跟你的代码逻辑并无关系,这样你的代码会因为可读性差而不容易被看懂
问题二:
这只是我们代码中的一个小逻辑,如果类似的逻辑多,那么每一次都需要判断这些内容,就会倒置我们的代码特别冗长。
总结:.
1.if判断式的异常处理只能针对某一段代码,对于不同的代码段的相同类型的错误你需要写重复的if来进行处理。
2.在你的程序中频繁的写与程序本身无关,与异常处理有关的if,会使得你的代码可读性极其的差
3.if是可以解决异常的,只是存在1,2的问题,所以,千万不要妄下定论if不能用来异常处理。
#方式2异常机制
-
语法
try: 被检测的代码块 except 异常类型: try中一旦检测到异常,就执行这个位置的逻辑 ##如果你不想在异常发生时结束你的程序,只需在try里捕获它首先,执行 try 子句 (在 try 和 except 关键字之间的部分) ##如果没有异常发生,except 子句在 try 语句执行完毕后就被忽略了 ##如果在 try 子句执行过程中发生了异常,那么该子句其余的部分就会被忽略 ##如果异常匹配于 except 关键字后面指定的异常类型,就执行对应的 except 子句。然后继续执行 try 语句之后的代码
-
异常类只能用来处理指定的异常情况,如果非指定异常则无法处理。
s1 = 'hello' try: int(s1) except IndexError as e: print(e)<br>#没有捕获到异常,程序直接报错 ###### a = 10 b = int(input("请输入")) c = [1,2,3] try: print(a/b) c[3] except ZeroDivisionError: ###判断两次 b = 0时,检测到错误 print("傻逼,你家除数能为0啊!") try: b = int(input("请重新输入")) print(a / b) except ZeroDivisionError: print("傻逼") except Exception as a: print("shabi") print("你可真是个傻逼") #注意:如果写明异常类型,异常类型要与上面发生的异常匹配,否则捕捉不到
-
多分支
- except可以指定捕获的类型,捕获多种异常
- 多个except即可,但是之后最多匹配一个异常
- 如果没有任何一个except语句捕获这异常,则该异常向外抛出
a = 10 b = int(input("请输入")) c = [1,2,3] try: print(a/b) c[5] except ZeroDivisionError: print("傻逼,你家除数能为0啊!") except Exception as a: 万能异常 ##使用exception as e:查看异常,以及是否按要求捕获到 print(e) >>list index out of range print("你可真是个傻逼")
- 如果想要的效果是,无论出现什么异常,我们统一丢弃,或者用同一段代码逻辑去处理他们只有一个Exception就足够了
- 如果想要的效果是,对于不同的异常定制不同的处理逻辑,需要
用到多分支
-
万能异常
- 可以捕获任意异常
s1 = 'hello' try: int(s1) except Exception as e: print(e)
-
多分支+万能异常
-
发生的异常中,有一些异常是需要不同的逻辑处理的,剩下的异常统一处理掉即可
dic = {1: login,2: register,3: dariy,4: article,5: comment,} print('''欢迎访问博客园系统: 1,登录 2,注册 3,访问日记页面 4,访问文章页面 5,访问评论页面''') try: choice = int(input('请输入:')) dic[choice]() except ValueError: print('请输入数字....') except KeyError: print('您输入的选项超出范围...') except Exception as e: print(e)
-
-
try...except...else组合
- 与循环中的else类似,try代码中,只要出现了异常,则不会执行else语
句,如果不出现异常,则执行else语句
try: print('扣第一个人钱') print('给第二个人加钱') except ValueError: print('必须输入数字。。。') else: print('转账成功')
- 与循环中的else类似,try代码中,只要出现了异常,则不会执行else语
-
finally:
- finally:最后一定要执行的语句块
- try...excet...finally组合
try: dic = {'name': 'shang'} print(dic[1]) except KeyError: print('出现了keyError错误....') finally: print('正常执行')
- 如果出现异常但是没有成功捕获,finally会在异常发生之前执行
try: dic = {'name': 'shang'} print(dic[1]) except NameError: print('出现了NameError错误....') finally: print('执行结束')
- finally应用场景: 关闭文件的链接链接,数据等链接时,需要用到finally
f = open('file',encoding='utf-8') try: '''各种操作''' print(f.read()) '''但是发生错误了, 此时没关闭文件句柄,所以''' finally: f.close()
- 函数中,finally也会在return之前先执行
def func(): try: return 1 finally: print('finally') func()
-
循环中
while 1: try: break finally: print('finally')
-
总结:
- finally一般是做收尾工作
- 在一些重要环节出错之前必须一定要做的比如关闭链接的问题时,最好是用上finally作为最后一道防线,收尾
-
主动发出异常
- 在类的约束中,我们已经用过此方法
- raise TypeError('类型错误')
-
断言
- 表示一种强硬的态度,只要assert后面的代码不成立,直接报错,下面的代码就不让你执行
- assert 条件
-
自定义异常
- python中提供的错误类型可能并不能解决所有错误
- 出现了某种异常无法用已知的错误类型捕获(万能异常只能捕获python中存在的异常)。可以尝试自定义异常,只要继承BaseException类即可
class HelloError(Exception): def __init__(self,n): self.n=n try: n=input("请输入数字:") if not n.isdigit(): raise HelloError(n) except HelloError as hi: print("HelloError:请输入字符。\n您输入的是:",hi.n) else: print("未发生异常")
11.#堆栈
- 变量位于栈
- 对象位于堆