Python学习--Day 20
- 视频来源:python视频 900集
148.面向对象之应用(小花猫)
- 跟着老师走了一遍。
- 加一个下节课听到的知识点在这里吧,这里写比较合适:类中普通方法之间的调用是要通过self进行的。
class cat:
type = 'cat'
# 通过魔术函数初始化
def __init__(self,nickname,age,color):
self.nickname = nickname
self.age = age
self.color = color
def eat(self,food):
print('{} like eat {}'.format(self.nickname,food))
def catch_mouse(self,color,weight):
print('The {} cat {} catch a mouse whose weight is {}'.format(color,self.nickname,weight))
def sleep(self,hour):
if hour < 8:
print('sleep !')
else :
print('get up!')
cat1 = cat('Amy',4,'red')
cat1.sleep(6)
149.面向对象之应用(小花猫)
非常尴尬,这集重复了,跟上面的一样,这里不再听一遍了2333…上传前大概是文件太多老师马虎了,不过作为白嫖党不敢挑了哈哈哈
150.面向对象之类方法
- 类方法:①定义的时候需要装饰器,
@classmethod
;②类方法中的参数不是对象,而是类cls;③类方法中只可以使用类属性,类方法不依赖于对象,在对象没有创建之前也可以使用(即加载时机是跟随类被创建时);④类方法不可以调用普通函数(因为self没有被传参),但是普通函数可以调用类方法。 - 类方法的作用:可以完成一些不依赖于需要先创建对象然后再执行的操作
- 从这里也可以回答昨天留下来的一个问题,魔术方法
__init__
创建的不是类属性,是对象属性,要先创建对象然后才能生成,这样的属性通过类方法调用不到,也因此类方法可以执行一些不需要创建对象就能进行的操作。 - 下面是老师讲过的代码,关键的地方都一样。
class Dog:
# 通过魔术函数初始化
def __init__(self,nickname):
self.nickname = nickname # 动态添加nickname属性
def run(self): # 依赖于对象才能调用
print('Dog is running.')
@classmethod
def test(cls): # 不依赖于对象调用
print(cls)
print(cls.nickname) # AttributeError: type object 'Dog' has no attribute 'nickname'
# 此时的nickname只是对象有的属性,并不是类具有的属性
d = Dog('Tom')
d.run()
Dog.test() # √
d.test() # 报错
151.面向对象之静态方法
- 在变量前添加两个横杠
__
使对象私有化——在类外部调用这个变量是无法访问的。
class Person(object):
__age = 16 # 变量私有化,对外不开放
def show(self):
print(self.age)
@classmethod
def test(cls):
print('类方法')
p = Person()
# 访问出错:
print(p.age) # AttributeError: 'Person' object has no attribute 'age'
- 静态方法:①类似类方法;②需要使用装饰器
@staticmethod
;③静态方法无须使用任何参数,可以自己定义参数,但是绝对不需要self
和cls
;④也只能访问类的属性和方法,对象的属性是无法访问的;⑤加载时机与类方法一致
class Person(object):
__age = 16 # 变量私有化,对外不开放
def __init__(self,name):
self.name = name
@staticmethod
def stm():
print(Person.__age)
Person.stm()
- 总结:类方法和静态方法
项目 | 类方法 | 静态方法 |
---|---|---|
装饰器 | @classmethod | @staticmethod |
参数项 | 有必要参数cls | 无必要参数 |
访问权限 | 只能访问类的属性和方法,对象的属性是无法访问的 | 同类方法 |
调用 | 都可以使用类名进行调用 | 同类方法 |
创建时间 | 都可以在创建对象之前使用,因为不依赖于对象 | 同类方法 |
- 总结:普通方法、静态和类方法
项目 | 普通方法 | 静态与类方法 |
---|---|---|
装饰器 | 无 | 有 |
对象依赖 | 依赖对象 | 不依赖 |
调用 | 只有创建对象才可以调用 | 不必创建对象 |
152.面向对象之魔术方法(init与new)
-
魔术方法之
__init__()
触发时机:初始化对象时触发(不是实例化触发,但是和实例化在一个操作中)
参数:至少一个self
返回值:无
作用:初始化对象的成员
注意:初始化的属性不是类属性 -
魔术方法之
__new__()
——实例化的魔术方法
触发时机:在实例化对象时触发
参数:至少一个cls接收当前类
返回值:必须返回一个对象实例
作用:实例化对象
注意:实例化对象是object类底层上实现,其他类继承了Object的__new__()
才能够示例实例化对象 -
new
与init
:使用new
会将init
进行覆盖
class Person:
def __init__(self,name):
print('___This is init()')
def __new__(cls,*args,**kwargs):
print('___This is new()')
p = Person('Amy') # 执行结果:___This is new()
print(p.name) # 执行结果:AttributeError: 'NoneType' object has no attribute 'name'
- 将系统自带的
__new__()
调用出来的方法:(注意这段老师讲的有点乱,下面代码是老师反复试错的成功案例)
执行Person()的时候,会先进入函数自己的__new__()
,然后该函数返回了object的__new__()
,object的__new__()
的功能比较单一,就是申请内存开辟空间并返回内存地址,代表对象实例化成功。对象实例化成功后,return出来的地址交给__init__()
并执行,获取init执行完毕之后的地址返回给p对象。可以参考图片。
__new__()
的时机功能是向内存要空间并返回一个地址。
class Person:
def __init__(self):
print('___This is init()')
def __new__(cls,*args,**kwargs):
print('___This is new()')
return object.__new__(cls,*args,**kwargs)
p = Person()
153.面向对象之魔术方法(del)
- 上节课的遗留问题,老师讲解了一下传参应该怎么写:老师随后通过打印
__new__()
返回的地址和__init__()
接收的地址、以及p对象的地址,三者是相同的,证实了上节课所讲的流程。
class Person:
def __init__(self,name):
self.name = name
print('___This is init()')
def __new__(cls,*args,**kwargs):
print('___This is new()')
return object.__new__(cls) # 注意这里不要写*args,**kwargs了
p = Person('aa')
# 结果:
# ___This is new()
# ___This is init()
- 魔术方法之
__call__()
——调用对象的魔术方法
触发时机:将对象当作函数调用时触发,如果我们向使用对象作为函数调用就要重写__call__()
,不重写会报错
参数:至少一个self接收对象,其余根据调用时参数决定
调用方法:对象()
返回值:根据情况而定
示例:
class Person:
def __init__(self,name):
self.name = name
print('___This is init()')
def __call__(self, *args, **kwargs):
print('call')
p = Person('aa')
p() # 将对象当作函数名调用的时候,触发call
- 魔术方法之
__del__()
——析构魔术方法
触发时机:当对象没有用(没有任何变量引用)的时候被触发
参数:一个self
返回值:无
作用:使用完对象进行资源回收 - 系统方法——
sys.getrefcount('p_object')
查看当前程序某地址被引用个数是多少,当使用该函数的时候会计一次,因此在程序其他地方引用该地址的数目是返回值-1。注意要使用import sys
。
import sys
class Person:
def __init__(self,name):
self.name = name
print('___This is init()')
p = Person('aa')
p1 = p
p2 = p
print(sys.getrefcount(p)) # 结果是4,因为该函数本身也调用一次,加前面p1、p、p2一共4次
p1.name = 'tom'
print(p.name) # 修改p1的同时也会修改p 输出结果是tom不是aa
- 关于
__del__()
的使用:定义__del__()
方法释放资源,执行del 对象
的时候只是断掉了p1对p的引用,p的name属性还在。
①对象赋值p = Person()
+p1 = p
,p和p1共同指向同一个地址;②删除地址引用,del p1
是删除对地址的引用。③查看引用次数使用sys.getrefcount('p_object')
;④当一个空间没有了任何引用的时候,才会执行__del__()
,python解释器会在程序末尾自动回收所有引用资源,因此如果程序还有未删除的引用该空间的变量名,__del__()
就会在最后退出程序的时候才执行。(下面有详细解释)⑤一般别碰这个函数,会导致回收机制紊乱。
import sys
class Person:
def __init__(self,name):
self.name = name
# print('___This is init()')
def __del__(self):
print('del----')
p = Person('aa')
p1 = p
p2 = p
print(sys.getrefcount(p)) # 4
del p2
del p1
print(sys.getrefcount(p)) # 3
# 输出结果:
# 4
# 3
# del----
__del__()
的执行顺序问题(上面④的详解)
第一段代码是全部铲除了对p的引用(包括p自身),可以发现__del__()
被"提前"执行了;第二段是没有全部删除对p的引用,可以发现__del__()
是最后才执行的,也就是程序默认的回收程序。
class Person:
def __init__(self,name):
self.name = name
def __del__(self):
print('del----')
p = Person('aa')
print('程序开始')
p1 = p
p2 = p
del p1
del p2
del p
print('程序末尾')
# 执行结果:
# 程序开始
# del----
# 程序末尾
class Person:
def __init__(self,name):
self.name = name
def __del__(self):
print('del----')
p = Person('aa')
print('程序开始')
p1 = p
p2 = p
del p1
del p2
print('程序末尾')
# 执行结果:
# 程序开始
# 程序末尾
# del----
154.面向对象之魔术方法(str)
__str__()
——如果单纯打印对象名称只会返回地址,对于开发者没有太大意义,因此重写该函数会给开发者返回更多的内容。
触发时机:打印对象名称的时候会自动触发调用__str__()
内的内容
注意:一定要写return,return后面是你需要看到的内容
示例:
class Person:
def __init__(self,name,age):
self.name = name
self.age = age
def __str__(self):
return '姓名是' + self.name + ',年龄是' + str(self.age) # 注意要使用str转换
p = Person('Amy',34)
print(p) # 因为重写了str魔法函数
# 返回内容:姓名是Amy,年龄是34
- 魔术方法:不需要调用,在满足特定条件的时候自动触发。
- 总结魔术方法:重点记init、str;new、del要了解,一般不需要重写,重写会带来更多的麻烦;call视情况自己定。
- 大总结:
普通方法定义:def 方法名 (self [, 参数]): 方法体
,调用的时候要使用对象.方法名()
,方法之间的调用要记得使用self.函数名()
类方法:@classmethod
装饰器的使用+def 方法名(cls,[参数]) : 函数体
,调用对象.方法名()
或类名.方法名()
静态方法:@staticmethod
装饰器的使用+def 方法名([参数]) : 函数体
,调用对象.方法名()
或类名.方法名()
魔术方法:触发后自动执行。