python(十二)--面向对象三大特性

1.简介

1.1 面向过程

1. “面向过程”(Procedure Oriented)是一种以过程为中心的编程思想。分析出解决问题所需要的步
骤,然后用函数把这些步骤一步一步实现,使用的时候一个一个依次调用就可以了。
面向对象的方法也是含有面向过程的思想。面向过程最重要的是模块化的思想方
法。比如拿学生早上起来这件事说明面向过程,粗略的可以将过程拟为:
		(1)起床
		(2)穿衣
		(3)洗脸刷牙
		(4)去学校
而这4步就是一步一步地完成,它的顺序很重要,你只需要一个一个地实现就行了。
而如果是用面向对象的方法的话,可能就只抽象出一个学生的类,它包括这四个
方法,但是具体的顺序就不一定按照原来的顺序。

2.特性:
		模块化 流程化
	优点:
		性能比面向对象高, 因为类调用时需要实例化,开销比较大,比较消耗资源;
		单片机、嵌入式开发、Linux/Unix等一般采用面向过程开发,性能是最重要的因素。
	缺点:
		没有面向对象易维护、易复用、易扩展

1.2 函数式编程

- 函数式编程是种编程方式,它将电脑运算视为函数的计算。函数编程语言最重要的基础是λ
  演算(lambda calculus),而且λ演算的函数可以接受函数当作输入(参数)和输出(返回值)。
  主要思想: 把运算过程尽量写成一系列嵌套的函数调用。

1.3 面向对象编程

1. 面向对象是按人们认识客观世界的系统思维方式,把构成问题事务分解成各个对象,建立对
象的目的不是为了完成一个步骤,而是为了描叙某个事物在整个解决问题的步骤中的行为。

2. 特性: 抽象 封装 继承 多态
   优点: 易维护、易复用、易扩展,由于面向对象有封装、继承、多态性的特性,
		  可以设计出低耦合 的系统,使系统更加灵活、更加易于维护
   缺点: 性能比面向过程低

1.4 面向过程和面向对象的处理过程

- 1.面向过程实现五子棋游戏
  五子棋,面向过程的设计思路就是首先分析问题的步骤:
	1、开始游戏
	2、黑子先走
	3、绘制画面
	4、判断输赢
	5、轮到白子
	6、绘制画面
	7、判断输赢
	8、返回步骤2
	9、输出最后结果。
  把上面每个步骤用不同的方法来实现。

- 2.面向对象实现五子棋
- 五子棋,面向对象的设计思路就是首先分析问题的步骤:
	1、黑白双方,这两方的行为是一模一样的。
	2、棋盘系统,负责绘制画面。
	3、规则系统,负责判定诸如犯规、输赢等。
  第一类对象(玩家对象)负责接受用户输入,并告知第二类对象(棋盘对象)棋子布局的
  变化,棋盘对象接收到了棋子的变化就要负责在屏幕上面显示出这种变化,同时利用第三
  类对象(规则系统)来对棋局进行判定。

2. 对象和类

类(Class)是现实或思维世界中的实体在计算机中的反应,它将数据以及这些数据上的操作封装在一起。类(Class)是是创建实例的模板
对象(Object)是具有类类型的变量。类和对象是面向对象编程中最基本的概念。对象(Object)是一个一个具体的实例
类和对象的区别: 鱼和三文鱼的区别;(鱼是宽泛概念,三文鱼是具体种类)

1. 如何定义类? class 类(): pass
2. 如何将类转换成对象?
	实例化是指在面向对象的编程中,把用类创建对象的过程称为实例化。是将一个抽象的概
念类,具体到该类实物的过程。实例化过程中一般由类名 对象名 = 类名(参数1,参数2...参数n)
构成。
#类class
class Cat:
    #属性:一般是名词
    name='name'
    kind='kind'
    #方法:一般情况是动词 ,eg:create,delete,eating,run...
    def eat(self):
        print('cat like eating fish.....')

#对象(Object):对类的实例化(具体化)
fentiao = Cat()
print(Cat)    #<class '__main__.Cat'>  ##当前环境下的类
print(fentiao)  #<__main__.Cat object at 0x01BDEF10>   ##当前环境下的类下的对象,存在内存环境中

3. 封装特性

3.1 介绍及操作

面向对象的三大特性是指:封装、继承和多态

1.封装,顾名思义就是将内容封装到某个地方,以后再去调用被封装在某处的内容。
  所以,在使用面向对象的封装特性时,需要:
	1). 将内容封装到某处
	2). 从某处调用被封装的内容
		1). 通过对象直接调用被封装的内容: 对象.属性名
		2). 通过self间接调用被封装的内容: self.属性名
		3). 通过self间接调用被封装的内容: self.方法名()
2.构造方法__init__与其他普通方法不同的地方在于,当一个对象被创建后,会立即调用构造方法。自动执行构造方法里面的内容
3.对于面向对象的封装来说,其实就是使用构造方法将内容封装到 对象 中,然后通过对象直接或者self间接获取被封装的内容。
#类class
class Cat:
    def __init__(self,name,kind): # 形参
        """
        1.构造方法,实例化对象时自动执行的方法
        2.self是什么?self实质上是实例化的对象
        3.类方法中python解释器会自动的把对象作为参数传给self
        """
        print('正在执行init构造方法')
        print('self:',self)   #self: <__main__.Cat object at 0x0125EF10>
    #属性:一般是名词
        self.name=name
        self.kind=kind
    #方法:一般情况是动词 ,eg:create,delete,eating,run...
    def eat(self):
        print('cat %s like eating fish.....'%(self.name))

#对象(Object):对类的实例化(具体化)
fentiao = Cat("炖条","美短")
print(fentiao.name)  #炖条
print(fentiao.kind)  #美短
fentiao.eat()    #cat 炖条 like eating fish.....

在这里插入图片描述

3.2 巩固封装特性

"""
创建一个类People,拥有的属性为姓名, 性别和年龄, 拥有的方法为购物,玩游戏,学习;实例化
对象,执行相应的方法。 显示如下:
    小明,18岁,男,去西安赛格购物广场购物
    小王,22岁,男,去西安赛格购物广场购物
    小红,10岁,女,在西部开源学习
提示:
    属性:name,age,gender
    方法:shopping(), playGame(), learning()

"""

class People:
    def __init__(self,name,age,gender):
        self.name = name
        self.age = age
        self.gender = gender
    def shopping(self):
        print(f'{self.name},{self.age}岁,{self.gender},去西安赛格购物广场购物')
    def playing(self):
        print(f'{self.name},{self.age}岁,{self.gender},在家玩游戏')
    def study(self):
        print(f'{self.name},{self.age}岁,{self.gender},在西部开源学习')

xiaoming = People('小明',18,'男').shopping()
xiaoyu = People('小玉',22,'男').shopping()
xiaohong = People('小红',18,'女').study()

在这里插入图片描述

4. 继承特性

4.1 介绍

- 面向对象的三大特性是指:封装、继承和多态
	1. 继承
	2. 多继承
	3. 私有属性与私有方法

- 继承描述的是事物之间的所属关系,当我们定义一个class的时候,可以从某个现有的class
	继承,新的class称为子类、扩展类(Subclass),而被继承的class称为基类、父类或超类(Baseclass、
	Superclass)。(父类有的继承,子类可以继承父类,也可以重新创造新类)

- 问题一: 如何让实现继承?
	子类在继承的时候,在定义类时,小括号()中为父类的名字
  问题二: 继承的工作机制是什么?
		父类的属性、方法,会被继承给子类。 举例如下: 如果子类没有定义__init__方法,父类有,那
		么在子类继承父类的时候这个方法就被继承了,所以只要创建对象,就默认执行了那个继承过来的
		__init__方法

4.2 继承示例


class Student:
    """父类Student"""
    def __init__(self,name,age):  ##构造方法
        self.name = name
        self.age = age

    def learning(self):
        print(f'{self.name}正在学习')

class MathStudent(Student):
    """MathStudent的父类是Student"""
    pass

##实例化
m1 = MathStudent('fentiao',8)
print(m1.name)
print(m1.age)
m1.learning   ##子类没有,父类有,所以继承父类方法

4.3 重写父类继承示例

- 重写父类方法: 就是子类中,有一个和父类相同名字的方法,在子类中的方法会覆盖掉父类中同名的方法。
- 调用父类的方法:
	1. 父类名.父类的方法名()   ##b不推荐
	2. super(): py2.2+的功能   ##推荐
class Student:
    """父类Student"""
    def __init__(self,name,age):  ##构造方法
        self.name = name
        self.age = age
    def learning(self):
        print(f'{self.name}正在学习')
    def choice_source(self):
        print('正在选课中'.center(50,'*'))
class MathStudent(Student):
    """MathStudent的父类是Student"""
    def choice_source(self):
        ##需求:先执行父类方法choice_source,在执行子类choice_source
        ##Student.choice_source(self)  ##解决方法1:直接执行父类方法,不推荐
        ##解决方法2:通过super找到父类,在执行方法(建议且生产环境代码常用方法)
        super(MathStudent,self).choice_source()
        info = """
            课程表
                1.高等数学
                2.线性代数
        """
        print(info)
##实例化
m1 = MathStudent('fentiao',8)
print(m1.name)
print(m1.age)
m1.learning   ##子类没有,父类有,所以继承父类方法
m1.choice_source()   ##重写父类执行

4.4 力扣题

题目链接
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

"""
参考链接:https://cnblogs.com/klyjb/p/11237361.html
数组需要连续的内存空间
链表是一种物理存储单元上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。
    不需要连续的内存空间

         数组      链表(增加和删除只需要删除next指针.查找和修改需要一个一个遍历)
增加元素   O(n)     O(1)
删除元素   O(n)     O(1)
查找元素   O(1)     O(n)
修改元素   O(1)     O(n)
"""

# 封装节点类
class ListNode:
    def __init__(self, val=0, next=None):
        self.val = val
        self.next = next

    def travel(self, head):
        """遍历链表里面的每一个元素"""
        while head:
            print(head.val, end=',')
            head = head.next

def create_l1():
    # l1 = 2,4,3
    # l2 = 5, 6, 4
    l1 = ListNode()
    node1 = ListNode(val=2)
    node2 = ListNode(val=4)
    node3 = ListNode(val=3)
    l1.next = node1
    node1.next = node2
    node2.next = node3
    return  l1.next

def create_l2():
    # l1 = 2,4,3
    # l2 = 5, 6, 4
    l2 = ListNode()
    node1 = ListNode(val=5)
    node2 = ListNode(val=6)
    node3 = ListNode(val=4)
    l2.next = node1
    node1.next = node2
    node2.next = node3
    return  l2.next

##主代码
def addTwoNumbers(l1: ListNode, l2: ListNode) -> ListNode:
    res = 0
    l3 = ListNode()
    cur = l3
    while(l1 or l2):
        if(l1):
            res += l1.val  # res=2
            l1 = l1.next
        if(l2):
            res += l2.val # res=2+5=7
            l2 = l2.next
        # res=10, val=0, res=>val val=res%10
        # res=14, val=4, 14%10=4
        l3.next = ListNode(res%10)
        l3 = l3.next
        # res=10, 进位为1, 10//10=1
        # res=14, 进位为1, 14//10=1
        res  //= 10
    if res == 1:
        l3.next = ListNode(1)
    return cur.next

# 中午留个作业:查一下它的功能
if __name__ == '__main__':
    l1 = create_l1()
    l2 = create_l2()
    l3 = addTwoNumbers(l1, l2)
    l3.travel(l3)

4.5 多继承

4.5.1 新式类和经典类

1. 在Python 2及以前的版本中,由任意内置类型派生出的类,都属于“新式类”,都会获得所有“新式类”的特性;反之,即不由任意内置类型派生出的类,则称之为“经典类”。
新式类  class 类名(object):
	    pass
经典类	
	class 类名:
	pass

2.“新式类”和“经典类”的区分在Python 3之后就已经不存在,在Python 3.x
	之后的版本,因为所有的类都派生自内置类型object(即使没有显示的继承
	object类型),即所有的类都是“新式类”。

在这里插入图片描述


"""
新式类: 广度优先算法
经典类: 深度优先算法(py2中的部分类属于经典类)

python3所有的类都属于新式类。新式类的继承算法是广度优先。

# 分析多继承的相关代码
>pip install djangorestframework
from rest_framework import viewsets
viewsets.ModelViewSet
"""

class D(object):
    def hello(self):
        print('D')
class C(D):
    # def hello(self):
    #     print('C')
    pass
class B(D):
    pass
    # def hello(self):
    #     print('B')
class A(B, C):
    pass
    # def hello(self):
    #     print('A')
a = A()
a.hello()

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

4.6 私有属性和私有方法

- 默认情况下,属性在 Python 中都是“public”, 大多数 OO 语言提供“访问控制符”来限定成员函数的访问。
  在 Python 中,实例的变量名如果以 __ 开头,就变成了一个私有变量/属性
	(private),实例的函数名如果以 __ 开头,就变成了一个私有函数/方法(private)只
	有内部可以访问,外部不能访问
class Student:
    """父类Student"""
    def __init__(self, name, age, score):
        self.name = name
        self.age = age
        # 私有属性,以双下划线开头。
        # 工作机制: 类的外部(包括子类)不能访问和操作,类的内部可以访问和操作。
        self.__score = score

    def learning(self):
        print(f'{self.name}正在学习')

    def get_score(self):     ##类内部调用modify私有方法
        self.__modify_score()
        return  self.__score

    # 私有方法是以双下划线开头的方法,
    #工作机制: 类的外部(包括子类)不能访问和操作,类的内部可以访问和操作。
    def __modify_score(self):
        self.__score += 20

class MathStudent(Student):
    """MathStudent的父类是Student"""
    def get_score(self):
        self.__modify_score()
        return  self.__score

# 报错原因: 子类无法继承父类的私有属性和私有方法。
s1 =Student('张三', 18, 100)
score = s1.get_score()    ##运行结果为120
#s1 = MathStudent('张三', 18, 100)  #父类私有方法依旧不可以调用,会报错
#score = s1.get_score()
print(score)     

5.多态特性

  • 多态(Polymorphism)按字面的意思就是“多种状态”。在面向对象语言中,接口的多种不同的实现方式即为多态。通俗来说: 同一操作作用于不同的对象,可以有不同的解释,产生不同的执行结果。
  • 多态的好处就是,当我们需要传入更多的子类,只需要继承父类就可以了,而方法既可以直接
    不重写(即使用父类的),也可以重写一个特有的。这就是多态的意思。调用方只管调用,不管
    细节,而当我们新增一种的子类时,只要确保新方法编写正确,而不用管原来的代码。这就是著
    名的“开闭”原则:
    1.对扩展开放(Open for extension):允许子类重写方法函数
    2.对修改封闭(Closed for modification):不重写,直接继承父类方法函数

6.项目案例:栈的封装

  • 栈是限制在一端进行插入操作和删除操作的线性表(俗称堆栈),允许进行操作的一端称为“栈顶”
    ,另一固定端称为“栈底”,当栈中没有元素时称为“空栈”。向一个栈内插入元素称为是进栈,push;
    从一个栈删除元素称为是出栈,pop。特点 :后进先出(LIFO)

在这里插入图片描述
在这里插入图片描述

class Stack(object):
    """栈的封装[1, 2, 3, 4]"""

    def __init__(self):
        self.stack = []

    def push(self, value):
        """入栈"""
        self.stack.append(value)
        print(f"入栈元素为{value}")

    def pop(self):
        """出栈"""
        if self.is_empty():
            raise  Exception("栈为空")
        item = self.stack.pop()
        print(f"出栈元素为{item}")
        return  item

    def is_empty(self):
        """判断栈是否为空"""
        return  len(self.stack) == 0

    def top(self):
        """返回栈顶元素"""
        if self.is_empty():
            raise  Exception("栈为空")
        return  self.stack[-1]

    def __len__(self):
        """魔术方法, len(object)自动执行的方法"""
        return  len(self.stack)

if __name__ == '__main__':
    stack = Stack()
    stack.push(1)
    stack.push(2)
    stack.push(3)
    print(len(stack))  # 3
    stack.pop()
    print(stack.is_empty()) # False
    print(stack.top())  # 2

7. 项目案例:队列的封装

  • 队列是限制在一端进行插入操作和另一端删除操作的线性表,允许进行插入操作的一端称为“队尾”
    ,允许进行删除操作的一端称为“队头”,当队列中没有元素时称为“空队”。特点 :先进先出(FIFO)。
    在这里插入图片描述
    在这里插入图片描述
class Queue(object):
    """
    队列的封装
    1.列表左侧作为队尾
    2.列表右侧作为队头
    """

    def __init__(self):
        self.queue = []

    def enqueue(self, value):
        """入队"""
        self.queue.insert(0,value)
        print(f"入队元素为{value}")

    def dequeue(self):
        """出队"""
        if self.is_empty():
            raise  Exception("队列为空")
        item = self.queue.pop()
        print(f"出队元素为{item}")
        return  item

    def is_empty(self):
        """判断栈是否为空"""
        return  len(self.queue) == 0

    def first(self):
        """返回对头元素"""
        if self.is_empty():
            raise  Exception("队为空")
        return  self.queue[-1]
    def last(self):
        """返回对尾元素"""
        if self.is_empty():
            raise  Exception("队为空")
        return  self.queue[0]
    def __len__(self):
        """魔术方法, len(object)自动执行的方法,获取队列的长度"""
        return  len(self.queue)

if __name__ == '__main__':
    stack = Queue()
    stack.enqueue(1)
    stack.enqueue(2)
    stack.enqueue(3)
    print(stack.is_empty())  # False
    print(len(stack))
    stack.dequeue()  #1出队,剩32
    print(stack.first())   #2
    print(stack.last())  # 3

在这里插入图片描述

8. 二叉树的封装

"""
二叉树:
    https://www.cnblogs.com/polly333/p/4740355.html
"""
class Node(object):
    """节点类"""
    def __init__(self, val=None, left=None, right=None):
        self.val = val
        self.left = left
        self.right = right

class BinaryTree(object):
    """封装二叉树"""
    def __init__(self, root):
        self.root = root

    def pre_travel(self, root):
        """先序遍历: 根左右"""
        if (root != None):
            print(root.val)
            self.pre_travel(root.left)
            self.pre_travel(root.right)

    def in_travel(self, root):
        """中序遍历: 左根右"""
        if (root != None):
            self.in_travel(root.left)
            print(root.val)
            self.in_travel(root.right)

    def last_travel(self, root):
        """后序遍历: 左右根"""
        if (root != None):
            self.last_travel(root.left)
            self.last_travel(root.right)
            print(root.val)

if __name__ == '__main__':
    node1 = Node(1)
    node2 = Node(2)
    node3 = Node(3)
    node4 = Node(4)
    node5 = Node(5)
    node6 = Node(6)
    node7 = Node(7)
    node8 = Node(8)
    node9 = Node(9)
    node10 = Node(10)

    bt = BinaryTree(root=node1)
    node1.left = node2
    node1.right = node3
    node2.left = node4
    node2.right= node5
    node3.left = node6
    node3.right = node7
    node4.left = node8
    node4.right = node9
    node5.left = node10
    # 先序遍历
    bt.pre_travel(node1)

8.1 先序遍历

在这里插入图片描述

8.2 中序遍历(箭头不看)

在这里插入图片描述

8.3 后序遍历(箭头不看)

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/qwerty1372431588/article/details/114161234