内置方法
Python的Class机制内置了很多特殊的方法来帮助使用者高度定制自己的类,这些内置方法都是以双下划线开头和结尾的,会在满足某种条件时自动触发。
内置方法,我们也称魔术方法,简称魔法!!!
如何学习魔术方法,只需要记住各个魔术方法的触发条件即可!!!
1. init
初始化方法,调用类的时候自动触发,里面有一个self参数,用来接收对象的
class Student():
def __init__(self, name, age):
print('123')
self.name = name
self.age = age
Student('kevin', 20)
# > 输出结果是:123
stu = Student('kevin', 20)
print(stu) # <__main__.Student object at 0x00000207E7CD0198>
print(stu.name) # kevin
print(stu.age) # 20
2. str,__repr__方法
__str__方法会在对象被打印时自动触发,print功能打印的就是它的返回值,我们通常基于方法来定制对象的打印信息,该方法必须返回字符串类型。__repr__方法相同。当__str__和__repr__方法同时存在的时候,__str__的优先级高于__repr__方法
__str__方法
class Student():
def __init__(self, name, age):
self.name = name
self.age = age
def __str__(self):
print('打印或者输出对象的时候,会自动触发的方法')
'''返回值必须是字符串类型,如果不指定return返回值也会报错(不指定返回None,不是字符串)'''
# return 123 返回值是整型会报错
return 'name:%s' % self.name
stu = Student('kevin', 20)
print(stu)
# > 输出结果是:
# 打印或者输出对象的时候,会自动触发的方法
# name:kevin
__repr__方法
class Student():
def __init__(self, name, age):
self.name = name
self.age = age
def __repr__(self):
print('打印或者输出对象的时候,会自动触发的方法')
return '返回值必须是字符串类型'
# return 123 会报错
stu = Student('kevin', 20)
print(stu)
当__str__和__repr__方法同时存在的时候,__str__的优先级高于__repr__方法
'''
相同点:
1.都是打印或者输出对象的时候,会自动触发的方法
2.返回值必须是字符串类型
不同点:
当__str__和__repr__方法同时存在的时候,__str__的优先级高于__repr__方法
'''
class Student():
def __init__(self, name, age):
self.name = name
self.age = age
'''打印或者输出对象的时候,自动触发的'''
# 返回值必须是字符串
def __repr__(self):
return 'repr执行了'
'''打印或者输出对象的时候,会自动触发的方法,是要掌握的'''
# 返回值必须是字符串类型
def __str__(self):
# return 'name:%s' % self.name
print('我执行了!!!')
return 'aaaa'
'''不同点:当__str__和__repr__方法同时存在的时候,__str__的优先级高于__repr__方法'''
stu = Student('kevin', 20)
print(stu)
# > 输出结果是:
# 我执行了!!!
# aaaa
拓展知识
f = open('a.txt', 'w')
# 打印出组装的返回类型,内部重写了str方法,然后组装成了如下形式返回
print(f) # <_io.TextIOWrapper name='a.txt' mode='w' encoding='cp936'>
案例
class School:
def __init__(self, name, addr, type):
self.name = name
self.addr = addr
self.type = type
def __repr__(self):
return 'School(%s,%s)' % (self.name, self.addr)
def __str__(self):
return '(%s,%s)' % (self.name, self.addr)
s1 = School('oldboy1', '北京', '私立')
# 可以指定使用方法
print('from repr: ', repr(s1)) # from repr: School(oldboy1,北京)
print('from str: ', str(s1)) # from str: (oldboy1,北京)
# str优先级更高
print(s1) # (oldboy1,北京)
3. __del__方法
__del__会在对象被删除时自动触发。当程序全部执行完毕,也会自动调用触发。
# 1.对象被删除时自动触发
class Student():
def __init__(self, name, age):
self.name = name
self.age = age
self.f = open('a.txt', 'r')
# 1. 删除对象的时候自动触发的方法
def __del__(self):
print('触发了')
self.f.close() # 系统资源
stu = Student('kevin', 20)
del stu
print('123')
# > 输出结果是:
# 触发了
# 123
# 2.当程序全部执行完毕,也会自动调用触发
class Student():
def __init__(self, name, age):
self.name = name
self.age = age
# 1. 删除对象的时候自动触发的方法
# 2. 当程序全部执行完毕,也会自动调用触发
def __del__(self):
print('触发了')
stu = Student('kevin', 20)
print('123')
# > 输出结果是:
# 123
# 触发了
由于Python自带的垃圾回收机制会自动清理Python程序的资源,所以当一个对象只占用应用程序级资源时,完全没必要为对象定制__del__方法,但在产生一个对象的同时涉及到申请系统资源(比如系统打开的文件、网络连接等)的情况下,关于系统资源的回收,Python的垃圾回收机制便派不上用场了,需要我们为对象定制该方法,用来在对象被删除时自动触发回收系统资源的操作
# 可以用于回收系统资源
class Student():
def __init__(self, name, age):
self.name = name
self.age = age
self.f = open('a.txt', 'r')
def __del__(self):
print('触发了') # 可以用于关闭文件,
self.f.close() # 回收系统资源
stu = Student('kevin', 20)
print(stu.f)
print('123') # 当程序全部执行完毕,也会自动调用触发__del__方法
# > 输出结果是:
# <_io.TextIOWrapper name='a.txt' mode='r' encoding='cp936'>
# 123
# 触发了
4.isinstance(obj,cls)和issubclass(sub,super)
之前学习的isinstance():是判断某个数据值是不是某个数据类型
if type('abc') is dict:
print('正确')
print(isinstance('abc', str))
1.isinstance(obj,cls)检查是否obj是否是类 cls 的对象
class Foo(object):
pass
class Bar(object):
pass
obj = Foo()
stu = Bar()
print(isinstance(obj, Foo)) # True
print(isinstance(stu, Foo)) # False
# 例子2:
class Student():
pass
class Foo(Student):
pass
class Bar(Foo):
pass
obj = Bar()
print(isinstance(obj, Bar)) # True
print(isinstance(obj, Foo)) # True
print(isinstance(obj, Student)) # True
2.issubclass(sub, super)检查sub类是否是 super 类的派生类(子类)
class Foo(object):
pass
class Bar(Foo):
pass
print(issubclass(Bar, Foo)) # True
print(issubclass(Bar, object)) # True
5.__doc__方法
提取注释中的信息
class Foo:
"""
这是注释!!!
author:ly
date:2020-03-20
email:123456@qq.com
"""
# 这是#注释 但是不能识别警号的注释,返回None
pass
print(Foo.__doc__)
该属性无法继承给子类
class Foo:
"""
这是注释!!!
"""
pass
class Bar(Foo):
pass
print(Foo.__doc__) # 这是注释!!!
print(Bar.__doc__) # None
'''该属性无法继承给子类'''
6. enter,__exit__方法
# 我们知道在操作文件对象的时候可以这么写
with open('a.txt') as f:
'代码块'
上述叫做上下文管理协议,即with语句,为了让一个对象兼容with语句,必须在这个对象的类中声明__enter__和__exit__方法
class Open:
def __init__(self, name):
self.name = name
def __enter__(self):
print('出现with语句,对象的__enter__被触发,有返回值则赋值给as声明的变量')
# return self
def __exit__(self, exc_type, exc_val, exc_tb):
print('with中代码块执行完毕时执行我啊')
with Open('a.txt') as f:
print('=====>执行代码块')
# print(f) # <__main__.Open object at 0x000001D8BFB6F208>
# print(f.name) # a.txt
exit()中的三个参数分别代表异常类型,异常值和追溯信息,with语句中代码块出现异常,则with后的代码都无法执行
class Open:
def __init__(self, name):
self.name = name
def __enter__(self):
print('出现with语句,对象的__enter__被触发,有返回值则赋值给as声明的变量')
def __exit__(self, exc_type, exc_val, exc_tb):
print('with中代码块执行完毕时执行我啊')
print(exc_type) # 异常类型
print(exc_val) # 异常值/异常信息
print(exc_tb) # 追溯信息:traceback
with Open('a.txt') as f:
print('=====>执行代码块')
raise TypeError('***着火啦,救火啊***') # 它是主动抛出异常
print('0' * 100) # ------------------------------->不会执行
# > 输出结果是:
'''
出现with语句,对象的__enter__被触发,有返回值则赋值给as声明的变量
=====>执行代码块
with中代码块执行完毕时执行我啊
<class 'TypeError'>
***着火啦,救火啊***
<traceback object at 0x0000022C7C11B948>
Traceback (most recent call last):
File "E:\python project\day28\01 魔术方法.py", line 215, in <module>
raise TypeError('***着火啦,救火啊***') # 它是主动抛出异常
TypeError: ***着火啦,救火啊***
'''
如果__exit()返回值为True,那么异常会被清空,就好像啥都没发生一样,with后的语句正常执行
class Open:
def __init__(self, name):
self.name = name
def __enter__(self):
print('出现with语句,对象的__enter__被触发,有返回值则赋值给as声明的变量')
def __exit__(self, exc_type, exc_val, exc_tb):
print('with中代码块执行完毕时执行我啊')
print(exc_type)
print(exc_val)
print(exc_tb)
return True
with Open('a.txt') as f:
print('=====>执行代码块')
raise TypeError('***着火啦,救火啊***') # 它是主动抛出异常
print('0' * 100) # ------------------------------->会执行
# > 输出结果是:
'''
出现with语句,对象的__enter__被触发,有返回值则赋值给as声明的变量
=====>执行代码块
with中代码块执行完毕时执行我啊
<class 'TypeError'>
***着火啦,救火啊***
<traceback object at 0x000002608EDDA948>
0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
'''
7. setattr,delattr,__getattr__方法
class Foo:
x=1
def __init__(self,y):
self.y=y
def __getattr__(self, item):
print('----> from getattr:你找的属性不存在')
def __setattr__(self, key, value):
print('----> from setattr')
# self.key=value #这就无限递归了,你好好想想
# self.__dict__[key]=value #应该使用它
def __delattr__(self, item):
print('----> from delattr')
# del self.item #无限递归了
self.__dict__.pop(item)
# __setattr__添加/修改属性会触发它的执行
f1=Foo(10)
print(f1.__dict__) # 因为你重写了__setattr__,凡是赋值操作都会触发它的运行,你啥都没写,就是根本没赋值,除非你直接操作属性字典,否则永远无法赋值
f1.z=3
print(f1.__dict__)
# __delattr__删除属性的时候会触发
f1.__dict__['a']=3 # 我们可以直接修改属性字典,来完成添加/修改属性的操作
del f1.a
print(f1.__dict__)
# __getattr__只有在使用点调用属性且属性不存在的时候才会触发
f1.xxx
7.1 getattr
class Foo:
x = 1
def __init__(self, y):
self.y = y
'''有对象.属性(属性是不存在的)这个句式,就会被触发'''
# 查找的属性不存在的时候会被执行
def __getattr__(self, item):
print('item:%s' % item)
print('----> from getattr:你找的属性不存在')
obj = Foo(2)
print(obj.y) # 2
print(obj.x) # 1
print(obj.z)
# > 输出结果是:
'''
item:z
----> from getattr:你找的属性不存在
None
'''
7.2 setattr
class Foo:
x = 1
def __init__(self, y):
self.y = y
'''只要存在,对象.属性名 = 值,(添加/修改属性)就会被触发'''
def __setattr__(self, key, value):
print('----> from setattr')
print(key, value)
# self.key=value #这就无限递归了,你好好想想
# self.__dict__[key] = value # 应该使用它
obj = Foo(2)
# 调用类名()就会触发__init__()方法,其中有self.y = y句式,由会触发__setattr__()方法
print(obj.__dict__) # {} # 因为你重写了__setattr__,凡是赋值操作都会触发它的运行,你啥都没写,就是根本没赋值,除非你直接操作属性字典,否则永远无法赋值
obj.z = 1
print(obj.__dict__)
7.3 delattr
class Foo:
x = 1
def __init__(self, y):
self.y = y
'''__delattr__删除属性的时候会触发'''
def __delattr__(self, item):
print('----> from delattr')
# del self.item #无限递归了
self.__dict__.pop(item)
obj = Foo(2)
print(obj.__dict__) # {'y': 2}
del obj.y # ----> from delattr
print(obj.__dict__) # {}
8.setitem,__getitem,__delitem__方法
class Foo:
def __init__(self, name):
self.name = name
def __getitem__(self, item):
print('__getitem__执行了', item)
print(self.__dict__[item])
def __setitem__(self, key, value):
print(key, value)
print('__setitem__执行了')
self.__dict__[key] = value
def __delitem__(self, key):
print('del obj[key]时,我执行')
self.__dict__.pop(key)
def __delattr__(self, item):
print('del obj.key时,我执行')
self.__dict__.pop(item)
obj = Foo('kevin')
# print(obj.name) # kevin
# __getitem__对象通过中括号取值的时候会触发它的执行,键存在,则调用。键不存在,则报错
obj['name']
# '''
# __getitem__执行了 name
# kevin
# '''
# __setitem__对象中括号添加/修改属性的时候,会触发它的执行
obj['name'] = 'tony' # 键存在,值改变
obj['name1'] = 'jack' # 键不存在,值增加
print(obj.__dict__)
'''
name tony
__setitem__执行了
name1 jack
__setitem__执行了
{'name': 'tony', 'name1': 'jack'}
'''
# __delitem__对象中括号删除属性的时候,会触发它的执行。键存在,则删除。键不存在,则报错。
del obj['name1']
print(obj.__dict__)
del obj.name
print(obj.__dict__)
'''
del obj[key]时,我执行
{'name': 'tony'}
del obj.key时,我执行
{}
'''
9.__call__方法
对象后面加括号,触发执行。
注:构造方法的执行是由创建对象触发的,即:对象 = 类名() ;而对于 call 方法的执行是由对象后加括号触发的,即:对象() 或者 类()()
class Foo:
def __init__(self):
pass
# 对象加括号的时候会自动触发执行
def __call__(self, *args, **kwargs):
print('__call__执行了')
obj = Foo() # 执行 __init__
obj() # 执行 __call__
'''后续课程flask框架源码的时候,__call__方法就是入口'''
反射
在Python中,反射指的是'通过字符串来操作对象的属性'
涉及到四个内置函数的使用:(Python中一切皆对象,类和对象都可以用下述四个方法)
1. getattr()
2. setattr()
3. delattr()
4. hasattr()
class Student():
school = 'SH'
def __init__(self, name):
self.name = name
def func(self):
print('from Student.func')
stu = Student('kevin')
# print(stu.school) # SH
print(stu.name) # kevin
print(stu.__dict__) # {'name': 'kevin'}
1. getattr()
获取对象属性,当获取的属性不存在的时候,会报错
res = getattr(stu, 'name')
print(res) # kevin
res1 = getattr(stu, 'name1')
# print(res1) # 会报错
如果给了第三个参数,当属性不存在的时候,第三个参数就是默认值,但不会改变对象字典
res = getattr(stu, 'name1', 666)
print(res) # 666
print(stu.__dict__) # {'name': 'kevin'}
如果是方法名,得到的结果就是函数的内存地址,要想执行,就要结果加括号
res = getattr(stu, 'func')
print(res) # <bound method Student.func of <__main__.Student object at 0x000002A83817D4A8>>
res() # from Student.func
# getattr(stu, 'func')() # 执行方法可以直接在后面加括号
2. setattr()
setattr(stu, 'x', 123) # 给对象增加属性, 属性名不存在的时候,增加属性
setattr(stu, 'name', '123') # 给对象增加属性,属性名存在的时候,修改属性
print(stu.__dict__)
# {'name': '123', 'x': '123'}
3. delattr()
delattr(stu, 'name')
print(stu.__dict__) # {}
4. hasattr()
res1 = hasattr(stu, 'name')
res2 = hasattr(stu, 'name1')
print(res1) # True
print(res2) # False
实际案例:
基于反射可以十分灵活地操作对象的属性,比如将用户交互的结果反射到具体的功能执行
class FtpServer:
def serve_forever(self):
while True:
# 输入方法名 文件名(以输入get a.txt为例)
inp = input('input your cmd>>: ').strip() # get a.txt
cmd, file = inp.split() # 用空格来切分,get a.txt
if hasattr(self, cmd): # 根据用户输入的cmd,判断对象self有无对应的方法属性
# 等同于self.full_name => server.get
func = getattr(self, cmd) # 根据字符串cmd,获取对象self对应的方法属性名字
func(file) # => get() # 调用方法
def get(self, file): # 下载文件的函数
print('Downloading %s...' % file) # 但没有写具体的功能
def put(self, file): # 卸载文件函数,没有写具体的功能
print('Uploading %s...' % file)
server = FtpServer() # 调用类产生对象
server.serve_forever() # 对象点方法名是对象调方法
补充
反射也可以用来操作模块
import time
# time.sleep(3)
time = getattr(time, 'sleep') # 通过字符串来操作模块的方法
print(time) # <built-in function sleep> #内置方法sleep
time(3)
# 以上可以简写为:
getattr(time, 'sleep')(3) # <==> time.sleep(3)
导入模块还可以直接使用:双下划import
# import time
# 导入模块还可以直接使用:双下划import
time = __import__('time') # <==> import time
time.sleep(3)
# import random
random = __import__('random')
res = random.randint(1, 9)
print(res) # 7