Python面向对象三大特征(python系列20)

1.封装

定义:

数据角度 :将基本数据类型 复合成一个自定义类型

作用:可读性更高,将数据与对数据的操作相关联。

行为角度 :对类外提供必要的功能,隐藏实现的细节

作用:让调用者不必了解实现代码,也能调用我们写的功能

让调用者操作变得简单

私有化成员:

定义 :变量名双下划线开头

如 self.__name = name

本质:障眼法,可以通过对象._类名__成员名调用。

2.继承

定义:重用现有类的功能,并在此功能上进行扩展。

多个类型有相同的行为,且概念统一,这个时候我们就可以将多个类型统一的行为抽出来做一个类,而这个类就为多个类的父类,当创建子类时继承父类,就可以继承父类中已经有的行为。

通俗一些的讲,就是子类继承父类,就相当于子类复制粘贴了父类代码。

继承语法:

class Person:
    pass
class Student(Person):
    Pass

# 代码1-1

代码1-1中class Person其实就相当于,class Person(object),python代码中,创建一个类时,当不选择父类时,默认继承object,因此:任何类都直接或间接继承object类。

鸭子原则

其实:默认继承object体现了鸭子原则的精神,关心的是对象的行为,而不是对象的类型,只要对象的行为符合我们的期待,我们就可以将其视为所需要的类型。为什么代码1-1Person后面可以不写object来明确认父,这是一种思想,想告诉写python的程序员,我管你什么类型和继承关系,你给我实现功能就好了。

代码案例:

class Duck:  
    def quack(self):  
        print("Quack!")  
  
class Cat:  
    def meow(self):  
        print("Meow!")  
  
def sound(animal):  
    animal.quack()  # 调用Duck类中的quack方法  
  
duck = Duck()  
cat = Cat()  
  
sound(duck)  # 输出 "Quack!"  
sound(cat)   # 抛出 AttributeError,因为Cat类没有定义quack方法

上述代码:

因为Cat类没有定义quack方法,因此才抛出异常,若Cat类中有queck方法则正常输出,

这里就能感受到,python只关心对象的行为,而不关心对象的类型,运行时才确定对象类型。而python中这种思想体现在动态类型语言检测和灵活的面向对象编程上。

总结:鸭子原则时一种思想,我们只关系对象的行为,而不关心对象的类型,而在python中这种思想体现在动态类型语言检测和灵活的面向对象编程上。在动态类型语言检测上,我们是在运行程序时才确定对象类型,因此当我们将一个对象当作参数传入一个函数中时,该对象可以时任何类型,只要能实现函数中的对象就不会抛出异常,反之不能满足函数中对象的行为则会抛出类型中没有此方法的异常。

在面向对象的继承上:

即使你不继承父类,但是你有和父类一样的行为,那么你就有了这个父类的特性,

从而可以在python中就可以达到你就是继承这个父类的效果。

类型判断:isinstance, type

class Animal:
    @staticmethod
    def eat():
        print("我吃吃吃")


class Dog(Animal):
    @staticmethod
    def run():
        print("我跑跑跑")


class Bird(Animal):
    @staticmethod
    def fly():
        print("我飞飞飞")


if __name__ == '__main__':
    bird = Bird()
    bird.fly()
    bird.eat()
    dog = Dog()
    dog.eat()
    dog.eat()
    animal = Animal()
    print(isinstance(dog, Dog))  # True
    print(isinstance(dog, Animal))  # True
    print(isinstance(animal, Dog))  # False
    print(type(dog) == Dog)  # True
    print(type(dog) == Animal)  # False
    print(type(animal) == Dog)  # False

# 代码1-2 

通过上述代码:我们可以得出结论,type和isinstance的区别,用法上的不同,type是判断一个对象是不是一个类型,是就返回True,不是返回False。而isinstance是判断一个对象是不是属于一种类型,像代码1-2中,dog对象虽然是Dog类型,但是Dog继承了Animal类型,所以dog对象也属于动物类型,用isinstance判断返回的是True。反之,animal对象不属于Dog类型,怎么理解,只有说儿子属于爸爸,没有说爸爸属于儿子,animal对象是Animal类型是Dog类型的父类。

构造函数的继承

若子类没有构造函数,将直接使用父类构造函数。

若子类有构造函数,将覆盖父类的构造函数。

若子类有构造函数,并且想使用父类的构造函数的成员,可以用super().init(参数,参数)

python可以多继承,但是我们一般都使用单继承,多实现的思想来设计模型。

继承父类变量的语法与代码

# 继承父类变量
class Car:
    def __init__(self, brand="", speed=""):
        self.brand = brand
        self.speed = speed


class Electric(Car):
    def __init__(self, brand="", speed="", capacity="", frequency=""):
        super().__init__(brand, speed)
        self.capacity = capacity
        self.frequency = frequency


electric = Electric("1", "1", "1", "1")
print(electric.capacity, electric.frequency, electric.brand, electric.speed)

# 代码1-3

父类中是共性,子类中是个性。

3.多态

概念:对于父类的一个方法,在不同子类上有不同的体现。

重写:目的是张显个性

双下划线开头和双下划线结尾,这些是python的内置函数。

重写内置函数:

重写之前:

class Animal:
    def __init__(self, name="", color=""):
        self.name = name
        self.color = color


animal = Animal("dog", "white")
print(animal)  # <__main__.Animal object at 0x000001E829958948>

# 代码1-4

重写之后:

class Animal:
    def __init__(self, name="", color=""):
        self.name = name
        self.color = color

    def __str__(self):
        return "name: %s, color: %s" % (self.name, self.color)


animal = Animal("dog", "white")
print(animal)  # name: dog, color: white

面向接口编程:

先确定用法,后决定做法,这是一种软件架构设计思想,

在1991年python祖师爷就确定了print()的用法,就是打印一个对象__str__方法的返回值。

这里的重写只是语法上,随便百度就能知道,并不值钱,而这里面牵涉到的软件架构思想才是重中之重,非常重要

下列是经常会重写的内置函数:

算数运算符:

代码案例:

class Animal:
    def __init__(self, name="", age=0):
        self.name = name
        self.age = age

    def __str__(self):
        return "name: " + self.name + ", age: " + str(self.age)

    def __add__(self, other):
        if type(other) == int:
            return Animal(self.name, self.age + other)
        else:
            return Animal(self.name, self.age + other.age)


animal = Animal("dog", 2)
print(animal + 1)  # name: dog, age: 3
print(animal + animal)  # name: dog, age: 4

在python中 animal + 1 相当于 animal.add(1)

我们再来看看重写__iadd__

class Animal:
    def __init__(self, name="", age=0):
        self.name = name
        self.age = age

    def __str__(self):
        return "name: " + self.name + ", age: " + str(self.age)

    def __iadd__(self, other):
        if type(other) == int:
            self.age += other
            return self
        else:
            self.age += other.age
            return self


animal = Animal("dog", 2)
print(id(animal))  # 140380752334848
animal += 1
print(id(animal))  # 140380752334848
print(animal)  # name: dog, age: 3

而__iadd__则是 +=, 同理加减乘除的累计运算符都是比算数运算符多个i

值得一提的是,当一个对象没有__iadd__时使用 += 对象会调用__add__,但是返回的也是一个新对象。而当一个对象没有__add__时,使用+对象时会抛出异常。

比较运算符对应的内置函数:

这里写两个比较常用的重写的代码案例:

"""
图书列表设计
类:BookModel
数据:书名 - name , 价格 - number
行为1,重写__eq__使其在列表容器中能够实现,remove, in , index等功能。
行为2, 重写__lt__ 或 __gt__ 使其在列表容器中能实现,sort 功能
"""


class BookModel:
    def __init__(self, name, price):
        self.name = name
        self.price = price

    def __str__(self):
        return self.name + " - " + str(self.price)

    def __eq__(self, other):
        return self.name == other.name

    def __lt__(self, other):
        return self.price < other.price


list_book = [
    BookModel("Java", 200),
    BookModel("Python", 100)
]

list_book.sort()
for item in list_book:  # Python - 100 Java - 200
    print(item)
    
list_book.remove(BookModel("Java", 200))
for item in list_book:  # Python - 100
    print(item)

lt:

当自定义类型重写了__lt__方法,自定义类型就可以比较大小,并且放入列表中还能使用列表的方法,如sort(),因为sort的实现代码是迭代列表,取列表中元素,让元素与元素之间比较,当列表中的对象类型没有重写__lt__方法时,会抛出异常。

eq:

当自定义类型写__eq__方法,就可以根据自己的业务逻辑来判断自定义对象是否相等,值得一提的是object默认使用对象的内存进行比较,实现这个方法,如果将自定义对象存入列表,将可以使用列表的romove方法,index方法等。

相关推荐
喜欢猪猪16 分钟前
Java技术专家视角解读:SQL优化与批处理在大数据处理中的应用及原理
android·python·adb
言之。16 分钟前
【面试题】构建高并发、高可用服务架构:技术选型与设计
架构
海绵波波10717 分钟前
flask后端开发(1):第一个Flask项目
后端·python·flask
林的快手23 分钟前
209.长度最小的子数组
java·数据结构·数据库·python·算法·leetcode
从以前38 分钟前
准备考试:解决大学入学考试问题
数据结构·python·算法
Ven%1 小时前
如何修改pip全局缓存位置和全局安装包存放路径
人工智能·python·深度学习·缓存·自然语言处理·pip
枫欢1 小时前
将现有环境192.168.1.100中的svn迁移至新服务器192.168.1.4;
服务器·python·svn
测试杂货铺1 小时前
UI自动化测试实战实例
自动化测试·软件测试·python·selenium·测试工具·测试用例·pytest
余~~185381628002 小时前
NFC 碰一碰发视频源码搭建技术详解,支持OEM
开发语言·人工智能·python·音视频