oop三大特征:继承、多态、封装
面向过程:一开始学习的,按照解决问题的步骤编程【根据业务逻辑从上到下编程】
函数式:将某功能代码封装到函数中,下次使用直接调用,无需重复编写
面向对象编程:将数据与函数绑在一起封装,这样能够更快速的开发程序,减少重复代码的重写过程 oop(object oriented programming),是一种解决软件复用的设计和编程方法,这种方法将软件系统中相近相似的操作逻辑和操作应用数据、状态,以类的形式描述出来,以对象实例的形式在软件系统中复用,以达到提高软件开发效率的作用
面向过程适合做小项目,面向对象适合做大项目
类:一个模板,模板里包含多个函数,函数中实现一些功能(汽车图纸、车类)
是一组具有相同或相似属性和行为的多个对象的组合
对象:类的实例化,可以执行类中的函数(宝马)
定义类:类结构=类名(首字母大写)+属性+方法行为
实例方法:在类内部,用def可以定义实例方法,与一般函数不同的是实例方法必须包含参数,默认第一个参数是self(名字标识可以是其他名字,但这个位置必须被占用)
- # class 类名:
- # 属性
- # 方法
- class Person:#类名:首字母大写
- #属性
- name='小明'#类属性
- age=20
- #方法(行为) 实例方法
- def eat(parms):
- parms.name='小红'#实例属性
- print('eating')
- pass
- def run(self):
- print('running')
- pass
- pass
创建对象:
格式:对象名=类名()
- #创建对象[类的实例化]
- xm=Person()
- #调用函数
- xm.eat()
- print('{}的年龄是:{}岁'.format(xm.name,xm.age))
属性:
- class Student:
- name='李明'#类属性
- def __init__(self,age):
- self.age=age#实例属性
- pass
- pass
- lm=Student(18)
- print(Student.name)
- print(lm.name)#类属性可以被类对象和实例对象访问
- print(lm.age)#实例属性只能通过实例对象访问
- print('不能通过类对象访问实例属性')
- print(Student.age)
- # 李明
- # 李明
- # 18
- # 不能通过类对象访问实例属性
- # Traceback (most recent call last):
- # File "E:\资源下载\workspace\shixun\pythonProject2\2.17多态.py", line 47, in <module>
- # print(Student.age)
- # AttributeError: type object 'Student' has no attribute 'age'
类属性和实例属性的访问原理:
Python自带的内置函数
是一个初始化方法,用来定义实例属性和初始化数据,在创建对象时自动调用
- class Person:
- def __init__(self):
- self.name='小倩'
- self.age=20
- def run(self):
- print('running')
- pass
- pass
- xq=Person()#创建新对象时,__init__自动调用
- print(xq.name)
- #小倩
传递参数(后面实例方法都可以直接使用该参数)
- class Person:
- def __init__(self,name,age):
- self.name=name
- self.age=age
- def eat(self,food):
- print(self.name+'喜欢吃'+food)
- xm=Person('小明',21)
- xm.eat('苹果')
self和对象指向同一内存地址,self是对象的引用,可以理解为对象自己
- class Person:
- def eat(self):
- print('self=%s',id(self))
- pass
- pass
- xm=Person()
- xm.eat()
- print('xm=%s',id(xm))
- #self=%s 1760559500112
- #xm=%s 1760559500112
self传参:
- class Person:
- def __init__(self,pro):
- self.pro=pro
- def eat(self,name,food,pro):
- print('%s喜欢吃%s,修的专业是%s,%s'%(name,food,self.pro,pro))
- pass
- pass
- xm=Person('通信工程')
- xm.eat('小明','榴莲','hhh')
- # 小明喜欢吃榴莲,修的专业是通信工程,hhh
定义:Python中的一些内置好的特定方法,方法名为“__xxx__”,前后有两个下划线
常见的魔术方法:
定义了__str__方法,在打印对象时,会执行__str__方法(__str__只能return一个字符串)
class Animal: def __init__(self,name,colour): self.name=name self.colour=colour def __str__(self): return '我的名字是%s,我的颜色是%s'%(self.name,self.colour) dog=Animal('旺财','白色') print(dog) #我的名字是旺财,我的颜色是白色 #改变打印对象的内容 class Animal: def __init__(self,name,colour): self.name=name self.colour=colour dog=Animal('旺财','白色') print(dog) # <__main__.Animal object at 0x000001E49FEF6FD0> #直接输出对象的地址
调用对象实例的方法,每调用一次生成一个新对象,cls是class的缩写
class Animal(object): def __init__(self,name,colour): self.name=name self.colour=colour print('__init__函数执行') def __str__(self): return '我的名字是%s,我的颜色是%s'%(self.name,self.colour) def __new__(cls, *args, **kwargs): print('__new__函数执行') # return object.__new__(cls)#这里真正创建对象实例 dog=Animal('旺财','白色') print(dog) #__new__函数执行 #None #解释:还没有生成对象实例
class Animal(object): def __init__(self,name,colour): self.name=name self.colour=colour print('__init__函数执行') def __str__(self): return '我的名字是%s,我的颜色是%s'%(self.name,self.colour) def __new__(cls, *args, **kwargs): print('__new__函数执行') return object.__new__(cls)#这里真正创建对象实例 # return super().__new__(cls)#若没有父类object,用super()创建对象实例 dog=Animal('旺财','白色') print(dog) # __new__函数执行 # __init__函数执行 # 我的名字是旺财,我的颜色是白色 #先执行__new__(),创建对象实例后执行__init__ #__new__()创建实例框架,__init__丰满
__new__方法在__init__方法前执行
__new__方法不能调用自己的__new__方法,即:return cls.__new__(cls)
__new__和__init__的区别
- __new__:类的实例化方法,必须返回该实例,不然对象创建不成功
- __init__:做对象的初始化工作,也可以认为是实例的构造方法,接受该实例self并对其构造
- __new__至少一个参数是cls,代表要实例化的类,该参数在实例化时由Python解释器自动提供
- __new__要早于__init__函数执行
课后问答
当一个对象被删除或销毁时,Python解释器默认调用__del__()方法,这个方法也称析构方法,也是一种魔术方法
程序执行结束自动调用__del__()
- class Animal:
- def __init__(self,name):
- self.name=name
- print('__init__执行')
- def __del__(self):
- print('__del__执行')
- pass
- cat=Animal('cat')
- print('程序等待中。。。')
- # __init__执行
- # 程序等待中。。。
- # __del__执行
对象被删除时,也会自动调用__del__方法
- class Animal:
- def __init__(self,name):
- self.name=name
- print('__init__执行')
- def __del__(self):
- print('__del__执行')
- pass
- cat=Animal('cat')
- del cat#手动去清理对象
- print('*'*20)
- # __init__执行
- # __del__执行
- # ********************
Python面向对象的三大特征:封装、继承、多态
封装:把内容封装到某个地方,便于后面的使用【其实就是使用初始化构造方法将内容封装到对象中,然后通过对象直接或者self来获取封装的内容】
继承:即子可以继承父的内容(属性和行为)
- class Animal:
- def eat(self):
- print('eating')
- class Cat(Animal):
- pass
- cat1=Cat()
- cat1.eat()
- #eating
子类继承多个父类,用逗号分隔 class C(A,B)
问题是:当多个父类中存在相同方法时,应该调用哪一个呢?
class E: def eat(self): print('E.eat') class D: def eat(self): print('D.eat') class C(E): # def eat(self): # print('C.eat') pass class B(D): pass class A(B,C): pass a1=A() a1.eat()#查找顺序,A中没有,找B,B中没有,找C print(A.__mro__) # D.eat # (<class '__main__.A'>, <class '__main__.B'>, <class '__main__.D'>, <class '__main__.C'>, <class '__main__.E'>, <class 'object'>)
__mro__方法:查询执行顺序
继承的传递性:爸爸继承了爷爷,儿子继承爸爸,也继承了爷爷
子类中有个跟父类方法名称一样的方法,相当于重写父类方法(方法覆盖)
重写父类方法后,子类调用父类方法时,调用的是子类的方法
- class Father:
- def eat(self):
- print('爸爸')
- pass
- class Son(Father):
- def eat(self):
- print('儿子')
- pass
- son=Son()
- son.eat()
- print(Son.__mro__)
- # 儿子
- # (<class '__main__.Son'>, <class '__main__.Father'>, <class 'object'>)
调用父类方法
class Dog: def __init__(self,name,color): self.name=name self.color=color def bark(self): print('汪汪汪。。') class Keji(Dog): def __init__(self,name,color,age):#重写父类方法 # Dog.__init__(self,name,color)#调用父类方法1--手动调用 super().__init__(name,color)#调用父类方法2--自动调用:super()自动查找父类方,进而调用方法 self.age=age def dark(self): print('啊啊啊。。') super().bark()#调用父类方法 print('啊啊啊。。') def msg(self): print('名字:%s,颜色:%s,年龄:%s'%(self.name,self.color,self.age)) keji=Keji('柯基','黄白',2) keji.dark() keji.msg() # 啊啊啊。。 # 汪汪汪。。 # 啊啊啊。。 # 名字:柯基,颜色:黄白,年龄:2
super():假如继承了多个父类,则会按顺序查找
class Animal: def bark(self): print('动物叫。。') class Keji(Animal,Dog): def __init__(self,name,color,age):#重写父类方法 # Dog.__init__(self,name,color)#调用父类方法1 super().__init__(name,color)#调用父类方法2:super()自动查找父类方,进而调用方法 self.age=age def dark(self): print('啊啊啊。。') super().bark()#调用父类方法 print('啊啊啊。。') def msg(self): print('名字:%s,颜色:%s,年龄:%s'%(self.name,self.color,self.age)) keji=Keji('柯基','黄白',2) keji.dark() keji.msg() # 啊啊啊。。 # 动物叫。。 # 啊啊啊。。 # 名字:柯基,颜色:黄白,年龄:2
定义:就是多种状态(形态),就是同一种行为,对于不同子类【对象】有不同的行为表现
想实现多态必须满足的两个条件:
作用:增加程序的灵活性和扩展性
鸭子类型:在程序设计中,鸭子类型是动态类型的一种风格。“鸭子测试”可以表述为:“当一个鸟走起来像鸭子,叫起来像鸭子,那么这只鸟可以称为鸭子”
class Animal: def say(self): print("动物叫") pass class Dog(Animal): def say(self): print('汪汪汪') pass class Cat(Animal): def say(self): print('喵喵喵') pass # dog=Dog() # dog.say() # cat=Cat() # cat.say() def common(obj): obj.say() i=[Dog(),Cat()] for item in i: common(item) # 汪汪汪 # 喵喵喵
类方法:类对象所拥有的方法,用装饰器@classmate标识,类方法的第一个参数必须是类对象,一般以cls作为第一个参数。
可以通过类对象、实例对象调用
class Student: name='李华'#类属性 @classmethod def get_name(cls): return cls.name#访问类属性 @classmethod def change_name(cls,new_name): cls.name=new_name#在类方法中修改类属性的值 print(Student.get_name())#类对象引用 lh=Student() print(lh.get_name())#实例对象引用 # 李华 # 李华 Student.change_name("小花") print(Student.get_name())#类对象引用 xh=Student() print(xh.get_name())#实例对象引用 # 小花 # 小花
静态方法:类对象所拥有的方法,需要用@staticmethod来表示静态方法
- class Student:
- name='李华'#类属性
- @staticmethod
- def getdate():
- return Student.name
- print(Student.getdate())#类对象引用
- lh=Student()
- print(lh.getdate())#实例对象引用
- # 李华
- # 李华
静态方法一般不会通过实例对象访问
为什么使用静态方法?
静态方法主要用来存放逻辑性代码,在静态方法中,不会涉及到类中方法和属性的操作,数据资源能得到有效利用
- #引入第三方时间模块
- import time
- class Timetest:
- @staticmethod
- def showtime():
- return time.strftime("%H:%M:%S",time.localtime())
- pass
- print(Timetest.showtime())
- # 15:46:56
保护属性安全,不得随意修改,将属性定义成私有化属性,添加可调用的方法去访问
使用私有化属性的场景:
语法:两个下划线开头,声明属性为私有属性,不能在类的外部被使用或直接访问
class Person: def __init__(self): self.name='李四' self.__age=18#属性私有化(外部不能访问,内部可以访问) pass def __str__(self): return('{}的年龄是{}'.format(self.name,self.__age)) x1=Person() print(x1) print(x1.name) print(x1.__age)#通过类对象在外部访问(私有化后,不能在外部直接访问) # Traceback (most recent call last): # File "E:\资源下载\workspace\shixun\pythonProject2\2.17多态.py", line 125, in <module> # print(x1.__age)#通过类对象在外部访问(私有化后,不能在外部直接访问) # AttributeError: 'Person' object has no attribute '__age' # 李四的年龄是18 # 李四
语法:方法名前加两个下划线
__xxx:方法私有化
__xxx__:魔术方法,Python自有,开发者不要创建
xxx_:避免属性名与Python关键字冲突
让调用者能直接以访问属性的方式,而且又能控制的方式
class Animal(object): def __init__(self): self.__age=18#私有化属性 def dog(self):#访问私有属性 return self.__age def chance(self,age):#修改私有实例属性 if age<0: print('年龄不能小于0') pass else: self.__age=age #定义一个类属性,实现直接访问属性的形式去访问私有的属性 age=property(dog,chance) a1=Animal() print(a1.age)#访问私有属性 #18 a1.age=2#修改私有属性 print(a1.age) #2
即在方法上使用装饰器
class Animal(object): def __init__(self): self.__age=18#私有化属性 @property#通过装饰器修饰添加属性标志,提供一个getter方法 def dog(self):#访问私有属性 return self.__age @dog.setter#提供一个setter方法 def chance(self,parms):#修改私有实例属性 if parms<0: print('年龄不能小于0') pass else: self.__age=parms pass a1=Animal() print(a1.dog)#访问私有属性 #18 a1.chance=2#修改私有属性 print(a1.dog) #2
10、__new__方法(参考3.2)和单例模式
单例模式:一种常用的软件设计模式
目的:确保某个类中只有一个实例存在
实现步骤:利用类属性保存初次创建的实例对象,第二次实例化对象的时候判断类属性是否保存实例对象,如果有就返回类属性保存的,如果没有就调用父类__new__方法创建新的实例对象
class Student(object): __instance=None def __init__(self,name,age): self.name=name self.age=age pass def __new__(cls, *args, **kwargs): if not cls.__instance: cls.__instance=super(Student,cls).__new__(cls)#没有保存实例,就保存 return cls.__instance else: return cls.__instance pass pass xm=Student('LH',19) print(id(xm)) xh=Student('XH',20) print(id(xh)) # 2390515351312 # 2390515351312 #id相同,说明实例化两次对象,都是同一对象
语法格式:
- try:
- 可能出现错误的代码块
- except:
- 出错后执行的代码块
- else:
- 没有错误执行的代码块
- finally:
- 不管有没有出错都要执行的代码块
- try:
- x=x/2
- print(x)
- except NameError as msg:
- print(msg)
- #name 'x' is not defined
class Toolong(Exception): def __init__(self,leng): self.leng=leng pass def __str__(self): return '你的长度为%s,过长了。。'%self.leng pass def name_test(): name=input('请输入你的名字:') try: if len(name)>4: raise Toolong(len(name)) else: print(name) pass pass except Toolong as msg: print(msg) finally: print('执行完毕') name_test() # 请输入你的名字:chenq # 你的长度为5,过长了。。 # 执行完毕
动态语言能在运行时改变其结构;Python、php、JavaScript是动态语言,c、c#、Java是静态语言
所有,Python在程序运行过程中添加属性和方法
#动态添加属性 class Animal: def __init__(self,name,age): self.name=name self.age=age def say(self): print('%s 年龄:%s'%(self.name,self.age)) pass cat=Animal('小猫',2) cat.say() cat.color='花色'#给对象动态添加属性 print(cat.color) Animal.color='白色'#给类动态添加属性 print(Animal.color) #小猫 年龄:2 #花色 #白色
语法:
- import types#引入代码块
- def 方法名(self):
- xxx
- pass
- class 类名:
- xxx
- pass
- 实例名=类名()
- 实例名.引入方法的新名字=types.MethodType(方法名,实例名)#动态添加方法
- 实例名.引入方法的新名字()#调用新方法
- import types#引入types模块
- def run(self):
- print("%s 岁的%s在跑。。"%(self.age,self.name))
- class Animal:
- def __init__(self,name,age):
- self.name=name
- self.age=age
- pass
- cat=Animal('小猫',2)
- cat.run=types.MethodType(run,cat)#动态添加方法
- cat.run()#调用方法
- #2 岁的小猫在跑。。
语法:类名.新名字=类方法名
@classmethod def classTest(cls): print('这是个类方法') pass @staticmethod def staticTest(): print("这是个静态方法") pass class Animal(): pass #动态给类添加类方法、静态方法 Animal.classnewname=classTest Animal.staticnewname=staticTest #调用类方法,静态方法 Animal.classnewname() Animal.staticnewname()
作用:限制可以添加的实例属性
- class Animal(object):
- __slots__ = ('name','age','weight')#限制添加的属性
- def __str__(self):
- return '%s 。。。%s'%(self.name,self.age)
- pass
- xh=Animal()
- xh.name='小花'
- xh.age=20
- xh.weight=45
- # xh.height 被限制,运行会报错
- print(xh)#小花 。。。20
__dict__:所有可用的属性都存储在这,占用内存高;使用__slots__后,实例中不再有__dict__
- class Animal(object):
- # __slots__ = ('name','age','weight')#限制添加的属性
- def __str__(self):
- return '%s 。。。%s'%(self.name,self.age)
- pass
- xh=Animal()
- xh.name='小花'
- xh.age=20
- xh.weight=45
- # xh.height 被限制,运行会报错
- print(xh)#小花 。。。20
- print(xh.__dict__)#没有设置__slots__时,所有可用的属性存储在这
- # 小花 。。。20
- # {'name': '小花', 'age': 20, 'weight': 45}
slots的作用:限制实例的属性;节约内存空间
子类继承父类时,若不声明slots,不继承父类的slots;声明slots后,子类限制属性=父类+自身
- class Animal(object):
- __slots__ = ('name','age','weight')#限制添加的属性
- def __str__(self):
- return '%s 。。。%s'%(self.name,self.age)
- pass
- class Dog(Animal):
- __slots__ = ('color')#未声明时,不继承父类__slots__,声明后,限制属性=父类+自身
- pass
- dog=Dog()
- dog.color='白色'
- print(dog.color)
- # 白色
联系客服