面向对象高级进阶

  一、反射

  定义:通俗的理解其实反省,自省的意思

          反射值的时一个对象具备在增 删  改 查 得的属性你能力

  操作方法:通过操作字符串操作属性 

  反射:设计的四个函数: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)


原文地址:https://www.cnblogs.com/mofujin/p/11272117.html