面向对象高级部分

一、反射

1.什么是反射?

反射指的是一个对象应该具备,可以检测,修改,增加自身属性的能力

反射就是通过字符串操作属性

主要涉及以下四个函数:

hasattr getattr setattr delattr

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

p = Person('jack',18)
print(p.name,p.age)  # jack 18


print(hasattr(p,'name'))  # 判断某个对象的属性是否存在某个属性;此处name在p中,所以返回结果为True


print(getattr(p,'name',None))  # 从对象中取出属性,第三个值为默认值,当属性不存在时返回默认值


setattr(p,'sex','man')  # 添加新的属性
print(p.sex)  # 会将sex这个属性添加到p当中
  
  
delattr(p,'age')  # 将从对象中删除属性
print(p.age)  # 打印结果不存在,会报错

2.使用场景:

反射其实就是对属性的增删查改,但是如果直接使用内置的dict来操作,语法繁琐,不好理解

另外一个最主要的问题是,如果对象不是我自己写的是另一方提供的,我就必须判断这个对象是否满足的要求,也就是是否我需要的属性和方法

框架设计方式:

框架代码:

反射被称为框架的基石,为什么 因为框架的设计者,不可能提前知道你的对象到底是怎么设计的 所以你提供给框架的对象 必须通过判断验证之后才能正常使用 判断验证就是反射要做的事情, 当然通过dict也是可以实现的, 其实这些方法也就是对dict的操作进行了封装

 

二、元类

1.元类是什么?

它是用于创建类的类,类也是一个对象

对象是通过类实例化产生的,既然类也是对象,那么类对象也是由另一个类实例化产生的

默认情况下所有累的元类都是type

2.验证:

class Person:
    pass
p = Person()

print(type(p))  # <class '__main__.Person'>
print(type(Person))  # <class 'type'>

Person类是通过type类实例化产生的

3.学习类的目的

高度的自定义一个类,例如控制类的名字必须以大驼峰的方式来书写

类也是对象,也有自己的类,

我们的需求是创建类对象做一些限制

想到了初始化方法 我们只要找到类对象的类(元类),覆盖其中 init方法就能实现需求

当然我们不能修改源代码,所以应该继承type来编写自己的元类,同时覆盖init来完成需求

代码:

只要继承了type,那么这个类就变成了一个元类
定义一个类
class MyType(type):
    def __init__(self,class_name,bases,dict):
        super().__init__(class_name,bases,dict)
        print(class_name,bases,dict)
        if not class_name.istitle():
            raise Exception('类名错误')

class pig(metaclass=MyType):
    pass

class Duck(metaclass=MyType):
    pass

4.元类中call的方法

当调用类对象时会自动启动元类中的__call__方法 ,并将这个类本身作为第一个参数传入,以及后面的一堆参数

覆盖元类中的call之后,这个类就无法产生对象,必须调用super().call来完成对象的创建 并返回其返回值

使用场景:

当想要控制对象的创建过程时,就覆盖call方法

当想要控制类的创建过程时,就覆盖init方法

例题:

实现将对象的所有属性名称转为大写

class MyType(type):
    def __call__(self, *args, **kwargs):
        new_args = []
        for a in args:
            new_args.append(a.upper())

            print(new_args)
            print(kwargs)
            return super().__call__(*new_args,**kwargs)


class Person(metaclass=MyType):
    def __init__(self,name,gender):
        self.name = name
        self.gender = gender

p = Person(name="jack",gender="woman")
print(p.name)
print(p.gender)

注意:一旦覆盖了call必须调用父类的call方法来产生对象并返回这个对象

5.补充new方法

当要创建类对象时,会首先执行元类中的__new__方法,拿到一个空对象,然后会自动调用__init__来对这个类进行初始化操作 注意:,如果你覆盖了该方法则必须保证,new方法必须有返回值且必须是,对应的类对象

6.单例设计模式

单例:指的是一个类产生一个对象

设计模式:是用于解决某种固定问题的套路

单例的作用:能够节省资源,当一个类的所有对象属性全部相同时,则没有必要创建多个对象

# 单例n元类
class Single(type):
    def __call__(self, *args, **kwargs):
        if hasattr(self,"obj"): #判断是否存在已经有的对象
            return getattr(self,"obj") # 有就返回

        obj = super().__call__(*args,**kwargs) # 没有则创建
        print("new 了")
        self.obj = obj # 并存入类中
        return obj


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


class Person(metaclass=Single):
    pass

# 只会创建一个对象
Person()
Person()

三、冒泡排序的补充

将此列表从大到小进行排序
s = [2,1,3,5]
第一圈:
[2,1,3,5]
第一次 得出2的位置
[2,1,3,5]
第二次
[2,3,1,5]
第三次
[2,3,5,1]

次数为  元素个数 - 1 - (圈数索引为0)


第二圈:
[2,3,5,1]
第一次
[3,2,5,1]
第二次
[3,5,2,1]

次数为 元素个数 - 1 - (圈数索引为1)

第三圈:
[3,5,2,1]
第一次 
[5,3,2,1]

次数为 元素个数 - 1 - (圈数索引为2)
 总结规律
圈数 是元素个数减一
次数 元素个数 - 1 - (圈数索引)
我们需要两层循环
一层控制圈数
一层控制次数
冒泡排序的模板
s = [2,1,3,5,100,24,12,12,1,2,1,1,4,32]
for i in range(len(s)-1):
    for j in range(len(s)-1-i):
        # 如果前面的小于后面的则交换位置
        if s[j] > s[j+1]:
            s[j],s[j+1] = s[j+1],s[j]
print(s)


 







原文地址:https://www.cnblogs.com/xiongying4/p/11271911.html