组合
组合就是一个对象拥有一个属性,该属性的值是另外一个对象
class Foo:
def __init__(self, m):
self.m = m
class Bar():
def __init__(self, n):
self.n = n
"""一个对象拥有一个属性,该属性的值是另外一个对象."""
obj=Bar(10)
obj1 = Foo(20)
# 超级对象,通过一个属性可以访问到另外一个对象的值
obj.x=obj1
print(obj.x.m)
继承一般情况用在:什么是什么的情况 is
组合一般用在:什么有什么的情况 has
"""什么场景下使用继承? 什么场景下使用组合?"""
class People():
school = 'SH'
def __init__(self, name, age, gender, course_name=None, course_price=None, course_period=None):
self.name = name
self.age = age
self.gender = gender
self.course_name = course_name
self.course_price = course_price
self.course_period = course_period
class Admin(People):
def __init__(self, name, age, gender):
super().__init__(name, age, gender)
class Course():
def __init__(self, name, price, period):
self.name = name
self.price = price
self.period = period
python=Course('python', 10000, '6mon')
linux=Course('linux', 20000, '5mon')
# print(python.name)
# print(python.price)
# print(python.period)
class Student(People):
def __init__(self, name, age, gender, course=None):
super().__init__(name, age, gender)
if course is None:
course = []
self.course = course
stu = Student('kevin', 20, 'male')
stu.course.append(python.name)
stu.course.append(linux.name)
print(stu.course) # [<__main__.Course object at 0x000001F258F86B80>, <__main__.Course object at 0x000001F258F511C0>]
# stu.course: []
for i in stu.course:
print(i)
class Teacher(People):
def __init__(self, name, age, gender, level):
super().__init__(name, age, gender)
self.level = level
tea = Teacher('ly', 19, 'female', 10)
print(tea.course_name)
tea.course=linux
print(tea.course.name)
print(tea.course.price)
反射
在Python中,反射指的是通过字符串来操作对象的属性,涉及到四个内置函数的使用(Python中一切皆对象,类和对象都可以用下述四个方法)
四个内置函数:
getattr: 获取属性
setattr:设置属性
hasattr:判断是否有某个属性
delattr:删除
class Teacher:
def __init__(self,full_name):
self.full_name =full_name
t=Teacher('Egon Lin')
# hasattr(object,'name')
hasattr(t,'full_name') # 按字符串'full_name'判断有无属性t.full_name
# getattr(object, 'name', default=None)
getattr(t,'full_name',None) # 等同于t.full_name,不存在该属性则返回默认值None
# setattr(x, 'y', v)
setattr(t,'age',18) # 等同于t.age=18
# delattr(x, 'y')
delattr(t,'age') # 等同于del t.age
内置方法之魔术方式(重要)
Python的Class机制内置了很多特殊的方法来帮助使用者高度定制自己的类,这些内置方法都是以双下划线开头和结尾的,会在满足某种条件时自动触发,我们以常用的__str__和__del__为例来简单介绍它们的使用。
__str__,__repr__方法
__str__方法会在对象被打印时自动触发,print功能打印的就是它的返回值,我们通常基于方法来定制对象的打印信息,该方法必须返回字符串类型
class Student():
def __init__(self, name, age):
self.name = name
self.age = age
"""
1. 打印对象的时候,输出对象的时候会自动触发类中得__str__方法
2. 返回值必须是字符串形式
3. 如果它跟__repr__方法同时存在,__str__的优先级更高
"""
def __str__(self): # 这个用的是最多的
print('str')
return 'from str'
def __repr__(self):
print('repr')
return 'from repr'
stu = Student('ly', 20)
# print(stu) # <__main__.Student object at 0x00000226AC4F5A60>
# print('repr:', repr(stu))
print('str:', str(stu))
__del__方法
__del__会在对象被删除时自动触发。由于Python自带的垃圾回收机制会自动清理Python程序的资源,所以当一个对象只占用应用程序级资源时,完全没必要为对象定制__del__方法,但在产生一个对象的同时涉及到申请系统资源(比如系统打开的文件、网络连接等)的情况下,关于系统资源的回收,Python的垃圾回收机制便派不上用场了,需要我们为对象定制该方法,用来在对象被删除时自动触发回收系统资源的操作
class MySQL:
def __init__(self, ip, port):
self.ip = ip
self.port = port
self.f = open('a.txt', 'w')
"""
1.当你删除对象的时候会触发__del__的执行
2. 当整个脚本的程序都执行完毕的时候,也会触发__del__的执行
3. 一般用在用来在对象被删除时自动触发回收系统资源的操作
"""
def __del__(self):
print('from _del')
self.f.close()
mysql = MySQL('127.0.0.1', 3306)
isinstance(obj,cls)和issubclass(sub,super)
isinstance(obj,cls)检查是否obj是否是类 cls 的对象
class Foo(object):
pass
obj = Foo()
isinstance(obj, Foo)
issubclass(sub, super)检查sub类是否是 super 类的派生类
class Foo(object):
pass
class Bar(Foo):
pass
issubclass(Bar, Foo)
__doc__
class Foo:
'我是描述信息'
pass
print(Foo.__doc__)
class Foo:
'我是描述信息'
pass
class Bar(Foo):
pass
print(Bar.__doc__) #该属性无法继承给子类
__enter__和__exit__
上下文管理协议,即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它可以用在很多地方,但是,出现with语句后面的对象中得类必须要声明__enter__和__exit__"""
with Open('a.txt') as f:
print('=====>执行代码块')
print('=====>执行代码块')
__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)
with Open('a.txt') as f:
print('=====>执行代码块')
raise AttributeError('***着火啦,救火啊***')
print('0'*100) #------------------------------->不会执行
如果__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 AttributeError('***着火啦,救火啊***')
print('0'*100) #------------------------------->会执行
谈一谈with上下文管理器:
首先with可以进行上下文管理,我们最常见的就打开文件比如with open,它的特点是自动打开和关闭文件。只要出现with语句就会自动触发__enter__()魔术方法,然后会继续执行with的代码块,执行完毕后会再执行__exit__()魔术方法。
__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.xxxxxx
__setitem__,__getitem,__delitem__
class Foo:
def __init__(self,name):
self.name=name
def __getitem__(self, item):
print(self.__dict__[item])
def __setitem__(self, key, value):
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)
f1=Foo('sb')
f1['age']=18
f1['age1']=19
del f1.age1
del f1['age']
f1['name']='alex'
print(f1.__dict__)
__call__
对象后面加括号,触发执行。
注:构造方法的执行是由创建对象触发的,即:对象 = 类名() ;而对于 __call__ 方法的执行是由对象后加括号触发的,即:对象() 或者 类()()
class Foo:
def __init__(self):
pass
def __call__(self, *args, **kwargs):
print('__call__')
obj = Foo() # 执行 __init__
obj() # 执行 __call__