面向对象的3个特征
- 封装
提高安全性
将数据(属性)和行为(方法)包装到类对象中,在内部对属性进行操作,在类对象外部调用方法,不关心方法内部是怎么实现的 - 继承
提高代码的复用性 - 多态
提高程序的可扩展和可维护性
封装
- 实现:定义一个类对象。
- 属性的私有化:
如果希望该属性在类对象外部被访问,前面使用‘_ _’
若一个属性不能在类对象外部访问:
class Student:
def __init__(self,name,age):
self.name=name
self.__age=age#年龄不希望在类的外部被使用
def show(self):
print(self.name,self.__age)
stu=Student('张三',20)
#在类外使用name和age
print(stu.name)#张三
#print(stu.__age)在类外不能使用age
#print(dir(stu))#stu能访问的显示所有属性
print(stu._Student__age)#在类外访问私有属性age
stu.show()#在类外访问实例方法,由于show在类对象内部访问age,故可输出age
继承
语法格式
class 子类类名(父类1,父类2,...)
pass
- 若一个类没有继承任何属性,则默认继承object
- 定义子类时,必须在其构造函数中调用父类的构造函数(方法)。
- 单继承
class Person(object):#Person没继承任何别的类
def __init__(self,name,age):
self.name=name
self.age=age
def info(self):
print(self.name,self.age)
class Student(Person):
def __init__(self,name,age,stu_num):
super().__init__(name,age)#调用/继承 父类的info()方法
self.stu_num=stu_num
class Teacher(Person):
def __init__(self,name,age,teacherofyear):
super().__init__(name,age)
self.teacherofyear=teacherofyear
stu=Student('张三',20,'2002110')
teacher=Teacher('李四',34,10)
stu.info()#info()是从Person类中继承而来的
teacher.info()#info()是从Person类中继承而来的
输出结果:
张三 20
李四 34
- 多继承
class A(object):
pass
class B(object):
pass
class c(A,B):
pass
- 方法重写
- 子类对父类的某个属性或方法不满意,可在子类中对方法体进行重新编写
- 重写后的方法可通过super().xxx()调用父类中被重写的方法
class Person(object):#Person没继承任何别的类
def __init__(self,name,age):
self.name=name
self.age=age
def info(self):
print(self.name,self.age,end=' ')
class Student(Person):
def __init__(self,name,age,stu_num):
super().__init__(name,age)#调用/继承 父类的info()方法
self.stu_num=stu_num
def info(self):#重写父类Person中的info()方法
super().info()#继承父类的info()方法
print('学号:',self.stu_num)#在上行的基础上输出stu_num
class Teacher(Person):
def __init__(self,name,age,teacherofyear):
super().__init__(name,age)
self.teacherofyear=teacherofyear
def info(self):
super().info()#继承父类的info()方法
print('教龄:',self.teacherofyear)#在上行的基础上输出teacherofyear
stu=Student('张三',20,'2002110')
teacher=Teacher('李四',34,10)
stu.info()
print('--------------------------')
teacher.info()#info()是从Person类中继承而来的
运行结果:
张三 20 学号: 2002110
--------------------------
李四 34 教龄: 10
- object类
- object类为所有类的父类,所以所有类都有object的属性和方法
dir()
可以查看指定类对象的所有属性- Object的
__str__()
方法用于返回对于‘对象的描述’,和函数str()
类似,常用于print()
方法帮助查看对象信息。
常对__str__()
方法进行重写
class Student:
def __init__(self,name,age):
self.name=name
self.age=age
def info(self):
print(self.name,self.age)
stu=Student('张三',20)
print(dir(stu))
print(stu)##返回地址的16进制表示(对stu的描述)<__main__.Student object at 0x0000025E67DD3FD0>
class Student:
def __init__(self,name,age):
self.name=name
self.age=age
def info(self):
print(self.name,self.age)
def __str__(self):
return '我叫{0},今年{1}岁'.format(self.name,self.age)
stu=Student('张三',20)
print(dir(stu))
print(stu)##默认调用__str__()方法,返回我叫张三,今年20岁
多态
即使不知道一个变量所引用的对象是什么类型仍可以通过这个变量调用方法。运行过程中,根据所引用对象的类型动态决定调用哪个对象中的方法。
class Animal(object):
def eat(self):
print('动物会吃')
class Dog(Animal):#Dog继承了Animal的eat方法并对这个方法进行重写
def eat(self):
print('狗吃骨头')
class Cat(Animal):
def eat(self):#Cat继承了Animal的eat方法并对这个方法进行重写
print('猫吃鱼')
class Person(object):
def eat(self):
print('人吃五谷杂粮')
#定义函数
def fun(obj):
obj.eat()
#调用函数fun
fun(Cat())#Cat()在这里指实例对象
fun(Dog())
fun(Animal())
print('----------------------------')
fun(Person())#Person没继承Animal,但Person中定义了eat()方法。即不关心Person是谁的子类,只关心它是否具有eat()行为
静态语言和动态语言
- 静态语言实现多态的必要条件:继承,方法重写,父类指向子类对象
JAVA为静态语言,python是动态语言。
特殊的属性和方法
- 特殊属性:
__dict__
:获得类对象或实例对象绑定的所有属性和方法的字典__class__
:查看对象所属类型__bases__
:查看类对象的所有父类__base__
:查看类对象的基类,即最先定义的父类__mro__
:查看类对象的层次结构(继承关系)__subclasses__()
:查看类对象的子类列表
class A:
pass
class B:
pass
class C(A,B):
def __init__(self,name,age):
self.name=name
self.age=age
#创建C类的对象
c=C('Anna',20)
print(C.__dict__)#{'__module__': '__main__', '__init__': <function C.__init__ at 0x000001CA90DDA310>, '__doc__': None}
print(c.__dict__)#查看实例对象c的属性的字典。 {'name': 'Anna', 'age': 20}
print('----------------')
print(c.__class__)#对象所属类型 <class '__main__.C'>
print(C.__bases__)#C类父类的类型 (<class '__main__.A'>, <class '__main__.B'>)
print(C.__base__)#C类的第一个父类 <class '__main__.A'>
print(C.__mro__)#类的层次结构(继承关系) (<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class 'object'>)
print(A.__subclasses__())#A类的子类列表 [<class '__main__.C'>]
- 特殊方法:
__len__()
:通过重写__len__()
方法,让内置函数len()
的参数可以是自定义类型__add__()
:通过重写__add__()
方法,可使用自定义对象具有‘+’功能__new__()
:创建对象__init__()
:对创建的对象初始化
class Student:
def __init__(self,name):
self.name=name
def __add__(self, other):
return self.name+other.name
stu1=Student('张三')
stu2=Student('李四')
#s=stu1+stu2#通过在类内编写__add__()方法,使得2个个对象可以相加
#print(s)
s1=stu1.__add__(stu2)#这种方式可以实现的前提是先在类内编写__add__()方法
print(s1)
lst=[1,2,34,5]
print(len(lst))#4 内置函数len()用于计算列表长度
print(lst.__len__()) #__len__()方法和内置函数len()相对应
print(stu1.__len__())#2 输出对象stu1的长度
__new__()
方法和 __init__()
方法
__new__()
方法创建的对象为self
和用户在类外创建的实例对象。
class Person(object):
def __new__(cls, *args, **kwargs):
print('__new__被调用执行了,cls的id值为{0}'.format(id(cls)))#返回的是Person类的地址
obj=super().__new__(cls)#继承object类的地址
print('创建的对象的ID为:{0}'.format(id(obj)))#返回创建的实例对象的id
return obj#把obj返回给self
def __init__(self,name,age):
print('__init__被调用了,self的id值为:{0}'.format(id(self)))#返回创建的实例对象的id
self.name=name
self.age=age
print('类对象object的id为:{0}'.format(id(object)))
print('类对象Person的id为:{0}'.format(id(Person)))
#创建Person类的实例对象
p1=Person('张三',20)
print('p1这个Person类的实例对象的id为:{0}'.format(id(p1)))
'''__new__方法是在创建实例对象后被调用的。
创建对象后,先调用__new__,返回;再调用__init__,返回。cls地址对应类的地址,self的地址对应创建的实例对象的地址'''
传参过程:代码Person(‘张三’,20)先执行__new__()
方法,把类对象Person传给cls,cls传给类对象object的__new__()
方法来创建对象obj,将object类中创建对象赋值给self。执行完初始化方法后,将self赋值给p1
类的浅拷贝和深拷贝
变量的赋值操作
虽然形成2个变量,但只创建了一个对象。实际上2个变量都指向一个对象
class CPU:
pass
class Disk:
pass
class Computer:
def __init__(self,cpu,disk):
self.cpu=cpu
self.disk=disk
print('----------------')#变量的赋值
cpu1=CPU()#创建类CPU的实例对象cpu1
cpu2=cpu1
print(cpu1)#<__main__.CPU object at 0x000002390E264FD0>
print(cpu2)#<__main__.CPU object at 0x000002390E264FD0>
浅拷贝
python 拷贝一般都是浅拷贝,一般不会拷贝对象包含的子对象的内容
(即拷贝对象和源对象共享子对象)
class CPU:
pass
class Disk:
pass
class Computer:
def __init__(self,cpu,disk):
self.cpu=cpu
self.disk=disk
print('----------------')#变量的赋值
cpu1=CPU()#创建类CPU的实例对象cpu1
disk=Disk()#创建类Disk的实例对象disk
computer=Computer(cpu1,disk)#创建类Computer的实例对象
#浅拷贝
import copy#导入copy模块
com=copy.copy(computer)
print(computer,computer.cpu,computer.disk)
print(com,com.cpu,com.disk)
返回结果
<__main__.Computer object at 0x000002390E264F70> <__main__.CPU object at 0x000002390E264FD0> <__main__.Disk object at 0x000002390E264FA0>
<__main__.Computer object at 0x000002390E264D90> <__main__.CPU object at 0x000002390E264FD0> <__main__.Disk object at 0x000002390E264FA0>
深拷贝
使用copy模块的deepcopy函数,递归拷贝对象中包含的子对象、源对象和拷贝对象所有的子对象都不相同(与备份类似,拷贝对象下的所有子数据集都拷贝一次)
class CPU:
pass
class Disk:
pass
class Computer:
def __init__(self,cpu,disk):
self.cpu=cpu
self.disk=disk
cpu1=CPU()#创建类CPU的实例对象cpu1
disk=Disk()#创建类Disk的实例对象disk
computer=Computer(cpu1,disk)#创建类Computer的实例对象
#深拷贝
print('-------------------------')
import copy
com2=copy.deepcopy(computer)
print(computer,computer.cpu,computer.disk)
print(com2,com2.cpu,com2.disk)
返回结果:
<__main__.Computer object at 0x0000023779E95CA0> <__main__.CPU object at 0x0000023779E95D00> <__main__.Disk object at 0x0000023779E95CD0>
<__main__.Computer object at 0x0000023779E959A0> <__main__.CPU object at 0x0000023779E954C0> <__main__.Disk object at 0x0000023779E930A0>