大家好,上一篇文章我们详细的讲解了继承,今天我们来学习面向对象中的多态与封装,这三种特性,大家必须好好掌握,以后会经常用到.
多态: 指的是事物有多种形态,是面向对象编程中的一个重要概念,它使得通过统一的接口可以调用不同类的对象,实现了代码的灵活性和可扩展性。
向不同的对象发送同一条信息(调用方法),不同对象在接收信息会产生不同的行为
(每个对象可以用自己的方式去响应函数)
多态允许不同的对象对同一消息作出不同的响应。
示例代码:
class Animal:
def make_sound(self):
print('你好')
class Dog(Animal):
def make_sound(self):#相同的方法
print("汪汪汪")
class Cat(Animal):
def make_sound(self):#相同的方法
print("喵喵喵")
animals = [Dog(), Cat()]# 同时实例化两个对象
for animal in animals:#迭代取出两个值
animal.make_sound()
运行结果:
在上图我们看到了想昨天继承的一些方法,我们在子类定义的方法覆盖了父类的方法,这就是多态的一种表现形式---->方法重写
方法重载(Overloading)是指在同一个类中定义多个方法,这些方法具有相同的名称但参数列表不同。方法重载的目的是为了提高代码的可读性和灵活性,使得程序员能够根据不同的需求选择合适的方法进行调用。
虽然在Python不支持方法重载,但是我们可以通过参数的默认值实现类似的效果。
示例代码:
class Calculator:
def add(self, a, b, c=0):#给如c默认参数值
return a + b + c
c = Calculator()#实例化对象c
print(c.add(1, 2)) #传入参数a,b,c是默认参数值0
print(c.add(1, 2, 3)) #传入参数a,b,c,c默认值被覆盖
在方法重载中,方法的参数个数、类型或顺序可以不同,但方法的返回类型必须相同或是它的子类型。当调用一个重载方法时,编译器会根据传入的参数列表的类型和数量来确定应该调用哪个方法。
方法重载可以使代码更加简洁,避免使用多个不同的方法名称来执行类似但稍有不同的操作。它也提供了方法的统一接口,使得程序员能够更轻松地理解和使用代码。
Python的abc模块提供了抽象基类和接口的支持,可以定义规定子类必须实现的方法,并实现多态.
这里面应用到我们上篇内容讲解的抽象类.
示例代码:
from abc import ABC, abstractmethod #导入抽象模块
class Shape(ABC):#定义定义抽象基类Shape
@abstractmethod #用抽象类中装饰器定义抽象方法
def area(self):#定义抽象方法area
pass
class Rectangle(Shape): #定义功能子类 继承抽象基类Shape
def __init__(self, width, height):
self.width = width
self.height = height
def area(self):
return self.width * self.height
class Circle(Shape):# 定义功能子类 继承抽象基类Shape
def __init__(self, radius):
self.radius = radius
def area(self):
return 3.14 * self.radius * self.radius
shapes = [Rectangle(2, 3), Circle(4)]# 同时传参,进行实例化
for shape in shapes:# 循环遍历这个列表,列表中含有 实例化后对像
print(shape.area())# 打印方法抽象area方法
我们可以看到抽象基类 Shape 提供了一个面积计算的接口,同时要求子类实现它。这样,我们可以确保所有的形状子类都有一个 area 方法,而且可以在主程序中通用地访问这个方法。
封装: 将数据和方法进行隐藏和保护.隐藏对象的属性和实现细节,仅对外提供公开访问方式.
但是,这仅仅只是一种变形操作:
我们可以使用下划线作为变量或方法的前缀来表示它们是私有的。尽管Python没有严格的私有访问控制,但使用下划线作为前缀会告诉其他开发人员这些变量或方法是意图为私有的,应该避免直接访问。
类中所有双下划线开头的名字:__n会自动变形成_类名__n
这种变形的特点:
1.类中定义的__n只能在内部使用,内部使用的self.__n的形式实际上使用的是self._类名__n的结果
2.这种变形是针对外部的变形,在外部无法通过__n这个名字访问到
3.在子类定义的__n不会覆盖父类定义的__n.因为在子类中变形成了_子类名__n,父类中变形成了_父类名__n.所以双下划线的属性在继承给子类时,子类无法覆盖.
class MyClass:
def __init__(self):
self.__private_var = 10 #创建私有变量
def __private_method(self): #定义私有方法
print("This is a private method.")
def public_method(self): #定义公共方法
self.__private_var += 1
print("Private variable value:", self.__private_var)
self.__private_method() # 在公共方法里面调用私有方法,在内部进行封装
obj = MyClass()
obj.public_method()
class MyClass:
def __private_method(self): #定义私有方法
print("This is a private method.")
def public_method(self): # 定义公共方法
print("This is a public method.")
self.__private_method() # 在公共方法的内部进行私有方法的调用,实现封装
obj = MyClass()
obj.public_method() # 在调用公共方法的时候会间接调用私有方法
运行结果:
需要注意的是,尽管Python使用下划线作为私有标识符的约定,但它并不能真正实现私有性。仍然可以通过特定的方式访问或调用私有变量或方法,但这样做违反了代码的约定和最佳实践。
因此,应尽量遵守这种约定,将私有变量和方法视为类的内部实现细节,不建议直接访问或调用它们。
property属性:
是一种特殊属性,访问它时会执行一段函数返回得到返回值
将一个类的函数定义成这个属性之后,对象再去使用对象名.属性名,无法察觉这个属性是执行函数后得到的.
面向对象中封装有三种方式:公开,保护,私有.python在语法中没有建立这种机制.在c++中一般会将数据设置成私有的,然后然后提供set和get方法去设置和获取,python是通过property属性实现的.
示例代码:
class Person:
def __init__(self, name):
self._name = name
#将name属性封装成一个属性装饰器
@property # 定义name 方法作为getter 方法
def name(self):
return self._name
@name.setter # name.setter 装饰器定义了name方法作为setter方法
def name(self, value):
self._name = value
@name.deleter # name.deleter 装饰器定义了name方法作为deleter方法
def name(self):
del self._name
# 通过property装饰器,我们可以向访问属性一样访问获取属性值
Person.name = '仁泽'
print(Person.name)
通过@property装饰器,我们可以像访问属性一样来获取属性值(例如:person.name)
通过@name.setter装饰器,我们可以像赋值属性一样来设置属性值(例如:person.name = “John”)
通过@name.deleter装饰器,我们可以删除属性值(例如:del person.name)
以上就property 的用法以及setter和 deleter的用法.
封装的好处:
可以隐藏内部实现细节,只对外提供必要的接口,从而保护数据的安全性和保证程序的稳定性。同时封装也使得代码更加模块化、可维护性更强,降低了代码的耦合度。
现在我们学习完了面向对象编程的三种特性,接下来我会更新几个对于面向对象编程的实战案例.
讲的不好,多多见谅,我们下次再见!
更多优质文章点这里