第9章 面向对象之三大特性

第9章 面向对象之三大特性

9.1 封装

将变量和函数写入类中的操作即为封装,即类中封装了属性和方法。

通过封装,我们可以将一些细节隐藏起来(私有),只暴露出必要的接口供调用者使用。

9.1.1 私有化

有时为了限制属性和方法只能在类内访问,外部无法访问;或父类中某些属性和方法不希望被子类继承。可以将其私有化。

1)单下划线:非公开API

大多数 Python 代码都遵循这样一个约定:有一个前缀下划线的变量或方法应被视为非公开的 API,例如 _var1。这种约定不具有强制力。

2)双下划线:名称改写

有两个前缀下划线,并至多一个后缀下划线的标识符,例如 __x,会被改写为 _类名__x。只有在类内部可以通过 __x 访问,其他地方无法访问或只能通过 _类名__x 访问。

9.1.2 私有属性

通过双下划线定义私有属性。

class Person:
    def __init__(self, name):
        self.__name = name

    def get_name(self):
        return self.__name

p = Person("张三")
print(p.get_name())  # 张三
print(p._Person__name)  # 张三
print(p.__name)  # 报错

9.1.3 私有方法

通过双下划线定义私有方法。

class Person:
    # 定义私有方法
    def __private_method(self):
        print("private method")

    # 定义实例方法,调用私有方法
    def do_something(self):
        self.__private_method()

p = Person()
p.do_something()  # private method
p._Person__private_method()  # private method
p.__private_method()  # 报错

9.1.4 property

1)方法转换为属性

可通过 @property 装饰器将一个方法转换为属性来调用。转换后可直接使用 .方法名 来使用,而无需使用 .方法名()

class Person:
    def __init__(self, name):
        self.name = name

    @property
    def eat(self):
        print(f"{self.name} is eating...")

p = Person("张三")
p.eat  # 张三 is eating...

2)只读属性

将方法名设置为去掉双下划线的私有属性名,方法中返回私有属性。

class Person:
    def __init__(self, name):
        self.__name = name

    @property
    def name(self):
        return self.__name

p = Person("张三")
print(p.name)  # 张三
p.name = "李四"  # 报错

3)读写属性

将方法名设置为去掉双下划线的私有属性名,使用 属性名.setter 装饰。

class Person:
    def __init__(self, name):
        self.__name = name

    @property
    def name(self):
        return self.__name

    @name.setter
    def name(self, name):
        self.__name = name

p = Person("张三")
print(p.name)  # 张三
p.name = "李四"
print(p.name)  # 李四

也可以在写方法中设置一些拦截条件来规范私有属性的写入。

class Person:
    def __init__(self, name):
        self.__name = name

    @property
    def name(self):
        return self.__name

    @name.setter
    def name(self, name):
        if name == "李四":
            print("不许叫李四")
        else:
            self.__name = name

p = Person("张三")
print(p.name)  # 张三
p.name = "李四"  # 提示 "不许叫李四"
print(p.name)  # 张三
p.name = "王五"
print(p.name)  # 王五

4)注意

@property 装饰的方法不要和变量重名,否则可能导致无限递归。

class Person:
    @property
    def name(self):
        return self.name

p = Person()
p.name  # 报错:RecursionError: maximum recursion depth exceeded

9.2 继承

子类(派生类)继承父类(基类)中的属性和方法,实现代码重用。子类可以新增自己特有的方法,也可以重写父类的方法。

子类不能继承父类的私有属性和私有方法,因为存在名称改写,但是可以通过改写后的名称直接访问父类的私有成员,不过,这种做法违背了封装原则,不建议使用。

9.2.1 单继承

1)语法

class 类名(父类):
    类体

在类名后括号内指定要继承的父类。

2)案例

class Person:
    """人的类"""
    home = "earth"  # 定义类属性

    def __init__(self, name):
        self.name = name  # 定义实例属性

    def eat(self):
        print("eating...")

class YellowRace(Person):
    """黄种人"""
    color = "yellow"  # 定义类属性

class WhiteRace(Person):
    """白种人"""
    color = "white"  # 定义类属性

class BlackRace(Person):
    """黑种人"""
    color = "black"  # 定义类属性

y1 = YellowRace("张三")
print(y1.home)  # earth
print(y1.color)  # yellow
print(y1.name)  # 张三
y1.eat()  # eating...

w1 = WhiteRace("李四")
print(w1.home)  # earth
print(w1.color)  # white
print(w1.name)  # 李四
w1.eat()  # eating...

b1 = BlackRace("王五")
print(b1.home)  # earth
print(b1.color)  # black
print(b1.name)  # 王五
b1.eat()  # eating...

9.2.2 多继承

调用方法时先在子类中查找,若不存在则从左到右依次查找父类中是否包含方法。

1)语法

class 类名(父类1, 父类2, ...):
    类体

2)案例

class Person:
    """人的类"""
    home = "earth"

    def __init__(self, name):
        self.name = name

    def eat(self):
        print("eating...")

class YellowRace(Person):
    """黄种人"""
    color = "yellow"

    def run(self):
        print("runing...")

class Student(Person):
    """学生"""
    def __init__(self, name, grade):
        self.name = name
        self.grade = grade

    def study(self):
        print("studying...")

class ChineseStudent(Student, YellowRace):  # 继承了Student和YellowRace
    """中国学生"""
    country = "中国"

y1 = ChineseStudent("张三", "三年级")
print(y1.home, y1.color, y1.country, y1.name, y1.grade)
y1.eat()
y1.run()
y1.study()

9.2.3 复用父类方法

子类可以在类中使用 super().方法名()父类名.方法名() 来调用父类的方法。

1)super().方法名()

class Person:
    """人的类"""
    home = "earth"

    def __init__(self, name):
        self.name = name

    def eat(self):
        print("eating...")

class YellowRace(Person):
    """黄种人"""
    color = "yellow"

    def run(self):
        print("runing...")

class Student(Person):
    """学生"""
    def __init__(self, name, grade):
        self.name = name
        self.grade = grade

    def study(self):
        print("先吃再学")
        super().eat()  # 子类中调用父类的方法
        print("studying...")

class ChineseStudent(Student, YellowRace):
    """中国学生"""
    country = "中国"

y1 = ChineseStudent("张三", "三年级")
print(y1.home, y1.color, y1.country, y1.name, y1.grade)
y1.study()

2)父类名.方法名()

class Student(Person):
    """学生"""
    def __init__(self, name, grade):
        self.name = name
        self.grade = grade

    def study(self):
        print("先吃再学")
        Person.eat(self)  # 子类中调用父类的方法
        print("studying...")

9.2.4 方法解析顺序

方法解析顺序(MRO — Method Resolution Order)。可使用 类名.__mro__ 访问类的继承链来查看方法解析顺序。

class Person:
    """人的类"""
    home = "earth"

    def __init__(self, name):
        self.name = name

    def eat(self):
        print("eating...")

class YellowRace(Person):
    """黄种人"""
    color = "yellow"

    def run(self):
        print("runing...")

class Student(Person):
    """学生"""
    def __init__(self, name, grade):
        self.name = name
        self.grade = grade

    def study(self):
        print("先吃再学")
        Person.eat(self)
        print("studying...")

class ChineseStudent(Student, YellowRace):
    """中国学生"""
    country = "中国"

y1 = ChineseStudent("张三", "三年级")
print(ChineseStudent.__mro__)
# (<class 'ChineseStudent'>, <class 'Student'>, <class 'YellowRace'>, <class 'Person'>, <class 'object'>)

9.2.5 方法重写

在子类中定义与父类方法重名的方法,调用时会调用子类中重写的方法。

class Person:
    home = "earth"

    def __init__(self, name):
        self.name = name

    def eat(self):
        print("eating...")

class Chinese(Person):
    color = "yellow"

    # 重写父类方法
    def eat(self):
        print("用筷子吃")

y1 = Chinese("张三")
y1.eat()  # 用筷子吃

注意: 子类重写 __init__() 并调用时,不会执行父类的 __init__() 方法。如有必要,需在子类 __init__() 中使用 super().__init__() 来调用父类的 __init__() 方法。

class Person:
    def __init__(self, name):
        self.name = name

class Chinese(Person):
    def __init__(self, name, area):
        super().__init__(name)  # 调用父类的__init__()
        self.area = area

y1 = Chinese("张三", "北京")
print(y1.name, y1.area)

9.3 多态

同一事物在不同场景下呈现不同状态。

class Animal:
    def go(self):
        pass

class Dog(Animal):
    def go(self):
        print("跑")

class Fish(Animal):
    def go(self):
        print("游")

class Bird(Animal):
    def go(self):
        print("飞")

def go(animal):
    animal.go()  # 将不同的实例传入,执行不同的方法

dog = Dog()
fish = Fish()
bird = Bird()
go(dog)   # 跑
go(fish)  # 游
go(bird)  # 飞
#python##python八股文#
Python 文章被收录于专栏

本专栏系统讲解 Python 编程,共 17 章,涵盖基础语法、数据结构、函数、面向对象、文件操作、异常处理、高级语法、进程与线程、网络编程、正则表达式等核心知识点,并通过&quot;愤怒的小鸟&quot;和&quot;客户信息管理系统&quot;等实战案例巩固所学。适合零基础入门者及希望系统梳理 Python 知识体系的开发者。

全部评论

相关推荐

03-12 16:48
已编辑
门头沟学院 C++
不想加班的L:我第一次字节审批了一周,不过是有新的hr对接的,你这个看起来像是原来招聘的hr。第二次就一天审批就完了,比较快 可以参考一下
点赞 评论 收藏
分享
饥饿的丹尼尔在刷代码:写笔试的时候用草稿纸还被监考提醒两次看屏幕
点赞 评论 收藏
分享
评论
1
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务