python基础之 面向对象之反射

1.isinstance和issubclass

issubclass(Son,Foo)
    判断雷与类之间的是否有继承关系,接受两个参数,一个是疑似子类,一个是疑似父类,判断Son是否是Foo的子类

obj = 1
isinstance(obj,int)
    判断obj是否是int类型,成人继承关系,更多的时判断对象与类之间的关系,结果判断包括这个类,也包括这个类的父类

type(obj) in int 
    判断obj是否是int类型,只是承认这个对象的类,不承认所有的继承关系

 2.什么是反射

官方解释:
  反射的概念是由Smith在1982年首次提出的,主要是指程序可以访问、检测和修改它本身状态或行为的一种能力(自省)。
python中的反射:
  通过字符串的形式操作对象相关的属性。python中的一切事物都是对象(都可以使用反射)

  用字符串类型的属性名/方法名找到属性的值或者方法的内存地址
  所有可以反射的内容实际上都是变量
  内存地址存的是"具体的值",直接能取到结果
  内存地址中存的是"函数\方法\类",取到的时内存地址

3.反射例子

class Manager:   # 管理员用户
    def __init__(self,name):
        self.name  = name
    def create_course(self):  # 创建课程
        print('in Manager create_course')

    def create_student(self): # 给学生创建账号
        print('in Manager create_student')

    def show_courses(self): # 查看所有课程
        print('in Manager show_courses')

    def show_students(self): # 查看所有学生
        print('in Manager show_students')

admin= Manager('alex')
operate_lst = [('创建课程','create_course'),('创建学生账号','create_student'),
               ('查看所有课程','show_courses'),('查看所有学生','show_students')]
for index,opt in enumerate(operate_lst,1):
    print(index,opt[0])
num = input('请输入您要做的操作 :')
if num.isdigit():
    num = int(num)
    if hasattr(admin,operate_lst[num-1][1]):
        getattr(admin,operate_lst[num-1][1])()
使用对象反射
class A:
    Country = '中国'

    @classmethod
    def show(cls):
        print('国家 : ',cls.Country)


print(getattr(A,'Country'))   # print(A.Country)
getattr(A,'show')()   # A.show()
使用类反射
import re
ret = re.findall('\d+','2985urowhn0857023u9t4')
print(ret)

ret = getattr(re,'findall')('\d+','2985urowhn0857023u9t4')
print(ret)

import time
print(time.time())
print(getattr(time,'time')())
使用模块反射
#反射本文件中的内容 :只要是出现在全局变量中的名字都可以通过getattr(modules[__name__],字符串数据类型的名字)都可以反射,其中的__name__指的是当前文件地址

from sys import modules
print(modules)   #其中modules是字典类型,字典中的key是模块的名字,字典中的value是模块相对应的文件地址

#语法
a = 1
b = 2
getattr(modules[__name__],'变量名')

#函数名
def func(a,b):
print('in func',a,b)

getattr(modules[__name__],'func') # func
func(1,2)
getattr(modules[__name__],'func')(1,2) # func

#类名
class Course:
def func(self):
print('in func')

print(Course)
'Course'
print(getattr(modules[__name__],'Course')) # Course
getattr(modules[__name__],'Course')() # 实例化的过程
使用文件内容反射

4.总结

# hasattr和getattr
    # 只要是a.b这种结构,都可以使用反射
    # 用对象\类\模块反射,都只有以下场景
    # 这种结构有两种场景
    #     a.b   b是属性或者变量值
    #         getattr(a,'b')   == a.b
    #     a.b()  b是函数或者方法
    #         a.b()
    #             getattr(a,'b')()
    #         a.b(arg1,arg2)
    #             getattr(a,'b')(arg1,arg2)
    #         a.b(*args,**kwargs)
    #             getattr(a,'b')(*args,**kwargs)
    # 如果是本文件中的内容,不符合a.b这种结构
        # 直接调用func()
            # getattr(sys.modules[__name__],'func')()
        # 直接使用类名 Person()
            # getattr(sys.modules[__name__],'Person')()
        # 直接使用变量名 print(a)
            # getattr(sys.modules[__name__],'a')
    # 所有的getattr都应该和hasattr一起使用
        # if hasattr():
             getattr()
# setattr 只用来修改或者添加属性\变量,不能用来处理函数或者是其他方法
    # a.b = value
    # setattr(a,'b',value)
    
# delattr 只用来删除 属性\变量
    # del a.b 删除属性  相当于删除了a对象当中的b属性
    # delattr(a,'b')
View Code

 5.内置函数

__call__()函数

  class A:
      def __call__(self, *args, **kwargs):  #触发机制为实例化的对象加上扩号()来调用一下__call__()
          print('in call')
  A()()
  #实例化对象()自动执行__call__内容,Flask框架中用的较多

 with 上下文处理

class A:
    def __enter__(self):
        print('start')

    def __exit__(self, exc_type, exc_val, exc_tb): #exc_type是异常的对象类型,exc_value是异常的值,exc_tb是一个traceback对象,对象中包含出错的行数、位置等数据。
        print('exit')

with A(): #实例化一个对象,但是未赋值
    print('啦啦啦啦')

#有点像是生成器的with上下文处理,其中with__enter__方法和__ecit__()方法混合使用
#在打印print('啦啦啦啦')之前,想执行以下__enter__函数,然后在执行with,最后执行__exit__函数

非内存资源使用with去关闭
内存资源使用垃圾回收机制和手动去关闭(xx.close())

上下文管理模块
import pickle
class mypickledump():
    def __init__(self,path,mode='ab'):
        self.path = path
        self.mode = mode

    def __enter__(self):
        print('start')
        self.info = open(self.path,self.mode)
        return self
    def __exit__(self, exc_type, exc_val, exc_tb):
        print('exit')
        self.info.close()
    def pickle_dump(self,text):
        pickle.dump(text,self.info)

with mypickledump('pickle_file',mode='ab') as info:  #as info的值是__enter__函数的返回值
    print("func")
    info.pickle_dump({1,2,3,4,5,6})
    info.pickle_dump({1, 2, 3, 4, 5, 6})
    info.pickle_dump({1, 2, 3, 4, 5, 6})
with处理pickle写入
class mypickleload():
    def __init__(self,path,mode='rb'):
        self.path = path
        self.mode =mode

    def __enter__(self):
        print('start')
        self.info = open(self.path,self.mode)
        return self
    def __exit__(self, exc_type, exc_val, exc_tb):
        print('exit')
        self.info.close()
    def pickle_load(self):
        while True:
            try:
                ret = pickle.load(self.info)
                yield ret
            except EOFError:
                break

with mypickleload('pickle_file','rb') as info:
    for item in info.pickle_load():
        print(item)
with处理pickle读取

 __new__():构造函数  面试必考

#必须先走__new__方法开辟空间,没有的话,就去父类找
class
Foo(): def __new__(cls, *args, **kwargs): #先执行,cls永远不能使用self参数,因为self是之后才被创建出来 触发机制为:在类被实例化的时候,必须先执行__new__方法 print('in new') obj = object.__new__(cls) #self在这个地方才被创建出来,在内存中开辟一块属于这个对象的内存地址,并且在这个类中间存放一个类指针,然后赋值给__init__的第一个参数self print (obj) return (obj) def __init__(self): #后执行 print('init',self) Foo() #实例化一个Foo的对象分为两步:包括创建对象和初始化对象。 #其中__new__是构造方法,是在实例创建之前被调用的,因为他的任务就是创建实例,然后返回该实例,是个静态方法 #__init__是当实例对象创建完后被调用,然后设置对象属性的初始值
单例模式是日常应用中最广泛的模式了,其目的就是令到单个进程中只存在一个类的实例,保证一个类无论 被实例化多少次,始终使用的是同一块内存地址从而可以实现数据的共享,节省系统开销,防止io阻塞等等
class A:
    _instance = None
    def __new__(cls, *args, **kwargs):
        if cls._instance is None:
            cls._instance = object.__new__(cls)
        return cls._instance

    def __init__(self,name=None,age=None):
        self.name = name
        if age:
            self.age = age
a1 = A('kobe',84)
print(a1)
a2 = A('kobe',83)
print(a2)
a3 = A('kobe',82)
print(a3)
print(a1.age)
print(A.__dict__)
因为类A本省没有__new__方法,只能借助期父类object来重写__new__方法。然后将类的一个实例绑定到类变量_instance上;
如果cls._instance为None,则说明该类还没有被实例化过,new一个该类的实例,并返回;如果cls._instance不为None,直接返回_instance,代码如下:
单例模式

__del__():析构函数

class A:
    def __init__(self,name,age):
        self.name = name
        self.age = age()
    def __del__(self):  #触发机制是碰到del obj(一定是对象,不能是删除属性)函数的时候先来执行__del__(self)函数,但是没有del 的时候,在python解释器执行到最后的时候也会执行一次__del__函数
        # 只和del obj语法有关系,在执行del obj之前会来执行一下__del__中的内容
        print('执行我啦')

obj = A('alex',84)
print(obj.name)
print(obj.age)
del obj   # 这个变量已经没了
time.sleep(1)
print('lalala')
# 在所有的代码都执行完毕之后,所有的值都会被python解释器回收
__del__析构方法,再删除一个对象之前,归还一些操作系统资源
    一般不关心内存级别的资源,而是关心操作系统的资源 
    python解释器会在不使用一个值的时候,会自动删除
    只和del obj语法有关系,在执行del obj之前会来执行一下__del__
    在所有的代码都执行完毕之后,所有的代码都会被python解释器回收
    在我们操作系统的资源要及时归还,否则会占用大量的系统的资源,如及时    关闭文件句柄,和网络资源,数据库链接等
    
那这个和with中的__exit__的区别是什么那?
    with中的__exit__是关心内存级别的资源浪费,如及时关闭文件句柄,和网络资源,数据库链接等
使用理解

 __str__&__repr__

class Course:
    def __init__(self,name,price,period):
        self.name = name
        self.price = price
        self.period = period
    def __str__(self):
        '''打印这个对象的时候 自动触发__str__'''
        '''使用%s进行字符串的拼接的时候 自动触发__str__'''
        return '%s,%s,%s'%(self.name,self.price,self.period)

python = Course('python',25000,'6 months')
print(python)
print('course %s'%python)
print(f'course {python}')
如果,不实现str方法,那么对象打印出来只是一串地址

========================================================================================================
class Course:
    def __init__(self,name,price,period):
        self.name = name
        self.price = price
        self.period = period

    def __repr__(self):   # 备胎
        return '%s,%s,%s'%(self.name,self.price,self.period)

    def __str__(self):
        return self.name

python = Course('python',25000,'6 months')
print(python)
print('course %s'%python)
print(f'course {python}')
print('course %r'%python)
如果str存在,repr也存在
    那么print(obj)和使用字符串格式化format,%s这两种方式 调用的都是__str__
    而repr(obj)和%r格式化字符串,都会调用__repr__
如果str不存在,repr存在
    那么print(obj),字符串格式化format,%s,%r 和repr(obj)都调用__repr__
如果str存在,repr不存在
    那么print(obj)和使用字符串格式化format,%s这两种方式 调用的都是__str__
    repr(obj)和%r格式化字符串 都会打印出内存地址
  
  
执行过程:
    打印对象 先走自己的str,如果没有,走父类的,如果除了object之外的所有父类都没有str
    再回来,找自己的repr,如果自己没有,再找父类的。
    repr是str的备胎
    和所有的字符串格式化以及直接打印这个对象相关


other:
    有了repr或者str在打印对象的时候 就不会显示用户不关心的内存地址了
    增强了用户的体验 在程序开发的过程中
    如果我们需要频繁打印对象中的属性,需要从类的外部做复杂的拼接,实际上是一种麻烦
    如果这个拼接工作在类的内部已经完成了,打印对象的时候直接就能显示
使用理解

 

面向对象中重要的方法

mro()       #用来查看类的继承顺序 print(类名.mro())
__bases__   #用来显示继承了多少个类 print(类名.__bases__)
__dict__    #用来查看类属性或者变量的属性或者内存空间的属性  print(类名.__dict__)/print(对象名象名.__dict__)
__name__    #用来显示当前文件

Python在import其它模块时,是从sys.path中搜索的。sys.path的初始值会受到PYTHONPATH、PYTHONHOME等环境变量的影响。也可以在脚本运行过程中动态修改sys.path从而import自己需要的模块。


sys.modules是一个全局字典,该字典是python启动后就加载在内存中。每当程序员导入新的模块,sys.modules都将记录这些模块。
字典sys.modules对于加载模块起到了缓冲的作用。当某个模块第一次导入,字典sys.modules将自动记录该模块。
当第二次再导入该模块时,python会直接到字典中查找,从而加快了程序运行的速度。

字典sys.modules具有字典所拥有的一切方法,可以通过这些方法了解当前的环境加载了哪些模块
import sys
print(sys.modules[__name__])
print(sys.modules[__name__].__dict__)
print(sys.modules.values())
print(sys.modules.keys())
print(sys.modules.items())            

重点:什么是cls?
  
cls是表示的时类本身,在类的方法里面用的到。
  在类的__new__函数里面的第一个参数就是cls,代表的时这个类的本身,类方法里面可以用的到,类方法是与类本身有关而与实例化无关的
  那么@staticmethod和classmethod两种类方法的区别是什么那?
    既然@staticmethod和@classmethod都可以直接类名.方法名()来调用,那他们有什么区别呢
    从它们的使用上来看,
    @staticmethod不需要表示自身对象的self和自身类的cls参数,就跟使用函数一样。
    @classmethod也不需要self参数,但第一个参数需要是表示自身类的cls参数。
    如果在@staticmethod中要调用到这个类的一些属性方法,只能直接类名.属性名或类名.方法名。
    而@classmethod因为持有cls参数,可以来调用类的属性,类的方法,实例化对象等,避免硬编码。

 

 

 

 

 

返回系列

 

posted @ 2019-04-01 15:55  thep0st  阅读(119)  评论(0)    收藏  举报