zoukankan      html  css  js  c++  java
  • Python进阶----反射(四个方法),函数vs方法(模块types 与 instance()方法校验 ),双下方法的研究

    Python进阶----反射(四个方法),函数vs方法(模块types 与 instance()方法校验 ),双下方法的研究

    一丶反射

    什么是反射:

    ​ 反射的概念是由Smith在1982年首次提出的,主要是指程序可以访问、检测和修改它本身状态或行为的一种能力(反射)。这一概念的提出很快引发了计算机科学领域关于应用反射性的研究。它首先被程序语言的设计领域所采用,并在Lisp和面向对象方面取得了成绩。

    Python面向对象的反射:

    ​ 通过字符串的形式操作对象相关的属性。python中的一切事物都是对象(都可以使用反射)如: 一个py文件, 一个类,一个实例对象,等等都是对象.

    ####  四个实现反射的函数
    	# hasattr(),	getattr(),	setattr(),	delattr()
        
    
    ####  反射 整合版
    class Foo:
        public_attr='静态属性'
    
        def __init__(self,name,age):
            self.name=name
            self.age=age
    
        def func(self):
            print('Hello Python')
    
    
    obj=Foo('张三',18)		# 实例化对象
    
    
    ##  检测是否含有XX属性
    print(hasattr(obj,'public_attr'))   #当前实例对象,是否含有静态变量public_attr
    print(hasattr(obj,'func'))          #当前实例对象,是否含有func方法
    
    
    
    ##  获取属性 getattr(对象,字符串,None)
    print(getattr(obj,'public_attr'),None)   #静态属性,不存在就返回None
    print(getattr(obj,'func'))  # <bound method Foo.func of <__main__.Foo object at 0x0000015FF30EC8D0>>
    
    
    
    ## 设置属性 setattr(对象,字符串,值)
    setattr(obj,'sex','男')  # 设置对象属性
    print(obj.__dict__) # {'name': '张三', 'age': 18, 'sex': '男'}
    
    ## 设置匿名方法 self为参数
    setattr(obj,'show_name',lambda self:self.name)
    print(obj.show_name(obj))   # 设置方法,
    print(obj.__dict__)# { 'show_name': <function <lambda> at 0x0000018512932EA0>}
    
    
    
    ## 删除属性
    delattr(obj,'name')
    delattr(obj,'name')    # 不存在就报错
    print(obj.__dict__) # {'age': 18, 'sex': '男', 'show_name': <function <lambda> at 0x000001D901BE2EA0>}
    
    
    
    
    
    
    ###  从当前 脚本(本类) 研究反射
    
    class B:
        sta_='abc'
    
    
    def func1():
        print('in func1')
    
    import sys
    this_module=sys.modules[__name__]  # 获取py文件对象
    print(this_module) # 这是一个对象  <module '__main__' from 'E:/File/oldboy学习笔记/Python之路/day26/01 反射.py'>
    
    print(hasattr(this_module,'func1')) # 判断函数名属性 存在不存在, True
    print(getattr(this_module,'func1')) # 获得函数名属性  <function func1 at 0x00000225EBA92EA0>
    
    getattr(this_module,'func1')()   #获得函数名属性+()执行func1函数
    
    print(hasattr(this_module,'B')) # 判断类名属性 存在不存在 True
    print(getattr(this_module,'B')) # 获得类名属性打印 <class '__main__.B'>
    cls=getattr(this_module,'B')    # cls 得到是一个B类的地址
    print(cls)                      # <class '__main__.B'>
    obj=cls()                       # cls+() 实例化一个对象
    print(obj.sta_)                 #  obj对象.B对象中的静态属性
    

    ​ 案例:

    class Auth:
        funcli=[('login','请登录'),('register','请注册'),('exit','退出')]
        def login(self):
            print('登录函数')
    
    
        def register(self):
            print('注册函数')
    
    
        def exit(self):
            print('退出...')
    
    
    while 1:
        obj=Auth()
    
        for num , option in enumerate(obj.funcli,1):
            print(num,option[0],option[1])
    
        func_name = input('请输入选择:').strip()
        if hasattr(obj,obj.funcli[int(func_name)-1][0]):
            getattr(obj,obj.funcli[int(func_name)-1][0],'不存在')()
    

    二丶函数vs方法

    函数和方法有什么区别和相同之处?

    ​ 函数是显性传参, 方法是隐性传参

    ####  通过打印函数名确定
    
        def func():
            pass
    
        print(func)  # 函数  <function func at 0x00000260A2E690D0>
    
    
        class A:
            def func(self):
                pass
    
        print(A.func)  # 函数 <function A.func at 0x0000026E65AE9C80>
        obj = A()
        print(obj.func)  # 方法 <bound method A.func of <__main__.A object at 0x00000230BAD4C9E8>>
    
    
    
    
    ####  通过types模块验证
        from types import FunctionType	# 函数
        from types import MethodType	# 方法 
    
        def func():
            pass
    
    
        class A:
            def func(self):
                pass
    
        obj = A()
    
    	## isinstance(obj,M) 判断 obj对象 ,是不是由M类 或 M类的派生类 实例化的对象
        print(isinstance(func,FunctionType))  		 # True  函数
        print(isinstance(A.func,FunctionType))  	 # True   类调用类中的func 是函数			
        print(isinstance(obj.func,FunctionType))  	 # False   实例对象调用 不是函数	
        print(isinstance(obj.func,MethodType)) 		 # True	   实例对象调用 是方法	
    
    

    研究类中的三个特殊的方法(静态方法,类方法,属性)

    ####  类中的静态方法是函数(静态函数)
        from types import FunctionType
        from types import MethodType
    
        class A:
    
            def func(self):
                pass
    
            @classmethod
            def func1(self):
                pass
    
            @staticmethod
            def func2(self):
                pass
        obj = A()
    
        # 静态方法其实是函数
        print(isinstance(A.func2,FunctionType))  # True
        print(isinstance(obj.func2,FunctionType))  # True
    
        
      
    
    
    
    ####  类中的类方法是方法(类方法)
        from types import FunctionType
        from types import MethodType
    
        class A:
            
            @classmethod
            def paas(cls):
                pass
            
            @property
            def a(self):
                pass
        obj=A()
    
    
        #   类方法 类和对象 调用  都是方法
        print(isinstance(obj.paas,FunctionType))    # False
        print(isinstance(obj.paas,MethodType))      # True
    
        print(isinstance(A.paas,FunctionType))      # False
        print(isinstance(A.paas,MethodType))        # True
    
        
     
    
    
    
    ####  类中的属性伪装函数 .  什么也不是,单纯的伪装属性
    
        from types import FunctionType
        from types import MethodType
    
        class A:
            
            @classmethod
            def paas(cls):		# 类方法是 cls, 实例对象或类调用.默认将从属的类地址传给cls (隐性传参)
                pass
            
            @property
            def a(self):
                pass
            
        obj=A()		# 实例化对象
    
        # 属性 ,     类和对象 调用 什么都不是
        print(isinstance(obj.a,FunctionType))    # False
        print(isinstance(obj.a,MethodType))      # False
    
        print(isinstance(A.a,FunctionType))      # False
        print(isinstance(A.a,MethodType))        # False
    
    
    
        
        
    
    ## 总结:
    	#1.类名 或 实例对象 调用类方法 都是 方法 method
    	#2.类名 或 实例对象 调用静态方法 都是 函数 function
    	#3.类名 或 实例对象 调用伪装属性函数. 什么都不是,单纯属性
      
    ## 函数 和方法的本质区别:
    	## 函数是显性传参,  方法是隐性传参
    

    三丶特殊的双下方法

    定义:

    ​ ' __ 方法名 __ ' 的具有特殊意义的方法,不同的双下方法有不同的触发方式,开发中尽量不要使用双下方法

    ' __ len __ ' 一个对象可以使用len()函数 ,根本原因是这个对象从属的类有__len__方法

    class A:
    
        def __init__(self,name,sex,age):
            self.name=name
            self.sex=sex
            self.age=age
    
        def __len__(self):  # 必须有返回值 , 如果没有会报错(TypeError: 'NoneType' object cannot be interpreted as an integer)
            print('触发了__len__')
            return len(self.__dict__)       # 返回当前实例对象具有几个属性
    
    obj=A('张三','男',10)
    print(len(obj))   # 从属的类中没有__len__方法就报错 TypeError: object of type 'A' has no len()
    

    ' __ hash __ '对象使用hash()函数,调用当前本类__hash__方法 ,本类没有就从父类找__hash__

    class A:
        def __init__(self):
            self.a=1
            self.b=2
    
        # 重写父类的__hash__方法
        def __hash__(self): #需要返回值 , 没有返回值 报错:TypeError: __hash__ method should return an integer
            return hash(str(self.a)+str(self.b))
    
    a=A()
    print(hash(a))  # 主动触发 __hash__方法, 当前类如果没有__hash__ 就从父类找
    

    ' __ str __ ' 和' __ repr __ '

    ###  这俩方法的返回值必须是字符串,否则抛出异常
    ###  str函数或者print函数--->obj.__str__()
    ###  repr或者交互式解释器--->obj.__repr__()
    ###  如果__str__没有被定义,那么就会使用__repr__来代替输出
    
    
    format_dict={
        'nat':'{obj.name}-{obj.age}-{obj.sex}',#学校名-学校地址-学校类型
        'tna':'{obj.sex}:{obj.name}:{obj.age}',#学校类型:学校名:学校地址
        'tan':'{obj.sex}/{obj.age}/{obj.name}',#学校类型/学校地址/学校名
    }
    class School:
    
        def __init__(self,name,sex,age):
            self.name=name
            self.age=age
            self.sex=sex
    
        def __repr__(self):
            return f'__repr__  {self.name} {self.age}'
    
        def __str__(self):
            return f'__str__ {self.name}{self.age}'
    
        def __format__(self, format_spec):
            if not format_spec or format_spec not in format_dict:
                format_spec = 'nat'
            fmt = format_dict[format_spec]
            return fmt.format(obj=self)
    
    s1=School('ABC','男','123')
    print(repr(s1))  #  现在本类本类找__repr__ 如果没有 .  直接去父类找,返回一个对象地址
    print(str(s1))  #  会触发__str__  , 先在本类找__str__ 如果没有 ,找__repr__ .如果还没有去object类找,返回的是一个对象地址
    
    
    print('%s'%obj)     # %s 调用__str__
    print('%r'%obj)     # %r 调用 __repr__
    print(s1)   ###  __str__的优先级高于__repr__
    
    
    
    ### 现在一般不再使用format()函数
    print(format(s1,'nat'))          # ABC-123-男
    print(format(s1,'tna'))          # 男:ABC:123
    print(format(s1,'tan'))          # 男/123/ABC
    print(format(s1,'asfdasdffd'))   # ABC-123-男
    
    

    ' __ call __ '

    class A:
        def __init__(self):
            self.a=1
            print(111)
    
        def __call__(self, *args, **kwargs):    # 不需要返回值,如果当前不存__call__在报错: TypeError: 'A' object is not callable
            print(666)
    
    obj1=A()
    obj1()      # 实例对象+() 调用__call__方法
    

    ' __ eq __'

    class A:
        def __init__(self):
            self.a=1
            self.b=2
    
        def __eq__(self, other):    #  没有return 默认返回None
            if self.a==other.a and self.b==other.b: # 条件不成立时,也返回None
                return True
    a=A()
    b=A()
    print(a==b)     # 调用__eq__方法
    
    
    
    
    
    
    ### 面试题:
    ###  set方法依赖集合中元素对象的__hash__ __eq__
    class Person:
        def __init__(self,name,age,sex):
            self.name = name
            self.age = age
            self.sex = sex
    
        def __hash__(self):       # TypeError: unhashable type: 'Person'
            # print('调用了1')
            return hash(self.name+self.sex)
    
        def __eq__(self, other):      #  判断当前对象是否 和其他对象的某些属性相同  other 接收set集合每次迭代的对象
            # print('调用了')
            # 解读 : 第一次实例化对象,self.name是列表中第一个对象执行__init__封装了
            if self.name == other.name and self.sex == other.sex:return True
    
    
    p_lst = []
    for i in range(84):
        p_lst.append(Person('egon',i,'male'))   # 存放实例化对象
    
    print(p_lst)
    print(set(p_lst))       #  必须调用 __hash__  , 如果有__eq__ 就调用 , 没有就不调用.  迭代去重. so 第一次的时候 name 的值为列表第一个元素执行__init__封装的属性
    # print(dir(set))
    
    	
    	
    

    ' __ del __ '析构方法,当对象在内存中被释放时,自动触发执行。

    ​ 此方法一般无须定义,因为Python是一门高级语言,程序员在使用时无需关心内存的分配和释放,因为此工作都是交给Python解释器来执行,所以,析构函数的调用是由解释器在进行垃圾回收时自动触发执行的。

    class Foo:
    
        def __del__(self):
            print('执行我啦')
    
    f1=Foo()
    del f1
    print('------->')
    

    ' __ new __ '

    class A:
    
        def __init__(self,name):
            self.name=name
            print('in the __init__')
    
        def __new__(cls, *args, **kwargs):      # cls 自动接收 类名地址
            print('in the __new__')
            result=object.__new__(A)#由于重写了父类的__new__,需要调用父类的__new__方法才能创造对象空间
            print(result)           # 生成对象空间的内存地址
            return result           # 必须返回这个对象空间地址返回 ,才能执行__init__为对象封装属性
    
        
    ####  类名()  先触发__new__ 并且将类名自动传递给cls
    obj=A('ale')        # 先执行__new__方法,如果没有执行父类的__new__.    
    				  # 只有返回对象空间,才继续执行__init__方法,如果没有返回对象空间,对象为None
    print(obj.name)
    print(obj)          #  __new__ 没有返回对象内存空间  对象=None
    
    

    单例模式 : 节省内存

    class F:
        __instance=None
        def __init__(self):
            print('执行了init方法')
    
        def __new__(cls, *args, **kwargs):
            if not cls.__instance:
                object1=object.__new__(cls)
                cls.__instance=object1
            return cls.__instance
    obj=F()
    obj1=F()
    obj2=F()
    print(obj,obj1,obj2)	# 三个内存地址都一样
    

    ' __ item __ ' 对对象进行类似于字典的操作

    ### 类似于 @property
    
    class Foo:
        def __init__(self, name):
            self.name = name
    
        def __getitem__(self, item):  #
            print('调用时执行 get')
            print(self.__dict__[item])
    
        def __setitem__(self, key, value):
            print('设置时调用 set')
            self.__dict__[key] = value
    
        def __delitem__(self, key):
            print('del obj[key]时,我执行')
            self.__dict__.pop(key)
    
        def __delattr__(self, item):
            print('del obj.key时,我执行')
            self.__dict__.pop(item)
    
    
    f1 = Foo('sb')
    
    f1['name']      # 调用时执行 __getitem__
    
    f1['age'] = 18  #设置新属性  __setitem__
    
    f1['age1'] = 19  # 设置新属性 __setitem__
    
    del f1.age1  # 删除属性 __delitem__
    
    del f1['age']  #删除属性 __delattr__
    
    f1['name'] = 'alex'  # 修改属性  __setitem__
    
    print(f1.__dict__)
    

    ' __ enter __ __ exit __

    ## __enter__   __exit__
    # 如果想要对一个类的对象进行with  as 的操作
    
    ## 案例一:  with语句
    
    class A:
        def __enter__(self):                            # 开启上下文管理器对象时触发此方法
            print('before')
    
        def __exit__(self, exc_type, exc_val, exc_tb):  # 执行完上下文管理器对象f1时触发此方法
            print('after')
    
    
    with A() as a:
        print('123')
    # before
    # 123
    # after
      
        
        
    ## 案例二: with语句和init
    class A:
        def __init__(self):
            print('init')
    
        def __enter__(self):
            print('before')
    
        def __exit__(self, exc_type, exc_val, exc_tb):
            print('after')
    
    
    with A() as a:
        print('123')
        
    # init 先执行    
        
        
        
        
    ### 案例三:with和文件操作
    
    class Myfile:
        def __init__(self,path,mode='r',encoding = 'utf-8'):
            self.path = path
            self.mode = mode
            self.encoding = encoding
    
        def __enter__(self):
            print('进来了')
            self.f = open(self.path, mode=self.mode, encoding=self.encoding)
            return self.f
    
        def __exit__(self, exc_type, exc_val, exc_tb):
            print('关闭了')
            self.f.close()
    
    
    with Myfile('file',mode='a') as f:
        f.write('wahaha')
        print('写完; ')
        
    
    
        
        
    ### 案例四:  with和pickle
    import  pickle
    class MyPickledump:
        def __init__(self,path):
            self.path = path
    
        def __enter__(self):
            self.f = open(self.path, mode='ab')
            return self
    
        def dump(self,content):
            pickle.dump(content,self.f)
    
        def __exit__(self, exc_type, exc_val, exc_tb):
            self.f.close()
    
    class Mypickleload:
        def __init__(self,path):
            self.path = path
    
        def __enter__(self):
            self.f = open(self.path, mode='rb')
            return self
    
    
        def __exit__(self, exc_type, exc_val, exc_tb):
            self.f.close()
    
        def load(self):
             return pickle.load(self.f)
    
    
        def loaditer(self):
            while True:
                try:
                    yield  self.load()
                except EOFError:
                    break
    
    
    
    # with MyPickledump('file') as f:
    #      f.dump({1,2,3,4})
    
    with Mypickleload('file') as f:
        for item in f.loaditer():
            print(item)
            
            
    
            
    ### 案例五: with和pickle和iter
    
    
    import  pickle
    class MyPickledump:
        def __init__(self,path):
            self.path = path
    
        def __enter__(self):
            self.f = open(self.path, mode='ab')
            return self
    
        def dump(self,content):
            pickle.dump(content,self.f)
    
        def __exit__(self, exc_type, exc_val, exc_tb):
            self.f.close()
    
    class Mypickleload:
        def __init__(self,path):
            self.path = path
    
        def __enter__(self):
            self.f = open(self.path, mode='rb')
            return self
    
    
        def __exit__(self, exc_type, exc_val, exc_tb):
            self.f.close()
    
        def __iter__(self):
            while True:
                try:
                    yield  pickle.load(self.f)
                except EOFError:
                    break
    
    
    
    # with MyPickledump('file') as f:
    #      f.dump({1,2,3,4})
    
    with Mypickleload('file') as f:
        for item in f:
            print(item)
    
    
    
  • 相关阅读:
    moss文档浏览次数统计
    C#中方法参数的四种类型
    [记录] JavaScript 中的深浅拷贝(克隆)
    [记录] JavaScript 中的事件分类
    [记录] JavaScript 中的数组操作
    [记录] JavaScript 中的事件(Event对象、事件源对象、事件流、事件绑定)
    [记录] JavaScript 中的try..catch 详细的错误信息
    [题目] JavaScript 练习题目(一) [020]
    [记录] JavaScript 中的this和call()、apply()方法
    [记录] JavaScript 中的对象操作和包装类
  • 原文地址:https://www.cnblogs.com/dengz/p/11178826.html
Copyright © 2011-2022 走看看