微专业 python面向对象语法精讲笔记 第二周

1. 运算的理解

  • 运算体现一种操作逻辑,广义角度,任何程序都被认为是运算。
  • Python解释器通过保留方法预留了一批运算的接口,需要重载。
  • 保留方法一般对应运算符,Python中运算体现为运算符重载。

运算重载的限制

  • 不能重载Python语言==内置类型(字典)==的运算符。
    这里的内置类型指的就是字典、列表、数字、字符串等原装类对象均不可重载,需继承后重载
  • 不能新建运算符,只能通过重载完成。
  • is and or not 不能被重载。

算数运算符的重载

  • 一元算术运算符:+ - ~
  • 二元算术运算符:+ - * / // % divmod() pow() ** << >> & ^ |

算数运算符的重载:一元算数运算符

保留方法 对应操作 描述
.neg(self) -obj 定义对象取负的运算逻辑
.pos(self) +obj 定义对象取正的运算逻辑
.abs(self) abs(obj) 定义对象绝对值的运算逻辑
.invert(self) ~obj 定义对象取反的运算逻辑

算数运算符的重载:二元算数运算符

保留方法 对应操作 描述
.add(self,other) Obj+other 定义两个对象加法的运算逻辑
.sub(self,other) Obj-other 定义两个对象减法的运算逻辑
.mul(self,other) Obj*other 定义两个对象乘法的运算逻辑
.truediv(self,other) Obj/other 定义两个对象除法的运算逻辑
.floordiv(self,other) Obj//other 定义两个对象整数除的运算逻辑
.mod(self,other) Obj%other 定义两个对象模的运算逻辑
.divmod(self,other) Divmod(obj,other) 定义两个对象除模的运算逻辑
.pow(self,other) Obj**other 定义对象幂的运算逻辑
.lshift(self,other) Obj< 定义对象左移的运算逻辑
.rshift(self,other) Obj>>other 定义对象右移的运算逻辑
.and(self,other) Obj&other 定义两个对象位与的运算逻辑
.xor(self,other) Obj^other 定义两个对象位异或的运算逻辑
.or(self,other) Objother 定义两个对象位或的运算逻辑
例:
class newlist(list): #继承list类型的新类型,重载加法。
	def __add__(self,other):
		result=[]
		for i in range(len(self)):
			try:
				result.append(self[i]+other[i])
			except:
				result.append(self[i])
		return result
ls=newlist([1,2,3,4,5,6])
lt=newlist([1,2,3,4])
print(ls+lt)
#输出结果为:
#2,4,6,8,5,6

比较运算的重载

  • 比较运算:<、<=、==、!=、>、>=
保留方法 对应操作 描述
.lt(self,other) obj 两个对象比较操作的运算重载
.le(self,other) obj<=other 两个对象比较操作的运算重载
.eq(self,other) obj==other 两个对象比较操作的运算重载
.ne(self,other) obj!=other 两个对象比较操作的运算重载
gt(self,other) obj>other 两个对象比较操作的运算重载
.ge(self,other) obj>=other 两个对象比较操作的运算重载
#例子:
class newlist(list):
	def __lt__(self,other):
		s,t=0,0
		for c in self:
			s+=c
		for c in other:
			t+=c
		return True if s<t else False
ls=newlist([1,2,3,4])
lt=newlist([1,3,4,5])
print([1,2,3,4]<[1,3,4,5])
print(ls<lt)
#输出结果为:
#False
#True

成员运算的重载

成员运算的种类

  • 成员获取:[]、del、reversed()
  • 成员判断:in、not in
保留方法 对应操作 描述
.getitem(self,key) Obj[k] 定义获取对象中序号k元素的运算逻辑,k为整数
.setitem(self,key,v) Obj[k]=v 定义赋值对象中序号k元素的运算逻辑
.delitem(self,key) Del obj[k] 定义删除对象中序号k元素的运算逻辑
.reversed(self) Obj.reversed() 定义对象逆序的运算逻辑
.contains(self,item) Item in obj 定义in操作符对应的运算逻辑
例子:
class newlist(list):
	def __contains__(self,item):
		s=0
		for c in self:
			s+=c
		if super().__contains__(item) or item==s
			return True
		else:
			return False
ls=newlist([6,1,2,3])
print(6 in ls,12 in ls)
#输出结果为:
#True True

其他运算的重载

Python内置函数

  • repr()、str()、len()、int()、float()、complex()、round()、bytes()、bool()、format()
  • 类的常用方法:.format()
保留方法 对应操作 描述
.repr(self) repr(obj) 定义对象可打印字符串的运算逻辑。
.str(self) str(obj) 定义对象字符串转换操作的运算逻辑
.len(self) len(obj) 定义对象长度操作的运算逻辑
.int(self) int(obj) 定义对象整数转换的运算逻辑
.float(self) float(obj) 定义对象浮点数转换的运算逻辑
.complex(self) complex(obj) 定义对象复数转换的运算逻辑
.round(self) round(obj) 定义对象四舍五入的运算逻辑
.bytes(self) bytes(obj) 定义对象字节串转换的运算逻辑
.bool(self) bool(obj) 定义对象布尔运算的运算逻辑
.format(self,format_spec) obj.format()\format(obj) 定义对象格式化输出的运算逻辑
#例子:
class  newlist(list):
	def __format__(self,format_spec):
		t=[]
		for c in self:
			if type(c)==type(“字符串”):
				t.append(c)
			else:
				t.append(str(c))
		return,.join(t)
ls=newlist([1,2,3,4])
print(format[1,2,3,4])
print(format(ls))
#输出结果为:
#[1,2,3,4]
#1,2,3,4

2.多态

多态的理解

dc1=DemoClass("老王")
dc2=DemoClass("老李")
dc1.lucky()
dc1.lucky(10)
dc1.lucky("10")
dc1.lucky(dc2)
dc1.lucky(10,100)

对于同一个类的方法

  • 方法可以接受多种类型作为其参数。
  • 方法可以接受多个参数作为其参数。

多态Polymorphism:仅针对方法,方法灵活性的抽象

  • 参数类型的多态:一个方法能够处理多个类型的能力。
  • 参数形式的多态:一个方法能够接受多个参数的能力。
  • 多态是面向对象编程的一个传统概念,Python天然支持多态,不需要特殊语法。

天然支持:Python方法无类型声明限制

  • Python的函数、方法没有类型声明限制,天然支持参数类型的多态。
  • Python编程理念在于:文档约束,而非语法约束。
  • 对不同参数类型的区分及功能,需要由程序员完成。
#例子:
class democlass:
	def __init__(self.name):
		self.name=name
	def __id__(self):	#重载id函数对应逻辑
		return len(self.name)
	def luck(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))

天然支持:python方法/函数支持多种参数形式

  • 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.python对象的引用

引用Reference:对象的指针

  • 引用是内存中真实对象的指针,表示为变量名或内存地址。
  • 每个对象存在至少一个引用,id()函数用于获得引用。
ls=[1,2,3,4,5]
lt=ls
print(id(ls))
print(id(lt))
#输出结果为
#93463456
#93463456

python内部机制对引用的处理

  • 不可变对象:immutable解释器为相同值维护尽量少的内存区域。
  • 可变对象:mutable解释器为每个对象维护不同内存区域。

不可变对象的引用:整数\字符串

a=10
b=a
c=10
print(id(a))
print(id(b))
print(id(c))
#输出结果为:
#1376965824
#1376965824
#1376965824
# 赋值仅实现了增加引用的功能。
#- python解释器内部对a和c维护1个对象。
a="python计算生态"
b=a
c="python"
d="计算生态"
e=c+d
f="python计算生态"
print(id(a))
print(id(b))
print(id(c))
print(id(d))
print(id(e))
print(id(f))
#输出结果a\b\f指向同一内存地址。
#但是e作为运算后产生的对象,由解释器重新建立。

可变对象的引用:列表

la=[]
lb=la
lc=[]
print(id(la))
print(id(lb))
print(id(lc))
#81732512
#81732512
#81021088
#赋值新增了一个对象的引用
#每个可变对象都由解释器重新创建,不复用内存

la=[]
lb=la
lb.append(1)
print(la,id(la))
print(lb,id(lb))
#对象修改后,所有引用的值都被修改。

导致引用+1的情况

  • 对象被创建:d=DemoClass()
  • 对象被引用:a=d
  • 对象被作为函数或方法的参数:sys.getrefcount(d)
  • 对象被作为一个容器中的元素:ls=[d]

导致引用 - 1的情况

  • 对象被删除:del d
  • 对象的名字被赋予新的对象
  • 对象离开作用域:foo()函数的局部变量count
  • 对象所在容器被删除:del ls

引用reference:对象的指针

  • 在传递参数和赋值时,python传递对象的引用,而不是复制对象。
  • 不可变对象与可变对象的内存管理略有不同。

浅拷贝和深拷贝

  • 拷贝:赋值一个对象为新对象,内存空间有“变换”。
  • 浅拷贝:仅复制最顶层对象的拷贝方式,默认拷贝方式。
  • 深拷贝:迭代复制所有对象的拷贝方式,采用copy库的deepcopy()。
  • 一般深拷贝都与可变类型关联。
ls=["python",[1,2,3]]
la=ls.copy()
lb=[:]
lc=list(ls)
print("ls",id(ls),ls)
print("la",id(la),la)
print("lb",id(lb),lb)
print("lc",id(lc),lc)
#此时la,lb,lc,ls不是赋值指向同一地址,而是生成了不用的内存地址id均不相同。
ls=["python",[1,2,3]]
la=ls.copy()
lb=[:]
lc=list(ls)
for i in [ls,la,lb,lc]:
	for c in i:
		print(c,id(c)," ",end=" ")
	print(" ",i,id(i))
#打印输出后你会发现列表被拷贝了,但是元素没有被拷贝。
#列表本身只是一个容器,他实际表达每个元素对象的指针。
ls=["python",[1,2,3]]
la=ls.copy()
lb=[:]
lc=list(ls)
lc[-1].append(4)
print(lc,la)
print(ls,lb)
#在lc最后一个列表改变后,所有la,lb,lc都发生了变换。
#这就是浅拷贝的问题。

深拷贝:完全拷贝对象内容

  • 采用copy库的deepcopy()方法。
  • 迭代拷贝对象内各层次对象,完全新开辟内存建立对象。
  • 深拷贝仅针对可变类型,不可变类型无需创建新对象。
import copy
ls=["python",[1,2,3]]
lt=copy.deepcopy(ls)
for i in [ls,lt]:
	for c in i:
		print(c,id(c)," ",end=" ")
	print(" ",i,id(i))
#列表内部各可变对象元素都被拷贝

类的方法引用

再看类的实例方法

  • 定义方式:def <实例方法名>(self,<参数列表>)
  • 实例方法名也是一种引用,即对方法本身的引用。
  • 当方法被引用时,方法(即函数)将产生一个对象:方法对象。
class DemoClass:
	def __init__(self,name):
		self.name=name
	def lucky(self,salt=0):
		s=0
		for c in self.name:
			s+=(ord(c)+id(salt))%100
		return s
dc1=DemoClass("老李")
lucky=dc1.lucky
print(DemoClass.lucky(dc1,10))#注释1
print(dc1.lucky(10))#注释1
print(lucky(10)) #与<对象名>.<方法>是一个对方法的引用
'''
注释1
<对象名>.<方法>(方法参数)
等价于
<类名>.<方法名>(<对象名>,方法参数)
'''

4.python类的高级话题

命名空间的理解

  • 作用域:全局变量名在模块命名空间,局部变量名在函数命名空间。
  • 属性和方法在类命名空间,名字全程:<命名空间>.<变量/函数名>。
  • 命名空间底层由一个dict实现,变量名是键,变量引用的对象是值。

命名空间:从名字到对象的一种映射

  • 复数z :z.real和z.image是对象z命名空间的两个属性。
  • 对象d:d.name和d.printName是对象d命名空间的属性和方法。
  • global和nonlocal是两个声明命名空间的保留字。
count=0 #模块的命名空间

def getCounting(a): #第一层函数的命名空间
	count=0
	if a!="":
		def doCounting(): #第二层函数的命名空间
			nonlocal count
			count+=1
		doCounting()
	return count

print(getCounting("1"),count)
print(getCounting("2"),count)
print(getCounting("3"),count)
#1 0
#1 0
#1 0
'''
nonlocal声明变量不在当前命名空间,变量在上层命名空间,但不是全局。
global声明变量在全局命名空间。
'''

类的特性装饰器

@property:类的特性装饰器

  • 使用@property把类中的方法变成对外可见的"属性"。
  • 类内部:表现为方法。
  • 类外部:表现为属性。
class DemoClass:
	def __init__(self,name):
		self.name=name
	@property #@property 用于转换方法为属性
	def age(self):
		return self._age #返回一个属性值
	
	@age.setter #@<方法名>.setter,用于设定属性的赋值操作。
	def age(self,value):
		if value<0 or value>100:
			value=30
		self._age=value #对同名属性值的赋值操作进行处理。
dc1=DemoClass("老李")
dc1.age=-100
print(dc1.age)
#输出结果为:30

自定义的异常类型

异常Exception 也是一种python类

  • try-except捕捉自定义的异常。
  • 继承exception类,可以给出自定义的异常类。
  • 自定义异常类是类继承的正常应用过程。
class DemoException(Exception): #自定义一个异常类型
	def __init__(self,name,msg="自定义异常")
		self.name=name
		self.msg=msg

try:
	raise DemoException("脚本错误") #raise保留字产生异常
except DemoException as e:
	print("{}异常的报警时{}".format(e.name,e.msg))

类的名称修饰

名称修饰:类中名称的变换约定

  • python通过名称修饰完成一些重要功能。
  • 采用下划线(_)进行名称修饰,分为5种情况。
    _ X 、 X _ 、 __ X 、 __ X __ 、 _

_ X:单下划线开头的名称修饰

  • 单下划线开头属性和方法为类内部使用 PEP8。
  • 只是约定,仍然可以通过<对象名>.<属性名>方式访问。
  • 功能 form XX import *时不会导入单下划线开头的属性和方法。
class DemoClass:
	def __init__(self,name):
		self.name=name
		self._nick=name+"同志" 约定内部使用
	
	def getNick(self):
		return self._nick

dc1=DemoClass("老李")
print(dc1.getNick())
print(dc1._nick) #仍然可以外部调用

X_ :单下划线结尾的名称修饰

  • 单下划线结尾属性或方法为避免与保留字或已命名冲突 。PEP8
  • 只是约定,无任何功能性对应。
  • 仅是为了避免重名。

__X:双下划线开头的名称修饰

  • 双下划线开头属性或方法将被解释器修改名称,避免命名冲突。
  • 不是约定,而是功能性,实现私有属性,私有方法。
  • _ _ X会被修改为: _<类名> _ _X

双下划线开头和结尾的名称修饰

  • 双下划线开头和结尾的属性或方法无任何特殊功能,名字不被修改。
  • 部分名称是保留属性或保留方法。

_:单下划线

  • 单下划线是一个无关紧要的名字,无特殊功能。

python最小空类

class <类名>():
  pass
  • 类是一个命名空间,最小空类可以当做命名空间使用。
  • 最小空类可以辅助数据存储和使用。
  • 动态增加属性是python类的一个特点。
class EmptyClass:
	pass
a=EmptyClass()
a.name="老李"
a.age=50
a.family={"儿子""小李""女儿":"马冬梅"}
print(a.family)
print(a.__dict__)
  • 建立一个最小空类,通过增加属性实现对数据被保存。

你可能感兴趣的:(微专业 python面向对象语法精讲笔记 第二周)