Python学习笔记

文章目录

  • Python学习笔记
  • 第十三站 接着找对象
    • 1. 封装
    • 2. 继承
    • 3. 方法重写
    • 4. object类
    • 5. 多态
    • 6. 特殊方法和特殊属性
    • 7. 类的赋值与拷贝
    • 8. 本章作业

课程笔记参考B站视频: Python全栈开发教程。


第十三站 接着找对象

本章重点讲解 面向对象的三大特征:封装、继承、多态,此外还会涉及方法重写、object类(重要),最后再讲一下特殊方法及特殊属性。

面向对象的三大特征语言本身没有关系,这是一种通用的编程思想。

封装: 提高程序的安全性。
继承: 提高代码的复用性。
多态: 提高程序的可扩展性和可维护性。

本章主要内容如图:

#mermaid-svg-Xjf1jrivlx3Pwn8v {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-Xjf1jrivlx3Pwn8v .error-icon{fill:#552222;}#mermaid-svg-Xjf1jrivlx3Pwn8v .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-Xjf1jrivlx3Pwn8v .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-Xjf1jrivlx3Pwn8v .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-Xjf1jrivlx3Pwn8v .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-Xjf1jrivlx3Pwn8v .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-Xjf1jrivlx3Pwn8v .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-Xjf1jrivlx3Pwn8v .marker{fill:#333333;stroke:#333333;}#mermaid-svg-Xjf1jrivlx3Pwn8v .marker.cross{stroke:#333333;}#mermaid-svg-Xjf1jrivlx3Pwn8v svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-Xjf1jrivlx3Pwn8v .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-Xjf1jrivlx3Pwn8v .cluster-label text{fill:#333;}#mermaid-svg-Xjf1jrivlx3Pwn8v .cluster-label span{color:#333;}#mermaid-svg-Xjf1jrivlx3Pwn8v .label text,#mermaid-svg-Xjf1jrivlx3Pwn8v span{fill:#333;color:#333;}#mermaid-svg-Xjf1jrivlx3Pwn8v .node rect,#mermaid-svg-Xjf1jrivlx3Pwn8v .node circle,#mermaid-svg-Xjf1jrivlx3Pwn8v .node ellipse,#mermaid-svg-Xjf1jrivlx3Pwn8v .node polygon,#mermaid-svg-Xjf1jrivlx3Pwn8v .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-Xjf1jrivlx3Pwn8v .node .label{text-align:center;}#mermaid-svg-Xjf1jrivlx3Pwn8v .node.clickable{cursor:pointer;}#mermaid-svg-Xjf1jrivlx3Pwn8v .arrowheadPath{fill:#333333;}#mermaid-svg-Xjf1jrivlx3Pwn8v .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-Xjf1jrivlx3Pwn8v .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-Xjf1jrivlx3Pwn8v .edgeLabel{background-color:#e8e8e8;text-align:center;}#mermaid-svg-Xjf1jrivlx3Pwn8v .edgeLabel rect{opacity:0.5;background-color:#e8e8e8;fill:#e8e8e8;}#mermaid-svg-Xjf1jrivlx3Pwn8v .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-Xjf1jrivlx3Pwn8v .cluster text{fill:#333;}#mermaid-svg-Xjf1jrivlx3Pwn8v .cluster span{color:#333;}#mermaid-svg-Xjf1jrivlx3Pwn8v div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-Xjf1jrivlx3Pwn8v :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}

本章内容
面向对象的三大特征
封装
继承
多继承
方法重写
多态
动态语言
关注对象的行为
静态语言
继承
方法重写
父类引用指向子类对象
object
所有类的父类
特殊方法new创建对象
特殊方法init初始化对象
特殊方法str描述对象

1. 封装

封装 可以提高程序的安全性。

  • 将数据(属性)和行为(方法)包装到类对象中。在方法内部对属性进行操作,在类对象的外部调用方法。这样,无需关心方法内部的具体实现细节,从而隔离了复杂度。
  • 在Python中没有专门的修饰符用于属性的私有(C++中使用private关键字),如果该属性不希望在类对象外部被访问,前边使用两个下划线。

下面是代码示例:

class Car:def __init__(self, brand='问界M7', engine='1.5T'):self.brand = brandself.__engine = engine # 不希望在类外访问def start(self):print('乘坐人员安全带检查完毕...')print('汽车状态检查完毕...')print('汽车点火...')print('汽车已完成启动')def info(self):print(self.brand, self.__engine)
print('-------演示封装的效果-------')
car1 = Car('问界M5')
car1.start()  # 不用关心具体是怎么实现的,只要一调用这个函数,车子就启动了
print('-------演示在类外调用属性-------')
car1.info()
print(car1.brand)
# print(car1.__engine) # AttributeError: 'Car' object has no attribute '__engine'
# print(dir(car1))  # 查看__engine的实际名称变成了什么
print(car1._Car__engine)  # 即,还是可以强制访问私有属性,靠程序员的自觉性
  • 运行结果
-------演示封装的效果-------
乘坐人员安全带检查完毕...
汽车状态检查完毕...
汽车点火...
汽车已完成启动
-------演示在类外调用属性-------
问界M5 1.5T
问界M5
1.5T

2. 继承

继承 可以提高代码的复用性。其语法格式为:

class 子类类名( 父类1, 父类2... )pass

如果一个类没有继承任何类,则默认继承object(相当于根节点的含义)。
Python支持多继承,定义子类时,必须在其构造函数中调用父类的构造函数

下面是代码示例:

'''演示类的继承'''
# 定义父类
class Person(object): # 可以不写objectdef __init__(self, name, age):self.name = nameself.age = agedef info(self):print(self.name, self.age)
# 定义子类
class Student(Person):def __init__(self, name, age, idnum):super().__init__(name, age) # 定义子类时,必须调用父类的构造函数self.idnum = idnumdef info1(self):print(self.name, self.age, self.idnum)
# 定义子类
class Teacher(Person):def __init__(self, name, age, teachofyear):super().__init__(name, age)self.teachofyear = teachofyeardef info1(self):print(self.name, self.age, self.teachofyear)stu1 = Student('张三', 15, '20220506')
teac1 = Teacher('张父', 35, 10)
print('--------调用父类里面的方法---------')
stu1.info()
teac1.info()
print('----------调用自己的方法-----------')
stu1.info1()
teac1.info1()
  • 运行结果
--------调用父类里面的方法---------
张三 15
张父 35
----------调用自己的方法-----------
张三 15 20220506
张父 35 10
#mermaid-svg-DgmVA0JRet4cSUZq {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-DgmVA0JRet4cSUZq .error-icon{fill:#552222;}#mermaid-svg-DgmVA0JRet4cSUZq .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-DgmVA0JRet4cSUZq .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-DgmVA0JRet4cSUZq .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-DgmVA0JRet4cSUZq .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-DgmVA0JRet4cSUZq .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-DgmVA0JRet4cSUZq .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-DgmVA0JRet4cSUZq .marker{fill:#333333;stroke:#333333;}#mermaid-svg-DgmVA0JRet4cSUZq .marker.cross{stroke:#333333;}#mermaid-svg-DgmVA0JRet4cSUZq svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-DgmVA0JRet4cSUZq .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-DgmVA0JRet4cSUZq .cluster-label text{fill:#333;}#mermaid-svg-DgmVA0JRet4cSUZq .cluster-label span{color:#333;}#mermaid-svg-DgmVA0JRet4cSUZq .label text,#mermaid-svg-DgmVA0JRet4cSUZq span{fill:#333;color:#333;}#mermaid-svg-DgmVA0JRet4cSUZq .node rect,#mermaid-svg-DgmVA0JRet4cSUZq .node circle,#mermaid-svg-DgmVA0JRet4cSUZq .node ellipse,#mermaid-svg-DgmVA0JRet4cSUZq .node polygon,#mermaid-svg-DgmVA0JRet4cSUZq .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-DgmVA0JRet4cSUZq .node .label{text-align:center;}#mermaid-svg-DgmVA0JRet4cSUZq .node.clickable{cursor:pointer;}#mermaid-svg-DgmVA0JRet4cSUZq .arrowheadPath{fill:#333333;}#mermaid-svg-DgmVA0JRet4cSUZq .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-DgmVA0JRet4cSUZq .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-DgmVA0JRet4cSUZq .edgeLabel{background-color:#e8e8e8;text-align:center;}#mermaid-svg-DgmVA0JRet4cSUZq .edgeLabel rect{opacity:0.5;background-color:#e8e8e8;fill:#e8e8e8;}#mermaid-svg-DgmVA0JRet4cSUZq .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-DgmVA0JRet4cSUZq .cluster text{fill:#333;}#mermaid-svg-DgmVA0JRet4cSUZq .cluster span{color:#333;}#mermaid-svg-DgmVA0JRet4cSUZq div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-DgmVA0JRet4cSUZq :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}

object
Person 父类
Student 子类
Teacher 子类
注:上图中箭头方向与实际相反,越靠近上面的类等级越高。

上图给出了代码中各个类的继承关系图。值得注意的是,若父类和子类的实例函数名称相同,那么会优先调用子类的实例函数(即下一节的方法重写)。并且,在一条很长的继承链上,若最后一级调用了一个自己没有的方法,那么编译器就会沿着继承链逐级向上查找,直到找到第一个同名的方法,并执行;若直到最顶级都没有找到,就抛异常。

多继承说的是一个类同时继承了两个以上的父类。假如要求C类同时继承A类和B类,下面则是完成这功能的代码示例以及继承关系图:

'''
多继承示例代码
'''
class A(object):pass
class B(object):pass
class C(A, B):pass
#mermaid-svg-k6rZkxF2vfmt79V7 {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-k6rZkxF2vfmt79V7 .error-icon{fill:#552222;}#mermaid-svg-k6rZkxF2vfmt79V7 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-k6rZkxF2vfmt79V7 .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-k6rZkxF2vfmt79V7 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-k6rZkxF2vfmt79V7 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-k6rZkxF2vfmt79V7 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-k6rZkxF2vfmt79V7 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-k6rZkxF2vfmt79V7 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-k6rZkxF2vfmt79V7 .marker.cross{stroke:#333333;}#mermaid-svg-k6rZkxF2vfmt79V7 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-k6rZkxF2vfmt79V7 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-k6rZkxF2vfmt79V7 .cluster-label text{fill:#333;}#mermaid-svg-k6rZkxF2vfmt79V7 .cluster-label span{color:#333;}#mermaid-svg-k6rZkxF2vfmt79V7 .label text,#mermaid-svg-k6rZkxF2vfmt79V7 span{fill:#333;color:#333;}#mermaid-svg-k6rZkxF2vfmt79V7 .node rect,#mermaid-svg-k6rZkxF2vfmt79V7 .node circle,#mermaid-svg-k6rZkxF2vfmt79V7 .node ellipse,#mermaid-svg-k6rZkxF2vfmt79V7 .node polygon,#mermaid-svg-k6rZkxF2vfmt79V7 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-k6rZkxF2vfmt79V7 .node .label{text-align:center;}#mermaid-svg-k6rZkxF2vfmt79V7 .node.clickable{cursor:pointer;}#mermaid-svg-k6rZkxF2vfmt79V7 .arrowheadPath{fill:#333333;}#mermaid-svg-k6rZkxF2vfmt79V7 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-k6rZkxF2vfmt79V7 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-k6rZkxF2vfmt79V7 .edgeLabel{background-color:#e8e8e8;text-align:center;}#mermaid-svg-k6rZkxF2vfmt79V7 .edgeLabel rect{opacity:0.5;background-color:#e8e8e8;fill:#e8e8e8;}#mermaid-svg-k6rZkxF2vfmt79V7 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-k6rZkxF2vfmt79V7 .cluster text{fill:#333;}#mermaid-svg-k6rZkxF2vfmt79V7 .cluster span{color:#333;}#mermaid-svg-k6rZkxF2vfmt79V7 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-k6rZkxF2vfmt79V7 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}

object
A类
C类
B类
注:上图中箭头方向与实际相反,越靠近上面的类等级越高。

3. 方法重写

如果子类对继承自父类的某个属性或方法不满意,可以在子类中对其(方法体)进行重新编写,称为方法重写。子类重写后的方法中可以通过super().xxx()调用父类中被重写的方法。

下面是代码示例:

# 定义父类
class Person(object): # 可以不写objectdef __init__(self, name, age):self.name = nameself.age = agedef info(self):print(self.name, self.age)
# 定义子类
class Student(Person):def __init__(self, name, age, idnum):super().__init__(name, age) # 定义子类时,必须调用父类的构造函数self.idnum = idnumdef info(self):print(self.name, self.age, self.idnum)
# 定义子类
class Teacher(Person):def __init__(self, name, age, teachofyear):super().__init__(name, age)self.teachofyear = teachofyeardef info(self):print(self.name, self.age, self.teachofyear)stu1 = Student('张三', 15, '20220506')
teac1 = Teacher('张父', 35, 10)
stu1.info()
teac1.info()
  • 运行结果
张三 15 20220506
张父 35 10

4. object类

object类是所有类的父类,因此所有类都有object类的属性和方法。

  • 内置函数dir()可以查看指定对象所有属性和方法。
  • object有一个_ _str_ _()方法, 用于返回一个对于“对象的描述”。str()经常用于print()方法,帮我们查看对象的信息,所以我们经常会对_ _str_ _()进行重写,来方便的查看我们想看到信息。

下面是代码示例:

'''演示object类'''
class Person(object): # 可以不写objectdef __init__(self, name, age):self.name = nameself.age = agedef __str__(self):return '我的名字是{0},今年{1}岁'.format(self.name, self.age) # 格式化字符串
print('------查看object()中的属性和方法-------')
ob1 = object()
print(type(ob1))
print(dir(object))
print('------查看object()中的属性和方法-------')
per1 = Person('张三', 24)
print(type(per1))
print(per1)
# 若没有重写__str()__,则返回<__main__.Person object at 0x000002823DBB69D0>
  • 运行结果
------查看object()中的属性和方法-------
<class 'object'>
['__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__',
'__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__',
'__lt__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__',
'__setattr__', '__sizeof__', '__str__', '__subclasshook__']
------查看object()中的属性和方法-------
<class '__main__.Person'>
我的名字是张三,今年24岁

注:运行结果中的断行是后期手动断行,控制台终端实际是只有一行。

5. 多态

简单地说,多态就是“具有多种形态”,它指的是:即便不知道一个变量所引用的对象到底是什么类型,仍然可以通过这个变量调用方法,在运行过程中根据变量所引用对象的类型,动态决定调用哪个对象中的方法。

因为Python中不需要用户自己定义变量的数据类型,于是很多人说“Python没有多态的概念”,但是Python其实也具备多态的特征。也就是说,C++中的多态针对同一个函数在定义时的不同类型的形式参数,Python中的多态是针对具有同名称类方法的不同类对象(个人观点)。

比如现在有如下结构的类对象树状图:

#mermaid-svg-3GQmWDBWmYiklNHi {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-3GQmWDBWmYiklNHi .error-icon{fill:#552222;}#mermaid-svg-3GQmWDBWmYiklNHi .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-3GQmWDBWmYiklNHi .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-3GQmWDBWmYiklNHi .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-3GQmWDBWmYiklNHi .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-3GQmWDBWmYiklNHi .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-3GQmWDBWmYiklNHi .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-3GQmWDBWmYiklNHi .marker{fill:#333333;stroke:#333333;}#mermaid-svg-3GQmWDBWmYiklNHi .marker.cross{stroke:#333333;}#mermaid-svg-3GQmWDBWmYiklNHi svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-3GQmWDBWmYiklNHi .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-3GQmWDBWmYiklNHi .cluster-label text{fill:#333;}#mermaid-svg-3GQmWDBWmYiklNHi .cluster-label span{color:#333;}#mermaid-svg-3GQmWDBWmYiklNHi .label text,#mermaid-svg-3GQmWDBWmYiklNHi span{fill:#333;color:#333;}#mermaid-svg-3GQmWDBWmYiklNHi .node rect,#mermaid-svg-3GQmWDBWmYiklNHi .node circle,#mermaid-svg-3GQmWDBWmYiklNHi .node ellipse,#mermaid-svg-3GQmWDBWmYiklNHi .node polygon,#mermaid-svg-3GQmWDBWmYiklNHi .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-3GQmWDBWmYiklNHi .node .label{text-align:center;}#mermaid-svg-3GQmWDBWmYiklNHi .node.clickable{cursor:pointer;}#mermaid-svg-3GQmWDBWmYiklNHi .arrowheadPath{fill:#333333;}#mermaid-svg-3GQmWDBWmYiklNHi .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-3GQmWDBWmYiklNHi .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-3GQmWDBWmYiklNHi .edgeLabel{background-color:#e8e8e8;text-align:center;}#mermaid-svg-3GQmWDBWmYiklNHi .edgeLabel rect{opacity:0.5;background-color:#e8e8e8;fill:#e8e8e8;}#mermaid-svg-3GQmWDBWmYiklNHi .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-3GQmWDBWmYiklNHi .cluster text{fill:#333;}#mermaid-svg-3GQmWDBWmYiklNHi .cluster span{color:#333;}#mermaid-svg-3GQmWDBWmYiklNHi div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-3GQmWDBWmYiklNHi :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}

object
Animal类
Dog类
Cat类
Person类
注:上图中箭头方向与实际相反,越靠近上面的类等级越高。

针对上述继承图,给出相应的代码示例:

class Animal(object):def eat(self):print('执行了Animal类的eat方法')
class Person(object):def eat(self):print('执行了Person类的eat方法')
class Dog(Animal):def eat(self):print('执行了Dog类的eat方法')
class Cat(Animal):def eat(self):print('执行了Cat类的eat方法')
def fun(creature):creature.eat()lst1 = [Dog(), Cat(), Person()]
for item in lst1:item.eat()
  • 运行结果
执行了Dog类的eat方法
执行了Cat类的eat方法
执行了Person类的eat方法

(感觉上面这个例子讲的不是很好,我就先这么记下来,以后把C++的多态学完了再来补充)

从上述结果可以看出,只要传进fun函数的对象具备.eat()方法,就可以执行,与这些对象是否具有继承关系无关。

下面给出静态语言和动态语言关于多态的区别

  1. 静态语言(比如C++、Java)实现多态的三个必要条件:继承、方法重写、父类引用指向子类对象。
  2. 动态语言的多态崇尚“鸭子类型”。当看到一只鸟走起来像鸭子、游泳起来像鸭子、收起来也像鸭子,那么这只鸟就可以被称为鸭子。在鸭子类型中,不需要关心对象是什么类型,到底是不是鸭子,只关心对象的行为

6. 特殊方法和特殊属性

前面提到过使用内置函数dir()可以查看一个类里面所有的属性和方法,并且通过重写_ _str_ _()方法,可以很方便的print()一个类的基本属性。本节则根据这些特殊功能,继续介绍在类定义时所需要注意到的一些特殊方法和特殊属性,他们的共同点都是两个下划线开始、两个下划线结束。

调用范围 名称 描述
特殊属性 类、实例对象 __dict__ 获得类对象的属性字典、方法字典;获取实例对象的属性字典
类、实例对象 __class__ 获得类或者实例对象所属的类
__bases__ 输出类的父类元组,只包括一级
__base__ 输出类定义时第一个父类,不适用于类对象
__mro__ 输出类的所有的父类对象直到object
__subcalsses__() 输出类的所有的子类对象
特殊方法 实例对象 __len__() 通过重写__len__()方法,让内置函数len()的参数可以是自定义类型
实例对象 __add__() 通过重写__add__()方法,可使用自定义对象具有“+”功能
实例对象 __new__() 用于创建对象
实例对象 __init__() 对创建的对象进行初始化,对新创建的对象属性进行初始化

注:调用范围指的是可以在类外直接加点调用的对象都有谁。

下面是特殊属性的代码示例:

class ini():pass
class A(ini):pass
class B(ini):pass
class C(B, A):def __init__(self, name, age):self.name = nameself.age = age
c1 = C('张三',20)
print('__dict__: ', C.__dict__) # 类对象的属性字典
print('__dict__: ', c1.__dict__)# 实例对象的属性字典、方法字典
print(C.__class__)              # 输出了对象所属的类,与type(C)功能一致
print(c1.__class__)             # 输出了类对象所属的类
print(C.__bases__)              # 输出了类的父类元组,再往上不会输出
# print(c1.__bases__) # AttributeError: 'C' object has no attribute '__bases__'
print(C.__base__)               # 输出类定义时第一个父类
print(C.__mro__)                # 按顺序输出类的所有的父类对象直到object
print(ini.__subclasses__())     # 按顺序输出类的所有的子类对象
  • 运行结果
__dict__:  {'__module__': '__main__', '__init__': <function C.__init__ at 0x0000029297030670>, '__doc__': None}
__dict__:  {'name': '张三', 'age': 20}
<class 'type'>
<class '__main__.C'>
(<class '__main__.B'>, <class '__main__.A'>)
<class '__main__.B'>
(<class '__main__.C'>, <class '__main__.B'>, <class '__main__.A'>, <class '__main__.ini'>, <class 'object'>)
[<class '__main__.A'>, <class '__main__.B'>]

下面是特殊方法 __add__()、__len__() 代码示例:

print('-------演示加法实现的本质--------')
a = 10
b = 100
c = a+b
d = a.__add__(b) # 加法实现的本质,与上一句等价
print(c)
print(d)print('-------演示对象加法--------')
class Student:def __init__(self, name):self.name = namedef __add__(self, other):return [self.name, other.name]def __len__(self):return len(self.name)
stu1 = Student('张三')
stu2 = Student('李四')
print(stu1 + stu2)
# 若不定义__add__,上一句报错TypeError: unsupported operand type(s) for +: 'Student' and 'Student'
print(stu1.__add__(stu2)) # 这一句与上一句功能相同print('-------演示__len__()--------')
lst1 = [11, 22, 33, 44]
print('调用内置函数len():', len(lst1))
print('调用特殊方法__len__():', stu1.__len__()) # 张三是两个字符
# 若不定义__len__(),则报错AttributeError: 'Student' object has no attribute '__len__'
  • 运行结果
-------演示加法实现的本质--------
110
110
-------演示对象加法--------
['张三', '李四']
['张三', '李四']
-------演示__len__()--------
调用内置函数len(): 4
调用特殊方法__len__(): 2

下面是特殊方法 __new__()、__init__() 代码示例:

class Person:def __new__(cls, *args, **kwargs):print('调用__new__()方法,cls地址:{0}'.format(id(cls)))obj = super().__new__(cls)print('调用__new__()方法,obj地址:{0}'.format(id(obj)))print('展示元素:', 'cls=', cls, ', *args=', *args, ', **args=', **kwargs)return objdef __init__(self, name, age):print('调用__init__()方法,self地址:{0}'.format(id(self)))self.name = nameself.age = ageprint('展示元素:', 'self=', self, ', name=', name, ', age=', age)
print('-----------演示类实例对象创建的过程----------')
print('onject类地址:{}'.format(id(object)))
print('Person类地址:{}'.format(id(Person)))
per1 = Person('张三', 20)
print('实例对象地址:{}'.format(id(per1)))
print('per1:', per1.name, per1.age)
print('-----------手动调用方法创建类对象----------')
per2 = per1.__new__(Person)
per2.__init__('李四', 15)
print('per2:', per2.name, per2.age)
  • 运行结果
-----------演示类实例对象创建的过程----------
onject类地址:140718211496784
Person类地址:2179080443424
调用__new__()方法,cls地址:2179080443424
调用__new__()方法,obj地址:2179088488624
展示元素: cls= <class '__main__.Person'> , *args= 张三 20 , **args=
调用__init__()方法,self地址:2179088488624
展示元素: self= <__main__.Person object at 0x000001FB5BCB98B0> , name= 张三 , age= 20
实例对象地址:2179088488624
per1: 张三 20
-----------手动调用方法创建类对象----------
调用__new__()方法,cls地址:2179080443424
调用__new__()方法,obj地址:2179088487856
展示元素: cls= <class '__main__.Person'> , *args= , **args=
调用__init__()方法,self地址:2179088487856
展示元素: self= <__main__.Person object at 0x000001FB5BCB95B0> , name= 李四 , age= 15
per2: 李四 15

从上述结果可以看出,实例化一个类的过程整体以上来看是先创建、后初始化的过程。将上述代码的执行过程翻译成文字,那么创建一个类的实例对象的完整过程(执行Person('张三', 20)语句)如下:

  1. 声明一个类后,内存中存在objectPerson两个类的类地址。
  2. 调用__new__()方法开辟出一块空间给新的类对象。其中cls直接就是Person类的地址,然后调用object父类的new方法生成了obj并返回。这个obj是开辟出了一块新的空间,类指针指向Perosn类。
  3. 调用__init__()方法初始化对象。空间开辟完成后,根据传递过来的参数进行类对象的初始化,完成后返回self
  4. 通过赋值语句将self赋给per1

7. 类的赋值与拷贝

变量的赋值操作:只是形成两个变量,实际上还是指向同一个变量对象。
浅拷贝:使用copy模块的.copy()函数,浅拷贝会生成新的类对象,但是这个类对象的子对象还是会指向源对象的子对象。Python拷贝一般都是浅拷贝
深拷贝:使用copy模块的.deepcopy()函数,递归拷贝,不仅生成新的类对象,其子类对象也都是新的。

下面是代码示例:

class CPU:def __init__(self, cores=6, threads=12):self.cores = coresself.threads = threads
class Disk:pass
class Computer:def __init__(self, price, CPU, Disk):self.prince = priceself.cpu = CPUself.disk = Disk
print('--------------演示对象赋值---------------')
cpu1 = CPU()
cpu2 = cpu1
print('CPU类地址为:{}'.format(id(CPU)))
print('cpu1:', id(cpu1), id(cpu1.cores), id(cpu1.threads))
print('cpu2:', id(cpu2), id(cpu2.cores), id(cpu2.threads))
cpu2.cores = 8
print('cpu2:', id(cpu2), id(cpu2.cores), id(cpu2.threads))
print('--------------演示浅拷贝---------------')
cpr1 = Computer(8000, cpu1, Disk())
import copy
cpr2 = copy.copy(cpr1)
print('cpr1:', id(cpr1), id(cpr1.prince), id(cpr1.cpu), id(cpr1.cpu.cores), id(cpr1.disk))
print('cpr2:', id(cpr2), id(cpr2.prince), id(cpr2.cpu), id(cpr2.cpu.cores), id(cpr2.disk))
print('--------------演示深拷贝---------------')
cpr3 = copy.deepcopy(cpr1)
print('cpr1:', id(cpr1), id(cpr1.prince), id(cpr1.cpu), id(cpr1.cpu.cores), id(cpr1.disk))
print('cpr3:', id(cpr3), id(cpr2.prince), id(cpr3.cpu), id(cpr1.cpu.cores), id(cpr3.disk))
  • 运行结果
--------------演示对象赋值---------------
CPU类地址为:1594275705168
cpu1: 1594283357952 140718106662720 140718106662912
cpu2: 1594283357952 140718106662720 140718106662912
cpu2: 1594283357952 140718106662784 140718106662912
--------------演示浅拷贝---------------
cpr1: 1594283627952 1594283423728 1594283357952 140718106662784 1594283629728
cpr2: 1594283741248 1594283423728 1594283357952 140718106662784 1594283629728
--------------演示深拷贝---------------
cpr1: 1594283627952 1594283423728 1594283357952 140718106662784 1594283629728
cpr3: 1594283741536 1594283423728 1594283742544 140718106662784 1594284360848

注意到浅拷贝与深拷贝都只是在探讨类对象的变化,拷贝过后,这些类对象的变量对象还是会指向原来的。

8. 本章作业

1. 编写程序实现乐手弹奏乐器

说明:乐手可以弹奏不同的乐器从而发出不同的声音。可以弹奏的乐器包括二胡、钢琴和琵琶。

下面是代码示例:

class Instrument(object):def make_sound(self):pass
class Erhu(Instrument):# 方法重写def make_sound(self):print('二胡在演奏...')
class Piano(Instrument):def make_sound(self):print('钢琴在演奏...')
class Violin(Instrument):def make_sound(self):print('小提琴在演奏...')
class Bird(object):def make_sound(self):print('小鸟在唱歌...')
def play(instrument): # 注意输入的参数没有具体的类型(泛型编程)instrument.make_sound()
if __name__ == '__main__':play(Erhu())play(Piano())play(Violin())play(Bird())  # 体现了Python类型
  • 运行结果
二胡在演奏...
钢琴在演奏...
小提琴在演奏...
小鸟在唱歌...

2. 出租车和家用轿车

说明:任务2:请使用面向对象的思想,设计自定义类,描述出租车和家用轿车的信息。

下面是代码示例:

class Car(object):def __init__(self, brand, lic_num):self.brand = brandself.lic_num = lic_numdef start(self):passdef stop(self):pass
class Taxi(Car):def __init__(self, brand, lic_num, company):super().__init__(brand, lic_num)self.company = companydef start(self):print('乘客您好!')print(f'我的车牌号是 {self.lic_num},隶属于 {self.company} 出租车公司,请问您要去哪里?')def stop(self):print('目的地到了,请您付款下车,欢迎再次乘坐!')
class FamilyCar(Car):def __init__(self, brand, lic_num, dirver):super().__init__(brand, lic_num)self.driver = dirverdef start(self):print(f'我是 {self.driver},我的车牌号是 {self.lic_num}')def stop(self):print('目的地到了,爷要去撒欢!')
if __name__ == '__main__':taxi1 = Taxi('小众', '喵A66666', '跑得快')famcar1 = FamilyCar('宝狗', '汪E88888', '狗哥')taxi1.start()taxi1.stop()print('-'*30)  # 注意新的写法!!!30个下划线!!!famcar1.start()famcar1.stop()
  • 运行结果
乘客您好!
我的车牌号是 喵A66666,隶属于 跑得快 出租车公司,请问您要去哪里?
目的地到了,请您付款下车,欢迎再次乘坐!
------------------------------
我是 狗哥,我的车牌号是 汪E88888
目的地到了,爷要去撒欢!

Python学习笔记:第十三站 接着找对象相关推荐

  1. Python学习笔记(十三):异常处理机制

    Python学习笔记(十三):异常处理机制 关于Python的异常处理机制 Python学习笔记(十三):异常处理机制 一.异常处理机制 常见异常类型 二.异常处理 try...except 异常类的 ...

  2. Python学习笔记(十三)

    Python学习笔记(十三): 模块 包 if name == main 软件目录结构规范 作业-ATM+购物商城程序 1. 模块 1. 模块导入方法 import 语句 import module1 ...

  3. 【Python学习笔记】b站@同济子豪兄 用pytorch搭建全连接神经网络,对Fashion-MNIST数据集中的时尚物品进行分类

    [Python学习笔记]原作b站@同济子豪兄 用pytorch搭建全连接神经网络,对Fashion-MNIST数据集中的时尚物品进行分类 跟着b站@同济子豪兄的视频自学写的代码,内容是用pytorch ...

  4. Python学习笔记:第二站 七十二变

    Python学习笔记 文章目录 Python学习笔记 第二站 七十二变 1. 二进制与字符编码 2. Python中的标识符与保留字 3. Python中的变量 4.Python中的数据类型 5. P ...

  5. Python学习笔记:第九站 一串连一串

    Python学习笔记 文章目录 Python学习笔记 第九站 一串连一串 1. 字符串的驻留机制 2. 字符串的常用操作 3. 字符串的比较 4. 字符串的切片操作 5. 格式化字符串 6. 字符串的 ...

  6. python学习笔记(二十三) -- 多进程和多线程

    目录 多线程多进程的意义 多进程的使用 方式一(fork):  只能在Unix/Linux/Mac系统下执行,windows不可以 方式二(multiprocessing.Process): 全平台通 ...

  7. python学习笔记(十三)标准库heapq

    heapq 堆(heap),是一种数据结构.用维基百科中的说明: 堆(英语:heap),是计算机科学中一类特殊的数据结构的统称.堆通常是一个可以被看做一棵树的数组对象. 对于这个新的概念,读者不要感觉 ...

  8. Python学习笔记:第十一站 全民来找茬

    Python学习笔记 文章目录 Python学习笔记 第十一站 全民来找茬 1. Bug的由来及分类 2. 不同异常类型的处理方式 3. 异常处理机制 4. PyCharm的调试模式 5. 本章作业 ...

  9. Python学习笔记:第七站 夫妻站

    Python学习笔记 文章目录 Python学习笔记 第七站 夫妻站 1. 什么是字典 2. 字典的创建 3. 字典的查询操作 4. 字典元素的增.删.改操作 5. 字典推导式 6. 本章作业 课程笔 ...

最新文章

  1. 杭州/北京/新加坡 | 蚂蚁集团数字身份团队招聘计算机视觉算法实习生
  2. docker容器中安装vim 、telnet、ifconfig, ping命令
  3. SAP UI5 Dropdown list binding debugging
  4. Process com.xxxxxxxx has died
  5. 20178.27 万径人踪灭 思考记录
  6. matlab 异步程序代码,正弦波电压源供电时三相异步电动机系统动态运行MATLAB仿真源程序...
  7. 数据和access数据的区别_Access处理数据
  8. iOS视频采集实战(AVCaptureSession)
  9. VisualStudio神级插件Resharper的基本配置和使用技巧大全+Resharper性能优化
  10. 一款由css3和jquery实现的卡面折叠式菜单
  11. 怎样获得正确的LINUX用户的文档音乐视频等目录?
  12. ibm watson_使用IBM Watson Assistant构建AI私人教练-第1部分
  13. mysql绘制er图教程_使用MySQLWorkBench绘制ER图
  14. 通过adb执行android脚本
  15. Decoupled Sparial-Temporal Attention Network forSkeleton-Based Action Recognition
  16. AD(altium designer)15原理图与PCB设计教程(六)——印制电路板设计的基础知识
  17. 【GlobalMapper精品教程】006:Excel等表格(.xls)或文本(.txt .csv)坐标文件生成矢量点
  18. 无需修改bios即可让任意主板实现NVME启动
  19. 对策论基础---引言
  20. meta20 无法安装 google play_【黑科技】安卓手机安装Google Play

热门文章

  1. python江红书后第六章上机答案_第六章上机题答案
  2. 那李逵是穿山度岭 水浒
  3. 三大数据库分页功能JDBC实现操作
  4. JAVA中 什么是方法签名?what is method signature in java
  5. Ubuntu环境下moos-Ivp编译
  6. 7. MyBatis多表查询 - 一对一 - 一对多 - 多对多
  7. 收好了!来自摩根大通的算法交易机器学习指南
  8. Qt 画图工具擦除操作,恢复透明色
  9. 拒绝踩坑!源码编译 tensorflow 解决 cuda 不配套 万金油方法
  10. 迅雷无法下载的解决方法