python面向对象封装与多态

面向对象之封装

含义

将类中的某些名字按照特殊的书写方式"隐藏"起来,不让外界直接调用,目的是为了不然外界可以直接修改类中的名字,而是提供专门的通道去访问,在通道内可以添加额外的功能。

image

实现

在类中可以使用双下划线开头(__)的命名方式来实现封装效果。

class A:
    # 使用双下划线开头
    __name = 'from A'
print(A.__name)  # 报错,显示找不到"__name"

类中的名字进行封装后就无法直接调用了,实际上,"__name"被变形成了"_A__name",就是"__名称"变成了"_类名__名称"。

我们可以调用__dict__查看类中的名称空间

print(A.__dict__)

结果

{'__module__': '__main__', '_A__name': 'from A', '__dict__': <attribute '__dict__' of 'A' objects>, '__weakref__': <attribute '__weakref__' of 'A' objects>, '__doc__': None}

类A中的名称空间多了一个'_A__name': 'from A'键值对,这个就是封装的结果,我们可以直接用A._A__name的方式获取到类中__name的值,但并不推荐这样使用。

一般情况下,我们封装的目的就是为了不让外界直接可以获取或者修改名称的值,而是提供一个接口(函数)让外界去获取或者修改值。

class A:
    # 封装
    __name = 'from A'
    # 提供接口
    def get(self):
        return self.__name
obj = A()
print(obj.get())  # 输出:from A

实际应用

class Student:
    def __init__(self, name, age):
        self.__name = name
        self.__age = age

    # 访问学生数据的通道(接口)
    def get_info(self):
        print("学生姓名:%s 学生年龄:%s" % (self.__name, self.__age))

    # 专门开设一个修改学生数据的通道(接口)
    def change_info(self, name, age):
        if len(name) == 0:
            print('用户名不能为空')
            return
        if not isinstance(age, int):
            print('年龄必须是数字')
            return
        self.__name = name
        self.__age = age


stu1 = Student('tom', 18)
stu1.get_info()  # 输出:学生姓名:tom 学生年龄:18
stu1.change_info('jason', 28)
stu1.get_info()  # 输出:学生姓名:jason 学生年龄:28
stu1.change_info('', 'haha')  # 输出:用户名不能为空

@property

@property的作用就是可以将方法伪装成数据,比如一个方法需要加上括号才可以调用,但是加了@property后不用加括号就调用了。

class A:
    def __init__(self, name):
        self.name = name
    @property
    def get_name(self):
        return self.name
obj = A('tom')
print('name = %s' % obj.get_name)  # 输出:name = tom

面向对象之多态

含义

多态指的是一类事物有多种形态,比如动物有多种形态:猫、狗、猪,水有多种形态:固态、液态、气态。

image

代码展示

# 动物类
class Animal: 
    def speak(self):
        pass
# 动物之一:猫
class Cat(Animal):
    def speak(self):
        print('喵喵喵')
# 动物之一:狗
class Dog(Animal):
    def speak(self):
        print('汪汪汪')
# 动物之一:猪
class Pig(Animal):
    def speak(self):
        print('哼哼哼')
# 实例化
cat = Cat()
dog = Dog()
pig = Pig()
# 
cat.speak()
dog.speak()
pig.speak()

以上代码体现的就是面向对象的多态特性,狗、猫、猪都是属于动物,那么它们就应该有一个共同的speak方法。

此外,我们还可以在定义一个函数让它更加灵活。

def speak(animal):
    animal.speak()
speak(cat)
speak(dog)
speak(pig)

其实在很早之前我们就已经接触过面向对象的多态性了,比如str、list、dict数据类型都有一个共同的方法len。

# 多态性
s1 = 'hello world'
l1 = [1, 2, 3, 4]
d1 = {'name': 'jason', 'pwd': 123}
print(len(s1))
print(len(l1))
print(len(d1))

多态性的好处在于增强了程序的灵活性和可扩展性,比如通过继承Animal类创建了一个新的类,实例化得到的对象obj,可以使用相同的方式使用obj.speak()。

强制多态

python在面向对象中提供了强制性的措施来实现多态性,但不推荐使用。

强制多态需要导入abc模块。

import abc
# 指定metaclass属性将类设置为抽象类,抽象类本身只是用来约束子类的,不能被实例化
class Animal(metaclass=abc.ABCMeta):
    @abc.abstractmethod # 该装饰器限制子类必须定义有一个名为talk的方法
    def talk(self): # 抽象方法中无需实现具体的功能
        pass
class Person(Animal): # 但凡继承Animal的子类都必须遵循Animal规定的标准
    def talk(self):
        pass
p1=Person() # 若子类中没有一个名为talk的方法则会抛出异常TypeError,无法实例化

多态衍生

由多态性衍生出一个鸭子类型理论:只要你看着像鸭子,走路像鸭子,说话像鸭子,那么你就是鸭子!

image

class Txt: #Txt类有两个与文件类型同名的方法,即read和write
    def read(self):
        pass
    def write(self):
        pass

class Disk: #Disk类也有两个与文件类型同名的方法:read和write
    def read(self):
        pass
    def write(self):
        pass

这两个类看起来都像文件,因而就可以当文件一样去用,然而它们并没有直接的关系,但它们属于鸭子类型。

posted @ 2022-04-08 17:59  Yume_Minami  阅读(81)  评论(0编辑  收藏  举报