组合、反射、内置方法

目录

一、组合

1、什么是组合?

2、什么场景下使用继承? 什么场景下使用组合? 

二、反射

三、内置方法之魔术方式 

__init__

__str__,__repr__

__del__

isinstance(obj,cls)和issubclass(sub,super)

 __doc__

__enter__和__exit__

__setattr__,__delattr__,__getattr__ 

__setitem__,__getitem,__delitem__

__call__


一、组合

组合不是一个新的技术栈,它是对象的另外一种用法

1、什么是组合?

在一个类中以另外一个类的对象作为数据属性,称为类的组合。组合与继承都是用来解决代码的重用性问题。不同的是:继承是一种“是”的关系,比如老师是人、学生是人,当类之间有很多相同的之处,应该使用继承;而组合则是一种“有”的关系,比如老师有生日,老师有多门课程,当类之间有显著不同,并且较小的类是较大的类所需要的组件时,应该使用组合。

组合就是,一个对象拥有一个属性,该属性的值是另外一个对象。

class Foo:
    def __init__(self, m):
        self.m = m

class Bar():
    def __init__(self, n):
        self.n = n

"""一个对象拥有一个属性,该属性的值是另外一个对象"""
obj = Bar(20)
obj1 = Foo(10)
# 超级对象,通过一个属性可以访问到另外一个对象的值

obj.x = obj1 # 这就是一种组合的用法
print(obj.x.z) # obj1.z

2、什么场景下使用继承? 什么场景下使用组合? 

继承一般情况用在:什么是什么的情况 ----> 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)  # python
print(python.price)  # 10000
print(python.period)  # 6mon


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)  # ['python', 'linux']
# 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)  # None
tea.course = linux

print(tea.course.name)  # linux
print(tea.course.price)  # 20000

二、反射

在Python中,反射指的是通过字符串来操作对象的属性,涉及到四个内置函数的使用(Python中一切皆对象,类和对象都可以用下述四个方法)。

四个内置函数:
    getattr:获取属性(用的最多)
    setattr:设置属性
    hasattr:判断是否有某个属性
    delattr:删除

class Student():
    school = 'SH'

    def __init__(self, name, age):
        self.name = name
        self.age = age

    def func(self):
        print('from func')

    def index(self):
        print('from index')


stu = Student('kevin', 20)
attr = input('请输入你要操作的属性名:')
print(stu.school)

1、getattr(获取属性)

"""当你查询的属性不存在的时候,如果给了第三个参数,就返回第三个参数"""
"""如果你给了第三个参数,查询的属性也存在,那就直接返回属性对应的值,
默认值就没用了"""

res = getattr(stu, 'x') # 直接拿到结果
res = getattr(stu, 'x1', 666) # 直接拿到结果
"""必须掌握!!!!!!"""
res = getattr(stu, 'func1', stu.index)
print(res)  # # 直接拿到结果函数的内存地址
# <bound method Student.func of <__main__.Student object at 0x000001963F5C5A60>>
res()  # from index

# 补充
getattr(Student,'x')
getattr(time,'time')() # 时间戳
getattr(random, 'randint')(0,9) # 生成0-9的随机数
...

2、setattr

setattr(stu, 'x', 666)  # stu.x = 666
print(stu.__dict__)  # {'name': 'kevin', 'age': 20, 'x': 666}

3、hasattr(判断是否存在)

print(hasattr(stu, 'func'))  # True
if hasattr(stu, 'func'):
    getattr(stu, 'func')()  # from func
else:
    ...

4、delattr(删除)

delattr(stu, 'name')
del stu.name
print(stu.__dict__)

补充:

time=__import__('time') # 通过字符串的形式可以导入一些模块
random=__import__('random') # 通过字符串的形式可以导入一些模块
import time
random.randint(0,9) # 不提示,就自己写,不错,只要你别写错

三、内置方法之魔术方式 

它的特点就是双下划线开头的方法,其满足一定的条件就会自动触发,简称魔法。

__init__

__str__,__repr__

1. 打印对象的时候,输出对象的时候会自动触发类中的__str__方法
2. 返回值必须是字符串形式
3. 如果__init__跟__repr__方法同时存在,__str__的优先级更高

class Student():
    def __init__(self, name, age):
        self.name = name
        self.age = age

    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))  # repr: from repr
print('str:', str(stu))  # str: from str

__del__

1.一般用在回收系统资源的时候用,比如:关闭文件,关闭链接等

2.当你使用del删除对象的时候会触发__del__的执行
3. 当整个脚本的程序都执行完毕的时候,也会触发__del__的执行 

class MySQL:
    def __init__(self, ip, port):
        self.ip = ip
        self.port = port
        self.f = open('a.txt', 'w')


    def __del__(self):
        print('from _del')
        self.f.close()


mysql = MySQL('127.0.0.1', 3306)
print('1223')

isinstance(obj,cls)和issubclass(sub,super)

isinstance(obj,cls) ---> 检查是否obj是否是类 cls 的对象

issubclass(sub, super) ---> 检查sub类是否是 super 类的派生类

print(isinstance(123, str))  # False
print(type(123) is int)  # True


class Foo:
    pass


class Bar(Foo):
    pass


class Bar1():
    pass


print(issubclass(Bar1, Foo))  # False

 __doc__

查看类中得注释内容,这个属性不会继承类父类

class Foo:
    """
    '我是描述信息asdasd'
    '我是描述信息asdasd'
    '我是描述信息asdasd'
    """

    pass


class Bar(Foo):
    pass


print(Bar.__doc__)  # 该属性无法继承给子类
# None

__enter__和__exit__

我们知道在操作文件对象的时候可以这么写

with open('a.txt') as f:
  pass

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 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声明的变量')

    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('=====>执行代码块')
    print('=====>执行代码块')
    print('=====>执行代码块')
    print('=====>执行代码块')
    print('=====>执行代码块')
    # print(f,f.name)

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 # 如果你在这个方法里面返回了True,with代码块中出现了异常,就相当于没有出现异常


with Open('a.txt') as f:
    print('=====>执行代码块')
    print('=====>执行代码块')
    print('=====>执行代码块')
    print('=====>执行代码块')
    print('=====>执行代码块')
    # print(f,f.name)

with Open('a.txt') as f:
    print('=====>执行代码块')
    raise AttributeError('***着火啦,救火啊***')  # 抛出异常,主动报错
print('0'*100) #------------------------------->会执行

__setattr__,__delattr__,__getattr__ 

        必须是使用点语法

1. 使用对象.属性获取结果的时候,会自动触发__getattr__,如果不是点语法不会触发
2. 使用对象.属性 = 属性值的时候,会自动触发__setattr__
3. 使用 del 对象.属性的时候,会自动触发__delattr__的执行

        以上三个方法只是满足触发的条件,不会具体的给你获取值、设置值、删除值等操作,所以,需要自己在对应的方法里面做相关操作。

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.a=20
        # 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__

        必须是使用中括号([]) obj.x----->obj['x']
1. 使用对象.属性获取结果的时候,会自动触发__getitem__,如果不是点语法不会触发
2. 使用对象.属性 = 属性值的时候,会自动触发__setitem__

3. 使用 del 对象.属性的时候,会自动触发__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()
print(obj) # 执行 __init__
# <__main__.Foo object at 0x0000021E99153A60>
obj() # 执行 __call_
# __call__

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值