[TOC]

一、反射

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

反射的四个函数

1、hasattr(object,name)

判断对象/类/模块中是否有某种属性或者方法,注意第二个参数是字符串

该函数返回的是,存在返回True,不存在返回False。

2、getattr(object, name, default=None)

获取对象/类/模块中某种属性或者方法的地址,第三个参数是如果没有获取到则返回自己设置的默认值,注意不能使用关键字参数的形式,注意第二个参数是字符串

该函数返回的是属性的值或者方法的内存地址

3、setattr(x, y, v)

设置对象/类/模块中某种属性或者方法的地址,注意第二个参数是字符串

,第三个参数是要设置的属性的的值或者方法的形式

该函数没有返回值

4、delattr(x, y)

删除对象/类/模块中某种属性或者方法的地址,注意第二个参数是字符串

使用方法例子

  1. class Goods:
  2. country = "china"

  3. def __init__(self,name,price):
  4. self.name = name
  5. self.price = price

  6. def rise(self):
  7. self.price = self.price + 1

  8. def discount(self):
  9. self.price = self.price - 2

  10. @classmethod
  11. def func(cls):
  12. print("类方法")

  13. @staticmethod
  14. def func2():
  15. print("静态方法")

  16. g = Goods("apple",5)

  17. #通过反射检测是否存在某个属性或者方法,存在返回True,不存在返回False
  18. print(hasattr(g,"name"))
  19. print(hasattr(g,"country")) #对象没有向上寻找父类的属性
  20. print(hasattr(g,"rise"))
  21. print(hasattr(g,"xxx")) #检测不到属性或者方法的存在返回False
  22. print(hasattr(g,"func")) #对象也可以检测类方法
  23. print(hasattr(Goods,"rise")) #也可以直接检测类的属性和方法
  24. print(hasattr(Goods,"country"))
  25. print(hasattr(Goods,"func2")) #类也可以检测,类也可以作为一个对象


  26. #获取类\对象\模块的属性或者方法内存地址
  27. print(getattr(g,"name"))
  28. print(getattr(g,"country")) #获取属性返回的是属性的值
  29. print(getattr(g,"rise")) #获取方法返回的是方法的内存地址
  30. # print(getattr(g,"xxx")) #获取不存在的属性或者方法,如果没有第三个参数会报错,有第三个参数则返回设置的第三个参数
  31. print(getattr(g,"xxx",None))
  32. print(getattr(Goods,"func"))
  33. print(getattr(Goods,"rise"))
  34. print(getattr(Goods,"country"))
  35. print(getattr(Goods,"func"))
  36. print(getattr(Goods,"func2"))


  37. #设置对象/类/模块的属性或者方法
  38. print(g.__dict__)
  39. # print(Goods.__dict__)
  40. setattr(g,"name","banana")
  41. setattr(g,"country","CHINA") #注意这里并非修改的是类的属性,而是在对象中增加了country属性
  42. setattr(g,"rise",lambda self:self.price + 10) #设置已经存在的方法需要自己再次传值
  43. setattr(g,"xxx",lambda self:self.price + 5) #
  44. setattr(Goods,"country","CA")
  45. print(g.__dict__)
  46. print(g.rise(g)) #输出结果15 ,这里修改的是根据修改后的方法得出的值,而非原有的值
  47. print(g.xxx(g)) #输出结果10
  48. print(g.price) #输出结果5,原有的值在setattr后不变


  49. # 删除对象/类中属性或者方法
  50. print(g.__dict__)
  51. print(Goods.__dict__)
  52. delattr(g,"name")
  53. delattr(Goods,"discount") #对象只有数据属性,但是可以访问类的函数属性,这里删除的是类的函数属性
  54. delattr(g,"aaa") #如果删除的不存在则报错
  55. print(g.__dict__)
  56. print(Goods.__dict__)

  

分析:类也是对象,也可以作为第一个参数,第二个参数一定是字符串。

导入当前模块成员的反射

  1. # 导入当前模块的成员的反射

  2. import sys

  3. val = "v1"

  4. def func1():
  5. print("s1")

  6. def func2():
  7. print("s2")


  8. this_modules = sys.modules[__name__] #sys.modules查看所有被导入的模块,以字典的形式存储,
  9. # __name__是当前模块的名字,如果当前文件是执行文件则__name__ = "__mian__",如果不是则是文件名

  10. print(hasattr(this_modules,"val")) #这里本文件是一个模块,也可以作为一个对象进行反射
  11. print(hasattr(this_modules,"func1"))
  12. print(getattr(this_modules,"val"))
  13. print(getattr(this_modules,"func1"))
  14. print(getattr(this_modules,"func2"))

  

输出结果

  1. True
  2. True
  3. v1
  4. <function func1 at 0x021B5DB0>
  5. <function func2 at 0x021B5D68>

  

导入其他文件模块的反射

例子

被导入文件模块

文件名:my_module.py

文件内容

  1. val = "hello"

  2. def func1():
  3. print("from func1")

  4. class A:
  5. c_val = "world"

  6. def __init__(self,name,age):
  7. self.name = name
  8. self.name = age

  9. def func2(self):
  10. print("from class A func2")

  

执行文件index.py,与被导入文件模块放在同一文件夹下

  1. import my_module

  2. print(hasattr(my_module,"val"))
  3. print(hasattr(my_module,"func1"))
  4. print(hasattr(my_module.A,"c_val"))
  5. print(hasattr(my_module.A,"func2"))


  6. a = my_module.A("nick",18)

  7. print(getattr(my_module,"val"))
  8. print(getattr(my_module,"func1"))
  9. print(getattr(my_module.A,"c_val")) #导入模块中的类的反射
  10. print(getattr(my_module.A,"func2"))
  11. print(getattr(a,"name")) #导入模块中类的对象的反射
  12. print(getattr(a,"c_val"))
  13. print(getattr(a,"func2"))

  14. res = getattr(my_module,"A") #反射一个类
  15. print(res)
  16. res() # 这里就是实例化一个类,产生了一个对象

  

分析:导入其他模块文件也可以用反射,也可以反射一个类

导入内置模块也可以用反射

例子

  1.  
  1. import time

  2. print(hasattr(time,"asctime"))
  3. print(hasattr(time,"localtime"))

  4. res = getattr(time,"asctime")
  5. print(res())

  6. res2 = getattr(time,"strftime") #获取方法函数内存地址
  7. print(res2("%Y--%m--%d %H:%M:%S")) #有参数可以在调用执行时时传入参数

  

输出结果

  1. True
  2. True
  3. Thu Jul 19 11:48:19 2018
  4. 2018--07--19 11:48:19

  

反射应用小例子

  1. class Ftpserver:

  2. def __init__(self):
  3. pass

  4. def get(self):
  5. print("下载中。。。。。")

  6. def put(self):
  7. print("上传中。。。。。")

  8. def run(self):

  9. cmds = input("请输入命令:")
  10. cmd = cmds.split()[0]
  11. if hasattr(self,cmd):
  12. func = getattr(self,cmd)
  13. func()

  14. f = Ftpserver()
  15. f.run()

  

二、内置函数

1、isinstance(obj,cls)和issubclass(sub,super)

isinstance(obj,cls)检查是否obj是否是类 cls 的对象

  1. class Foo:
  2. pass

  3. obj = Foo()
  4. print(isinstance(obj,Foo)) #输出结果 True

  

issubclass(sub, super)检查sub类是否是 super 类的派生类

  1. class Foo(object):
  2. pass

  3. class Son(Foo):
  4. pass

  5. print(issubclass(Son,Foo)) #输出结果True
  6. print(issubclass(Foo,object)) #输出结果True

  

2、__setattr__,__delattr__,__getattr__

__setattr___添加/修改属性会触发它的执行

__delattr___删除属性的时候会触发

__getattr__只有在使用点调用属性且属性不存在的时候才会触发,调用存在的属性或者方法时不触发

例子1  __setattr__

  1.  
  1. class Foo():

  2. def __init__(self,name):
  3. self.name = name

  4. def func(self):
  5. print("func------")

  6. def __setattr__(self, key, value):
  7. print("执行__setattr__")
  8. # self.key=value #进入无限递归
  9. # self.__dict__[key]=value #应该使用它

  10. p = Foo("jack") #实例化的过程中会增加属性,所以执行__setattr__ :执行__setattr__
  11. print(p.__dict__) #而__setattr__目前只是执行了打印工作,所以这里并没有为name赋值,也就是没有name属性
  12. # 去掉 # self.__dict__[key]=value语句的注释即可为name属性赋值

  

输出结果

  1. 执行__setattr__
  2. {}

  

例子2 __getattr__

  1. class Foo():
  2. def __init__(self,name):
  3. self.name = name

  4. def func(self):
  5. print("func------")

  6. def __getattr__(self, item):
  7. print("执行__getattr__",item)

  8. p = Foo("jack") #实例化的过程中会增加属性,
  9. print(p.__dict__)
  10. print(p.name)
  11. print(p.age) #调用不存在的属性会执行__getattr__

  

输出结果

  1. {'name': 'jack'}
  2. jack
  3. 执行__getattr__ age
  4. None

  

例子3 __setattr__

  1. class Foo():

  2. def __init__(self,name):
  3. self.name = name

  4. def func(self):
  5. print("func------")


  6. def __delattr__(self, item):
  7. print("执行__delattr__")
  8. # del self.item #无限递归
  9. self.__dict__.pop(item)

  10. def __setattr__(self, key, value):
  11. print("执行__setattr__")
  12. # self.key=value #进入无限递归
  13. self.__dict__[key]=value #应该使用它

  14. p = Foo("jack") #实例化的过程中会增加属性,执行__setattr__,执行self.__dict__[key]=value语句,为name属性赋值
  15. print(p.name) #
  16. p.age = 18 #执行__setattr__方法,对象新增加属性
  17. print(p.__dict__) #输出结果{'name': 'jack', 'age': 18}
  18. del p.age #调用__delattr__方法,删除对象的age属性
  19. print(p.__dict__) #输出结果{'name': 'jack'},此时已经不包含age属性

  

输出结果

  1. 执行__setattr__
  2. jack
  3. 执行__setattr__
  4. {'name': 'jack', 'age': 18}
  5. 执行__delattr__
  6. {'name': 'jack'}

  

3、二次加工标准类型

​ 包装:python为大家提供了标准数据类型,以及丰富的内置方法,其实在很多场景下我们都需要基于标准数据类型来定制我们自己的数据类型,新增/改写方法,这就用到了我们刚学的继承/派生知识(其他的标准类型均可以通过下面的方式进行二次加工),简单来说就是对以前的类的各种内置方法进行改写,继承原有功能,然后增加功能。

例子

  1.  
  1. class LIST(list): #继承list所有的属性

  2. def append(self, object):
  3. if type(object) is not str: #这里对append方法进行二次处理,增加了新的功能
  4. print("%s 必须是字符串"%object)
  5. super().append(object)
  6. #list.append(self,object) 与上面一句作用相同,都是调用父类list的append方法

  7. @property #使用property使得mid方法更像对象的数据属性,可以直接获得返回值
  8. def mid(self): #获得列表中间的元素的值
  9. index = len(self)
  10. mid_index = index//2 #这里是取除以2的整数商部分,向下取整
  11. return self[mid_index]


  12. la = LIST("hellonick") #调用list的方法将字符串转为列表
  13. print(la,type(la)) #输出结果 ['h', 'e', 'l', 'l', 'o', 'n', 'i', 'c', 'k'] <class '__main__.LIST'>
  14. # 这里的la是类LIST的一个对象
  15. print(la.mid) #输出结果 o

  16. lb = list("hellonick")
  17. print(lb,type(lb)) #输出结果['h', 'e', 'l', 'l', 'o', 'n', 'i', 'c', 'k'] <class 'list'>,
  18. # 这里直接显示lb是列表类型,也是list的一个对象

  

输出结果

  1. ['h', 'e', 'l', 'l', 'o', 'n', 'i', 'c', 'k'] <class '__main__.LIST'>
  2. o
  3. ['h', 'e', 'l', 'l', 'o', 'n', 'i', 'c', 'k'] <class 'list'>
  4. 111 必须是字符串
  5. ['h', 'e', 'l', 'l', 'o', 'n', 'i', 'c', 'k', 111, 'aada']

  

例子2

  1. class LIST(list): #继承list所有的属性

  2. def __init__(self,item,tag=False):
  3. super().__init__(item)
  4. self.tag = tag

  5. def append(self, object):
  6. if type(object) is not str: #这里对append方法进行二次处理,增加了新的功能
  7. print("%s 必须是字符串"%object)
  8. super().append(object)
  9. #list.append(self,object) 与上面一句作用相同,都是调用父类list的append方法

  10. def clear(self):
  11. if not self.tag: #改写原列表方法clear,增加权限限制
  12. raise PermissionError
  13. super().clear()

  14. la = LIST("hellonick") #调用list的方法将字符串转为列表,输出结果 ['h', 'e', 'l', 'l', 'o', 'n', 'i', 'c', 'k']
  15. print(la)
  16. print(la.tag)
  17. # la.clear() #会报错


  18. lb = LIST([2,3,4,5,6,7])
  19. print(lb)
  20. # lb.clear() #会报错
  21. print(lb.tag)

  22. lc = LIST([5,3,4,5,7],)
  23. lc.tag = True
  24. lc.clear()
  25. print(lc) #输出结果为 []

  

输出结果

  1. ['h', 'e', 'l', 'l', 'o', 'n', 'i', 'c', 'k']
  2. False
  3. [2, 3, 4, 5, 6, 7]
  4. False
  5. []

  

授权:授权是包装的一个特性, 包装一个类型通常是对已存在的类型的一些定制,这种做法可以新建,修改或删除原有产品的功能。其它的则保持原样。授权的过程,即是所有更新的功能都是由新类的某部分来处理,但已存在的功能就授权给对象的默认属性。

简单来说就是更新的功能用自己定义类的方法,没有更新的功能用父类的方法。自己定义了一个方法,与父类同名,那么就用自己的定义的方法,其他的方法继续用父类的方法。授权也是一种包装,但授权的实现不是通过继承,而是使用__getattr__方法。

实现授权的关键点就是覆盖__getattr__方法

例子

  1. class FileHandle:

  2. def __init__(self,file,mode,encoding):
  3. self.file = open(file,mode,encoding=encoding)

  4. def __getattr__(self, item):
  5. return getattr(self.file,item)
  6. #这里调用属性的时候自动执行__getattr__方法,执行之后用getattr方法返回原属于open的方法

  7. def read(self):
  8. print("读取文件中。。。")
  9. #如果有同名的方法那么就用自己定义的方法,不用__getattr__获得的方法,
  10. # 因为__getattr__只有在使用点调用属性且属性不存在的时候才会触发
  11.  

  12. f = FileHandle("test.txt","w+","utf-8")
  13. f.write("aaaaa")
  14. f.seek(0)
  15. data = f.read() #这里是执行了FileHandle的自己定义的read()方法,所以输出结果是None
  16. print(data)

  

输出结果

  1. 读取文件中。。。
  2. None

  

例子2

  1. import time

  2. class FileHandle:

  3. def __init__(self,file,mode,encoding):
  4. self.file = open(file,mode,encoding=encoding)

  5. def __getattr__(self, item):
  6. return getattr(self.file,item)
  7. #这里调用属性的时候自动执行__getattr__方法,执行之后用getattr方法返回原属于open的方法

  8. def write(self,msg):
  9. self.file.write("%s %s"%(time.strftime("%Y-%m-%d %X"),msg)) #调用self.file中open的方法
  10. #为每次写入增加时间

  11. f = FileHandle("test.txt","w+","utf-8")
  12. f.write("aaaaa")
  13. f.seek(0)
  14. data = f.read()
  15. print(data)

  

输出结果

  1.  
  1. 2018-08-03 16:12:52 aaaaa

  

Python之路(第二十五篇) 面向对象初级:反射、内置方法的更多相关文章

  1. Python之路(第二十九篇) 面向对象进阶:内置方法补充、异常处理

    一.__new__方法 __init__()是初始化方法,__new__()方法是构造方法,创建一个新的对象 实例化对象的时候,调用__init__()初始化之前,先调用了__new__()方法 __ ...

  2. Python之路(第二十六篇) 面向对象进阶:内置方法

    一.__getattribute__ object.__getattribute__(self, name) 无条件被调用,通过实例访问属性.如果class中定义了__getattr__(),则__g ...

  3. Python之路(第二十四篇) 面向对象初级:多态、封装

    一.多态 多态 多态:一类事物有多种形态,同一种事物的多种形态,动物分为鸡类,猪类.狗类 例子 import abc class H2o(metaclass=abc.ABCMeta): ​ def _ ...

  4. Python之路(第二十二篇) 面向对象初级:概念、类属性

    一.面向对象概念 1. "面向对象(OOP)"是什么? 简单点说,“面向对象”是一种编程范式,而编程范式是按照不同的编程特点总结出来的编程方式.俗话说,条条大路通罗马,也就说我们使 ...

  5. Python之路(第二十八篇) 面向对象进阶:类的装饰器、元类

    一.类的装饰器 类作为一个对象,也可以被装饰. 例子 def wrap(obj): print("装饰器-----") obj.x = 1 obj.y = 3 obj.z = 5 ...

  6. Python之路(第十五篇)sys模块、json模块、pickle模块、shelve模块

    一.sys模块 1.sys.argv 命令行参数List,第一个元素是程序本身路径 2.sys.exit(n) 退出程序,正常退出时exit(0) 3.sys.version . sys.maxint ...

  7. Python之路(第二十七篇) 面向对象进阶:内置方法、描述符

    一.__call__ 对象后面加括号,触发执行类下面的__call__方法. 创建对象时,对象 = 类名() :而对于 __call__ 方法的执行是由对象后加括号触发的,即:对象() 或者 类()( ...

  8. Python之路【第五篇】:面向对象及相关

    Python之路[第五篇]:面向对象及相关   面向对象基础 基础内容介绍详见一下两篇博文: 面向对象初级篇 面向对象进阶篇 其他相关 一.isinstance(obj, cls) 检查是否obj是否 ...

  9. Python之路【第五篇】:面向对象和相关

    Python之路[第五篇]:面向对象及相关   面向对象基础 基础内容介绍详见一下两篇博文: 面向对象初级篇 面向对象进阶篇 其他相关 一.isinstance(obj, cls) 检查是否obj是否 ...

随机推荐

  1. centos6安装自带php

    一.Centos 6安装自带PHP CentOS 默认仓库中包含了php套件,我们可以直接使用yum安装.下面是最小化安装,我们使用php-fpm来解析php. yum install -y php- ...

  2. CSS同时选择器

    [CSS同时选择器] 同一个div拥有多个class时,我们可以作多个class作为组合来选择对象.方法就是将多个.className直接连接在一起(中间不能有空格). <p class=&qu ...

  3. asp.net MVC 导出excle(转)

    转载网址: http://www.cnblogs.com/imr3/articles/2856109.html 还是放到自己这边比较保险. ExportExcel Code public FileRe ...

  4. ICMP隧道 ptunnle

    通过ICMP echo(ping request)和reply(ping reply)实现隧道 适用于防火墙只允许ping出站流量的环境 支持多并发连接,性能优 支持身份验证 使用时需要root用户 ...

  5. 六:python 对象类型详解二:字符串(下)

    一:字符串方法: 方法就是与特定对象相关联在一起的函数.从技术的角度来讲,它们是附属于对象的属性,而这些属性不过是些可调用的函数罢了.Python 首先读取对象方法,然后调用它,传递参数.如果一个方法 ...

  6. ubuntu下java的安装即使用

    1.首先在官方网站(点击可以下载)下载最新的JDK,要选用self extracting installer 2.在/usr/下新建java目录,把下载的文件放到这个目录下 sudo mkdir /u ...

  7. 基础DP(初级版)

    本文主要内容为基础DP,内容来源为<算法导论>,总结不易,转载请注明出处. 后续会更新出kuanbin关于基础DP的题目...... 动态规划: 动态规划用于子问题重叠的情况,即不同的子问 ...

  8. TZOJ 3711 浪漫自习(最大流)

    描述 如今的校园谈恋爱已是习以为常,这不,去上自习也要成双成对的.现在假设某班里有N对情侣从同一寝室楼出发,到达同一个教室上自习.途中,她们可能会经过长廊.静溪等一系列的景点观光游览.但情侣们不希望在 ...

  9. [剑指Offer]判断一棵树为平衡二叉树(递归)

    题目链接 https://www.nowcoder.com/practice/8b3b95850edb4115918ecebdf1b4d222?tpId=0&tqId=0&rp=2&a ...

  10. cipher的各个模式

    block cipher 工作模式(引自百度)Electronic Codebook Mode 最经典的模式,把明文按64比特为单位分为block, 对所有block使用同样的密钥来加密,最后把输出的 ...