面向对象(反射,元类) 排序方法

1.反射(reflect)

  1.概念: 反射指的是一个对象应该具备可以检测修改增加自身属性的能力,反射就是通过字符串操作属性

  2 反射设计4个函数

    1.hasattr  判断对象中是不是又name属性,第一个参数是对象,第二个是属性

    2.gerattr  获取对象属性,第一个为对象,第二是属性,第三个可以设置不存在后返回的提示信息

      语法:getattr(object, name, default=None)

    3.setattr  为对象添加属性.第一个为对象,第二个为属性名,第三个为添加属性的值

      语法:setattr(x, y, v)  k: key v:value     x.y = v

    4. delattr  删除对象属性, 第一个为对象,第二个属性名

    案例:

class Person:

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

p = Person('小明','')
print(hasattr(p,'name'))  # True 判断p中是不是有name属性
print(getattr(p,'name',None)) # 如果找到返回属性的值,没有返回None
res = getattr(p,'age',None)  # None
print(res)  # None
res1 = getattr(p,'age','属性不存在') # # 如果找到返回属性的值,没有返回第三个参数
print(res1)  # 属性不存在
setattr(p,"age",18)   # 为p添加age属性.属性的值为18
print(p.age)   # 18
delattr(p,'name')  # 删除name属性
print(p.name)   # 报错.name已经不存在了

  3. 使用场景:反射就是对属性的增删改查,如果对象不是自己写的,而是另一方提供,那么就必须判断这个对象是否有需要的属性和方法,例如框架,所以说反射式框架的基石

class WinCMD:
    def cd(self):
        print("wincmd 切换目录")

    def delete(self):
        print("wincmd 删除目录")

    def dir(self):
        print("wincmd 目录列表")


class LinuxCMD:
    def cd(self):
        print("linuxcmd 切换目录")

    def rm(self):
        print("linuxcmd 删除目录")

    def ls(self):
        print("linuxcmd 目录列表")

def run(plugin):
    while 1:
        cmd = input("请输入操作指令")
        if cmd == "exit":
            break
        if hasattr(plugin,cmd):
            func = getattr(plugin,cmd,None)
            func()
        else:
            print("命令不存在")

p = WinCMD
run(p)

  4. 动态导入:上述框架代码中 写死了必须使用某个类,这是不合理的,因为无法提前知道对方的类在什么地方 以及类叫什么

所以我们应该为框架的使用者提供一个配置文件,要求对方将累的信息写入配置文件

然后框架自己去加载需要的模块

  插件部分 lib目录下,文件名plugin.py

class WinCMD:

    def cd(self):
        print("wincmd 切换目录....")

    def delete(self):
        print("wincmd 要不要删库跑路?")

    def dir(self):
        print("wincmd 列出所有文件....")

class LinuxCMD:

    def cd(self):
        print("Linuxcmd 切换目录....")

    def rm(self):
        print("Linuxcmd 要不要删库跑路?")

    def ls(self):
        print("Linuxcmd 列出所有文件....")

  settings 中路径

CLASS_PATH = "lib.plugin.LinuxCMD"

  框架代码

import importlib
import settings

# 框架已经实现的部分
def run(plugin):
    while True:
        cmd = input("请输入指令:")
        if cmd == "exit":
            break
        # 因为无法确定框架使用者是否传入正确的对象所以需要使用反射来检测
        # 判断对象是否具备处理这个指令的方法
        if hasattr(plugin,cmd):
            # 取出对应方法方法
            func = getattr(plugin,cmd)
            func() # 执行方法处理指令
        else:
            print("该指令不受支持...")
    print("see you la la!")


# 创建一个插件对象 调用框架来使用它
# wincmd = plugins.WinCMD()
# 框架之外的部分就有自定义对象来完成

# 框架 得根据配置文件拿到需要的类

path = settings.CLASS_PATH
# 从配置中单独拿出来 模块路径和 类名称
module_path,class_name = path.rsplit(".",1)
#拿到模块
mk = importlib.import_module(module_path)
# 拿到类
cls = getattr(mk,class_name)
# 实例化对象
obj = cls()
#调用框架
run(obj)

2.元类 metaclass

  1.概念:  元类式创建类的类,再python中万物皆对象,那么对象是通过类实例化产生的,那么既然类也是对象,也就是类也是有产生他的类,默认情况下所有类的元类都是type类

  2.使用方法: 只要继承了type类,这个类就变成了一个元类,  而下面的类通过metaclass = 自定义的元类就可以指定元类了 

  3.  一个类的基本组成部分  1:类名 (字符串) 2 类的父类们(元组)  3 类的名称空间(字典类型)

  3.作用:高度的自定义一个类.如在创建类对象的时候做一些限制

# 控制类的名字必须以大驼峰的方式来书写
class MyClass(type):
    def __init__(self,class_name,bases,dict):
        super().__init__(class_name,bases,dict)
        if not class_name.istitle():
            raise Exception("名字书写错误")

class Pig(metaclass=MyClass):
    pass

class dog(metaclass=MyClass):  # Exception: 名字书写错误
    pass

  4.元类中__call__方法:

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

    注意: 当把元类的__call__方法覆盖后,就无法产生对象,所以应该调用父类的__call__方法,比且一定要返回值

    使用场景: 当想要控制对象的创建过程时

# 将对象所有属性转换为大写
class MyClass(type):
    def __call__(self, *args, **kwargs):
        new_args = []
        for i in args:
            new_args.append(i.upper())
        return super().__call__(*new_args,**kwargs)


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


a = Person('jay','boy')
print(a.__dict__)   # {'name': 'JAY', 'gender': 'BOY'}

  5.__new__方法:又称为构造方法

    执行时间: 在创建类对象时执行,第一执行元类中的__new__方法,拿到一个空对象,将这个空对象返回给__init__方法初始化

    注意: __new__方法必须又返回值,返回相印的类对象,并且在使用时会覆盖原__new__方法,所以要调用父类的__new__方法

  测试:

class MyClass(type):

    def __new__(cls, *args, **kwargs):
        print(cls)  # 元类自己
        print(args)  # 创建类需要的几个参数  类名,基类,名称空间
        print(kwargs)  #空字典
        print("new run")
        return super().__new__(cls,*args,**kwargs)
        # obj = type.__new__(cls,*args,**kwargs)
        # return obj
    def __init__(self,a,b,c):
        super().__init__(a,b,c)
        print("init run")
class A(metaclass=MyClass):
    pass
print(A)

  6.单例设计模式:

    1.设计模式: 解决某种固定问题的方法

    2.单例,一个类只能产生一个对象(单个实例)

    3. 单例设计模式是为了节省资源,当一个类的所有对象属性全部都相同时使用

  测试代码:

class Single(type):
    def __call__(self, *args, **kwargs):
        if hasattr(self,'obj'):  # 判断对象是否已经存在
            return getattr(self,'obj')  # 返回对象
        obj = super().__call__(*args,**kwargs)  # 调用父类的的__call__方法
        print("new")
        self.obj = obj  # 添加对象
        return self.obj  # 返回对象


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

s1 = Person('小明',18)
s2 = Person('小明',18)
s3 = Person('小明',18)
s4 = Person('小明',18)
s5 = Person('小明',18)
s6 = Person('小明',18)

3.排序方法

  1.选择排序法 : 每一趟从中选出最小(或最大)的一个元素,顺序放在已排好序的数列的最后,直到全部待排序的数据元素排完

l = [1,23,453,66,231,586,234,131]
for i in range(len(l)-1):
    for j in range(i+1,len(l)):
        if l[i] > l[j]:
            l[i],l[j] = l[j],l[i]

print(l)  # [1, 23, 66, 131, 231, 234, 453, 586]

  2.冒泡排序法: 它重复地走访过要排序的元素列,依次比较两个相邻的元素,如果他们的顺序(如从大到小、首字母从A到Z)错误就把他们交换过来。走访元素的工作是重复地进行直到没有相邻元素需要交换,也就是说该元素列已经排序完成。

l = [1,23,453,66,231,586,234,131]
for i in range(len(l)-1):
    for j in range(len(l)- i -1):
        if l[j] > l[j+1]:
            l[j],l[j+1] = l[j+1],l[j]


print(l)  # [1, 23, 66, 131, 231, 234, 453, 586]

 

原文地址:https://www.cnblogs.com/yanglingyao/p/11272518.html