文章目录
一、继承和组合
继承和组合都是用来解决代码的重用性问题
继承和组合
'''继承和组合'''
'''
组合:
组合是:一种 '有'的关系
eg:老师有课程、学生有生日
# 在一个类中以另一个类的对象作为数据属性,称为类的组合
(当类有显著不同时,并且小类是较大类所需要的一种组件时,应该用组合)
继承是:一种 '是(is)' 的关系
eg:老师是人、学生是人,
# 当类与类之间有很多相同之处,应该用继承
'''
# 组合的运用
class FOO():
def __init__(self,m):
self.m = m
class Bar():
def __init__(self,n):
self.n = n
'''一个对象拥有一个属性,而这个属性的值是另一个对象'''
f1 = FOO(10)
b1 = Bar(100)
# 超级对象,通过一个属性可以访问到另一个对象的值
f1.m = b1
print(f1.m.n)
'''
以一个小例子来了解继承和组合
如下在管理系统中:
有老师类、学生类和管理员类中,有大量的重复冗余的数据,这个时候有很多相同之处,可以用继承来
2.但是学生也有课,老师也有课,这种属于有什么的关系,可以用组合
'''
class People():
school = 'HF'
def __init__(self,name,age,gender):
self.name = name
self.age =age
self.gender = gender
'课程类'
class Course():
def __init__(self, name, price, period):
self.name = name # 课程名称
self.price = price # 课程单价
self.period = period # 课程周期
'直接实例化产生几个对象'
python = Course('python',20000,'6month')
linux = Course('linux',20000,'5month')
print(python.__dict__)
print(linux.__dict__)
'管理员类'
class Admin(People):
def __init__(self, name, age, gender):
super().__init__(name, age, gender)
'学生类'
'''继承People的所有属性,解决数据冗余问题'''
class Student(People):
def __init__(self, name, age, gender, course=None):
# 使用超级对象super来调用父类对象方法,这种方式依赖于继承
super().__init__(name, age, gender)
if course is None:
course = []
self.course = course
'老师类'
class Teacher(People):
def __init__(self,name,age,gender,level):
# 使用超级对象super来调用父类对象方法,这种方式依赖于继承
super().__init__(name, age, gender)
self.level = level
'实例化各个大类'
stu = Student('jack',18,'male')
tea = Teacher('tom',25,'male',10)
adm = Admin('chen',19,'male')
'直接把课程类当成实例化对象的数据属性'
stu.course.append(python)
print(stu.course)# [<__main__.Course object at 0x000001EC7AA46B50>]
'取出课程名称'
for i in stu.course:
print(i.name) # python
print(i.price) # 20000
print(i.period) # 6month
'添加一个course数据'
tea.course = linux
print(tea.course) # <__main__.Course object at 0x000002A30E63B0D0>
print(tea.course.name) # linux
print(tea.course.price) # 20000
print(tea.__dict__)
# {'name': 'tom', 'age': 25, 'gender': 'male', 'level': 10,
#'course': <__main__.Course object at 0x000001EC3353B0D0>}
二、面向对象之反射
在Python中,反射指的是通过字符串来操作对象的属性或方法
其中有四种内置函数的使用(Python中一切皆对象,类和对象都可以用下述四个方法)
1.hasattr() 判断对象是否含有某个字符串对应的属性名或方法
2.getattr() 根据字符串获取对象对应的属性名(值)或方法名(函数体代码)
3.setattr() 根据字符串给对象设置或者修改数据
4.delattr() 根据字符串删除对象或者类中的属性
'''
hasattr
根据字符串判断这个对象里面是否存在该属性,存在返回True,否则返回False
语法:
hasattr(对象,属性名/方法名)
'''
class Student():
school = 'hafo'
def __init__(self, name, age, gender):
self.name = name
self.age = age
self.gender = gender
def test(self):
print('hello world')
def index(self):
print('from index')
stu = Student('jack', 18, 'male')
print(hasattr(stu, 'name')) # 判断是否存在这个属性 True
print(hasattr(stu, 'school')) # 判断是否存在该属性 True
print(hasattr(stu, 'test')) # 判断是否存该方法 True
'''下面这种方式也可以达到判断是否存在的效果'''
print(stu.__dict__)
print('name' in stu.__dict__) # True
print('===============================================')
'''
getattr
根据字符串获取对象对应的属性或方法
语法:
getattr(对象,属性名/方法名)
'''
stu1 = Student('tom', 20, 'male')
print(getattr(stu, 'name')) # 获取对象对应的属性
print(getattr(stu, 'school')) # 获取对象对应的属性
print(getattr(stu, 'test')) # 获取对象对应的方法的内存地址
print(getattr(stu,'test',stu.index))
print(getattr(Student,'school')) # hafo
# 可以通过加小括号直接调用,但是不如对象直接调用来的实在
getattr(stu,'test')() # hello world
'''
当你查询的属性不存在时,如果给了第三个参数,就返回第三个参数
如果给了第三个参数,查询的属性也存在时,那就直接返回属性对应的值,默认值就没用了
'''
print(getattr(stu,'hello', 666)) # 当获取对象不存在的属性时,返回666
print(getattr(stu,'school' ,777)) # 当获取对象存在属性时,返回对应的属性
print('===============================================')
'''
setattr
根据字符串给对象或者类设置属性和方法,或者修改已存在的属性和方法
语法:
# 设置属性的语法
setattr(对象/类名,属性名/属性值)
# 设置方法的语法
setattr(对象/类名,方法名/函数名或其他对象的方法名)
'''
stu2 = Student('chen', 19, 'male')
setattr(stu2,'school','BJ') # 当对象属性存在时,修改已存在的属性值
print(stu2.school) # BJ
setattr(Student,'country' ,'China') # 当类属性存在时,修改已存在的属性值
print(Student.country) # China
# print(Student.__dict__)
'''设置方法,较为复杂一些,需要现在外部定义一个函数'''
# def test():
# print('from test')
'''可以把外部定义的函数,替换对象已存在的方法'''
# setattr(stu2,'test',test()) # from test
'''
上面只是定义的普通的函数,而我们设置到对象或者类里面是要进行调用的,
既然调用就要访问对象的属性,我们连对象都没有传递怎么访问,上面那种只是将方法
反射到了对象里面,所以我们需要在上面的基础上优化
'''
def test(self):
print('这里通过外部设置进来的方法,打印结果就知道了:',self.age)
setattr(stu2,'test',test(stu2)) # 这里通过外部设置进来的方法,打印结果就知道了: 19
'''已经出效果了,结尾还是需要通过对象去调用,因为方法内有一个self参数,需要将对象传入'''
print('===============================================')
'''
delattr
根据字符串删除对象或类中的属性
语法:
delattr(对象/类名,属性名/方法名)
'''
stu3 = Student('ankn',20,'female')
print(stu3.__dict__)
delattr(stu3,'name') # 删除对象属性
print(stu3.__dict__)
print(Student.__dict__)
delattr(Student,'school') # 删除类中的属性
print(Student.__dict__)
'''删除对象或类中的属性,如果不存在数据,则会报错'''
三、面向对象之魔法方法
魔法方法就是类中定义的双下方法
为什么叫魔法方法呢?:因为不需要人为调用,在特定的条件下会自动触发运行
eg:__init__方法给对象设置独有的数据的时候会自动触发实例化
1.常用魔法方法
序号 | 魔法 | 触发时 |
---|---|---|
1 | init | 对象添加独有数据时触发 |
2 | str | 对象被执行打印操作时自动触发 |
3 | del | 对象被执行删除操作时自动触发 |
4 | enter | 当对象被当做with上下文管理操作的开始自动触发,并且该方法返回什么,as后面的变量就会接收到什么 |
5 | exit | with上下文管理语法运行完毕后自动触发(子代码结束) |
6 | getattr | 对象点不存在的名字时自动触发(只有在使用点语法调用不存在的属性时才会触发 |
6.1 | getattribute | 对象点名字就会自动触发,有它的存在就不会执行__getattr__方法 |
7 | setattr | 给对象添加或者修改数据的时候自动触发,对象.名字 = 值 |
8 | delattr | 给对象删除属性的时候自动触发 |
9 | getitem | 当通过中括号获取对象属性时,自动触发 |
10 | setitem | 当通过中括号添加或修改对象属性时,自动触发 |
11 | delitem | 当通过中括号删除对象属性时,自动触发 |
12 | call | 对象加小括号调用的时候自动触发 |
__str__方法和__repr__方法
'''务必记住,魔法方法都必须明确的知道触发条件'''
'''1.__str__和__repr__方法'''
class Student():
def __init__(self,name):
self.name = name
print('hello world')
'''
1.打印对象时,输出对象的时候会自动触发类中的__str__方法
2.返回值必须是字符串形式
3.如果它跟__repr__方法同时存在,__str__优先级更高
'''
def __str__(self):
print('from __str__')
# return '必须返回一个字符串数据' # 此处如果没有返回值会报错
return self.name
# def __repr__(self):
# print('from __repr__')
# return 'repr' # 和__str__方法一模一样,但是没有__str__优先级高
stu = Student('jack') # 实例化对象
print(stu) # 对象调用触发__str__方法
'''对象被执行打印操作时会自动触发,返回什么数据就打印什么数据'''
__del__方法
class Myclass():
country = 'China'
def __init__(self):
print('from dl')
self.f = open(r'a.txt','w')
'''
1.当你删除对象的时候会触发类中的__del__方法
2.当整个程序的代码都执行完毕的时候也会触发__del__的执行
3.一般是用来在对象被删除时自动触发回收系统资源的操作
'''
def __del__(self):
print('from __del__')
self.f.close()
obj = Myclass()
del obj.country
print(obj)
__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中的代码块执行完毕时执行!')
# print(exc_type) # 异常类型
# print(exc_val) # 异常值
# print(exc_tb) # 追溯信息
return True # 如果你的这个方法里面返回了True,with代码块出现了异常相当于没有出现异常
'''with它可以用在很多地方,但是出现with语句后面的对象中得类必须要声明__enter__和__enter__方法'''
obj = Open('cccc')
'''如果with代码块中出现了异常,则with语句之后的代码都不能正常执行'''
with Open('a.txt') as f:
print('=========>执行代码块')
raise AttributeError('error error') # 抛出异常,主动报错
# 抛出异常后后面代码就不会走,如果不抛出异常后面代码会在执行完毕后执行
print('0' * 5)
'''
执行顺序:最先执行with open(),然后执行__enter__方法,
如果有返回值,就把返回值返回给as f后继续执行下一步,如果没有则执行下一步,
而下一步执行with open里面的代码块,执行完毕后再执行__exit__方法。
(所以with open的底层逻辑就是在执行完毕后执行__exit__体内代码完后,写入了close() )
'''
__getattr__方法,settattr__方法,delattr__方法
'''这三个方法都是通过点语法触发执行的。'''
class Foo():
x = 1
def __init__(self,y):
self.y = y
# 当实例化对象后,因为里面设置了使用了点语法,设置对象点一个不存在的属性
# 所以会直接直接__setattr__方法
def __getattr__(self, item):
print('----->from getattr:你找的属性不存在')
def __setattr__(self, key, value):
print('----->from setattr')
print(key,value) # z 1 属性名、属性值
# self.key = value # 这样就无限递归了
'''
这个无限递归是,因为当对象使用点语法设置不存在的属性时,执行__setattr__方法后
执行内部代码,而self.key = value 其实就是对象点不存在的那个属性 重新赋值的
然后它又重新去执行__setattr__方法了,一直循环执行
'''
'''所以为了不让代码出现无限递归的情况,我们使用中括号来赋值'''
self.__dict__[key] = value
def __delattr__(self, item):
print('----->from delattr')
# del self.item # 这个也会无限递归,也是使用了点语法,和上面原因相同
self.__dict__.pop(item)
f1 = Foo(10)
f1.z # 当使用点语法的时候:对象点一个不存在的属性时,会自动触发getattr方法
f1.z = 1 # 当使用点语法的时候:对象设置一个不存在的属性时,会自动触发setattr方法
print(f1.z)
del f1.z # 当使用点语法的时候:对象删除了一个不存在的属性时,会自动触发delattr方法
'''
在新式类中,继承的object类中,__getattr__,__setattr__,__delattr__都自带,
如果在类中重写了,就会用重写的方法
'''
__getitem__方法,__setitem__方法,__delitem__方法
'''这三个方法都是通过中括号触发执行的。'''
class Foo():
def __init__(self,name):
self.name = name
def __getitem__(self, item):
self.__dict__[item] = item
print('----->from getitem')
return self.__dict__[item]
def __setitem__(self, key, value):
print('----->from setitem')
print(key,value) # z 1 属性名、属性值
# self[key] = value
# 这样就无限递归了,因为self[key]=value 会反回头调用__setitem__方法
self.__dict__['key'] = value
def __delitem__(self, item):
print('----->del obj.[key]时,执行')
self.__dict__.pop(item)
# def __delattr__(self, item):
# print('--------> del obj.key时,执行')
# self.__dict__.pop(item)
f1 = Foo('jack')
# print(f1["fdsdf"])
print(f1['z']) # 当使用了中括号的方式时,对象获取属性时,会自动触发__getitem__方法
f1['z'] = 1 # 当使用了中括号的方式时,对象设置属性时,会自动触发__setitem__方法
del f1['z'] # 当使用了中括号的时候:对象删除属性时,会自动触发__delitem__方法
'''
在新式类中,继承的object类中,__getitem__,__setitem__,__delitem__都自带,
如果在类中重写了,就会用重写的方法
'''
__doc__方法
class index():
'''
这是注释
这是注释
这是注释
'''
pass
class Bar(index):
pass
# doc :document:可以查看出类内部的注释信息
print(index.__doc__) # __doc__是输出类中的注释信息
print(Bar.__doc__) # __doc__无法继承到父类里面,只能在本类中
isinstance(object,cls),issubclass(sub,super)
'''isinstance(object,cls),issubclass(sub,super)'''
'isinstance(obj,cls)检查是否obj是否是类 cls 的对象'
class People():
pass
class Student(People):
pass
stu = Student()
print(isinstance(stu,Student)) # True
print(isinstance('hello',int)) # False
'issubclass(sub, super)检查sub类是否是 super 类的派生类(子类)'
print(issubclass(Student,People)) # True
__call__方法
'''
构造方法的执行是由创建对象触发的,即对象 = 类名(),
而对于__call__方法的执行是由对象加小括号触发的, 对象(),类()
'''
class Foo():
def __init__(self,name):
self.name = name
def __call__(self, *args, **kwargs):
print('__call__')
f1 = Foo('jack')
f1() # 当对象使用小括号时,会自动触发__call__