Python继承和组合、面向对象之反射、面向对象之魔法方法

一、继承和组合

继承和组合都是用来解决代码的重用性问题

继承和组合

	'''继承和组合'''
'''
组合:
    组合是:一种 '有'的关系
        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.常用魔法方法

序号魔法触发时
1init对象添加独有数据时触发
2str对象被执行打印操作时自动触发
3del对象被执行删除操作时自动触发
4enter当对象被当做with上下文管理操作的开始自动触发,并且该方法返回什么,as后面的变量就会接收到什么
5exitwith上下文管理语法运行完毕后自动触发(子代码结束)
6getattr对象点不存在的名字时自动触发(只有在使用点语法调用不存在的属性时才会触发
6.1getattribute对象点名字就会自动触发,有它的存在就不会执行__getattr__方法
7setattr给对象添加或者修改数据的时候自动触发,对象.名字 = 值
8delattr给对象删除属性的时候自动触发
9getitem当通过中括号获取对象属性时,自动触发
10setitem当通过中括号添加或修改对象属性时,自动触发
11delitem当通过中括号删除对象属性时,自动触发
12call对象加小括号调用的时候自动触发

__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__
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值