栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Python

第六章:Python面向对象基础

Python 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

第六章:Python面向对象基础

  • 菜鸟教程

文章目录
  • 一:类
    • (1)定义一个类并实例化
    • (2)实例变量与类变量
    • (3)构造函数
    • (4)方法
      • A:实例方法
      • B:类方法
      • C:静态方法
  • 二:类中成员的可见性
    • (1)通过方法控制变量
    • (2)成员的公开与私有
  • 三:继承

关于 面向对象这个概念这里我不想再谈了(因为我已经说了无数遍,都有点反胃了),如果你对这个概念不清楚或者了解不深,可以移步本人其他专栏中去学习(尤其是软件工程和C++),这里我只给出一些核心概念

①:面向对象和面向过程的区别(通俗解释)

  • 采用面向过程思想设计出来的程序就像一份蛋炒粉,米饭和炒菜混合完全,入围均匀,不会像盖浇饭那样出现菜多饭少或菜少饭多的情况;但是如果你不想吃蛋炒饭,想吃肉炒饭了,那么必须推倒重做
  • 采用面向对象设计出来的程序就像一份盖浇饭,也即米饭和盖菜是分别制作好的,盖浇饭虽然没有蛋炒饭那样味道均匀,但是它可以随意组合,形成各种各样的盖浇饭

②:面向对象和面向过程的区别(专业解释)

  • 面向过程编程就是分析出解决问题的步骤,然后把这些步骤一步步实现,使用使自顶向下依次调用即可
  • 面向对象编程就是把问题分解为各个对象,建立对象的目的不是为了完成一个步骤,而是为了描述某个事物在解决问题的步骤中的行为

③:面向对象的一些核心概念

  • 类(Class): 用来描述具有相同的属性和方法的对象的集合。它定义了该集合中每个对象所共有的属性和方法。对象是类的实例。
  • 方法:类中定义的函数。
  • 类变量:类变量在整个实例化的对象中是公用的。类变量定义在类中且在函数体之外。类变量通常不作为实例变量使用。
  • 数据成员:类变量或者实例变量用于处理类及其实例对象的相关的数据。
  • 方法重写:如果从父类继承的方法不能满足子类的需求,可以对其进行改写,这个过程叫方法的覆盖(override),也称为方法的重写。
  • 局部变量:定义在方法中的变量,只作用于当前实例的类。
  • 实例变量:在类的声明中,属性是用变量来表示的,这种变量就称为实例变量,实例变量就是一个用 self 修饰的变量。
  • 继承:即一个派生类(derived class)继承基类(base class)的字段和方法。继承也允许把一个派生类的对象作为一个基类对象对待。例如,有这样一个设计:一个Dog类型的对象派生自Animal类,这是模拟"是一个(is-a)"关系(例图,Dog是一个Animal)。
  • 实例化:创建一个类的实例,类的具体对象。
  • 对象:通过类定义的数据结构实例。对象包括两个数据成员(类变量和实例变量)和方法

一:类 (1)定义一个类并实例化
  • 提前说明:self并不是Python的关键字,你当然可以使用其他名字

在Python中定义一个类并实例化的方法如下

  • 它是一个Student类,代表了学生信息

(2)实例变量与类变量

如果你没有任何面向对象编程语言基础(例如C++、Java等),你可能会对其中的self感到奇怪。这是因为,类中的变量分为实例变量和类变量,实例变量针对某个对象,而类变量针对的是整个类。在Python中,当实例化后的对象调用方法时,系统会自动将对象本身传入函数,传入时默认就是第一个参数,只不过不需要你手动传入,但是方法在接受时必须在第一个参数的位置处写上self(其他字眼也可以)用于接受对象

  • 为什么要传入对象本身:这是因为对象与对象之间的区别就在于变量的不同,每个对象在借用方法操作变量只能操作属于自己的那份变量,所以需要传入对象本身

  • 其实这和C++中的this指针一个道理,只不过有所区别: 在C++中,当实例化后的对象调用函数时,系统也会将实例化后的对象传入函数,而且这个传递过程中是自动不可见的,只有在函数内部才可以显示的使用this。并且方法的第一个参数可以不显示的写this

另外,C++类里面定义的函数默认都是给实例化后的对象调用的,如果不想给它调用,那么必须加上staic关键字,变为静态成员函数,此时可以通过类调用函数;而Python中并没有强制说明某个函数应该给类还是应该给对象用,它只是规定:如果类调用函数那么就是普通的调用,如果对象调用,那么就自动传参。现在,大家应该也能明白为什么下面的例子中又不需要写self了吧

  • 是因为类在调用方法,并不是某个对象在调用,因此不需要传参,也不需要self
class Student:
    def test(x):
        print(x)


student = Student()
Student.test(123)  # 类在调用
#  student.test(1) 如果你写这一句,就会报错,提示了多传入了参数


之所以把类中的变量分为类变量和实例变量,也是为了尽可能模仿人类世界中的各种行为。例如Student这个类,将sum设置为类变量,用于表示某班学生总数就比较合适;而对于name、age这种变量,设置为实例变量就比较合适,因为特定的学生是有姓名和年龄的

(3)构造函数

如下语句在执行后,会实例化出三个对象,从我们的角度来看,这个三个对象是一模一样的

  • 注意:这里的一模一样指的是对于用户来说,它代表的意义是相同的,但是在计算机内部,很显然不是同一个东西
class Student:
    name = ' '
    age = 0

    def show_info(self):
        print('name: ' + self.name)
        print('age: ' + str(self.age))


student1 = Student()  # 对象1
student2 = Student()  # 对象2
student3 = Student()  # 对象3

但我们知道,每个学生有其各自的name和age,因此使用同一个name和age显然不合适,那么如何实现 实例化时传入不同的参数形成不同的对象呢? 其实,这也就是类中的一个 特殊方法 __init__() 的作用所在。用法如下,其中 __init__()会在类被实例化时自动调用

  • 其作用本质就是构造函数
  • __init__()可以直接被调用,但是不会也不能有返回值
class Student:
    name = ' '
    age = 0

    def __init__(self, name, age):  # 构造函数
        self.name = name
        self.age = age

    def show_info(self):
        print('name: ' + self.name)
        print('age: ' + str(self.age))


student1 = Student('张三', 27)  # 对象1
student2 = Student('李四', 25)  # 对象2
student1.show_info()
student2.show_info()

上面的self.name是实例变量,而name=' '、age=0则是类变量;实例变量与特定对象相关,是某个对象私有的

class Student:
    name = ' '
    age = 0

    def __init__(self, name, age):  # 构造函数
        self.name = name
        self.age = age

    def show_info(self):
        print('name: ' + self.name)
        print('age: ' + str(self.age))


student1 = Student('张三', 27)  # 对象1
student2 = Student('李四', 25)  # 对象2
print(student1.age)  # 实例变量
print(student2.age)  # 实例变量
print(Student.age)  # 类变量

(4)方法 A:实例方法

变量在类中可以分为类变量和实例变量,相应的,放在在类中也同样分为实例方法和类方法,如果类中某个方法第一个参数处是self(或其他),就称其为实例方法

实例方法访问实例变量是再正常不过的事情了,并且我们也经常这样干。那实例方法是否可以访问类变量呢?答案也是可以的,两种方法如下

class Student:
    name = ' '
    age = 0

    def __init__(self, name, age):  # 构造函数
        self.name = name
        self.age = age

    def show_info(self):
        print('name: ' + self.name)
        print('age: ' + str(self.age))
        
    def class_var(self):
        print(Student.age)  # 方法一
        print(self.__class__.age)  # 方法二


student1 = Student('张三', 27)  # 对象1
student1.class_var()

下面是一个很典型的例子,用于统计一个类到底实例化出了多少个对象

class Student:
    sum = 0  # 用于统计学生总数
    name = ' '
    age = 0

    def __init__(self, name, age):  # 构造函数
        self.name = name
        self.age = age
        Student.sum += 1  # 构造函数每调用一次人数+1

    def show_info(self):
        print('name: ' + self.name)
        print('age: ' + str(self.age))


student1 = Student('张三', 27)  # 对象1
student2 = Student('李四', 23)  # 对象2
student3 = Student('王麻子', 26)  # 对象3
student4 = Student('张鹏', 25)  # 对象4
print("当前学生总数为:" + str(Student.sum))

B:类方法

类方法的定义格式与实例方法有些不同,需要用到装饰器的知识,在这里大家只需记住这样定义就好

@classmethod
def class_method(cls):
    pass

显而易见,类方法就是专门用于操作类变量的,它与对象无关

class Student:
    sum = 0  # 用于统计学生总数
    name = ' '
    age = 0

    @classmethod
    def add_student_number(cls):
        cls.sum += 1  # 学生人数+1

    def __init__(self, name, age):  # 构造函数
        self.name = name
        self.age = age

    def show_info(self):
        print('name: ' + self.name)
        print('age: ' + str(self.age))


student1 = Student('张三', 27)  # 对象1
Student.add_student_number()
student2 = Student('李四', 23)  # 对象2
Student.add_student_number()
student3 = Student('王麻子', 26)  # 对象3
Student.add_student_number()
student4 = Student('张鹏', 25)  # 对象4
Student.add_student_number()
print("当前学生总数为:" + str(Student.sum))

C:静态方法
  • 静态方法不要经常使用,可以用类方法代替

Python类中静态方法的定义格式如下

@staticmethod
def static_method():
	pass

对象和类都是可以调用静态方法的,且可以访问类变量

class Student:
    sum = 0  # 用于统计学生总数
    name = ' '
    age = 0

    @staticmethod
    def test_static():
        Student.sum += 1  # 静态方法访问类变量
        print("this is a static method")

    def __init__(self, name, age):  # 构造函数
        self.name = name
        self.age = age

    def show_info(self):
        print('name: ' + self.name)
        print('age: ' + str(self.age))


student1 = Student('张三', 27)  # 对象1
student1.test_static()  # 对象调用
Student.test_static()  # 类调用
print(Student.sum)

但是注意:静态方法和类方法中是不能访问实例变量的

  • 这一点和C++中的静态成员函数不能调用非静态成员变量是一个道理
二:类中成员的可见性

在面向对象中,类最大的作用就是封装,类的出现使代码分为了类内和类外 。在之前的代码中,我们可以发现有时我们会在类内访问变量或函数,有时又会在类外面进行操作,内部访问无需多言,但是外部访问却要考虑一个很重要的问题:能不能访问?有没有权限访问? 就拿Student类来说,访问它的name和age这是一个很正常的事情,无需限制,但是访问一些隐私信息,例如身份证号、银行卡时难道还要不加限制吗?

因此,前面所讲到的封装,只是一种不安全的封装,而类如果不具有安全性,那么类的那道围墙就形同虚设

(1)通过方法控制变量

面向对象方法学建议:不要让外部用户直接操作变量,需要操作时,可以将该变量置于某个方法下,控制其行为

  • 一旦别人给了负的分值,内部方法就可以禁止这种行为
class Student:
    sum = 0

    def __init__(self, name, age):
        self.name = name
        self.age = age
        self.score = 0  # 学生成绩

    def grading(self, score):  # 用于给学生打分
        if score < 0:
            return "数值错误"
        self.score = score

    def show_info(self):
        print("name:" + self.name)
        print("age:" + str(self.age))
        print("grade:" + str(self.score))


student1 = Student("张翼德", 28)
student1.grading(99)
student1.show_info()

(2)成员的公开与私有

上面的操作还是不能解决本质问题,因为你还是可以在外部修改成员

class Student:
    sum = 0

    def __init__(self, name, age):
        self.name = name
        self.age = age
        self.score = 0  # 学生成绩

    def grading(self, score):  # 用于给学生打分
        if score < 0:
            return "数值错误"
        self.score = score

    def show_info(self):
        print("name:" + self.name)
        print("age:" + str(self.age))
        print("grade:" + str(self.score))


student1 = Student("张翼德", 28)
student1.score = -1
student1.show_info()

如果要彻底解决问题,那么必须要对成员本身的可见性进行标记,也即成员的公开(public)和私有(private)。但是在Python中却没有这样的关键字,他是通过在成员名字前是否有__(两个下划线)来判断它是公开还是私有的

  • 如果成员前面有__,那么就是私有的
  • 如果成员前面没有__,那么就是公开的

如下,把成员函数设置为私有,外部将无法访问

    def __grading(self, score):  # 用于给学生打分
        if score < 0:
            return "数值错误"
        self.score = score

但是对于实例变量,你会发现即便这样设置,仍然也是没有效果的

class Student:
    sum = 0

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

    def grading(self, score):  # 用于给学生打分
        if score < 0:
            return "数值错误"
        self.__score = score

    def show_info(self):
        print("name:" + self.name)
        print("age:" + str(self.age))
        print("score:" + str(self.__score))


student1 = Student("张翼德", 28)
student1.__score = 2  # 竟然不报错
print(student1.__score)  # 而且还打印成功

其实,这也是Python这门动态语言的一个很大的特性,当你在执行student1.__score时,其实已经为这个对象设置了一个新的实例变量__score

  • 虽然名字相同,但是这里的__score和类中的self.__score确实不是一个东西。原有的私有变量会直接被改名(私有变量其实已经被保护了)
三:继承

从面向对象方法学角度上讲,继承就是为了实现代码的复用,能够直接获得已经存在的某些性质或特征;观察现实世界继承无处不在,比如学生和人,学生一定是人,但人不一定是学生,因此人所具有的某些属性和方法,学生也必然具有

在Python中继承的一般格式如下:

  • 此时Student被称为子类;People被称为父类
class People:
    pass


class Student(People):
    pass

继承真的发挥作用了吗?在下面的例子中,People类中有类变量sum,Student类继承了People,但Student类中什么都没有写,但你会发现通过Student却可以访问到sum,这表明Student确实得到了People中的变量和方法

class People:
    sum = 0
    
    def __init__(self, name, age):
        self.name = name
        self.age = age
        
    def get_name(self):
        print(self.name)


class Student(People):
    pass


print(Student.sum)

实例化一个Student对象,也是正常继承了

class People:
    sum = 0

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

    def get_name(self):
        print(self.name)


class Student(People):
    pass


student1 = Student('老王', 27)
print(student1.name)
print(student1.age)
student1.get_name()


上面的例子并不能展示出继承的真正作用,因为学生不止具有人的基本属性,而且还有自己的一些特殊属性。因此继承中,子类也可以设定自己的属性和方法

下面的例子中Student子类设立了自己的构造函数,要求Student在实例化时除了要传入基本的name和age外,还要传入学生所在的school信息,那么正确的写法如下。可以发现,参数在传入时,属于父类的name和age要手动调用父类的构造函数进行赋值

  • 千万不要忘记self
class People:
    sum = 0

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

    def show_basic_info(self):
        print("Name:" + self.name)
        print("Age:" + str(self.age))


class Student(People):

    def __init__(self, name, age, school):
        People.__init__(self, name, age)  # 父类,特别注意self别忘记了
        self.school = school

    def show_student_info(self):
        print("Name:" + self.name)
        print("Age:" + str(self.age))
        print("school:" + self.school)


student1 = Student('老王', 27, '天河路小学')
student1.show_student_info()
print("-"*20)
student1.show_basic_info()

仔细观察, People.__init__(self, name, age)其本质仍然是类调用实例方法。这样写其实存在一个很大的问题,就是当继承关系变得很复杂或者说存在多个地方调用时,一旦继承关系改变,比如说换一个类那么这里的语句肯定得全部修改

所以正确的方式如下,也即采用super关键字,它会指代父类

class People:
    sum = 0

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

    def show_info(self):
        print("Name:" + self.name)
        print("Age:" + str(self.age))


class Student(People):

    def __init__(self, name, age, school):
        super(Student, self).__init__(name, age)  # 采用super关键字
        self.school = school

    def show_info(self):
        super(Student, self).show_info()  # 采用super关键字
        print("school:" + self.school)


student1 = Student('老王', 27, '天河路小学')
student1.show_info()

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/859184.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号