【Python】第六篇:封装、继承、多态——面向对象的三大特征
💖星光不问赶路人,时光不负有心人!💖
系列文章目录🍁
第一篇:基础语法与结构 整理万字,秃头的路上,我们一起成为卷王
第二篇:Python四大内置数据结构列表、字典、元组、集合看这一篇完全够用
第三篇:字符串与函数,越学越上头的python,你确定不来看看?
第四篇:Bug——程序员的一生之敌,先别急着写Bug,快来学学怎么在Python中调教它
第五篇:类与对象 找对象不积极,思想有问题
文章目录
🌴前言
大家好,我是小沐!😃编程路上一个人可能走的更快,但一群人才能走得更远,关注小沐一起学习不迷路!今天分享的是 面向对象的三大特征——封装、继承、多态的有关知识,话不多说,秃头走起——>冲冲冲👊👊👊!!!
🌿零、面向对象的三大特征
封装、继承和多态是面向对象的三大特征。
封装是为了提高程序的安全性;
继承是为了提高代码的复用性;
多态是为了提高程序的可扩展性和可维护性。
值得一提的是,这三大特征与语言本身无关,这是一种面向对象的编程思想。*
🌿一、封装
什么是封装?
其实封装就是包装,例如银行里的ATM机里放着钱,机器包装着钱,而我们根本不知道钱在机器的哪个位置放着,我们只能通过合适的方式去取钱。这样就提高了钱的安全性。
写程序时的封装也是一样的,我们将数据(属性)和行为(方法)包装到类对象中。而在方法内部对属性进行操作,在类对象的外部调用方法。这样我们只需要用合适的方式去用它,而不用关心刚方法时如何具体实现的。
在Python中没有专门的修饰符用于属性的私有,如果不希望该属性在类对象外部被访问,可在前边使用两个"_"
class Student:
def __init__(self,name,age):
self.name=name
self.__age=age #前边加两_表示不希望再类外部被访问
def show(self):
print(self.name,self.__age)
stu=Student('苏沐',20)
stu.show()
print(stu.name)
#print(stu.__age) #报错
print(dir(stu)) #可查看类内的属性和方法
print(stu._Student__age) #在类的外部可通过_Student__age访问
注意:如果一定要在类外部访问,可通过_Student__age访问,但大家都比较自觉,人家都不让你问访问了,你就别这么访问了😤
🌿二、继承
1.继承
简单来说继承就是定义子类,子类继承父类的属性和方法。
语法:
class 子类类名(父类1,父类2…):
pass
如果一个类没有继承任何类,默认继承object;
Python支持多继承;
定义子类时,必须在其构造函数中调用父类的构造函数。
#定义父类
class Person(object): #object可写可不写
def __init__(self,name,age):
self.name=name
self.__age=age #前边加两_表示不希望在类外部被访问
def show(self):
print(self.name,self.__age)
#定义子类
class Student(Person):
def __init__(self,name,age,score):
super().__init__(name,age) #调用父类的构造函数
self.score=score #自己额外的属性
#调用
stu=Student('苏沐',20,99)
stu.show()
2.方法重写
上述代码中由于我们继承了Person类,调用show时只输出name和age,我们需要它还输出score就需要方法重写。
如果子类对继承自父类的某个属性或方法不满意,可以在子类中对其方法进行重新编写;
子类重写后的方法中,可以通过super().xxx()调用父类中被写过的方法。
#定义父类
class Person(object): #object可写可不写
def __init__(self,name,age):
self.name=name
self.__age=age #前边加两_表示不希望在类外部被访问
def show(self):
print(self.name,self.__age)
#定义子类
class Student(Person):
def __init__(self,name,age,score):
super().__init__(name,age) #调用父类的构造函数
self.score=score #自己额外的属性
def show(self): #方法重写
super().show()
print(self.score)
#调用
stu=Student('苏沐',20,99)
stu.show()
3.object类
object类是所有类的父类,所以所有类内都有object类的属性和方法
内置函数dir()可查看指定对象的所有属性
object有一个__str__()方法,用于返回一个对于”对象的描述”,对应于内置函数str(),经常用于print()方法,帮我们查看对象信息,所以我们经常会对__str__进行重写。
class Student:
def __init__(self,name,age):
self.name=name
self.age=age
def __str__(self):
return '我的名字是{0},今年{1}岁'.format(self.name,self.age)
stu=Student('苏沐',20)
print(dir(stu)) #查看对象信息
print(str(stu)) #没重写__str__前输出对象地址,重写后输出你重写返回的内容
print(stu) #和上面一句等价,直接输出的话默认调用__str__()方法
print(type(stu))
🌿三、多态
很多人说,Python中没有多态的概念,因为Python中的变量是没有数据类型的。
其实不然,虽然Python中变量没有数据类型,但是仍然具备多态的特征。
Python中的多态指的是,在运行过程中根据变量所引用对象的类型,动态的决定调用哪个类对象中的方法。
class Animal(object):
def run(self):
print('动物会跑')
class Dog(Animal):
def run(self):
print('狗会跑')
class Cat(Animal):
def run(self):
print('猫会跑')
class Person():
def run(self):
print('人会跑')
#定义一个函数
def fun(obj):
obj.run()
#调用函数
fun(Animal())
fun(Cat())
fun(Dog())
fun(Person())
这里Animal、Cat、Dog、Person都调用了run方法,Cat和Dog是对Animal中run的重写,很容易理解,但是Person中的run与其他三个中的是并列的,调用的时候就看传入对象的类型属于哪个就调用哪个。这是与其他编程语言所不同的,我们称为“鸭子类型”。
Python是一种动态的语言。
静态语言与动态语言关于多态的区别:
静态语言实现多态的三个必要条件:
继承、方法重写、父类引用指向子类对象
动态语言的多态称为“鸭子类型”,当一只鸟走路,游泳,啄食等行为都像鸭子的时候,我们认为它就是鸭子。我们不关心对象是什么类型,只关心对象的行为(方法)。
🌿四、特殊方法和特殊属性(***)
以两个下划线开始和结束的如__xxx__称为特殊属性和特殊方法。
1.特殊属性
#特殊属性__dict__获得类对象或实例化对象所绑定的所有属性和方法的字典
#dir(object)可查看object中的属性、方法
class A:
pass
class B:
pass
class C(A,B):
def __init__(self,name,age):
self.name=name
self.age=age
#创建C类对象
c=C('苏沐',20)
#查看实例对象的属性字典,方法是在类对象中的,实例对象只能调用
print(c.__dict__)# {'name': '苏沐', 'age': 20}
#查看类对象的属性和方法字典
print(C.__dict__) #{'__module__': '__main__', '__init__': <function C.__init__ at 0x000001CB9F854D30>, '__doc__': None}
print(c.__class__) #输出对象所属的类 <class '__main__.C'>
print(C.__bases__) #输出C类的父类元组 (<class '__main__.A'>, <class '__main__.B'>)
print(C.__base__) #输出类的第一个父类 <class '__main__.A'>
print(C.__mro__) #输出类的层次结构
print(A.__subclasses__()) #输出A的子类
2.特殊方法
#特殊方法
# __add__ 通过重写__add__方法,可使自定义对象具有+功能
# 通过重写__len__方法,让内置函数len()的参数可以是自定义类型
a=10
b=20
c=a+b #实际过程为下一行代码
d=a.__add__(b)
print(c,d)
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('秃头')
stu=stu1+stu2 #重定义__add__后就不会报错了,因为在Student中编写了特殊方法
stu=stu1.__add__(stu2)
print(stu)
print(len(stu))
#__new__()用于创建对象
#__init__()对创建的对象进行初始化
class Person(object):
def __new__(cls,*args,**kwargs):
print('__new__被调用了,cls的id值为{0}'.format(id(cls)))
obj=super().__new__(cls)
print('创建的对象的id为:{0}'.format(id(obj)))
return obj
def __init__(self,name,age):
print('__init__被调用了,self的id值为:{0}'.format(id(self)))
self.name=name
self.age=age
print('object这个类对象的id为:{0}'.format(id(object)))
print('Person这个类对象的id为:{0}'.format(id(Person)))
#创建实例对象
p1=Person('苏沐',20)
print('p1这个Person类的实例对象的id为{0}:'.format(id(p1)))
执行过程为:在创建实例对象的过程中将Student传给cls,然后object中创建一个对象obj,传给init方法中的self初始化,完成后赋值给p1.所以后输出的三个的id地址都是一样的。
总的来说,我们想让该方法实现哪些内容,该方法的方法体就由我们来写。许多的特殊方法都对应于一些内置函数。
🌿五、类的赋值与拷贝(***)
1.类的赋值
只是形成了两个变量,实际上还是指向同一个实例对象。
class A():
pass
a1=A()
a2=a1
print(id(a1)) #2364867875104
print(id(a2)) #2364867875104
2.类的浅拷贝
Python拷贝一般都是浅拷贝,拷贝时,对象包含的子对象内容不拷贝,因此,源对象与拷贝对象会引用同一个子对象
import copy
a2=copy.copy(a1)
3.类的深拷贝
使用copy模块的deepcopy函数,递归拷贝对象中包含的子对象,源对象和拷贝对象所有的子对象也不相同。
#深拷贝
import copy
a3=copy.deepcopy(a1)
🌴总结
今日分享到此结束👊👊👊,由于笔者还在求学之路上辗转徘徊🏃,水平有限,文章中可能有不对之处,还请各位大佬指正🙏,祝愿每一个热爱编程的人都能实现追求,考研上岸进大厂,熬夜秃头卷中王。最后欢迎关注小沐,学习路上不迷路!😜
来源:小沐想秃头