Python-8.面向对象程序设计

砂糖桑
Aug 12, 2022
Last edited: 2022-8-14
type
Post
status
Published
date
Aug 12, 2022
slug
python-8
summary
面向对象的思想在C++中已经涉及得很多了,所以在Python面向对象的学习中主要学习Python语言面向对象的一些特性,如类对象、实例对象、类属性、实例属性等,并进一步理解Python中“一切皆对象”。
tags
Python课堂笔记合集
category
Python
icon
password
Property
Aug 14, 2022 11:17 AM
面向对象不仅是一种语言特性,更是一种程序设计思想。在C++中已经研究了很多面向对象的思想了,所以这篇文章的学习中更注重的是”Python中的面向对象”。
面向对象的三大核心内容是封装、继承与多态。封装就是使用类,继承与多态也要使用类来实现。

类与封装

Python中定义一个类的方式是:
class ClassName: 属性 方法
使用类可以实例化很多个对象或实例:instance_name=ClassName()

两种对象与两种属性

两种对象

Python中一切皆对象——类其实也是对象。创建类其实就是创建了一个类对象,它由type类实例化而来:
notion image
使用类实例化的对象或实例也是对象,它是对应的类类型的对象,这点很好理解。
notion image
类对象只有一个,而使用类实例化的实例对象可以有很多个。

两种属性

Python中与类相关的有类对象与实例对象,对应的,类中的属性有类属性与实例属性。
类属性就是在类的公共部分定义的属性,类对象和实例对象都能访问到类属性;实例属性在方法(实例方法)中定义,前面使用self加以限定,表明这是一个实例属性:
class Student: amount=100 # 类属性 def __init__(self,name='Sato',age=18): self.name=name # 实例属性 self.age=age
为什么Python要分类属性和实例属性,而不像C++一样直接用一个属性?主要目的还是为了更好地模拟现实世界:类属性可以设置为一个群体的属性,如班里学生的数量;实例属性则设置为个体的属性,如学生的姓名、年龄等。
  1. 对于类对象和每个实例对象,类属性是公用的。改一个其他都会变。
    1. # 通过类对象修改类属性,实例对象再访问类属性就会发生变化 std1=Student(name='Sato') print(std1.amount) # 输出100 Student.amount+=1 print(std1.amount) # 输出101
  1. 类对象访问不到实例属性,且对于不同的实例,实例属性不互通。
    1. # 两个实例对应的实例属性不互通 std1=Student(name='Sato') std2=Student(name='Prexius') print(std1.name+' VS '+std2.name)
  1. 使用实例对象.属性可以修改或添加实例属性——如果存在这个实例属性,那么就修改;如果不存在,那么就添加这个实例属性。需要注意如果实例属性和类属性重名,那么实例属性会覆盖掉类属性。只有删除这个实例属性才能恢复对类属性的访问。
    1. 在第一点中没有通过实例对象来修改实例属性正是因为这个原因:
      std1=Student(name='Sato') std1.amount+=1 print(Student.amount) # 依然输出100 print(std1.amount) # 输出101
      std1.amount相当于添加了一个名为amount的实例属性覆盖掉了原来的类属性,所以二者不再互通了。
      要验证通过实例对象也能修改类属性,需要使用类方法:
      class Student: amount=100 # 类属性 def __init__(self,name='Sato',age=18): self.name=name # 实例属性 self.age=age @classmethod def add(cls): cls.amount += 1 std1=Student(name='Sato') std1.add() print(Student.amount) # 输出101 print(std1.amount) # 输出101

一切皆对象

如何理解python中一切皆对象呢?看type,object,class的关系
在Python的学习中我们肯定会听到一句话: 「python中一切皆对象」 。 如果再接着学习下去的话,我们就会接触到Python中的type, object, class等概念。网上也有不少文章阐述了这三者之间的关系,但是在看了大部分文章之后我还是似懂非懂,感觉就像有什么东西卡在了喉咙一直咽下不去一样。 于是为了能让自己晚上顺利吃上饭,我立马对着搜索引擎就是一顿操作,终于赶在外卖小哥打响我电话之前,咽下了这几个如鲠在喉的概念,舒服!趁着外卖小哥上楼这会,分享下我学习研究后的理解吧。 python作为面向对象的语言之一,符合一切基于面向对象理念的设计。在面向对象的体系中,对象存在着两种关系。 简单来说即子类继承于父类,子类拥有其自身及父类的方法和属性,同名的子类方法和属性将会覆盖父类的方法和属性。语义化的理解栗子为: 「蛇」类继承自「爬行动物类」,所以「蛇是一种爬行动物」,英文说「snake is a kind of reptile」。在python中的继承关系可以简单表示如下: class Reptile: title = '爬行动物' def crawl(self): print(self.title) class Snake(Reptile): def crawl_a(self): print(self.title) p = Snake() p.crawl() # 爬行动物 p.crawl_a() # 爬行动物 print(p.title) # 爬行动物 上面这个栗子中,子类Snake继承了父类Reptile的title属性和crawl方法,使得类B的实例对象b能调用父类的a属性和get方法。另外,如果要查看一个类(class)的父类,可以使用 class_name.__bases__ 的形式来查看。 如果继承关系好比父与子的关系,实例化关系则是一个从抽象到具体的过程。实例是某个类中具体的个体的表示。语义化的理解栗子为: 「小p是一条蛇」,「蛇」是一个分类,「小p」则是这个分类中的一个具体的个体。英文说「小p is an instance of snake」
如何理解python中一切皆对象呢?看type,object,class的关系
notion image
Python中一切皆对象。如这些常用类型和类一样也是type的实例。
有这样一个很好的例子:(__base__代表基类)
class A:pass a = A() print(A.__bases__) # (<class 'object'>,) print(object.__bases__) # () print(type(a)) # <class '__main__.A'> print(type(A)) # <class 'type'> print(type(object)) # <class 'type'> print(type.__bases__) # (<class 'object'>,)
可以推出原型链和继承链:
notion image
结论是所有对象都是type的实例,所有对象都是继承于object的子类。
不太懂为什么,还需要继续研究。先记住这个结论吧。

三种方法

类中可以有三种:实例方法、类方法和静态方法。

实例方法

类中直接定义的方法就是实例方法,它有一个默认的形参self指向调用这方法的实例,类对象不能调用实例方法。
class Student: __amount = 100 # 类属性 def __init__(self, name='Sato', age=18): self.name = name # 实例属性 self.age = age def get_amount(self): # 实例方法 return self.__amount std = Student() print(std.get_amount()) # 实例对象调用实例方法 try: Student.get_amount() except: print("类对象不能调用实例方法")

类方法

类方法使用@classmethod修饰符加以限定,类方法有一个默认的形参cls,指向类对象本身。所以类对象和实例对象都可以调用类方法,实例方法主要使用类方法来修改类属性的值。
class Student: amount=100 # 类属性 def __init__(self,name='Sato',age=18): self.name=name # 实例属性 self.age=age @classmethod def add(cls): cls.amount += 1 std1=Student(name='Sato') std1.add() print(Student.amount) # 输出101 print(std1.amount) # 输出101

静态方法

静态方法没有cls,也没有self。它和类外面的函数的区别是它可以访问到类中的私有属性,但必须使用类名.类属性的方式。类对象和实例对象都可以调用。
class Student: __amount = 100 # 类属性 def __init__(self, name='Sato', age=18): self.name = name # 实例属性 self.age = age @staticmethod def get_amount(): return Student.__amount print(Student.get_amount()) std = Student() print(std.get_amount())
[Python]实例方法、类方法、静态方法
文章目录: 一、类和实例: 简单阐述类和实例的概念 二、初探实例方法、类方法、静态方法 从三种方法的 定义形式上 比较其不同之处 三、再探实例方法、类方法、静态方法 从三种方法的 调用形式上 比较其异同 四、类方法使用场景 五、静态方法使用场景 六、小结 类是创建实例的模板,而实例则是一个一个具体的对象,各个实例拥有的数据都互相独立,互不影响。以Dog类为例,类就像一个对象工厂,可以生产一个或多个实例对象。 先直接上代码: 上述MyClass类中分别定义了三种不同类型的方法。 这三种方法在 定义上(形式上) 有如下的不同之处: 实例方法是一个普通的函数,类方法和静态方法都是通过函数装饰器的方式实现的; 实例方法需要传入self,类方法需要传入cls参数,静态方法无需传入self参数或者是cls参数(但不等同于不能传入参数) 注意:self参数和cls参数的区别,下文会提到。 下面分别尝试调用三个方法: 1️⃣当实例化时,my_class指向实例化对象: 2️⃣当调用实例方法(instance method)时,self参数指向的是刚刚实例化出的my_class实例对象: 3️⃣当调用类方法(class method)时,cls参数指向的是一开始定义的MyClass类对象(注意不是实例对象): 4️⃣当调用静态方法(static method)时: 本小节内容,主要区别了实例方法、类方法、静态方法的定义形式,以及区别了self、cls参数的指向对象。 注意:不管self参数,还是cls参数,都是一种约定俗成的用法,其实是可以使用其他的参数名代替。但是不建议使用其他参数名,毕竟代码不是只是写给自己看的。 上文都是通过实例化出的对象,对三种方法进行的调用,并且都得到了我们想要的结果;如果不通过实例化对象,而是直接通过类对象(Python中一切皆对象,包括类本身)调用,会有什么结果呢? 1️⃣当通过类对象调用实例方法时,直接报错,提示缺参数self。 因为 点语法 会将类对象MyClass传给instance_method()方法,但是instance_method()参数需要指向的是一个实例对象,而非类对象。 2️⃣ 当通过类对象调用类方法时,这里的结果与通过实例对象调用是完全一样的。 Python官方文档的解释:It can be called either on the class (such as C.f()) or on an instance (such as C().f()).
[Python]实例方法、类方法、静态方法

构造与析构

Python的构造函数是__init__,析构函数是__del__。分别用于初始化对象时赋值和清除对象的空间。
class Student: def __init__(self): print("执行了构造函数") def __del__(self): print("执行了析构函数") std = Student() # 执行了构造函数 del std # 执行了析构函数
此外还有一个__new__方法,它的作用是创建并返回对象。与__init__相比,__new__完成的是初始化的工作,而__init__完成的是初始化后的赋值工作。

继承与派生

继承

一个类的默认父类是object,可以通过__base__来验证。
所以说,前面定义Student类写完整应该如下:
class Student(object): pass
将父类放在括号里面就行了。
如果父类有构造函数,子类也有构造函数,那么需要在子类的构造函数中手动调用父类的构造函数。
class People(object): def __init__(self,name,age): self.name=name self.age=age class Student(People): __amount=100 def __init__(self,name,age,std_id): People.__init__(self,name,age) # 调用父类的构造函数 self.std_id=std_id std1=Student('Sato',20,'202001')

派生

可以在子类中重写父类方法。
class People(object): def __init__(self,name,age): self.name=name self.age=age def identity(self): print("我是人") class Student(People): __amount=100 def __init__(self,name,age,std_id): People.__init__(self,name,age) self.std_id=std_id def identity(self): print("我是学生") std1=Student('Sato',20,'202001') std1.identity() # 我是学生
Python-7.函数与模块示例文章-密码1234