文章目录
前言
1.北理工 嵩天/黄天宇/礼欣 Python面向程序设计
2.面向有编程基础、支撑就业需求的python语言入门到精通系列课程
3.正确理解python面向对象编程理念与方法
4.掌握python面向对象程序设计的具体方法
5.能独立完成带有面向对象风格的python程序编写
6.实践、实践、再实践
7.本课程分两周
提示:以下是本篇文章正文内容,下面案例可供参考
一、任务分析
第一周:python面向对象基础
第二周:python面向对象进阶
二、第一周
2.1.面向对象编程模式
课程之前:
- 掌握python基础语法,能够开展过程式编程
- 已经编写过20-30个python小程序
- 熟练使用IDLE或pycharm开发工具
- 完整学习过"python基础语法精讲"课程
单元开篇:
1. 万物皆对象
2. 面向对象编程思想
3. 面向对象的三个特征
4. Python面向对象术语概述
5. Python面向对象实例入门
万物皆对象
对象:独立的存在或作为目标的事物
- 独立性:对象都存在清晰的边界,重点在于划分边界
- 功能性:对象都能表现出一些功能、操作或行为
- 交互性:对象之间存在交互,如:运算和继承
python语言的“万物皆对象”
- python语言中所有数据类型都是对象、函数是对象、模块是对象
- python所有类都继承于最基础类object
- python语言中数据类型的操作功能都是类方法的体现
OOP:objec-oriente programmi
- OOP:面向对象编程,一种编程思想,重点在于高抽象的复用代码
- OOP把对象当作程序的基本单元,对象包含数据和操作数据的函数
- OOP本质是把问题解决抽象作为以对象为中心的计算机程序
- OOP在较大规模或复杂项目中十分有用,OOP可以提高写作产量
- OOP最主要价值在于代码复用
- OOP只是一种编程方式,并非解决问题的高级方法
面向过程VS.面向对象
- 面向过程:以解决问题的过程步骤为核心编写程序的方式
- 面向对象:以问题对象构建和应用为核心编程程序的方式
- 所有OOP能解决的问题,面向过程都能解决
面向对象编程概念实列
OOP三个重要特征
- 封装:属性和方法的抽象,用 数据和操作数据的方法来形成对象逻辑
- 继承:代码复用的高级抽象,用对象之间的继承关系来形成代码复用
- 多态:方法灵活行的抽象,让对象的操作更加灵活、更多复用代码
封装Encapsulation:属性和方法的抽象
- 属性的抽象:对类的属性(变量)进行定义、隔离及保护
- 方法的抽象:对类的方法(函数)进行定义、隔离及保护
- 目标是形成一个类对外可操作属性和方法的接口
继承Inheritance:代码复用的高级抽象
- 继承是面向对象程序设计的精髓之一
- 实现了以类为单位的高抽象级别代码复用
- 继承是新定义类能够几乎完全使用原有类与方法的过程
多态Polymorphism:仅针对方法,方法灵活性的抽象
- 参数类型的多态:一个方法能够处理多个类型的能力
- 参数形式的多态:一个方法能够接受多个参数的能力
- 多态是OOP的一个传统概念,pytho天然支持多态,不需要特殊语法
类class和对象object
- 类:逻辑抽象和产生对象的模板,一组变量和函数的特定编排
- 对象:具体表达数据及操作的实体,相当于程序中的”变量“
- 实例化:从类到对象的过程,所有”对象“都源于某个”类“
面向对象术语概述
- 对象:类对象、实例对象
- 属性:存储数据的”变量“,包括:类属性、实例属性
- 方法:操作数据的“函数”
包括:类方法、实例方法、自由方法、静态方法、保留方法
类对象VS.实例对象
- 类对象:class object,维护每个python类基本信息的数据结构
- 实例对象:instance object,python类实例后产生的对象,简称:对象
- 这是一组概念,类对象全局只有一个,实例对象可以生成多个
面向对象术语概述
- 三个特征:封装、继承、多态
- 继承:基类、派生类、子类、父类、超类、重载
- 命名空间:程序元素作用域的表达
- 构造和析构:生成对象和删除对象的过程
python OOP实列
class Product():
def __init__(self,name):
self.neme=name
self.label_price=0
self.real_price=0
c=Product("电脑")
d=Product("打印机")
e=Product("投影仪")
c.label_price,c.real_price=10000,8000
d.label_price,d.real_price=2000,1000
e.label_price,e.real_price=1500,900
s1,s2=0,0
for i in [c,d,e]:
s1+=i.label_price
s2+=i.real_price print(s1,s2)
print(s1,s2)
2.2python类的构建
单元开篇
- 类的基本构建
- 类的析构函数
- 类的属性
- 类的方法
- 类的析构函数
类的基本构建方法
使用class保留字定义类
class <类名>:
[类描述:"documentation string"]
<语句块>
**类定义不限位置,可以包含在分支或其他从属语句块中,执行时存在即可**
使用class保留字定义类
- 类的名字:可以是任何有效标识符,建议采用大写单词的组合
如:ClassName,BasicAuto,BasicCreature
- 类描述:在类定义后首行,以独立字符串形式定义
定义后通过<类名>._doc_属性来访问
class DemoClass:
"This is a demo for Python class"
pass
print(DemoClass.__doc__)
*类对象:class object
- 类定义完成后,默认生成一个类对象
- 每个类唯一对应一个类对象,用于存储这个类的基本信息
- 类对象是type类的实例,表达为type类型
class DemoClass:
"This is a demo for Python class"
print("Hello Demo Class")
print(DemoClass.__doc__)
print(type(DemoClass))
>>>Hello Demo Class
>>>This is a demo for Python class
>>><class 'type'>
**类对象内直接包含的语句会被执行,因此,一般不在类定义中直接包含语句**
类的使用方法
通过创建实例对象(instance object)使用类的功能
<对象名>=<类名>([<参数>])
进一步采用<对象名>.<属性名>和<对象名>.<方法名>()体现类的功能
class DemoClass:
"This is a demo for Python class"
print("Hello Demo Class")
print(type(DemoClass))
cn=DemoClass()
print(type(cn))
>>>Hello Demo Class
>>>class 'type'>
>>><class '__main__.DemoClass'>
**实列对象和类对象的类型是不同的**
类的构造函数
了解python类的构造函数
- 类的构造函数用于从类创建实例对象的过程
- 类的构造函数为实例对象创建提供了参数输入方式
- 类的构造函数为实例属性的定义和复制提供了支持
类的属性和方法
了解python类的属性和方法
- 类的属性:类中定义的变量,用来描述类的一些特性参数
- 类的方法:类中定义且与类相关的函数,用来给出类的操作功能
- 属性和方法是类对外交互所提供的两种接口方式
类的构造函数
python使用预定义的_init_()作为构造函数
class <类名>:
def _init_(self,<参数列表>)
<语句块>
......
**类实例化时所使用的函数,可以接受参数并完成初始操作**
class DemoClass:
def __init__(self,name):
print(name)
dc1=DemoClass("老王")
dc2=DemoClass("老李")
>>>老王
>>>老李
_ init _()使用说明
- 参数:第一个参数约定是self,表示类实例自身,其他参数是实例参数
- 函数名:python解释器内部定义,由下划线(_)开始和结束
- 返回值:构造函数没有返回值,或返回None,否则产生TypeError异常
self在类定义内部代表类的实例
- self是python面向对象中约定的一个类参数
- self代表类的实例,在类内部,self用于组合访问实例相关的属性和方法
- 相比,类名代表类对象本身
类的属性
-
类属性:类对象的属性,由所有实例对象所共享
访问:<类名>.<类属性> 或<对象名>.<类属性> -
实例属性:实例对象的属性,由各实例对象所有独享
访问:<对象名>.<实例属性>
属性是类内部定义的变量class <类名>:
<类属性名>=<类属性初值>
def init(self,<参数列表>)
self.<实例属性名>=<实例属性初值>
…
class DemoClass:
count=0
def __init__(self,name,age):
self.name=name
self.age=age
DemoClass.count+=1
dc1=DemoClass("老王",45)
dc2=DemoClass("老李",51)
print("总数:",DemoClass.count)
print(dc1.name,dc2.name)
>>>总数:2
>>>老王 老李
类的方法
方法是类内部定义的函数
- 实例方法;实例对象的方法,由各实例对象独享
- 类方法:类对象的方法,由所有实例对象共享
- 自由方法:类中的一个普通函数,由类所在命名空间管理,类对象独享
- 静态方法:类中的要给普通函数,由类对象和实例对象共享
- 保留方法:由双下划线开始和结束的方法,保留使用,如_len_()
类的实例方法
- 实例方法是类内部定义的函数,与实例对象相关
class <类名>:
def <方法名>(self,<参数列表>):
....
实例方法采用<对象名>.<方法名>(<参数列表>)方式使用
class DemoClass:
def __init__(self,name):
self.name=name
def lucky(self):
s=0
for c in self.name:
s+=ord(c) %100
return s
dc1=DemoClass("老王")
dc2=DemoClass("老李")
print(dc1.name,"的幸运数字是:",dc1.lucky())
print(dc2.name,"的幸运数字是:",dc2.lucky())
>>>老王 的幸运数字是: 148
>>>老李 的幸运数字是: 115
**实例方法的定义第一个参数是self**
类的类方法
- 类方法至少包含一个参数,表示类对象,建议用cls
- 类方法是与类对象相关的函数,由所有实例对象共享
- @classmethod是装饰器,类方式定义所必须
- 类方式只能操作类属性和其他类方法,不能操作实例属性和实例方法
class <类名>:
@classmethod
def <方法名>(cls,<参数列表>):
.....
类方法采用<类名>.<方法名>(<参数列表>)或
<对象名>.<方法名>(<参数列表>)方法使用
class DemoClass:
count=0
def __init__(self,name):
self.name=name
DemoClass.count+=1
@classmethod
def getChrCount(cls):
s="零一二三四五六七八九十多"
return s[DemoClass.count]
dc1=DemoClass("老王")
dc2=DemoClass("老李")
print(dc1.getChrCount())
print(DemoClass.getChrCount())
**类方法调用可以用类名或对象名**
类的自由方法
- 自由方法不需要self或cls这类参数,可以没有参数
- 自由方法只能操作类属性和(其他)类方法,不能操作实例属性和实例方法
- 自由方法的使用只能使用<类名>
- 自由方法是定义在类命名空间中的普通函数
class <类名>:
def <方法名>(<参数列表>):
.....
自由方法采用<类名>.<方法名>(<参数列表>)方式使用,<类名>表示命名空间
class DemoClass:
count=0
def __init__(self,name):
self.name=name
DemoClass.count+=1
def foo():
DemoClass.count *=100
return DemoClass.count
dc1=DemoClass("老王")
print(DemoClass.foo())
**自由方法就是类中普通函数
理解为:
定义在类命名空间中的函数也可以定义在类外,习惯不同**
类的静态方法
静态方法是定义在类中的普通函数,能够被所有实例对象共享
- 静态方法可以没有参数,可以理解为定义在类中的普通函数
- @staticmethod是装饰器,静态方法定义所必须
- 静态方法只能操作类属性和其他类方法,不能操作实例属性和实例方法
- 相比于自由方法,静态方法能够使用<类名>和<对象名>两种方式使用
class <类名>:
@staticmethod
def <方法名>(<参数列表>):
......
静态方法采用 <类名>.<方法名>(<参数列表>)或<对象名>.<方法名>(<参数列表>)方式使用
class DemoClass:
count=0
def __init__(self,name):
self.name=name
DemoClass.count+=1
@staticmethod
def foo():
DemoClass.count *=100
return DemoClass.count
dc1=DemoClass("老王")
dc2=DemoClass("老李")
print(dc2.foo())
print(dc1.foo())
**静态方法就是类中普通函数
但:
这类方法能够被类和对象调用**
类的保留方法
保留方法由双下划线开始和结束发的方法,保留使用
class <类名>:
def <保留方法名>(<参数列表>)
.......
保留方法一般都对应类的某种操作,但操作产生时调用
class DemoClass:
count=0
def __init__(self,name):
self.name=name
DemoClass.count+=1
def __len__(self):
return len(self.name)
dc1=DemoClass("老王")
dc2=DemoClass("小诸葛")
print(type(dc2))
print(len(dc1))
print(len(dc2))
**_len_()方法对应len()函数操作
理解为:
这是python解释器保留方法
已对应,只需要编写代码即可**
类的析构函数
del()的使用说明
- 函数名和参数:python解释器内部约定,保留方法
- 调用条件:当实例对象被真实删除时,才调用改函数内语句
- 真实删除:当前对象的引用数为0或当前程序退出(垃圾回收)
python使用预定义的_del_()作为析构函数
class <类名>:
def _del_(self):
<语句块>
.....
析构函数在真实删除实例对象时被调用
class DemoClass:
def __init__(self,name):
self.name=name
def __del__(self):
print("再见",self.name)
dc1=DemoClass("老王")
del dc1
>>>再见 老王
**使用del删除对象且对象被真实删除时调用析构函数_del_()**
sys.getrefcount(<对象名>)获得对象的引用次数
- 返回对象引用次数的方法,辅助删除对象时的分析
- sys.getrefcount()函数返回值为被引用值+1
- 非特定目的,不建议自己撰写析构函数,利用pytho垃圾回收机制
2.3实列1:银行ATM等待时间
import random
class ATM():
def __init__(self,maxtime=5):
self.t_max=maxtime
def getServCompleteTime(self,start=0): # 获得每次ATM服务完成的时间
return start+random.randint(1,self.t_max)
class Customers():
def __init__(self,n):
self.count=n
self.left=n
def getNextArrvTime(self,start=0,arrvtime=10): #返回下个客户到达的时间
if self.left!=0:
self.left-=1
return start+random.randint(1,arrvtime)
else:
return 0
def isOver(self): #判断客户库存是否清零
return True if self.left==0 else False
c=Customers(100)
a=ATM()
wait_list=[] #每次等待队列
wait_time=0 #队列中总等待时间
cur_time=0 #当前时间
cur_time+=c.getNextArrvTime() #用户每次到达时间
wait_list.append(cur_time)
while len(wait_list)!=0 or not c.isOver():
if wait_list[0] <= cur_time:
next_time=a.getServCompleteTime(cur_time) #下次时间
del wait_list[0]
else:
next_time=cur_time+1 #下次时间
if not c.isOver() and len(wait_list) ==0:
next_arrv=c.getNextArrvTime(cur_time)
wait_list.append(next_arrv)
if not c.isOver() and wait_list[-1] <next_time:
next_arrv=c.getNextArrvTime(wait_list[-1])
wait_list.append(next_arrv)
while next_arrv<next_time and not c.isOver():
next_arrv=c.getNextArrvTime(next_arrv)
wait_list.append(next_arrv)
for i in wait_list:
if i <=cur_time:
wait_time+=next_time-cur_time
elif cur_time<i<next_time:
wait_time+=next_time-i
else:
pass
cur_time=next_time
print(wait_time/c.count)
提示:没看懂得可以去看看这位博主写的
Python 实例详解:银行 ATM 等待时间分析
2.4python类的封装
封装Encapsulation:属性和方法的抽象
- 属性的抽象:可以选择公开或隐藏属性,隐藏属性的内在机理
- 方法的抽象:可以选择公开或隐藏方法,隐藏方法的内部逻辑
- 封装:让数据和代码成为类的过程,表达为:类-属性-方法
**公开类属性:即类属性**
class <类名>:
<类属性名>=<类属性初值>
def _init_(self,<参数列表>)
self.<实例属性名>=<实例属性初值>
....
**私有类属性:仅供当前类访问的类属性,子类亦不能访问**
class <类名>:
<私有类属性名>=<类睡醒初值>
def _init_(self,<参数列表>)
....
**区别:私有类属性名开始需要有两个下划线(_),如__count**
**私有类属性可以通过类方法来获取值**
**公开实例属性:即实例属性**
class <类名>:
<类属性名>=<类属性初值>
def _init_(self,<参数列表>)
self.<实例属性名>=<实例属性初值>
....
**私有实例属性:仅供当前类内部访问的实例属性,子类亦不能访问**
class <类名>:
def _init_(self,<参数列表>)
self.<实例属性名>=<实例属性初值>
....
**区别:私有实例属性名开始需要有两个下划线(_),如__name**
私有实例属性:仅供当前类内部访问的实例属性,子类亦不能访问
- 只能在类的内部被方法所访问
- 不能通过<类名>.<属性名>或<对象名>.<属性名>方式访问
- 有效保证了属性维护的可控性
私有属性
python没有真正意义上的私有属性,可以都过实实例名._类名__私有属性名来直接访问
**私有方法是类内部定义并使用的函数**
class <类名>:
def <方法名>(self,<参数列表>)
....
私有方法名开始需要有两个下划线(__),如__getCount()
私有方法是类内部定义并使用的函数
- 各类方法都可以通过增加双下划线变为私有方法
- 私有方法从形式上保护了python类内部使用的函数逻辑
- 私有与公开是程序员逻辑,不是安全逻辑,重视约定
私有方法
python没有真正意义上的私有方法,可以都过实例名._类名__私有方法名来直接访问
python解释器预留的类属性,以双下划线开头和结尾
- 也叫:特殊属性,Special Attributes
- 特点:双下划线开头和结尾
- 作用:为理解python类提供了统一的属性接口
- 属性值:具有特定含义,类定义后直接使用
仅用<类名>访问的保留属性
- __ name __ 类的名称
- _ qualname __ 以.分隔从模块全局命名空间开始的类名称
- __ bases __ 类所继承的基类名称
保留属性 :类和对象都可以使用
<类>.dict 包含类成员信息的字典,key是属性和方法的名称,value是地址
<对象>.__dict __包含对象属性信息的字典,key是属性名称,value是值
__ class __ 对象所对应的类信息,即type信息
__ doc __ 类描述,写在类定义下的首行字符串,不能继承
__ module __ 类所在模块的名称(返回元组类型) 我测试时是字符串类型
python解释器预留的类方法,以双下划线开头和结尾
- 也叫:特殊方法,Special Methods
- 特点:双下划线开头和结尾
- 作用:为操作python类提供了统一的方法接口
- 方法逻辑:具有特定含义,一般与操作符关联,类定义需要重载
2.5python类的继承
继承Inheritance:代码复用的高级抽象
- 继承是面向对象程序设计的我精髓之一
- 实现了以类为单位的高抽象级别代码复用
- 继承是新定义类能几乎完全使用原有类属性与方法的过程
**在定义类时声明继承关系**
class <类名>(<基类名>):
def __init__(self,<参数列表>)
<语句块>
.......
基类名可以带有路径:ModultName.BaseClassName
派生类可以直接使用基类的属性和方法
- 基类的属性基本等同于定义在派生类中
- 派生类可以直接使用基类的类属性、实例属性
- 派生类可以直接使用基类的各种方法
- 使用基类的类方法和类属性时,要用基类的类名调用
派生类的约束
- 派生类只能继承基类的公开属性和方法
- 派生类不能继承基类的私有属性和私有方法
object类是python所有类的基类
- object是python最基础类的名字,不建议翻译理解
- 所有类定义时默认继承object类
- 保留属性和保留方法本质上是object类的属性和方法
python对象的三个要素:标识、类型和值
- 标识identity:对象一旦构建不会改变,用id()获得,一般是内存地址
- 类型type:对象的类型,用type()获得
- 值value:分为可变mutable与不可变immutable两种
2个与基础类有关的python内置功能
重载:派生类对基类属性或方法的再定义
- 属性重载:派生类定义并使用了与基类相同名称的属性
- 方法重载:派生类定义并使用了与基类相同名称的方法
类的属性重载
最近的覆盖原则:重载无需特殊标记
- 步骤1:优先使用派生类重定义的属性和方法
- 步骤2:然后寻找基类的属性和方法
- 步骤3:再寻找超类的属性和方法
方法重载:派生类对基类方法的再定义
- 完全重载:派生类完全重定义与基类相同名称的方法,直接在派生类中定义同名方法即可
- 增量重载:派生类扩展定义与基类相同名称的方法
**增量重载:使用super()方法**
calss <派生类名>(<基类名>):
def <方法名>(self,<参数列表>)
super().<基类方法名>(<参数列表>)
class DemoClass:
count=0
def __init__(self,name):
self.name=name
DemoClass.count+=1
def printCount(self):
return str(DemoClass.count)+self.name
class HumanNameClass(DemoClass):
count = 2
def __init__(self,name):
self.name=name
def printCount(self):
return super().printCount()+"同志"
dc2=DemoClass("老李")
dc1=HumanNameClass("老王")
print(dc2.printCount())
print(dc1.printCount())
print(dc2.name)
.......
<<<1老李
<<<1老王同志
<<<老李
三、第二周
3.1python类的运算
运算Operatioin:操作逻辑的抽象
- 运算体现一种操作逻辑,广义角度,任何程序都被认为是运算
- python解释器通过保留方法预留了一批运算的接口,需要重载
- 保留方法一般对应运算符,python中运算体现为运算符的重载
运算重载的限制
- 不能重载python语言内置类型的运算符
- 不能新建运算符,只能通过重载完成
- is and not or不能被重载
算术运算的种类:
算术运算符的重载:一元算术运算符
算术运算符的重载:二元算术运算符
比较运算的种类
成员运算的种类
其他运算的种类
提示:Python中__repr__和__str__区别
所有的数据类型都自带布尔值,数据只有在0,None和空的时候为False。
3.2python类的多态
多态Polymorphism:仅针对方法,方法林灵活性得抽象
- 参数类型的多态:一个方法能够处理多个类型的能力
- 参数形式的多态:一个方法能够接受多个参数的能力
- 多态是OOP的一个传统概念,python天然支持多态,不需要特殊语法
天然支持:python方法无类型申明限制
- python的函数/方法没有类型申明限制,天然支持参数类型的多态性
- python编程理念在于:文档约束,而非语法约束
- 对不同参数类型的区分及功能,需要由程序员完成
参数类型的多态:
class DemoClass:
def __init__(self,name):
self.name=name
def __id__(self):
return len(self.name)
def lucky(self,salt):
s=0
for c in self.name:
s+=(ord(c)+id(salt))%100
return s
dc1=DemoClass("老王")
dc2=DemoClass("老李")
print(dc1.lucky(10))
print(dc1.lucky("10"))
print(dc1.lucky(dc2))
天然支持:pytho方法/函数支持多种函数形式
- python的函数/方法可以支持可变参数,支持参数形式的多态性
- python的类方法也是函数,函数的各种定义方式均有效
- 对不同参数个数及默认值的确定,需要由程序员完成
参数形式的多态:
class DemoClass:
def __init__(self,name):
self.name=name
def __id__(self):
return len(self.name)
def lucky(self,salt=0,more=9):
s=0
for c in self.name:
s+=(ord(c)+id(salt)+more)%100
return s
dc1=DemoClass("老王")
print(dc1.lucky())
print(dc1.lucky(10))
print(dc1.lucky(10,100))
3.3实列2:图像的四则运算
图像之间进行四则运算:加减乘除
- 加减法:两个图像相加减
- 乘除法:一个图像与一个数字之间的乘除法
- 通过面向对象程序设计方法,简化程序表达
- 让图像处理变得更加有趣、更加人性化
- 图像变换的抽象层次更加高级(抽象是追求的目标)
numpy库和PIL库
- PIL库是一个能够简单读写图像文件的库
- numpy库是一个矩阵表示的数学库
- 图像可以理解为何一个三维数据,像素是(r,g,b),长宽是2个维度
import numpy as np
from PIL import Image
class ImageObject:
def __init__(self,path=""):
self.path=path
try:
self.data=np.array(Image.open(path))
except:
self.data=None
def __add__(self, other):
image=ImageObject()
try:
image.data=np.mod(self.data+other.data,255)
except:
image.data=self.data
return image
def __sub__(self, other):
image=ImageObject()
try:
image.data=np.mod(self.data-other.data,255)
except:
image.data=self.data
return image
def __mul__(self, factor):
image=ImageObject()
try:
image.data=np.mod(self.data*factor,255)
except:
image.data=self.data
return image
def __truediv__(self, factor):
image=ImageObject()
try:
image.data=np.mod(self.data//factor,255)
except:
image.data=self.data
return image
def saveImage(self,path):
try:
im=Image.fromarray(self.data)
im.save(path)
return True
except:
return False
a=ImageObject(r"D:\PycharmProjects\Image\earth.jpg")
b=ImageObject(r"D:\PycharmProjects\Image\gray.jpg")
(a+b).saveImage((r"D:\PycharmProjects\Image\result_add.jpg"))
(a-b).saveImage((r"D:\PycharmProjects\Image\result_sub.jpg"))
(a*b).saveImage((r"D:\PycharmProjects\Image\result_mul.jpg"))
(a/b).saveImage((r"D:\PycharmProjects\Image\result_div.jpg"))
3.4python对象的引用
引用Reference:对象的指针
- 引用是内存中真实对象的指针,表示为变量名或内存地址
- 每个对象存在至少1个引用,id()函数用于获得引用
- 在传递参数和赋值时,python传递对象的引用,而不是复制对象
python内部机制对引用的处理
- 不可变对象:immutable解释器为相同值维护尽量少的内存区域
- 可变对象:mutable解释器为每个对象维护不同内存区域
导致引用+1的情况
- 对象被创建:d=DemoClass()
- 对象被引用:a=d
- 对象被作为函数或方法的参数:sys.getrefcount(d)
- 对象被作为一个容器中的元素:ls=[d]
我的理解:sys.getrefcount()方法运行时就调用了一次对象,多以加1,同理,ls赋值时也是如此
导致引用-1的情况
- 对象被删除:del d
- 对象的名字被赋予新的对象:d=123
- 对象离开作用域:foo()函数的局部变量count
- 对象所在容器被删除:del ls
浅拷贝和深拷贝
- 拷贝:复制一个对象为新的对象,内存空间有“变化”
- 浅拷贝:仅复制最顶层对象的拷贝方式,默认拷贝方式
- 深拷贝:迭代复制所有对象的拷贝方式
完全拷贝对象内容
- 采用copy库的deepcopy()方法
- 迭代拷贝对象内各层次对象,完全新开辟内存建立对象
- 深拷贝仅针对可变类型,不可变类型无需创建新对象
提示:浅拷贝和深拷贝可以看看这位up主的讲解,简单易懂十分钟!彻底弄懂Python深拷贝与浅拷贝机制
类的实例方法
- 定义方式:def <实例方法名>(self,<参数列表>)
- 实例方法名也是一种引用,即对方法本身的引用
- 当方法被引用时,方法(即函数)将产生一个对象:方法对象
3.5python类的高级话题
命名空间Namespace:从名字到对象的一种映射
- 作用域:全局变量名在模块命名空间,局部变量名在函数命名空间
- 属性和方法在类命名空间,名字全称:<命名空间>.<变量/函数名>
- 命名空间底层由一个dict实现,变量名是键,变量引用的对象是值
- 复数z,z.real和z.imag是对象z命名空间的两个属性
- 对象d,d.name和d.printName()是对象d命名空间的属性和方法
- global和nonlocal是两个声明命名空间的保留字
提示:可以看看这两个博主写的很清楚nonlocal关键字使用场景及与global关键字的区别和深入了解Python中什么是闭包函数
@property:类的特征装饰器
- 使用@property把类中的方法变成对外可见的“属性"
- 类内部:表现为方法
- 类外部:表现为属性
异常Exception也是一种python类
- try-except捕捉自定义的异常
- 继承Exception,可以给出自定义的异常类
- 自定义异常类是类继承的正常应用过程
名称修饰:Name Mangling:类中名称的变换约定
- python通过名称修饰完成一些重要功能
- 采用下划线(_)惊醒名称修饰,分为5中情况
- _ x、 x_、__ x 、__ x __、 _
_x:单下划线开头的名称修饰
- 单下划线开头属性或方法为类内部使用 PEP8
- 只是约定,仍然可以通过(对象名).<属性名>方式访问
- 功能:from xx import*时不会导入单下划线开头的属性或方法
x_:单下划线结尾的名称修饰
- 单下划线结尾属性或方法为避免与保留字或已有命名冲突 PEP8
- 只是约定,无语任何功能性对应
__x:双下划线开头的名称修饰
- 双下划线开头属性或方法将被解释器修改名称,避免命名冲突
- 不是约定,而是功能性,实现私有属性、私有方法
- __x会被修改为:_<类名>__x
__ x __:双下划线开头和结尾的名称修饰
- 双下划线开头和结尾的属性或方法无任何特殊功能,名字不被修改
- 部分名称时保留属性或保留方法
_:单下划线
- 单下划线是一个无关紧要的名字,无特殊功能
python:最小空类
- 类是一个命名空间,最小空类可以当作命名空间使用
- 最小空类可以辅助数据存储和使用
- 动态增加属性是python类的一个特点