文章目录
在上一篇文章中我们讲到了继承知道了继承是一种什么“是”什么的关系。
然而类与类之间还有另一种关系,这就是组合
一、组合
组合不是一个新的技术栈,他是对象的另一种说法
什么是组合:组合就是一个对象拥有的属性,该属性的值是另一个对象。
class Aaa():
def __init__(self,m):
self.m = m
class Bbb():
def __init__(self, n):
self.n = n
obj = Bbb(10)
obj1 = Aaa(20)
"""一个对象拥有一个属性,该属性的值是另外一个对象"""
obj.x = obj1
# 超级对象:通过一个属性可以访问到另一个对象的值
print(obj.x.m)
什么场景下使用继承? 什么场景下使用组合?
继承一般情况用在:什么是什么的情况 is
组合一般用在:什么有什么的情况 has
class People(): # 1. 定义一个父类People,将共同拥有的属性与方法放在一起
school = "BHYEY"
def __init__(self, name, age, gender, ):
self.name = name
self.age = age
self.gender = gender
class Course(): # 2.单独方法提取出来做一个课程类,
def __init__(self, course_name, course_money, course_time):
self.course_name = course_name
self.course_money = course_money
self.course_time = course_time
python = Course('python', 20000, '6month')
linux = Course('linux', 10000, '5month')
class Student(People): # 建一个子类Student(学生)
def __init__(self, name, age, gender, course=None):
super().__init__(name, age, gender)
if course is None:
course = []
self.course = course
stu = Student('jack', 18, 'male')
stu.course.append(python)
stu.course.append(linux)
for i in stu.course:
print(i.course_name)
class Teacher(People): # 建一个子类Teacher(老师)
def __init__(self, name, age, gender, level):
super().__init__(name, age, gender)
self.level = level
tea = Teacher('gg', 20, 'male', 10)
tea.course = linux
print(tea.course.course_name)
二、反射
反射:可以用字符串的方式去访问对象的属性,调用对象的方法(但是不能去访问方法),python中一切皆对象,都可以使用反射。
反射有四种方法:
hasattr:hasattr(object,name)判断一个对象是否有name属性或者name方法。有就返回True,没有就返回False
getattr:获取对象的属性或者方法,如果存在则打印出来。hasattr和getattr配套使用
需要注意的是,如果返回的是对象的方法,返回出来的是对象的内存地址,如果需要运行这个方法,可以在后面添加一对()
setattr:给对象的属性赋值,若属性不存在,先创建后赋值
delattr:删除该对象指定的一个属性
1、对象应用反射
class Foo:
def __init__(self):
self.name = 'egon'
self.age = 51
def func(self):
print('hello')
egg = Foo()
print(hasattr(egg,'name')) #先判断name在egg里面存在不存在,结果是True
print(getattr(egg,'name')) #如果为True它才去得到,结果是egon
print(hasattr(egg,'func')) #结果是True
print(getattr(egg,'func')) #得到的是地址<bound method Foo.func of <__main__.Foo object at 0x0000000001DDA2E8>>
getattr(egg,'func')() #在这里加括号才能得到,因为func是方法,结果是hello
一般用法如下,先判断是否hasattr,然后取getattr
if hasattr(egg,'func'):
getattr(egg,'func')() #结果是hello
else:
print('没找到')
2. 类应用反射
class Foo:
f = 123
@classmethod
def class_method_dome(cls):
print('class_method_dome')
@staticmethod
def static_method_dome():
print('static_method_dome')
print(hasattr(Foo,'class_method_dome')) #结果是True
method = getattr(Foo,'class_method_dome')
method() #结果是class_method_dome
print(hasattr(Foo,'static_method_dome')) #结果是True
method1 = getattr(Foo,'static_method_dome')
method1() #结果是static_method_dome
三 、内置方法之魔术方法
1 .__str__ 和 __repr__
class People():
def __init__(self, name, age):
self.name = name
self.age = age
def __str__(self):
return f'str is {self.name} obj'
def __repr__(self):
return f'repr is {self.name} obj'
"""
1. 打印对象的时候,输出对象的时候会自动触发类中得__str__方法
2. 返回值必须是字符串形式
3. 如果它跟__repr__方法同时存在,__str__的优先级更高
"""
peo = People('jack', 20)
print(peo)
"""__str__ 和 __repr__都是返回对象描述信息,当两个同时出现在同一个类中__str__的优先级会比__repr__优先级要高"""
2 .__del__
"""
1.当你删除对象的时候会触发__del__的执行
2. 当整个脚本的程序都执行完毕的时候,也会触发__del__的执行
3. 一般用在用来在对象被删除时自动触发回收系统资源的操作
"""
# 主动删除运行:
class Student():
def __init__(self, name, age, gender):
self.name = name
self.age = age
self.gender = gender
def __del__(self):
print('del运行了')
stu = Student('jack', 20, 'male')
del stu.name
# 程序结束运行:
class Student():
def __init__(self, name, age, gender):
self.name = name
self.age = age
self.gender = gender
def __del__(self):
print('del运行了')
stu = Student('jack', 20, 'male')
print(stu.name)
3.___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它可以用在很多地方,但是,出现with语句后面的对象中得类必须要声明__enter__和__exit__"""
with Open('a.txt') as f:
print('=====>执行代码块')
print('=====>执行代码块')
print('=====>执行代码块')
print('=====>执行代码块')
print('=====>执行代码块')
# print(f,f.name)
简答题:先聊聊with 的用法? 问with上下文管理协议的执行原理? 为什么打开文件只会可以自动关闭?请说出原因
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中代码块执行完毕时执行我啊')
print(exc_type) # 异常类型
print(exc_val) # 异常值
print(exc_tb) # 追溯信息
return True # 如果你在这个方法里面返回了True,with代码块中出现了异常,就相当于没有出现异常
"""with它可以用在很多地方,但是,出现with语句后面的对象中得类必须要声明__enter__和__exit__"""
# with Open('a.txt') as f:
# print('=====>执行代码块')
# print('=====>执行代码块')
# print('=====>执行代码块')
# print('=====>执行代码块')
# print('=====>执行代码块')
# # print(f,f.name)
"""如果with代码块中出现了异常,则with语句之后的代码都不能正常执行"""
with Open('a.txt') as f:
print('=====>执行代码块')
raise AttributeError('***着火啦,救火啊***') # 抛出异常,主动报错
print('0'*100) #------------------------------->不会执行
4. __setattr__,__delattr__,__getattr__
点拦截方法
为什么这称为:点拦截方法呢,因为此类魔法方法只有在通过对象.操作属性时才会触发,所以在触发方法时,对给属性赋的值进行一个处理
这个方法有点类似于我们封装里面提到过的property类似效果,那么是否相似接着向下了解吧!!!
__setattr__在对象.属性=值时自动触发,也就是更改属性值时自动触发
__getattr__在对象.属性时自动触发,前提是:当属性不在对象内的时候才会触发,有点鸡肋
__delattr__在del 对象.属性时自动触发,也就是在删除对象属性时自动触发
setattr:
"""当我们通过.增加或修改对象属性的时候,会触发此方法"""
class People():
def __setattr__(self, key, value): # 默认携带3个参数,属性、属性名、属性值
print('修改了对象属性')
# 注意:如果我们查找对象的属性,那么则会报错,
# 因为对象在增加属性的时候就会执行这个方法
# 而那时候属性还没有添加到对象里,所以会报错
# print(self.name)
p = People()
p.name = 'tom'
> '我们修改了对象属性' # 可以看到,当通过:对象.属性赋值时 触发__setattr__方法的运行
getatter:
"""在我们通过.查询属性的时候且查询的方法不存在才会触发这个方法"""
class People():
def __init__(self, name, age):
self.name = name
self.age = age
def __getattr__(self, item):
# 不可以这样使用:self.属性名,因为涉及到了查询属性,这样也会造成无限递归
return f'{item}属性不存在'
p = People('jack', 18)
p.name = 'tom'
print(p.name) # 存在,不触发__getattr__方法
> 'tom'
print(p.n) # 查找的属性不存在,触发__getattr__方法
> 'n属性不存在'
delatter:
"""当通过del 对象.属性删除时,才会触发此方法"""
class People():
def __init__(self, name, age):
self.name = name
self.age = age
def __delattr__(self, item):
print(f'已经删除{item}属性')
# 我们这里不可以使用:del self.属性,因为在这里再进行属性删除会造成递归
p = People('jack', 18)
p.name = 'tom'
del p.name
> '已经删除name属性'
del p.s
> '已经删除s属性'
5.__setitem__,__getitem,__delitem__
这三种拦截方法都是通[ ]操作对象时才会触发
setitem:通过中括号向对象设置值时会触发
getitem:通过中括号向对象查询值时会触发
delitem:通过中括号删除对象值时会触发
setitem:
"""操作起来类似于字典赋值"""
class People:
def __init__(self,name,age):
self.name = name
self.age = age
# 当我们通过对象[key] = value时会触发这个方法
def __setitem__(self, key, value):
# 只能将值写入到属性内,因为如果是这样:self[key] = value,
# 则会继续触发__setitem__这个方法的运行,最终导致递归
self.__dict__[key] = value
p = People('jack',18)
p['sex'] = 'male'
print(p.sex)
> 'male'
getitem:
"""已经可以实现,通过[]向对象赋值,那么我们接下来使用[]进行取值"""
class People:
def __init__(self,name,age):
self.name = name
self.age = age
def __setitem__(self, key, value):
self.__dict__[key] = value
def __getitem__(self, key):
# 不可以这样返回值,我们并不是一个真正的字典,因为我们的属性还都只是存在于属性字典__dict__内。
# return self[key]
return self.__dict__[key] # 返回对象属性字典内对应key值
p = People('jack',18)
p['sex'] = 'male'
print(p['sex'])
> 'male'
print(p.sex)
> 'male'
delitme
"""当我们进行del 属性[key]时,也就是通过key删除对象属性时,触发的方法"""
class People:
def __init__(self,name,age):
self.name = name
self.age = age
def __setitem__(self, key, value):
self.__dict__[key] = value
def __getitem__(self, key):
return self.__dict__[key]
def __delitem__(self, key):
print(f'已经删除{key}属性值')
del self.__dict__[key] # 删除对象属性字典内对应的key
p = People('jack',18)
p['sex'] = 'male'
del p['sex']