lchengshao

组合 反射 魔术方法的使用

1. 组合
组合:把另外一个类的对象属性赋值给当前对象的属性

首先要明白 什么是什么的区别 为什么要继承父类
比如三个类  老师类  学生类 课程类;   虽然老师和学生有课程  但是老师和学生是课程吗? 还是老师和同学是课程
"""
    is a 的关系 代表的是 继承的关系
    和什么有什么的关系 就是组合
"""
所以不想继承 又想用到其他类中的属性 就需要 创建超级对象 来 获取其他类中的属性和方法 
class Student:
    pass

class Teacher:
    pass

class cuosce:
    pass


案例如下:
"""
1. 老师类 : 初始化属性有  name,gender,age ,evol.salary
2. 学生类 : 初始化属性有  name , gender age uid class_name
3. 课程类: name gender age course price day 
4. 把类中冗余的代码写一个类 来继承
"""
class Tar_Stu:
    def __init__(self,name,age,gender):
        self.name = name
        self.age = age
        self.gender = gender

class Teacher(Tar_Stu):

    def __init__(self,name,age,gender,evol,salary,course):
        super().__init__(name,age,gender)
        self.evol = evol
        self.salary = salary
        self.course = course

    def ter_course(self):
        print('%s 老师有 %s 课 ' % (self.name,self.course))

class Student(Tar_Stu):

    def __init__(self, name, age, gender,uid,class_name,course):
        super().__init__(name, age, gender)
        self.uid = uid
        self.class_name = class_name
        self.course = course

    def stu_course(self):
        print('%s 同学有 %s 课 ' % (self.name,self.course))

class Course:
    def __init__(self,course,price,day):
        self.course = course
        self.price = price
        self.day = day

    def course_time(self):
        print('%s 课程 %s 钱 %s 天' % (self.course,self.price,self.day))


terchar = Teacher('huang',28,'male','10级',15000,'python')  # 老师对象
stu = Student('lu',88,'male',88,'九五班','python')         # 学生对象

python = Course('python',22000,'180')                  # 课程对象
linux = Course('linux',25000,200)                       # 课程对象

terchar.tar_course = python             # python 对象会赋值给 tar_course
terchar.tar_course.course_time()        # 调用的时候 调用的对象.被赋值的名字.方法即可 完成了不用继承也可以用其他类

stu.stu_course = linux
stu.stu_course.course_time()

stu.stu_course = [python,linux]     # 可以用列表来装多个 赋值对象

for i in stu.stu_course:            # 调用的时候 for 循环即可
    i.course_time()

2. 反射

"""反射:就是通过字符串的形式来操作对象的属性"""

四个内置函数:
    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)

# 1. 获取属性
"""当你查询的属性不存在的时候,如果给了第三个参数,就返回第三个参数"""
"""如果你给了第三个参数,查询的属性也存在,那就直接返回属性对应的值,默认值就没用了"""
# res=getattr(stu, 'school1', 666) # SH 666

res=getattr(stu, 'func1', stu.index) # 获取类中的方法 第一个参数是 对象 第二的参数是 类中的方法 没有找到的情况下 就返回第三个默认值
print(res())

## 设置
setattr(stu, 'x', 666) # 类中有这个属性就修改 没有就增加
print(stu.__dict__)

### hasattr 判断有没有这个属性 返回布尔值
print(hasattr(stu, 'func'))
if hasattr(stu, 'func'):
    getattr(stu, 'func')()
else:
    ...

## delattr 删除属性 没有就报错 有就删除
delattr(stu, 'name1')
del stu.name
print(stu.__dict__)

 3. 魔术方法

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

1. __str____repr__  方法

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

    def __str__(self):
        print('%s 姓名 %s 年龄' % (self.name,self.age))
        return '11'

    def __repr__(self):
        print(self.name,self.age)
        return '22'

    """
    __str__ 和 __repr__ 这两个都是在打印对象或者输出对象的时候触发 但是 都必须返回字符串类型 否则报错
     而且__str__优先级更高
    """

obj = Student('huang',18)
print(obj)

print(str(obj))
print(repr(obj))

2. __del__ 方法:

class Student():
    def __init__(self,name,age):
        self.name = name
        self.age = age
        self.f = open('a.txt', 'w')

    def __del__(self):
        print('form del')
        self.f.close()

stu = Student('huang',18)

del stu

print(123)
"""
    1.当你删除对象的时候会触发__del__的执行
    2. 当整个脚本的程序都执行完毕的时候,也会触发__del__的执行
    3. 一般用在用来在对象被删除时自动触发回收系统资源的操作
"""

3. 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__和__exit__"""

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

#     # print(f,f.name)
"""如果with代码块中出现了异常,则with语句之后的代码都不能正常执行"""
with Open('a.txt') as f:
    print('=====>执行代码块')
    raise AttributeError('***着火啦,救火啊***')  # 抛出异常,主动报错
print('0' * 100)  # ------------------------------->不会执行

"""
1. with的执行顺序 第一步 有个类名字叫 Open 里面有三个方法 一个是__init__,传一个文件名字的 
 第二个是 __enter__ 方法,这个是出现with语句,对象的__enter__被触发,有返回值则赋值给as声明的变量
第三个 执行with代码块的时候 执行__exit__ 方法 如果with代码块中出现了异常,则with语句之后的代码都不能正常执行
raise AttributeError('***着火啦,救火啊***')  # 抛出异常,主动报错 后面的代码不会执行了
__exit__ 里面有三个参数 异常类型 异常值  追溯信息
"""

4. __dco__

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

print(Bar.__doc__)

# 打印类中的描述信息  但只能是当前类


5.  isinstance 判断类型 返回布尔值 issubclass 
print(isinstance(123, str))

print(issubclass(Bar, Foo)) # 第一个参数是子类 第二个是父类 判断是否是继承类 返回布尔值

4. __setattr__,__delattr__,__getattr__

class Foo:
    x=1
    def __init__(self,y):
        self.y=y

    """当你找的属性不存在的时候,会触发__getattr__,但是必须是点语法的时候才会  __dict__ 不会"""
    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)

obj=Foo(10)

obj.z  # 找不到属性的情况下 先执行 __setattr__ 查找,找不到就执行__getattr__
obj.a = 'a'  # 如果是进行赋值操作 第一次执行 __setattr__ 查找字典 没有找到 就添加进去字典 在执行一次

# print(obj.a)
del obj.a # 删除的时候触发 __delattr__
"""
----> from setattr
----> from getattr:你找的属性不存在
----> from setattr
----> from delattr
"""

5. __setitem__,__getitem,__delitem__

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

    """当你通过中括号获取对象的属性的时候,会自动触发__getitem__"""
    def __getitem__(self, item):
        print('__getitem__')
        print(self.__dict__[item])

    def __setitem__(self, key, value):
        # key:age
        # value:18
        print('__setitem__')
        self.__dict__[key] = value
        #  self.__dict__['age'] = 18

    def __delitem__(self, key):
        print('del obj[key]时,我执行')
        self.__dict__.pop(key)
        pass

    # def __delattr__(self, item):
    #     print('del obj.key时,我执行')
    #     self.__dict__.pop(item)
obj=Foo('tom')

print(obj.name)  # 直接获取的是 属性

obj['name']  #  """当你通过中括号获取对象的属性的时候,会自动触发__getitem__"""

obj['age'] = 18   # 通过字典设置值 或者修改值 的时候触发 __setitme__
obj['age'] = 19
# print(obj.age)
#
# del obj['age']   # 通过字典删除值的时候触发

6. __call__方法

class Foo:

    def __init__(self):
        pass
    #### 当对象加括号的时候会触发__call__的执行
    def __call__(self, *args, **kwargs):
        print('__call__')

obj=Foo()
print(obj)
obj() # #### 当对象加括号的时候会触发__call__的执行

 

 

posted on 2023-10-10 22:11  Lubomierz  阅读(3)  评论(0编辑  收藏  举报

导航