一、反射
定义:通俗的理解其实反省,自省的意思
反射值的时一个对象具备在增 删 改 查 得的属性你能力
操作方法:通过操作字符串操作属性
反射:设计的四个函数:hasattr ,get attr, setattr , delattr 者四个 函数就是普通内置函数,没有双下划线,就像print,sort,len() 区别于__getattr__ 函数操作
# 使用场景:
# 反射其实就是对属性增删改查,如果直接使用内置dict,太过于繁琐,不好理解
# 反射是框架的基石,为什么
1.1 实列一:
class Person: def __init__(self,name,age): self.name = name self.age = age p = Person("Sen", 18) if hasattr(p, 'name'): # print(getattr(p,'name')) setattr(p, 'id', 123) # 注意setattr 设置值的时候是有三个值的(对象,‘key’ ,value) print(p.id) # delattr(p, 'id') # ttributeError: 'Person' object has no attribute 'id' # print(p.id) # 此时‘id’已被删除
print(p, 'id') 此时是不会取到对象的值的
1.2 案例二:默认Djiang中间件 实现对象 的调用
前戏:
# 使用场景: # 反射其实就是对属性增删改查,如果直接使用内置dict,太过于繁琐,不好理解 # 反射是框架的基石,为什么因位框架设计者补课不可能提前知道你的对象到底是怎么 # 设计的所以你提供给框架的对象 必须通过判断验证之后才能正常使用 # 判断就是反射要做的事情,当然也可以通过__dict__也可以实现的,其实这些方法也就对__dict__的操作进行了封装 # 需求:要实现一个用于处理用户的终端指令的小框架 # 框架:已经实现了最基础的架构代码逻辑,就是所有项目都是一样的部分
# 框架 的根据配置 文件拿到所需的类
class WinCmd: def cd(self): print('windcmd 切换目录。。。' ) def delete(self): print('windcmd ..。删库') def dir(self): print('wind 列出当前的所有文件') class LinCmd: def cd(self): print('lindcmd 切换目录。。。' ) def rm(self): print('lindcmd ..。删库') def ls(self): print('lincmd 列出所有文件')
通过模块导入对象 实列化 调用
# 导入刚才的库 from day24.lib import my_work def run(my_work): while True: cmd = input('输入指令') if cmd == 'exit': break if hasattr(my_work,cmd): func = getattr(my_work,cmd) func() # else: print('不支持') print('再见!!!') wind = my_work.WinCmd() run(wind)
很明显上述中我们的设计框架中写死必须用某个类,这是不合理的 以为我们无法提前预知对方的类在什么地方以及类名 所以我们应该为框架的使用者提供一个配置文件,要求对方将类写入文件 然后框架自己可以加载需要的模块
实现代码:
二、元类
"""该文件为框架的配置文件"""
# 作为框架的使用者,在配置文件中指定你配合框架的类是哪个
CLASS_PATH = 'lib.my_work.WinCmd'
# 导入刚才的库 import importlib from day24.lib import my_work from day24 import settings def run(my_work): while True: cmd = input('输入指令') if cmd == 'exit': break if hasattr(my_work,cmd): func = getattr(my_work,cmd) func() # else: print('不支持') print('再见!!!')
# 创建一个插件对象 调用库框架来使用
# wind = my_work.WinCmd()
# run(wind)
# 很明显这种实现不是完全的合理
# 所以需要 根据文件拿到需要的类
path = settings.CLASS_PATH
# 从配中单独拿出来 模块路径 和 类名称
print(path.rsplit('.',1)) # ['lib.my_work', 'WinCmd'] 者按照点切割的列表解压赋值
module_path, class_name = path.rsplit('.', 1)
# res = path.rsplit('.',1)
print(class_name)
# 拿到模块
mk = importlib.import_module(module_path)
print(mk) # <module 'lib.my_work' from 'D:\datas\day24\lib\my_work.py'>
print(class_name)
# 拿到类
cls = getattr(mk,class_name)
print(cls)
obj = cls()
# 调用框架
run(obj) 实现代码 调
obj 也就是这个类的实列对象class WinCmd
""" 在框架设计中 我们不可能提前知道 框架的用户要提供类相关的信息 """ import importlib import abc # 拿到模块 根据模块的路径 pl = importlib.import_module("libs.plugins") print(pl) # 从模块中取出类 cls = getattr(pl,"WinCMD") print(cls) # 实例化产生对象 obj = cls() obj.cd()
二、元类
2.1 定义:什么元类:用于创建类的类,万物皆对象,类也是对象(类对象)对象是通过 类实列化产生,类对象也是另一个了实例化产生的
默认情况下所有类的元类都是type 类的类是type
# type 可以自定义类 拦截的类的产生 # 我们的需求是创建类对象做一些限制 # 实现方法一 既然是创建想到了初识化方法 我们只要找到类对象的类(元类) # 覆盖init 方法就能实现需求 # 只要继承type类 那么这个类就变成了一个元类 # 自定义类 first
实现代码:
lass MyClass(type): # 元类 # 拦截在父类产生Student 初始化走 的__init__ def __init__(self,class_name,bases,dict): # self Student 类对象、 print(self) # 类 对象 <class '__main__.Student'> print(class_name) # 类名 print(bases) print(dict) super().__init__(class_name, bases, dict) # 重写父类放入方法 if not class_name.istitle(): # 限制类的产生过程 必需满足我们限制的条件 raise Exception('大哥不能呐必需是首字母大写') else: print('创建类成功')
我们要自定义的类
class Student(metaclass=MyClass): # 自定义这个类怎么 重新type def __init__(self,name,age,b): self.name = name self.age = age self.b = b a = Student('KOKO',(1,2),b=1) # 实列化 # __new__ print(a.__dict__) # {'name': 'KOKO', 'age': (1, 2), 'ui': {}}
方法二其实就是内部本质先走元类自己的__new__ 1.元类中构建类再 返回类对象
# 默认继承type 的类称为元类 # 类的创建第二种方法__new__ # __new__是帮我们创建类对象的初始化进行添加限制 class Myself(type): # 继承type >>>元类 def __new__(cls, *args, **kwargs): # cls 是Myself # 方法一 # return super().__new__(cls,*args,**kwargs) # 必需返回给__init 生成相应的类,并返回 # 方法二 obj = type.__new__(cls, *args, **kwargs) # 在元类中该函数用来构建类本身 return obj # 返回一个对象 给__init__ # 在类中,该函数用来构建类实例 def __init__(self, a, b, c): super().__init__(a, b, c) print('实列创建类') class Teacher(metaclass=Myself): # 限制老师类的产生 pass 属性 Teacher()
分为两个阶段:
1.类的生成:由元类生成;
2.实例的生成:由上述生成的类接着生成。
具体描述:
结合上一篇文章中,我们总结出生成一个类实例的全部过程:
1.类首先查找内部__metaclass__属性是否被自定义元类赋值,若赋值则准备用该自定义元类生成类,否则用type作为元类生成类;
2.解释器调用该元类的__new__函数(该函数为静态函数),并将要实例化的类中定义的各种属性传递给该函数固定的四个参数:其中cls是该元类本身,name是要被实例化的类的类名,bases是该类父类组成的元组,attrs则是该类{属性名:属性值,函数名:函数对象}组成的字典。
3.最终通过type类生成该类,并返回。
4.该类生成后,调用类中的__new__函数(该函数是静态函数)创建该类的实例,并返回该实例;
5.该实例接着调用它的__init__函数初始化实例,这样一个完整的实例就被生成出来了。
5,自定义元类的核心
由于是元类构建了类,因此若要更改某些固定类(str,int等)的用法,就必须在元类中做文章了。而元类的核心是__new__函数,自然在该函数内做文章。比如可以通过atrris字典为元类添加新的属性和函数,或改变以往的属性或函数等。这样的改变会传播到所有用该元类创建的类中。
总结:
__new__函数:
1.在元类中该函数用来构建类本身;
2.在类中,该函数用来构建类实例;
---------------------
元类中__call__
当你调用类对象时会自动触发元类中的__call__方法 ,并将这个类本身作为第一个参数传入,以及后面的一堆参数
覆盖元类中的call之后,这个类就无法产生对象,必须调用super().__call__来完成对象的创建
并返回其返回值
使用场景:
当你想要控制对象的创建过程时,就覆盖call方法 对象创建过程__call__
当你想要控制类的创建过程时,就覆盖init方法 控制类的创建 __init__
案例:
# 元类中的__call__方法 在对象加括号 会自动触发类中的__call__ 方法
# 覆盖父类的__call__ 这个类无法差生对象 必须调用super().__call__
# 来完成对象的创建,并返回其返回值
#实现将对象的所有属性名称转为大写
# 实现一
class Mytype(type):
# 控制对象的产生
def __call__(self, *args, **kwargs):
if args:
raise Exception('不能用位置参数')
return super().__call__(*args,**kwargs) # 必须返回这个对象
class Person(metaclass=Mytype):
def __init__(self,name,age):
self.name =name
self.age =age
a = Person(name='jcsk',age=19)
print(a.name)
print(a.age)
单列的实现
class Single(type):
def __call__(self, *args, **kwargs):
print(self) # Student 类
if hasattr(self, 'obj'):
return getattr(self, 'obj')
obj = super().__call__(*args, **kwargs) # 没有则有创建
print(obj) # 实例化的对象
print('新开的试试看看')
self.obj = obj # 并保存到self 对象自己的名称空间中
return obj
class Student(metaclass=Single):
def __init__(self, name, age):
self.name = name
self.age = age
a = Student('jj', 12) #
Student('mm', 12)
Student('kk', 12)
Student('pp', 12)
Student('tt', 12)
# l = [2, 1, 3, 5] # 冒泡算是遍历里里面的元素 由第一个和第二个做比较 如是:按大到小进行编排
# 列表的个数
#
# 先比较 2,1 得到2 1 1 3 1 5
# 第一圈 0 索引
# >>> 元素比较元素次数 次数len-1 3
# 第二圈 1 索引
# 2 3 2 5 2和1已经再第一次拿到最小值了 不用再比较 比较元素次数 为2 len -1 -1
# l2 = [3, 5, 2, 1]
# 第三圈 2 索引
# 3 5 3和2 再 第二圈已经比较过了
# l3 = [5, 3, 2, 1]
# 需求要用函数的方法实现 找关系
s = [2, 3, 5, 1,10,309]
for i in range(len(s) - 1): # 元素比较的次数 第一次 顾头不顾尾range(3) 0 1 2 三次比较
# print(i) # 0 1 2 3 元素的索引下标
for j in range(len(s) - 1 - i): # i相当于圈数的索引i =1 第二次 顾头不顾尾range(2) 0 1 三次比较
if s[j] < s[j+1]:
# 解压赋值
s[j], s[j+1] = s[j+1], s[j]
print(s)
class Num:
def __init__(self,name,age):
self.age = name
self.age = age
def __str__(self):
return self.age
def __gt__(self, other):
return self.age > other.age
a1 = Num('json','48')
a2 = Num('json','19')
a3 = Num('json','26')
排序的 ????
stu = [a1.age, a2.age, a3.age]
for i in range(len(stu)-1):
for j in range(len(stu)-1-i):
if stu[j]>stu[j+1]:
stu[j],stu[j+1] = stu[j],stu[j+1]
print(stu)
单列2:
# 利用__new__ 在创建对象的时后先走 会帮我们构建以及返回一个对象 # 对象给__init__ 对象进行初始赋值
""" # 利用__new__ 在创建对象的时后先走 >>>么有创建 有返回 会帮我们构建以及返回一个对象 # 对象给__init__ 对象进行初始赋值 """ class Mysql(object): # 默认继承object _instance = None # 可以定义变量 def __init__(self, name): self.name = name print(self) print('run init') def __new__(cls, *args, **kwargs): if not cls._instance: print(cls) print('run _new_') cls._instance = object.__new__(cls) # 如果没有 重写object 的__new__ 将对象存到名称空间 print('1111') return cls._instance obj = Mysql('Jack') obj2 = Mysql('rose') # obj3 = Mysql('kk print(id(obj), id(obj2)) # 2705130166368 2705130166368 # <class '__main__.Mysql'> # run _new_ >>>先走 # 1111 # <__main__.Mysql object at 0x0000017048A5C240> # run init >>>再走 # <__main__.Mysql object at 0x0000017048A5C240> # run init >>>一样的
单列3基于元类:对象调用__call__ 么有值 重写父类的__call__
# 单列实现一 利用元类 既然是对象的产生 >>>__call__ class MyClass(type): # 继承type # 控制类的产生__call__ 覆盖父类的__call__ 对象调用走__call__ 方法 def __call__(self, *args, **kwargs): if hasattr(self, 'obj'): return getattr(self, 'obj') # 没有创建重写父类的__call__ obj = super().__call__(*args, **kwargs) # 将对象保存 print('new=====') self.obj = obj return obj class Student(metaclass=MyClass): # 继承MyClass 父类 def __init__(self,name,age): self.name = name self.age = age a1 = Student('jack',18) a2 = Student('rose',20) a3 = Student('roe',20) a4 = Student('roye',20) print(a1) print(a2) print(a3) print(a4) # new===== 只走了一次 # <__main__.Student object at 0x0000024DBFC1D160> # <__main__.Student object at 0x0000024DBFC1D160> # <__main__.Student object at 0x0000024DBFC1D160> # <__main__.Student object at 0x0000024DBFC1D160>
单列4:装饰器 实现
def singleton(cls): # 该对象在类Mysql被装饰上singleton的时候就已经实例化完毕 _instance = cls('127.0.0.1',3306) def inner(*args,**kwargs): # 判断是否传入参数,传入参数表示要实例化新的,不传表示用默认的 if args or kwargs: obj = cls(*args,**kwargs) return obj return _instance return inner @singleton class Mysql: def __init__(self,ip,port): self.ip = ip self.port = port obj1 = Mysql() obj2 = Mysql() obj3 = Mysql() print(obj1,obj2,obj3)def singleton(cls): # 该对象在类Mysql被装饰上singleton的时候就已经实例化完毕 _instance = cls('127.0.0.1',3306) def inner(*args,**kwargs): # 判断是否传入参数,传入参数表示要实例化新的,不传表示用默认的 if args or kwargs: obj = cls(*args,**kwargs) return obj return _instance return inner @singleton class Mysql: def __init__(self,ip,port): self.ip = ip self.port = port obj1 = Mysql() obj2 = Mysql() obj3 = Mysql() print(obj1,obj2,obj3)
单列5基于
基于classmethod)
class Mysql(object): _instance = None def __init__(self, ip, port): self.ip = ip self.port = port @classmethod def singleton(cls): if not cls._instance: cls._instance = Mysql('127.0.0.1', 3306) return cls._instance obj1 = Mysql.singleton() obj2 = Mysql.singleton() print(obj1) print(obj2)
单列6:
# 单独在一个py文件中定义一个类,并实例化一个对象,之后在其他文件导入这一对象,实现单例
# 单独在一个py文件中定义一个类,并实例化一个对象,之后在其他文件导入这一对象,实现单例 class Singleton(object): def __init__(self,host,port): self.host = host self.port = port singleton = Singleton('127.0.0.1',3306)