Python3基础(6)面向对象编程

时间:2022-12-20 14:03:26

---------------个人学习笔记---------------

----------------本文作者吴疆--------------

---配套视频个人购买,可有偿提供---

------点击此处链接至博客园原文------

 

1.编程范式

面向过程编程(procedural programming):如果只是写一些简单脚本,做一些一次性任务,用面向过程的方式更好,如果要处理的任务是复杂的,且需要不断迭代和维护,那还是用面向对象更方便。

面向对象编程(object-oriented programming ):OOP利用“类”和“对象”来创建各种模型来实现对真实世界的描述,可以使程序的维护和扩展更为简单,可大大提高开发效率,可使他人便于理解代码逻辑。

2.面向对象的3个核心特性

--------------------封装 Encapsulation---------------------

类变成了一个胶囊或容器,内部包含类的数据和方法。

------------------------继承 Inheritance---------------------- 

一个类可以派生出子类,在父类中定义的属性、方法自动被子类继承。

------------------------多态 Polymorphism-------------------

一个接口,多种实现,即一个基类派生了不同的子类,且每个子类在继承了同样的方法名的同时又对父类的方法做了不同的实现。

------------------------类 Class-----------------------------

一个类是对一类具有相同属性的对象的抽象、蓝图、原型,在类中定义了这些对象都具备的属性、共同的方法。

-----------------------------对象 Object----------------------

一个对象是一个类的实例化,一个类必须经过实例化后才能在程序中被调用,一个类可以实例化多个对象,每个对象也可以有不同的属性。

3.面向对象编程例子

--------------构造函数__init__(self)------------------

作用:在类的实例化时做一些类的初始化工作

实例化实质:d1 = Dog("small black")同时传入d1变量名和"small black"传入,并对r1赋值,如r1.name = "small black",相当于d1 = Dog(d1,"small black"),因此__init__()对应会有self形参,实例化时并不copy类内定义的函数,仍在类的内存中,为了使类的实例化对象能够调用函数,函数声明中也包括了self形参,d1.bulk()相关于Dog.bulk(d1)。

类变量与实例变量:对于同名变量,默认优先找实例变量,如果没有才找类变量

# -*- coding:utf-8 -*-
# Author: WUJiang
# 面向对象编程


class Dog:
    name = "我是类变量"  # 类变量
    n = 123  # 类变量
    def __init__(self, name):  # 构造函数
        self.name = name  # 实例变量(静态属性),作用域为实例本身

    def bulk(self):  # 类的方法(动态属性)
        print("%s wang wang wang" % self.name)


d1 = Dog("small black")
d1.bulk()  # small black wang wang wang

d2 = Dog("small white")
# 对于同名变量,优先找实例变量,如果没有才找类变量
print(Dog.name, d2.name)  # 我是类变量 small white
print(Dog.n, d2.n)  # 123 123

类变量的作用:共用的属性,可节省内存开销,比如一类国籍均为“中国”的人。

析构函数:__del__(self)在实例释放、销毁时(如删除实例或整个程序执行完退出)自动执行的,通常做一些收尾工作,如关闭一些数据库链接、打开的临时文件。

4.私有属性与私有方法

私有属性 self.__name = name 加__即变为私有,外部不可再访问该属性,只允许内部访问。

私有方法 同理,加__,如def __bulk(self)

# -*- coding:utf-8 -*-
# Author: WUJiang


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

    def __bulk(self):
        print("%s wang wang wang" % self.name)

    def info_age(self):  # 内部访问私有属性
        print("%s is %d years old" % (self.name, self.__age),)

    def func_bulk(self):  # 内部调用私有方法
        self.__bulk()

    def __del__(self):
        print("我是析构函数")


d1 = Dog("black", 2)
# 外部不能访问私有属性
# print(d1.__age)  # error: 'Dog' object has no attribute '__age'
d1.info_age()  # black is 2 years old
# 外部不能调用私有方法
# d1.__bulk()  # 'Dog' object has no attribute '__bulk'
d1.func_bulk()  # black wang wang wang

5.类的继承

# -*- coding:utf-8 -*-
# Author: WUJiang
# 单继承


#class People:  # 经典类
class People(object):  # 新式类
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def eat(self):
        print("%s can eat" % self.name)

    def drink(self):
        print("%s can drink" % self.name)


class Man(People):
    def __init__(self, name, age, salary):  # 为了输入多个形参,需要重构构造函数
        # People.__init__(self, name, age)  # 也可以写成下面这句,经典类写法
        super(Man, self).__init__(name, age)  # 新式类写法
        self.salary = salary

    def talk(self):
        print("%s can talk" % self.name)

    def drink(self):
        People.drink(self)  # 与父类同名函数增加新功能,重构
        print("子类drink被调用")


m1 = Man("wujiang", 24, 9000)
m1.eat()
m1.drink()

注意:经典类与新式类的写法、为在实例化时能在父类定义基础上新增传入参数需要重构__init__(),为在父类基础上增加同名函数功能需在子类中对对应函数进行重构,super()的写法可以避免在(单/多继承)时父类名称更换时需要大量修改源码。

# -*- coding:utf-8 -*-
# Author: WUJiang
# 多继承


#class People:  # 经典类
class People(object):  # 新式类
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def eat(self):
        print("%s can eat" % self.name)

    def drink(self):
        print("%s can drink" % self.name)


class Relation(object):
    def make_friends(self, obj):
        print("%s makes friends with %s" % (self.name, obj.name))


class Man(People, Relation):  # 多继承!!!执行顺序从左People到右Relation
    def __init__(self, name, age, salary):  # 为了输入多个形参,需要重构构造函数
        People.__init__(self, name, age)  # 也可以写成下面这句,经典类写法
        # super(Man, self).__init__(name, age)  # 新式类写法
        self.salary = salary

    def talk(self):
        print("%s can talk" % self.name)

    def drink(self):
        People.drink(self)  # 与父类同名函数增加新功能,重构
        print("子类drink被调用")


m1 = Man("zhangsan", 24, 9000)
m2 = Man("lisi", 20, 6000)
m1.make_friends(m2)  # zhangsan makes friends with lisi

注意:多继承时,父类名顺序影响执行结果。

6.经典类与新式类的多继承顺序区别

 -*- coding:utf-8 -*-
# Author: WUJiang
# 继承顺序

class A:
    def __init__(self):
        print("A")

class B(A):
    def __init__(self):
        print("B")

class C(A):
    def __init__(self):
        print("C")

class D(B, C):
    def __init__(self):
        print("D")

d = D()

若存在上述继承关系,每个类内定义了构造函数,不注释时将只执行D中构造函数打印“D”,若注释D中构造函数将执行B(B在C左边)中构造函数打印“B”,若再注释B中构造函数将执行C中构造函数打印“C”,再注释将只执行A中构造函数打印"A",因此,继承顺序为DBCA,也被称为广度优先。广度优先(Python3经典类、新式类均按广度优先继承),深度优先(Python2经典类按深度优先DBA来继承、新式类按广度优先来继承)

7.多态

多态性是允许将父对象设置成为和一个或更多子对象相等的技术,赋值之后,父对象就可以根据当前赋值给他的子对象的特性以不不同方式运作,即允许将子类类型的指针赋值给父类类型的指针。封装可以隐藏实现细节,使得代码可以模块化,继承可以扩展已存在的代码块,他们的目的都是为了代码重用,而多态可以实现接口重用,类在继承和派生的时候,可以保证使用“家谱”中任一类的实例的某一属性时的正确调用。

# -*- coding:utf-8 -*-
# Author: WUJiang
# 多态

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

    def talk(self):
        # raise NotImplementedError("Subclass must implement abstract method")
        pass

    @staticmethod
    def animal_talk(obj):  # @staticmethod就不用实例化(加self)
        obj.talk()

class Cat(Animal):
    def talk(self):
        print( "Miao!")

class Dog(Animal):
    def talk(self):
        print("Wang!")

"""
c = Cat("white")
c.talk()
d = Dog("black")
d.talk()
"""
# 一种接口,多种实现
# Python间接实现多态
c = Cat("white")
d = Dog("black")
Animal.animal_talk(c)
Animal.animal_talk(d)

07